Restore proper maxrows calculation
[llpp.git] / main.ml
blob1d2bbdf41f98d15c98ac31ac40a9d8f1ca5d749e
1 type under =
2 | Unone
3 | Ulinkuri of string
4 | Ulinkgoto of (int * int)
5 | Utext of facename
6 and facename = string;;
8 let dolog fmt = Printf.kprintf prerr_endline fmt;;
9 let now = Unix.gettimeofday;;
11 exception Quit;;
13 type params = (angle * proportional * trimparams
14 * texcount * sliceheight * memsize
15 * colorspace * wmclasshack * fontpath)
16 and pageno = int
17 and width = int
18 and height = int
19 and leftx = int
20 and opaque = string
21 and recttype = int
22 and pixmapsize = int
23 and angle = int
24 and proportional = bool
25 and trimmargins = bool
26 and interpagespace = int
27 and texcount = int
28 and sliceheight = int
29 and gen = int
30 and top = float
31 and fontpath = string
32 and memsize = int
33 and aalevel = int
34 and wmclasshack = bool
35 and irect = (int * int * int * int)
36 and trimparams = (trimmargins * irect)
37 and colorspace = | Rgb | Bgr | Gray
40 type platform = | Punknown | Plinux | Pwindows | Posx | Psun
41 | Pfreebsd | Pdragonflybsd | Popenbsd | Pmingw | Pcygwin;;
43 external init : Unix.file_descr -> params -> unit = "ml_init";;
44 external seltext : string -> (int * int * int * int) -> unit = "ml_seltext";;
45 external copysel : string -> unit = "ml_copysel";;
46 external getpdimrect : int -> float array = "ml_getpdimrect";;
47 external whatsunder : string -> int -> int -> under = "ml_whatsunder";;
48 external zoomforh : int -> int -> int -> float = "ml_zoom_for_height";;
49 external drawstr : int -> int -> int -> string -> float = "ml_draw_string";;
50 external measurestr : int -> string -> float = "ml_measure_string";;
51 external getmaxw : unit -> float = "ml_getmaxw";;
52 external postprocess : opaque -> bool -> int -> int -> unit = "ml_postprocess";;
53 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
54 external platform : unit -> platform = "ml_platform";;
55 external setaalevel : int -> unit = "ml_setaalevel";;
56 external realloctexts : int -> bool = "ml_realloctexts";;
58 let platform_to_string = function
59 | Punknown -> "unknown"
60 | Plinux -> "Linux"
61 | Pwindows -> "Windows"
62 | Posx -> "OSX"
63 | Psun -> "Sun"
64 | Pfreebsd -> "FreeBSD"
65 | Pdragonflybsd -> "DragonflyBSD"
66 | Popenbsd -> "OpenBSD"
67 | Pcygwin -> "Cygwin"
68 | Pmingw -> "MingW"
71 let platform = platform ();;
73 let is_windows =
74 match platform with
75 | Pwindows | Pmingw -> true
76 | _ -> false
79 type x = int
80 and y = int
81 and tilex = int
82 and tiley = int
83 and tileparams = (x * y * width * height * tilex * tiley)
86 external drawtile : tileparams -> opaque -> unit = "ml_drawtile";;
88 type mpos = int * int
89 and mstate =
90 | Msel of (mpos * mpos)
91 | Mpan of mpos
92 | Mscrolly | Mscrollx
93 | Mzoom of (int * int)
94 | Mzoomrect of (mpos * mpos)
95 | Mnone
98 type textentry = string * string * onhist option * onkey * ondone
99 and onkey = string -> int -> te
100 and ondone = string -> unit
101 and histcancel = unit -> unit
102 and onhist = ((histcmd -> string) * histcancel)
103 and histcmd = HCnext | HCprev | HCfirst | HClast
104 and te =
105 | TEstop
106 | TEdone of string
107 | TEcont of string
108 | TEswitch of textentry
111 type 'a circbuf =
112 { store : 'a array
113 ; mutable rc : int
114 ; mutable wc : int
115 ; mutable len : int
119 let bound v minv maxv =
120 max minv (min maxv v);
123 let cbnew n v =
124 { store = Array.create n v
125 ; rc = 0
126 ; wc = 0
127 ; len = 0
131 let drawstring size x y s =
132 Gl.enable `blend;
133 Gl.enable `texture_2d;
134 ignore (drawstr size x y s);
135 Gl.disable `blend;
136 Gl.disable `texture_2d;
139 let drawstring1 size x y s =
140 drawstr size x y s;
143 let drawstring2 size x y fmt =
144 Printf.kprintf (drawstring size (x+1) (y+size+1)) fmt
147 let cbcap b = Array.length b.store;;
149 let cbput b v =
150 let cap = cbcap b in
151 b.store.(b.wc) <- v;
152 b.wc <- (b.wc + 1) mod cap;
153 b.rc <- b.wc;
154 b.len <- min (b.len + 1) cap;
157 let cbempty b = b.len = 0;;
159 let cbgetg b circular dir =
160 if cbempty b
161 then b.store.(0)
162 else
163 let rc = b.rc + dir in
164 let rc =
165 if circular
166 then (
167 if rc = -1
168 then b.len-1
169 else (
170 if rc = b.len
171 then 0
172 else rc
175 else max 0 (min rc (b.len-1))
177 b.rc <- rc;
178 b.store.(rc);
181 let cbget b = cbgetg b false;;
182 let cbgetc b = cbgetg b true;;
184 type page =
185 { pageno : int
186 ; pagedimno : int
187 ; pagew : int
188 ; pageh : int
189 ; pagex : int
190 ; pagey : int
191 ; pagevw : int
192 ; pagevh : int
193 ; pagedispx : int
194 ; pagedispy : int
198 let debugl l =
199 dolog "l %d dim=%d {" l.pageno l.pagedimno;
200 dolog " WxH %dx%d" l.pagew l.pageh;
201 dolog " vWxH %dx%d" l.pagevw l.pagevh;
202 dolog " pagex,y %d,%d" l.pagex l.pagey;
203 dolog " dispx,y %d,%d" l.pagedispx l.pagedispy;
204 dolog "}";
207 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
208 dolog "rect {";
209 dolog " x0,y0=(% f, % f)" x0 y0;
210 dolog " x1,y1=(% f, % f)" x1 y1;
211 dolog " x2,y2=(% f, % f)" x2 y2;
212 dolog " x3,y3=(% f, % f)" x3 y3;
213 dolog "}";
216 type conf =
217 { mutable scrollbw : int
218 ; mutable scrollh : int
219 ; mutable icase : bool
220 ; mutable preload : bool
221 ; mutable pagebias : int
222 ; mutable verbose : bool
223 ; mutable debug : bool
224 ; mutable scrollstep : int
225 ; mutable maxhfit : bool
226 ; mutable crophack : bool
227 ; mutable autoscrollstep : int
228 ; mutable maxwait : float option
229 ; mutable hlinks : bool
230 ; mutable underinfo : bool
231 ; mutable interpagespace : interpagespace
232 ; mutable zoom : float
233 ; mutable presentation : bool
234 ; mutable angle : angle
235 ; mutable winw : int
236 ; mutable winh : int
237 ; mutable savebmarks : bool
238 ; mutable proportional : proportional
239 ; mutable trimmargins : trimmargins
240 ; mutable trimfuzz : irect
241 ; mutable memlimit : memsize
242 ; mutable texcount : texcount
243 ; mutable sliceheight : sliceheight
244 ; mutable thumbw : width
245 ; mutable jumpback : bool
246 ; mutable bgcolor : float * float * float
247 ; mutable bedefault : bool
248 ; mutable scrollbarinpm : bool
249 ; mutable tilew : int
250 ; mutable tileh : int
251 ; mutable mustoresize : memsize
252 ; mutable checkers : bool
253 ; mutable aalevel : int
254 ; mutable urilauncher : string
255 ; mutable colorspace : colorspace
256 ; mutable invert : bool
257 ; mutable colorscale : float
258 ; mutable redirectstderr : bool
259 ; mutable ghyllscroll : (int * int * int) option
263 type anchor = pageno * top;;
265 type outline = string * int * anchor;;
267 type rect = float * float * float * float * float * float * float * float;;
269 type tile = opaque * pixmapsize * elapsed
270 and elapsed = float;;
271 type pagemapkey = pageno * gen;;
272 type tilemapkey = pageno * gen * colorspace * angle * width * height * col * row
273 and row = int
274 and col = int;;
276 let emptyanchor = (0, 0.0);;
278 type infochange = | Memused | Docinfo | Pdim;;
280 class type uioh = object
281 method display : unit
282 method key : int -> uioh
283 method special : Glut.special_key_t -> uioh
284 method button :
285 Glut.button_t -> Glut.mouse_button_state_t -> int -> int -> uioh
286 method motion : int -> int -> uioh
287 method pmotion : int -> int -> uioh
288 method infochanged : infochange -> unit
289 method scrollpw : (int * float * float)
290 method scrollph : (int * float * float)
291 end;;
293 type mode =
294 | Birdseye of (conf * leftx * pageno * pageno * anchor)
295 | Textentry of (textentry * onleave)
296 | View
297 and onleave = leavetextentrystatus -> unit
298 and leavetextentrystatus = | Cancel | Confirm
299 and helpitem = string * int * action
300 and action =
301 | Noaction
302 | Action of (uioh -> uioh)
305 let isbirdseye = function Birdseye _ -> true | _ -> false;;
306 let istextentry = function Textentry _ -> true | _ -> false;;
308 type currently =
309 | Idle
310 | Loading of (page * gen)
311 | Tiling of (
312 page * opaque * colorspace * angle * gen * col * row * width * height
314 | Outlining of outline list
317 let nouioh : uioh = object (self)
318 method display = ()
319 method key _ = self
320 method special _ = self
321 method button _ _ _ _ = self
322 method motion _ _ = self
323 method pmotion _ _ = self
324 method infochanged _ = ()
325 method scrollpw = (0, nan, nan)
326 method scrollph = (0, nan, nan)
327 end;;
329 type state =
330 { mutable csock : Unix.file_descr
331 ; mutable ssock : Unix.file_descr
332 ; mutable errfd : Unix.file_descr option
333 ; mutable stderr : Unix.file_descr
334 ; mutable errmsgs : Buffer.t
335 ; mutable newerrmsgs : bool
336 ; mutable w : int
337 ; mutable x : int
338 ; mutable y : int
339 ; mutable scrollw : int
340 ; mutable hscrollh : int
341 ; mutable anchor : anchor
342 ; mutable maxy : int
343 ; mutable layout : page list
344 ; pagemap : (pagemapkey, opaque) Hashtbl.t
345 ; tilemap : (tilemapkey, tile) Hashtbl.t
346 ; tilelru : (tilemapkey * opaque * pixmapsize) Queue.t
347 ; mutable pdims : (pageno * width * height * leftx) list
348 ; mutable pagecount : int
349 ; mutable currently : currently
350 ; mutable mstate : mstate
351 ; mutable searchpattern : string
352 ; mutable rects : (pageno * recttype * rect) list
353 ; mutable rects1 : (pageno * recttype * rect) list
354 ; mutable text : string
355 ; mutable fullscreen : (width * height) option
356 ; mutable mode : mode
357 ; mutable uioh : uioh
358 ; mutable outlines : outline array
359 ; mutable bookmarks : outline list
360 ; mutable path : string
361 ; mutable password : string
362 ; mutable invalidated : int
363 ; mutable memused : memsize
364 ; mutable gen : gen
365 ; mutable throttle : (page list * int * float) option
366 ; mutable autoscroll : int option
367 ; mutable ghyll : int option -> unit
368 ; mutable help : helpitem array
369 ; mutable docinfo : (int * string) list
370 ; mutable deadline : float
371 ; mutable texid : GlTex.texture_id option
372 ; hists : hists
373 ; mutable prevzoom : float
374 ; mutable progress : float
376 and hists =
377 { pat : string circbuf
378 ; pag : string circbuf
379 ; nav : anchor circbuf
383 let defconf =
384 { scrollbw = 7
385 ; scrollh = 12
386 ; icase = true
387 ; preload = true
388 ; pagebias = 0
389 ; verbose = false
390 ; debug = false
391 ; scrollstep = 24
392 ; maxhfit = true
393 ; crophack = false
394 ; autoscrollstep = 2
395 ; maxwait = None
396 ; hlinks = false
397 ; underinfo = false
398 ; interpagespace = 2
399 ; zoom = 1.0
400 ; presentation = false
401 ; angle = 0
402 ; winw = 900
403 ; winh = 900
404 ; savebmarks = true
405 ; proportional = true
406 ; trimmargins = false
407 ; trimfuzz = (0,0,0,0)
408 ; memlimit = 32 lsl 20
409 ; texcount = 256
410 ; sliceheight = 24
411 ; thumbw = 76
412 ; jumpback = true
413 ; bgcolor = (0.5, 0.5, 0.5)
414 ; bedefault = false
415 ; scrollbarinpm = true
416 ; tilew = 2048
417 ; tileh = 2048
418 ; mustoresize = 128 lsl 20
419 ; checkers = true
420 ; aalevel = 8
421 ; urilauncher =
422 (match platform with
423 | Plinux | Pfreebsd | Pdragonflybsd | Popenbsd | Psun -> "xdg-open \"%s\""
424 | Posx -> "open \"%s\""
425 | Pwindows | Pcygwin | Pmingw -> "iexplore \"%s\""
426 | _ -> "")
427 ; colorspace = Rgb
428 ; invert = false
429 ; colorscale = 1.0
430 ; redirectstderr = false
431 ; ghyllscroll = None
435 let conf = { defconf with angle = defconf.angle };;
437 type fontstate =
438 { mutable fontsize : int
439 ; mutable wwidth : float
440 ; mutable maxrows : int
444 let fstate =
445 { fontsize = 14
446 ; wwidth = nan
447 ; maxrows = -1
451 let setfontsize n =
452 fstate.fontsize <- n;
453 fstate.wwidth <- measurestr fstate.fontsize "w";
454 fstate.maxrows <- (conf.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
457 let gotouri uri =
458 if String.length conf.urilauncher = 0
459 then print_endline uri
460 else
461 let re = Str.regexp "%s" in
462 let command = Str.global_replace re uri conf.urilauncher in
463 let optic =
464 try Some (Unix.open_process_in command)
465 with exn ->
466 Printf.eprintf
467 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
468 flush stderr;
469 None
471 match optic with
472 | Some ic -> close_in ic
473 | None -> ()
476 let version () =
477 Printf.sprintf "llpp version %s (%s/%dbit, ocaml %s)" Help.version
478 (platform_to_string platform) Sys.word_size Sys.ocaml_version
481 let makehelp () =
482 let strings = version () :: "" :: Help.keys in
483 Array.of_list (
484 let r = Str.regexp "\\(http://[^ ]+\\)" in
485 List.map (fun s ->
486 if (try Str.search_forward r s 0 with Not_found -> -1) >= 0
487 then
488 let uri = Str.matched_string s in
489 (s, 0, Action (fun u -> gotouri uri; u))
490 else s, 0, Noaction) strings
494 let noghyll _ = ();;
496 let state =
497 { csock = Unix.stdin
498 ; ssock = Unix.stdin
499 ; errfd = None
500 ; stderr = Unix.stderr
501 ; errmsgs = Buffer.create 0
502 ; newerrmsgs = false
503 ; x = 0
504 ; y = 0
505 ; w = 0
506 ; scrollw = 0
507 ; hscrollh = 0
508 ; anchor = emptyanchor
509 ; layout = []
510 ; maxy = max_int
511 ; tilelru = Queue.create ()
512 ; pagemap = Hashtbl.create 10
513 ; tilemap = Hashtbl.create 10
514 ; pdims = []
515 ; pagecount = 0
516 ; currently = Idle
517 ; mstate = Mnone
518 ; rects = []
519 ; rects1 = []
520 ; text = ""
521 ; mode = View
522 ; fullscreen = None
523 ; searchpattern = ""
524 ; outlines = [||]
525 ; bookmarks = []
526 ; path = ""
527 ; password = ""
528 ; invalidated = 0
529 ; hists =
530 { nav = cbnew 10 (0, 0.0)
531 ; pat = cbnew 1 ""
532 ; pag = cbnew 1 ""
534 ; memused = 0
535 ; gen = 0
536 ; throttle = None
537 ; autoscroll = None
538 ; ghyll = noghyll
539 ; help = makehelp ()
540 ; docinfo = []
541 ; deadline = nan
542 ; texid = None
543 ; prevzoom = 1.0
544 ; progress = -1.0
545 ; uioh = nouioh
549 let vlog fmt =
550 if conf.verbose
551 then
552 Printf.kprintf prerr_endline fmt
553 else
554 Printf.kprintf ignore fmt
557 let redirectstderr () =
558 if conf.redirectstderr
559 then
560 let rfd, wfd = Unix.pipe () in
561 state.stderr <- Unix.dup Unix.stderr;
562 state.errfd <- Some rfd;
563 Unix.dup2 wfd Unix.stderr;
564 else (
565 state.newerrmsgs <- false;
566 begin match state.errfd with
567 | Some fd ->
568 Unix.close fd;
569 Unix.dup2 state.stderr Unix.stderr;
570 state.errfd <- None;
571 | None -> ()
572 end;
573 prerr_string (Buffer.contents state.errmsgs);
574 flush stderr;
575 Buffer.clear state.errmsgs;
579 module G =
580 struct
581 let postRedisplay who =
582 if conf.verbose
583 then prerr_endline ("redisplay for " ^ who);
584 Glut.postRedisplay ();
586 end;;
588 let addchar s c =
589 let b = Buffer.create (String.length s + 1) in
590 Buffer.add_string b s;
591 Buffer.add_char b c;
592 Buffer.contents b;
595 let colorspace_of_string s =
596 match String.lowercase s with
597 | "rgb" -> Rgb
598 | "bgr" -> Bgr
599 | "gray" -> Gray
600 | _ -> failwith "invalid colorspace"
603 let int_of_colorspace = function
604 | Rgb -> 0
605 | Bgr -> 1
606 | Gray -> 2
609 let colorspace_of_int = function
610 | 0 -> Rgb
611 | 1 -> Bgr
612 | 2 -> Gray
613 | n -> failwith ("invalid colorspace index " ^ string_of_int n)
616 let colorspace_to_string = function
617 | Rgb -> "rgb"
618 | Bgr -> "bgr"
619 | Gray -> "gray"
622 let intentry_with_suffix text key =
623 let c = Char.unsafe_chr key in
624 match Char.lowercase c with
625 | '0' .. '9' ->
626 let text = addchar text c in
627 TEcont text
629 | 'k' | 'm' | 'g' ->
630 let text = addchar text c in
631 TEcont text
633 | _ ->
634 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
635 TEcont text
638 let writecmd fd s =
639 let len = String.length s in
640 let n = 4 + len in
641 let b = Buffer.create n in
642 Buffer.add_char b (Char.chr ((len lsr 24) land 0xff));
643 Buffer.add_char b (Char.chr ((len lsr 16) land 0xff));
644 Buffer.add_char b (Char.chr ((len lsr 8) land 0xff));
645 Buffer.add_char b (Char.chr ((len lsr 0) land 0xff));
646 Buffer.add_string b s;
647 let s' = Buffer.contents b in
648 let n' = Unix.write fd s' 0 n in
649 if n' != n then failwith "write failed";
652 let readcmd fd =
653 let s = "xxxx" in
654 let n = Unix.read fd s 0 4 in
655 if n != 4 then failwith "incomplete read(len)";
656 let len = 0
657 lor (Char.code s.[0] lsl 24)
658 lor (Char.code s.[1] lsl 16)
659 lor (Char.code s.[2] lsl 8)
660 lor (Char.code s.[3] lsl 0)
662 let s = String.create len in
663 let n = Unix.read fd s 0 len in
664 if n != len then failwith "incomplete read(data)";
668 let makecmd s l =
669 let b = Buffer.create 10 in
670 Buffer.add_string b s;
671 let rec combine = function
672 | [] -> b
673 | x :: xs ->
674 Buffer.add_char b ' ';
675 let s =
676 match x with
677 | `b b -> if b then "1" else "0"
678 | `s s -> s
679 | `i i -> string_of_int i
680 | `f f -> string_of_float f
681 | `I f -> string_of_int (truncate f)
683 Buffer.add_string b s;
684 combine xs;
686 combine l;
689 let wcmd s l =
690 let cmd = Buffer.contents (makecmd s l) in
691 writecmd state.csock cmd;
694 let calcips h =
695 if conf.presentation
696 then
697 let d = conf.winh - h in
698 max 0 ((d + 1) / 2)
699 else
700 conf.interpagespace
703 let calcheight () =
704 let rec f pn ph pi fh l =
705 match l with
706 | (n, _, h, _) :: rest ->
707 let ips = calcips h in
708 let fh =
709 if conf.presentation
710 then fh+ips
711 else (
712 if isbirdseye state.mode && pn = 0
713 then fh + ips
714 else fh
717 let fh = fh + ((n - pn) * (ph + pi)) in
718 f n h ips fh rest;
720 | [] ->
721 let inc =
722 if conf.presentation || (isbirdseye state.mode && pn = 0)
723 then 0
724 else -pi
726 let fh = fh + ((state.pagecount - pn) * (ph + pi)) + inc in
727 max 0 fh
729 let fh = f 0 0 0 0 state.pdims in
733 let getpageyh pageno =
734 let rec f pn ph pi y l =
735 match l with
736 | (n, _, h, _) :: rest ->
737 let ips = calcips h in
738 if n >= pageno
739 then
740 let h = if n = pageno then h else ph in
741 if conf.presentation && n = pageno
742 then
743 y + (pageno - pn) * (ph + pi) + pi, h
744 else
745 y + (pageno - pn) * (ph + pi), h
746 else
747 let y = y + (if conf.presentation then pi else 0) in
748 let y = y + (n - pn) * (ph + pi) in
749 f n h ips y rest
751 | [] ->
752 y + (pageno - pn) * (ph + pi), ph
754 f 0 0 0 0 state.pdims
757 let getpagedim pageno =
758 let rec f ppdim l =
759 match l with
760 | (n, _, _, _) as pdim :: rest ->
761 if n >= pageno
762 then (if n = pageno then pdim else ppdim)
763 else f pdim rest
765 | [] -> ppdim
767 f (-1, -1, -1, -1) state.pdims
770 let getpagey pageno = fst (getpageyh pageno);;
772 let layout y sh =
773 let sh = sh - state.hscrollh in
774 let rec f ~pageno ~pdimno ~prev ~py ~dy ~pdims ~accu =
775 let ((w, h, ips, xoff) as curr), rest, pdimno, yinc =
776 match pdims with
777 | (pageno', w, h, xoff) :: rest when pageno' = pageno ->
778 let ips = calcips h in
779 let yinc =
780 if conf.presentation || (isbirdseye state.mode && pageno = 0)
781 then ips
782 else 0
784 (w, h, ips, xoff), rest, pdimno + 1, yinc
785 | _ ->
786 prev, pdims, pdimno, 0
788 let dy = dy + yinc in
789 let py = py + yinc in
790 if pageno = state.pagecount || dy >= sh
791 then
792 accu
793 else
794 let vy = y + dy in
795 if py + h <= vy - yinc
796 then
797 let py = py + h + ips in
798 let dy = max 0 (py - y) in
799 f ~pageno:(pageno+1)
800 ~pdimno
801 ~prev:curr
804 ~pdims:rest
805 ~accu
806 else
807 let pagey = vy - py in
808 let pagevh = h - pagey in
809 let pagevh = min (sh - dy) pagevh in
810 let off = if yinc > 0 then py - vy else 0 in
811 let py = py + h + ips in
812 let pagex, dx =
813 let xoff = xoff +
814 if state.w < conf.winw - state.scrollw
815 then (conf.winw - state.scrollw - state.w) / 2
816 else 0
818 let dispx = xoff + state.x in
819 if dispx < 0
820 then (-dispx, 0)
821 else (0, dispx)
823 let pagevw =
824 let lw = w - pagex in
825 min lw (conf.winw - state.scrollw)
827 let e =
828 { pageno = pageno
829 ; pagedimno = pdimno
830 ; pagew = w
831 ; pageh = h
832 ; pagex = pagex
833 ; pagey = pagey + off
834 ; pagevw = pagevw
835 ; pagevh = pagevh - off
836 ; pagedispx = dx
837 ; pagedispy = dy + off
840 let accu = e :: accu in
841 f ~pageno:(pageno+1)
842 ~pdimno
843 ~prev:curr
845 ~dy:(dy+pagevh+ips)
846 ~pdims:rest
847 ~accu
849 if state.invalidated = 0
850 then (
851 let accu =
853 ~pageno:0
854 ~pdimno:~-1
855 ~prev:(0,0,0,0)
856 ~py:0
857 ~dy:0
858 ~pdims:state.pdims
859 ~accu:[]
861 List.rev accu
863 else
867 let clamp incr =
868 let y = state.y + incr in
869 let y = max 0 y in
870 let y = min y (state.maxy - (if conf.maxhfit then conf.winh else 0)) in
874 let getopaque pageno =
875 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
876 with Not_found -> None
879 let putopaque pageno opaque =
880 Hashtbl.replace state.pagemap (pageno, state.gen) opaque
883 let itertiles l f =
884 let tilex = l.pagex mod conf.tilew in
885 let tiley = l.pagey mod conf.tileh in
887 let col = l.pagex / conf.tilew in
888 let row = l.pagey / conf.tileh in
890 let vw =
891 let a = l.pagew - l.pagex in
892 let b = conf.winw - state.scrollw in
893 min a b
894 and vh = l.pagevh in
896 let rec rowloop row y0 dispy h =
897 if h = 0
898 then ()
899 else (
900 let dh = conf.tileh - y0 in
901 let dh = min h dh in
902 let rec colloop col x0 dispx w =
903 if w = 0
904 then ()
905 else (
906 let dw = conf.tilew - x0 in
907 let dw = min w dw in
909 f col row dispx dispy x0 y0 dw dh;
910 colloop (col+1) 0 (dispx+dw) (w-dw)
913 colloop col tilex l.pagedispx vw;
914 rowloop (row+1) 0 (dispy+dh) (h-dh)
917 if vw > 0 && vh > 0
918 then rowloop row tiley l.pagedispy vh;
921 let gettileopaque l col row =
922 let key =
923 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
925 try Some (Hashtbl.find state.tilemap key)
926 with Not_found -> None
929 let puttileopaque l col row gen colorspace angle opaque size elapsed =
930 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
931 Hashtbl.add state.tilemap key (opaque, size, elapsed)
934 let drawtiles l color =
935 GlDraw.color color;
936 let f col row x y tilex tiley w h =
937 match gettileopaque l col row with
938 | Some (opaque, _, t) ->
939 let params = x, y, w, h, tilex, tiley in
940 if conf.invert
941 then (
942 Gl.enable `blend;
943 GlFunc.blend_func `zero `one_minus_src_color;
945 drawtile params opaque;
946 if conf.invert
947 then Gl.disable `blend;
948 if conf.debug
949 then (
950 let s = Printf.sprintf
951 "%d[%d,%d] %f sec"
952 l.pageno col row t
954 let w = measurestr fstate.fontsize s in
955 GlMisc.push_attrib [`current];
956 GlDraw.color (0.0, 0.0, 0.0);
957 GlDraw.rect
958 (float (x-2), float (y-2))
959 (float (x+2) +. w, float (y + fstate.fontsize + 2));
960 GlDraw.color (1.0, 1.0, 1.0);
961 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
962 GlMisc.pop_attrib ();
965 | _ ->
966 let w =
967 let lw = conf.winw - state.scrollw - x in
968 min lw w
969 and h =
970 let lh = conf.winh - y in
971 min lh h
973 Gl.enable `texture_2d;
974 begin match state.texid with
975 | Some id ->
976 GlTex.bind_texture `texture_2d id;
977 let x0 = float x
978 and y0 = float y
979 and x1 = float (x+w)
980 and y1 = float (y+h) in
982 let tw = float w /. 64.0
983 and th = float h /. 64.0 in
984 let tx0 = float tilex /. 64.0
985 and ty0 = float tiley /. 64.0 in
986 let tx1 = tx0 +. tw
987 and ty1 = ty0 +. th in
988 GlDraw.begins `quads;
989 GlTex.coord2 (tx0, ty0); GlDraw.vertex2 (x0, y0);
990 GlTex.coord2 (tx0, ty1); GlDraw.vertex2 (x0, y1);
991 GlTex.coord2 (tx1, ty1); GlDraw.vertex2 (x1, y1);
992 GlTex.coord2 (tx1, ty0); GlDraw.vertex2 (x1, y0);
993 GlDraw.ends ();
995 Gl.disable `texture_2d;
996 | None ->
997 GlDraw.color (1.0, 1.0, 1.0);
998 GlDraw.rect
999 (float x, float y)
1000 (float (x+w), float (y+h));
1001 end;
1002 if w > 128 && h > fstate.fontsize + 10
1003 then (
1004 GlDraw.color (0.0, 0.0, 0.0);
1005 let c, r =
1006 if conf.verbose
1007 then (col*conf.tilew, row*conf.tileh)
1008 else col, row
1010 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
1012 GlDraw.color color;
1014 itertiles l f
1017 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
1019 let tilevisible1 l x y =
1020 let ax0 = l.pagex
1021 and ax1 = l.pagex + l.pagevw
1022 and ay0 = l.pagey
1023 and ay1 = l.pagey + l.pagevh in
1025 let bx0 = x
1026 and by0 = y in
1027 let bx1 = min (bx0 + conf.tilew) l.pagew
1028 and by1 = min (by0 + conf.tileh) l.pageh in
1030 let rx0 = max ax0 bx0
1031 and ry0 = max ay0 by0
1032 and rx1 = min ax1 bx1
1033 and ry1 = min ay1 by1 in
1035 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
1036 nonemptyintersection
1039 let tilevisible layout n x y =
1040 let rec findpageinlayout = function
1041 | l :: _ when l.pageno = n -> tilevisible1 l x y
1042 | _ :: rest -> findpageinlayout rest
1043 | [] -> false
1045 findpageinlayout layout
1048 let tileready l x y =
1049 tilevisible1 l x y &&
1050 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
1053 let tilepage n p layout =
1054 let rec loop = function
1055 | l :: rest ->
1056 if l.pageno = n
1057 then
1058 let f col row _ _ _ _ _ _ =
1059 if state.currently = Idle
1060 then
1061 match gettileopaque l col row with
1062 | Some _ -> ()
1063 | None ->
1064 let x = col*conf.tilew
1065 and y = row*conf.tileh in
1066 let w =
1067 let w = l.pagew - x in
1068 min w conf.tilew
1070 let h =
1071 let h = l.pageh - y in
1072 min h conf.tileh
1074 wcmd "tile"
1075 [`s p
1076 ;`i x
1077 ;`i y
1078 ;`i w
1079 ;`i h
1081 state.currently <-
1082 Tiling (
1083 l, p, conf.colorspace, conf.angle, state.gen, col, row,
1084 conf.tilew, conf.tileh
1087 itertiles l f;
1088 else
1089 loop rest
1091 | [] -> ()
1093 if state.invalidated = 0 then loop layout;
1096 let preloadlayout visiblepages =
1097 let presentation = conf.presentation in
1098 let interpagespace = conf.interpagespace in
1099 let maxy = state.maxy in
1100 conf.presentation <- false;
1101 conf.interpagespace <- 0;
1102 state.maxy <- calcheight ();
1103 let y =
1104 match visiblepages with
1105 | [] -> 0
1106 | l :: _ -> getpagey l.pageno + l.pagey
1108 let y = if y < conf.winh then 0 else y - conf.winh in
1109 let h = state.y - y + conf.winh*3 in
1110 let pages = layout y h in
1111 conf.presentation <- presentation;
1112 conf.interpagespace <- interpagespace;
1113 state.maxy <- maxy;
1114 pages;
1117 let load pages =
1118 let rec loop pages =
1119 if state.currently != Idle
1120 then ()
1121 else
1122 match pages with
1123 | l :: rest ->
1124 begin match getopaque l.pageno with
1125 | None ->
1126 wcmd "page" [`i l.pageno; `i l.pagedimno];
1127 state.currently <- Loading (l, state.gen);
1128 | Some opaque ->
1129 tilepage l.pageno opaque pages;
1130 loop rest
1131 end;
1132 | _ -> ()
1134 if state.invalidated = 0 then loop pages
1137 let preload pages =
1138 load pages;
1139 if conf.preload && state.currently = Idle
1140 then load (preloadlayout pages);
1143 let layoutready layout =
1144 let rec fold all ls =
1145 all && match ls with
1146 | l :: rest ->
1147 let seen = ref false in
1148 let allvisible = ref true in
1149 let foo col row _ _ _ _ _ _ =
1150 seen := true;
1151 allvisible := !allvisible &&
1152 begin match gettileopaque l col row with
1153 | Some _ -> true
1154 | None -> false
1157 itertiles l foo;
1158 fold (!seen && !allvisible) rest
1159 | [] -> true
1161 let alltilesvisible = fold true layout in
1162 alltilesvisible;
1165 let gotoy y =
1166 let y = bound y 0 state.maxy in
1167 let y, layout, proceed =
1168 match conf.maxwait with
1169 | Some time when state.ghyll == noghyll ->
1170 begin match state.throttle with
1171 | None ->
1172 let layout = layout y conf.winh in
1173 let ready = layoutready layout in
1174 if not ready
1175 then (
1176 load layout;
1177 state.throttle <- Some (layout, y, now ());
1179 else G.postRedisplay "gotoy showall (None)";
1180 y, layout, ready
1181 | Some (_, _, started) ->
1182 let dt = now () -. started in
1183 if dt > time
1184 then (
1185 state.throttle <- None;
1186 let layout = layout y conf.winh in
1187 load layout;
1188 G.postRedisplay "maxwait";
1189 y, layout, true
1191 else -1, [], false
1194 | _ ->
1195 let layout = layout y conf.winh in
1196 if true || layoutready layout
1197 then G.postRedisplay "gotoy ready";
1198 y, layout, true
1200 if proceed
1201 then (
1202 state.y <- y;
1203 state.layout <- layout;
1204 begin match state.mode with
1205 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
1206 if not (pagevisible layout pageno)
1207 then (
1208 match state.layout with
1209 | [] -> ()
1210 | l :: _ ->
1211 state.mode <- Birdseye (
1212 conf, leftx, l.pageno, hooverpageno, anchor
1215 | _ -> ()
1216 end;
1217 preload layout;
1219 state.ghyll <- noghyll;
1222 let conttiling pageno opaque =
1223 tilepage pageno opaque
1224 (if conf.preload then preloadlayout state.layout else state.layout)
1227 let gotoy_and_clear_text y =
1228 gotoy y;
1229 if not conf.verbose then state.text <- "";
1232 let getanchor () =
1233 match state.layout with
1234 | [] -> emptyanchor
1235 | l :: _ -> (l.pageno, float l.pagey /. float l.pageh)
1238 let getanchory (n, top) =
1239 let y, h = getpageyh n in
1240 y + (truncate (top *. float h));
1243 let gotoanchor anchor =
1244 gotoy (getanchory anchor);
1247 let addnav () =
1248 cbput state.hists.nav (getanchor ());
1251 let getnav dir =
1252 let anchor = cbgetc state.hists.nav dir in
1253 getanchory anchor;
1256 let gotoghyll y =
1257 let rec scroll f n a b =
1258 (* http://devmaster.net/forums/topic/9796-ease-in-ease-out-algorithm/ *)
1259 let snake f a b =
1260 let s x = 3.0*.x**2.0 -. 2.0*.x**3.0 in
1261 if f < a
1262 then s (float f /. float a)
1263 else (
1264 if f > b
1265 then 1.0 -. s ((float (f-b) /. float (n-b)))
1266 else 1.0
1269 snake f a b
1270 and summa f n a b =
1271 (* courtesy:
1272 http://integrals.wolfram.com/index.jsp?expr=3x%5E2-2x%5E3&random=false *)
1273 let iv x = -.((-.2.0 +. x)*.x**3.0)/.2.0 in
1274 let iv1 = iv f in
1275 let ins = float a *. iv1
1276 and outs = float (n-b) *. iv1 in
1277 let ones = b - a in
1278 ins +. outs +. float ones
1280 let rec set (_N, _A, _B) y sy =
1281 let sum = summa 1.0 _N _A _B in
1282 let dy = float (y - sy) in
1283 state.ghyll <- (
1284 let rec gf n y1 o =
1285 if n >= _N
1286 then state.ghyll <- noghyll
1287 else
1288 let go n =
1289 let s = scroll n _N _A _B in
1290 let y1 = y1 +. ((s *. dy) /. sum) in
1291 gotoy_and_clear_text (truncate y1);
1292 state.ghyll <- gf (n+1) y1;
1294 match o with
1295 | None -> go n
1296 | Some y' -> set (_N/2, 0, 0) y' state.y
1298 gf 0 (float state.y)
1301 match conf.ghyllscroll with
1302 | None ->
1303 gotoy_and_clear_text y
1304 | Some nab ->
1305 if state.ghyll == noghyll
1306 then set nab y state.y
1307 else state.ghyll (Some y)
1310 let gotopage n top =
1311 let y, h = getpageyh n in
1312 let y = y + (truncate (top *. float h)) in
1313 gotoghyll y
1316 let gotopage1 n top =
1317 let y = getpagey n in
1318 let y = y + top in
1319 gotoghyll y
1322 let invalidate () =
1323 state.layout <- [];
1324 state.pdims <- [];
1325 state.rects <- [];
1326 state.rects1 <- [];
1327 state.invalidated <- state.invalidated + 1;
1330 let writeopen path password =
1331 writecmd state.csock ("open " ^ path ^ "\000" ^ password ^ "\000");
1334 let opendoc path password =
1335 invalidate ();
1336 state.path <- path;
1337 state.password <- password;
1338 state.gen <- state.gen + 1;
1339 state.docinfo <- [];
1341 setaalevel conf.aalevel;
1342 writeopen path password;
1343 Glut.setWindowTitle ("llpp " ^ Filename.basename path);
1344 wcmd "geometry" [`i state.w; `i conf.winh];
1347 let scalecolor c =
1348 let c = c *. conf.colorscale in
1349 (c, c, c);
1352 let scalecolor2 (r, g, b) =
1353 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
1356 let represent () =
1357 state.maxy <- calcheight ();
1358 state.hscrollh <-
1359 if state.w <= conf.winw - state.scrollw
1360 then 0
1361 else state.scrollw
1363 match state.mode with
1364 | Birdseye (_, _, pageno, _, _) ->
1365 let y, h = getpageyh pageno in
1366 let top = (conf.winh - h) / 2 in
1367 gotoy (max 0 (y - top))
1368 | _ -> gotoanchor state.anchor
1371 let reshape =
1372 let firsttime = ref true in
1373 fun ~w ~h ->
1374 GlDraw.viewport 0 0 w h;
1375 if state.invalidated = 0 && not !firsttime
1376 then state.anchor <- getanchor ();
1378 firsttime := false;
1379 conf.winw <- w;
1380 let w = truncate (float w *. conf.zoom) - state.scrollw in
1381 let w = max w 2 in
1382 state.w <- w;
1383 conf.winh <- h;
1384 setfontsize fstate.fontsize;
1385 GlMat.mode `modelview;
1386 GlMat.load_identity ();
1388 GlMat.mode `projection;
1389 GlMat.load_identity ();
1390 GlMat.rotate ~x:1.0 ~angle:180.0 ();
1391 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
1392 GlMat.scale3 (2.0 /. float conf.winw, 2.0 /. float conf.winh, 1.0);
1394 invalidate ();
1395 wcmd "geometry" [`i w; `i h];
1398 let enttext () =
1399 let len = String.length state.text in
1400 let drawstring s =
1401 let hscrollh =
1402 match state.mode with
1403 | View -> state.hscrollh
1404 | _ -> 0
1406 let rect x w =
1407 GlDraw.rect
1408 (x, float (conf.winh - (fstate.fontsize + 4) - hscrollh))
1409 (x+.w, float (conf.winh - hscrollh))
1412 let w = float (conf.winw - state.scrollw - 1) in
1413 if state.progress >= 0.0 && state.progress < 1.0
1414 then (
1415 GlDraw.color (0.3, 0.3, 0.3);
1416 let w1 = w *. state.progress in
1417 rect 0.0 w1;
1418 GlDraw.color (0.0, 0.0, 0.0);
1419 rect w1 (w-.w1)
1421 else (
1422 GlDraw.color (0.0, 0.0, 0.0);
1423 rect 0.0 w;
1426 GlDraw.color (1.0, 1.0, 1.0);
1427 drawstring fstate.fontsize
1428 (if len > 0 then 8 else 2) (conf.winh - hscrollh - 5) s;
1430 let s =
1431 match state.mode with
1432 | Textentry ((prefix, text, _, _, _), _) ->
1433 let s =
1434 if len > 0
1435 then
1436 Printf.sprintf "%s%s_ [%s]" prefix text state.text
1437 else
1438 Printf.sprintf "%s%s_" prefix text
1442 | _ -> state.text
1444 let s =
1445 if state.newerrmsgs
1446 then (
1447 if not (istextentry state.mode)
1448 then
1449 let s1 = "(press 'e' to review error messasges)" in
1450 if String.length s > 0 then s ^ " " ^ s1 else s1
1451 else s
1453 else s
1455 if String.length s > 0
1456 then drawstring s
1459 let showtext c s =
1460 state.text <- Printf.sprintf "%c%s" c s;
1461 G.postRedisplay "showtext";
1464 let gctiles () =
1465 let len = Queue.length state.tilelru in
1466 let rec loop qpos =
1467 if state.memused <= conf.memlimit
1468 then ()
1469 else (
1470 if qpos < len
1471 then
1472 let (k, p, s) as lruitem = Queue.pop state.tilelru in
1473 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
1474 let (_, pw, ph, _) = getpagedim n in
1476 gen = state.gen
1477 && colorspace = conf.colorspace
1478 && angle = conf.angle
1479 && pagew = pw
1480 && pageh = ph
1481 && (
1482 let layout =
1483 match state.throttle with
1484 | None ->
1485 if conf.preload
1486 then preloadlayout state.layout
1487 else state.layout
1488 | Some (layout, _, _) ->
1489 layout
1491 let x = col*conf.tilew
1492 and y = row*conf.tileh in
1493 tilevisible layout n x y
1495 then Queue.push lruitem state.tilelru
1496 else (
1497 wcmd "freetile" [`s p];
1498 state.memused <- state.memused - s;
1499 state.uioh#infochanged Memused;
1500 Hashtbl.remove state.tilemap k;
1502 loop (qpos+1)
1505 loop 0
1508 let flushtiles () =
1509 Queue.iter (fun (k, p, s) ->
1510 wcmd "freetile" [`s p];
1511 state.memused <- state.memused - s;
1512 state.uioh#infochanged Memused;
1513 Hashtbl.remove state.tilemap k;
1514 ) state.tilelru;
1515 Queue.clear state.tilelru;
1516 load state.layout;
1519 let logcurrently = function
1520 | Idle -> dolog "Idle"
1521 | Loading (l, gen) ->
1522 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
1523 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
1524 dolog
1525 "Tiling %d[%d,%d] page=%s cs=%s angle"
1526 l.pageno col row pageopaque
1527 (colorspace_to_string colorspace)
1529 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
1530 angle gen conf.angle state.gen
1531 tilew tileh
1532 conf.tilew conf.tileh
1534 | Outlining _ ->
1535 dolog "outlining"
1538 let act cmds =
1539 (* dolog "%S" cmds; *)
1540 let op, args =
1541 let spacepos =
1542 try String.index cmds ' '
1543 with Not_found -> -1
1545 if spacepos = -1
1546 then cmds, ""
1547 else
1548 let l = String.length cmds in
1549 let op = String.sub cmds 0 spacepos in
1550 op, begin
1551 if l - spacepos < 2 then ""
1552 else String.sub cmds (spacepos+1) (l-spacepos-1)
1555 match op with
1556 | "clear" ->
1557 state.uioh#infochanged Pdim;
1558 state.pdims <- [];
1560 | "clearrects" ->
1561 state.rects <- state.rects1;
1562 G.postRedisplay "clearrects";
1564 | "continue" ->
1565 let n =
1566 try Scanf.sscanf args "%u" (fun n -> n)
1567 with exn ->
1568 dolog "error processing 'continue' %S: %s"
1569 cmds (Printexc.to_string exn);
1570 exit 1;
1572 state.pagecount <- n;
1573 state.invalidated <- state.invalidated - 1;
1574 begin match state.currently with
1575 | Outlining l ->
1576 state.currently <- Idle;
1577 state.outlines <- Array.of_list (List.rev l)
1578 | _ -> ()
1579 end;
1580 if state.invalidated = 0
1581 then represent ();
1582 if conf.maxwait = None
1583 then G.postRedisplay "continue";
1585 | "title" ->
1586 Glut.setWindowTitle args
1588 | "msg" ->
1589 showtext ' ' args
1591 | "vmsg" ->
1592 if conf.verbose
1593 then showtext ' ' args
1595 | "progress" ->
1596 let progress, text =
1598 Scanf.sscanf args "%f %n"
1599 (fun f pos ->
1600 f, String.sub args pos (String.length args - pos))
1601 with exn ->
1602 dolog "error processing 'progress' %S: %s"
1603 cmds (Printexc.to_string exn);
1604 exit 1;
1606 state.text <- text;
1607 state.progress <- progress;
1608 G.postRedisplay "progress"
1610 | "firstmatch" ->
1611 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1613 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1614 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1615 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1616 with exn ->
1617 dolog "error processing 'firstmatch' %S: %s"
1618 cmds (Printexc.to_string exn);
1619 exit 1;
1621 let y = (getpagey pageno) + truncate y0 in
1622 addnav ();
1623 gotoy y;
1624 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
1626 | "match" ->
1627 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
1629 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
1630 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
1631 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
1632 with exn ->
1633 dolog "error processing 'match' %S: %s"
1634 cmds (Printexc.to_string exn);
1635 exit 1;
1637 state.rects1 <-
1638 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
1640 | "page" ->
1641 let pageopaque, t =
1643 Scanf.sscanf args "%s %f" (fun p t -> p, t)
1644 with exn ->
1645 dolog "error processing 'page' %S: %s"
1646 cmds (Printexc.to_string exn);
1647 exit 1;
1649 begin match state.currently with
1650 | Loading (l, gen) ->
1651 vlog "page %d took %f sec" l.pageno t;
1652 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
1653 begin match state.throttle with
1654 | None ->
1655 let preloadedpages =
1656 if conf.preload
1657 then preloadlayout state.layout
1658 else state.layout
1660 let evict () =
1661 let module IntSet =
1662 Set.Make (struct type t = int let compare = (-) end) in
1663 let set =
1664 List.fold_left (fun s l -> IntSet.add l.pageno s)
1665 IntSet.empty preloadedpages
1667 let evictedpages =
1668 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
1669 if not (IntSet.mem pageno set)
1670 then (
1671 wcmd "freepage" [`s opaque];
1672 key :: accu
1674 else accu
1675 ) state.pagemap []
1677 List.iter (Hashtbl.remove state.pagemap) evictedpages;
1679 evict ();
1680 state.currently <- Idle;
1681 if gen = state.gen
1682 then (
1683 tilepage l.pageno pageopaque state.layout;
1684 load state.layout;
1685 load preloadedpages;
1686 if pagevisible state.layout l.pageno
1687 && layoutready state.layout
1688 then G.postRedisplay "page";
1691 | Some (layout, _, _) ->
1692 state.currently <- Idle;
1693 tilepage l.pageno pageopaque layout;
1694 load state.layout
1695 end;
1697 | _ ->
1698 dolog "Inconsistent loading state";
1699 logcurrently state.currently;
1700 raise Quit;
1703 | "tile" ->
1704 let (x, y, opaque, size, t) =
1706 Scanf.sscanf args "%u %u %s %u %f"
1707 (fun x y p size t -> (x, y, p, size, t))
1708 with exn ->
1709 dolog "error processing 'tile' %S: %s"
1710 cmds (Printexc.to_string exn);
1711 exit 1;
1713 begin match state.currently with
1714 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
1715 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
1717 if tilew != conf.tilew || tileh != conf.tileh
1718 then (
1719 wcmd "freetile" [`s opaque];
1720 state.currently <- Idle;
1721 load state.layout;
1723 else (
1724 puttileopaque l col row gen cs angle opaque size t;
1725 state.memused <- state.memused + size;
1726 state.uioh#infochanged Memused;
1727 gctiles ();
1728 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
1729 opaque, size) state.tilelru;
1731 let layout =
1732 match state.throttle with
1733 | None -> state.layout
1734 | Some (layout, _, _) -> layout
1737 state.currently <- Idle;
1738 if gen = state.gen
1739 && conf.colorspace = cs
1740 && conf.angle = angle
1741 && tilevisible layout l.pageno x y
1742 then conttiling l.pageno pageopaque;
1744 begin match state.throttle with
1745 | None ->
1746 preload state.layout;
1747 if gen = state.gen
1748 && conf.colorspace = cs
1749 && conf.angle = angle
1750 && tilevisible state.layout l.pageno x y
1751 then G.postRedisplay "tile nothrottle";
1753 | Some (layout, y, _) ->
1754 let ready = layoutready layout in
1755 if ready
1756 then (
1757 state.y <- y;
1758 state.layout <- layout;
1759 state.throttle <- None;
1760 G.postRedisplay "throttle";
1762 else load layout;
1763 end;
1766 | _ ->
1767 dolog "Inconsistent tiling state";
1768 logcurrently state.currently;
1769 raise Quit;
1772 | "pdim" ->
1773 let pdim =
1775 Scanf.sscanf args "%u %u %u %u" (fun n w h x -> n, w, h, x)
1776 with exn ->
1777 dolog "error processing 'pdim' %S: %s"
1778 cmds (Printexc.to_string exn);
1779 exit 1;
1781 state.uioh#infochanged Pdim;
1782 state.pdims <- pdim :: state.pdims
1784 | "o" ->
1785 let (l, n, t, h, pos) =
1787 Scanf.sscanf args "%u %u %d %u %n"
1788 (fun l n t h pos -> l, n, t, h, pos)
1789 with exn ->
1790 dolog "error processing 'o' %S: %s"
1791 cmds (Printexc.to_string exn);
1792 exit 1;
1794 let s = String.sub args pos (String.length args - pos) in
1795 let outline = (s, l, (n, float t /. float h)) in
1796 begin match state.currently with
1797 | Outlining outlines ->
1798 state.currently <- Outlining (outline :: outlines)
1799 | Idle ->
1800 state.currently <- Outlining [outline]
1801 | currently ->
1802 dolog "invalid outlining state";
1803 logcurrently currently
1806 | "info" ->
1807 state.docinfo <- (1, args) :: state.docinfo
1809 | "infoend" ->
1810 state.uioh#infochanged Docinfo;
1811 state.docinfo <- List.rev state.docinfo
1813 | _ ->
1814 dolog "unknown cmd `%S'" cmds
1817 let idle () =
1818 if state.deadline == nan then state.deadline <- now ();
1819 let r =
1820 match state.errfd with
1821 | None -> [state.csock]
1822 | Some fd -> [state.csock; fd]
1824 let rec loop delay =
1825 let deadline =
1826 if state.ghyll == noghyll
1827 then state.deadline
1828 else now () +. 0.02
1830 let timeout =
1831 if delay > 0.0
1832 then max 0.0 (deadline -. now ())
1833 else 0.0
1835 let r, _, _ = Unix.select r [] [] timeout in
1836 begin match r with
1837 | [] ->
1838 state.ghyll None;
1839 begin match state.autoscroll with
1840 | Some step when step != 0 ->
1841 let y = state.y + step in
1842 let y =
1843 if y < 0
1844 then state.maxy
1845 else if y >= state.maxy then 0 else y
1847 gotoy y;
1848 if state.mode = View
1849 then state.text <- "";
1850 state.deadline <- state.deadline +. 0.005;
1852 | _ ->
1853 state.deadline <- state.deadline +. delay;
1854 end;
1856 | l ->
1857 let rec checkfds c = function
1858 | [] -> c
1859 | fd :: rest when fd = state.csock ->
1860 let cmd = readcmd state.csock in
1861 act cmd;
1862 checkfds true rest
1863 | fd :: rest ->
1864 let s = String.create 80 in
1865 let n = Unix.read fd s 0 80 in
1866 if conf.redirectstderr
1867 then (
1868 Buffer.add_substring state.errmsgs s 0 n;
1869 state.newerrmsgs <- true;
1870 Glut.postRedisplay ();
1872 else (
1873 prerr_string (String.sub s 0 n);
1874 flush stderr;
1876 checkfds c rest
1878 if checkfds false l
1879 then loop 0.0
1880 end;
1881 in loop 0.007
1884 let onhist cb =
1885 let rc = cb.rc in
1886 let action = function
1887 | HCprev -> cbget cb ~-1
1888 | HCnext -> cbget cb 1
1889 | HCfirst -> cbget cb ~-(cb.rc)
1890 | HClast -> cbget cb (cb.len - 1 - cb.rc)
1891 and cancel () = cb.rc <- rc
1892 in (action, cancel)
1895 let search pattern forward =
1896 if String.length pattern > 0
1897 then
1898 let pn, py =
1899 match state.layout with
1900 | [] -> 0, 0
1901 | l :: _ ->
1902 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
1904 let cmd =
1905 let b = makecmd "search"
1906 [`b conf.icase; `i pn; `i py; `i (if forward then 1 else 0)]
1908 Buffer.add_char b ',';
1909 Buffer.add_string b pattern;
1910 Buffer.add_char b '\000';
1911 Buffer.contents b;
1913 writecmd state.csock cmd;
1916 let intentry text key =
1917 let c = Char.unsafe_chr key in
1918 match c with
1919 | '0' .. '9' ->
1920 let text = addchar text c in
1921 TEcont text
1923 | _ ->
1924 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
1925 TEcont text
1928 let textentry text key =
1929 let c = Char.unsafe_chr key in
1930 match c with
1931 | _ when key >= 32 && key < 127 ->
1932 let text = addchar text c in
1933 TEcont text
1935 | _ ->
1936 dolog "unhandled key %d char `%c'" key (Char.unsafe_chr key);
1937 TEcont text
1940 let reqlayout angle proportional =
1941 match state.throttle with
1942 | None ->
1943 if state.invalidated = 0 then state.anchor <- getanchor ();
1944 conf.angle <- angle mod 360;
1945 conf.proportional <- proportional;
1946 invalidate ();
1947 wcmd "reqlayout" [`i conf.angle; `b proportional];
1948 | _ -> ()
1951 let settrim trimmargins trimfuzz =
1952 if state.invalidated = 0 then state.anchor <- getanchor ();
1953 conf.trimmargins <- trimmargins;
1954 conf.trimfuzz <- trimfuzz;
1955 let x0, y0, x1, y1 = trimfuzz in
1956 invalidate ();
1957 wcmd "settrim" [
1958 `b conf.trimmargins;
1959 `i x0;
1960 `i y0;
1961 `i x1;
1962 `i y1;
1964 Hashtbl.iter (fun _ opaque ->
1965 wcmd "freepage" [`s opaque];
1966 ) state.pagemap;
1967 Hashtbl.clear state.pagemap;
1970 let setzoom zoom =
1971 match state.throttle with
1972 | None ->
1973 let zoom = max 0.01 zoom in
1974 if zoom <> conf.zoom
1975 then (
1976 state.prevzoom <- conf.zoom;
1977 let relx =
1978 if zoom <= 1.0
1979 then (state.x <- 0; 0.0)
1980 else float state.x /. float state.w
1982 conf.zoom <- zoom;
1983 reshape conf.winw conf.winh;
1984 if zoom > 1.0
1985 then (
1986 let x = relx *. float state.w in
1987 state.x <- truncate x;
1989 state.text <- Printf.sprintf "zoom is now %-5.1f" (zoom *. 100.0);
1992 | Some (layout, y, started) ->
1993 let time =
1994 match conf.maxwait with
1995 | None -> 0.0
1996 | Some t -> t
1998 let dt = now () -. started in
1999 if dt > time
2000 then (
2001 state.y <- y;
2002 load layout;
2006 let enterbirdseye () =
2007 let zoom = float conf.thumbw /. float conf.winw in
2008 let birdseyepageno =
2009 let cy = conf.winh / 2 in
2010 let fold = function
2011 | [] -> 0
2012 | l :: rest ->
2013 let rec fold best = function
2014 | [] -> best.pageno
2015 | l :: rest ->
2016 let d = cy - (l.pagedispy + l.pagevh/2)
2017 and dbest = cy - (best.pagedispy + best.pagevh/2) in
2018 if abs d < abs dbest
2019 then fold l rest
2020 else best.pageno
2021 in fold l rest
2023 fold state.layout
2025 state.mode <- Birdseye (
2026 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
2028 conf.zoom <- zoom;
2029 conf.presentation <- false;
2030 conf.interpagespace <- 10;
2031 conf.hlinks <- false;
2032 state.x <- 0;
2033 state.mstate <- Mnone;
2034 conf.maxwait <- None;
2035 Glut.setCursor Glut.CURSOR_INHERIT;
2036 if conf.verbose
2037 then
2038 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
2039 (100.0*.zoom)
2040 else
2041 state.text <- ""
2043 reshape conf.winw conf.winh;
2046 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
2047 state.mode <- View;
2048 conf.zoom <- c.zoom;
2049 conf.presentation <- c.presentation;
2050 conf.interpagespace <- c.interpagespace;
2051 conf.maxwait <- c.maxwait;
2052 conf.hlinks <- c.hlinks;
2053 state.x <- leftx;
2054 if conf.verbose
2055 then
2056 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
2057 (100.0*.conf.zoom)
2059 reshape conf.winw conf.winh;
2060 state.anchor <- if goback then anchor else (pageno, 0.0);
2063 let togglebirdseye () =
2064 match state.mode with
2065 | Birdseye vals -> leavebirdseye vals true
2066 | View -> enterbirdseye ()
2067 | _ -> ()
2070 let upbirdseye (conf, leftx, pageno, hooverpageno, anchor) =
2071 let pageno = max 0 (pageno - 1) in
2072 let rec loop = function
2073 | [] -> gotopage1 pageno 0
2074 | l :: _ when l.pageno = pageno ->
2075 if l.pagedispy >= 0 && l.pagey = 0
2076 then G.postRedisplay "upbirdseye"
2077 else gotopage1 pageno 0
2078 | _ :: rest -> loop rest
2080 loop state.layout;
2081 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2084 let downbirdseye (conf, leftx, pageno, hooverpageno, anchor) =
2085 let pageno = min (state.pagecount - 1) (pageno + 1) in
2086 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2087 let rec loop = function
2088 | [] ->
2089 let y, h = getpageyh pageno in
2090 let dy = (y - state.y) - (conf.winh - h - conf.interpagespace) in
2091 gotoy (clamp dy)
2092 | l :: _ when l.pageno = pageno ->
2093 if l.pagevh != l.pageh
2094 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
2095 else G.postRedisplay "downbirdseye"
2096 | _ :: rest -> loop rest
2098 loop state.layout
2101 let optentry mode _ key =
2102 let btos b = if b then "on" else "off" in
2103 let c = Char.unsafe_chr key in
2104 match c with
2105 | 's' ->
2106 let ondone s =
2107 try conf.scrollstep <- int_of_string s with exc ->
2108 state.text <- Printf.sprintf "bad integer `%s': %s"
2109 s (Printexc.to_string exc)
2111 TEswitch ("scroll step: ", "", None, intentry, ondone)
2113 | 'A' ->
2114 let ondone s =
2116 conf.autoscrollstep <- int_of_string s;
2117 if state.autoscroll <> None
2118 then state.autoscroll <- Some conf.autoscrollstep
2119 with exc ->
2120 state.text <- Printf.sprintf "bad integer `%s': %s"
2121 s (Printexc.to_string exc)
2123 TEswitch ("auto scroll step: ", "", None, intentry, ondone)
2125 | 'Z' ->
2126 let ondone s =
2128 let zoom = float (int_of_string s) /. 100.0 in
2129 setzoom zoom
2130 with exc ->
2131 state.text <- Printf.sprintf "bad integer `%s': %s"
2132 s (Printexc.to_string exc)
2134 TEswitch ("zoom: ", "", None, intentry, ondone)
2136 | 't' ->
2137 let ondone s =
2139 conf.thumbw <- bound (int_of_string s) 2 4096;
2140 state.text <-
2141 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
2142 begin match mode with
2143 | Birdseye beye ->
2144 leavebirdseye beye false;
2145 enterbirdseye ();
2146 | _ -> ();
2148 with exc ->
2149 state.text <- Printf.sprintf "bad integer `%s': %s"
2150 s (Printexc.to_string exc)
2152 TEswitch ("thumbnail width: ", "", None, intentry, ondone)
2154 | 'R' ->
2155 let ondone s =
2156 match try
2157 Some (int_of_string s)
2158 with exc ->
2159 state.text <- Printf.sprintf "bad integer `%s': %s"
2160 s (Printexc.to_string exc);
2161 None
2162 with
2163 | Some angle -> reqlayout angle conf.proportional
2164 | None -> ()
2166 TEswitch ("rotation: ", "", None, intentry, ondone)
2168 | 'i' ->
2169 conf.icase <- not conf.icase;
2170 TEdone ("case insensitive search " ^ (btos conf.icase))
2172 | 'p' ->
2173 conf.preload <- not conf.preload;
2174 gotoy state.y;
2175 TEdone ("preload " ^ (btos conf.preload))
2177 | 'v' ->
2178 conf.verbose <- not conf.verbose;
2179 TEdone ("verbose " ^ (btos conf.verbose))
2181 | 'd' ->
2182 conf.debug <- not conf.debug;
2183 TEdone ("debug " ^ (btos conf.debug))
2185 | 'h' ->
2186 conf.maxhfit <- not conf.maxhfit;
2187 state.maxy <-
2188 state.maxy + (if conf.maxhfit then -conf.winh else conf.winh);
2189 TEdone ("maxhfit " ^ (btos conf.maxhfit))
2191 | 'c' ->
2192 conf.crophack <- not conf.crophack;
2193 TEdone ("crophack " ^ btos conf.crophack)
2195 | 'a' ->
2196 let s =
2197 match conf.maxwait with
2198 | None ->
2199 conf.maxwait <- Some infinity;
2200 "always wait for page to complete"
2201 | Some _ ->
2202 conf.maxwait <- None;
2203 "show placeholder if page is not ready"
2205 TEdone s
2207 | 'f' ->
2208 conf.underinfo <- not conf.underinfo;
2209 TEdone ("underinfo " ^ btos conf.underinfo)
2211 | 'P' ->
2212 conf.savebmarks <- not conf.savebmarks;
2213 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
2215 | 'S' ->
2216 let ondone s =
2218 let pageno, py =
2219 match state.layout with
2220 | [] -> 0, 0
2221 | l :: _ ->
2222 l.pageno, l.pagey
2224 conf.interpagespace <- int_of_string s;
2225 state.maxy <- calcheight ();
2226 let y = getpagey pageno in
2227 gotoy (y + py)
2228 with exc ->
2229 state.text <- Printf.sprintf "bad integer `%s': %s"
2230 s (Printexc.to_string exc)
2232 TEswitch ("vertical margin: ", "", None, intentry, ondone)
2234 | 'l' ->
2235 reqlayout conf.angle (not conf.proportional);
2236 TEdone ("proportional display " ^ btos conf.proportional)
2238 | 'T' ->
2239 settrim (not conf.trimmargins) conf.trimfuzz;
2240 TEdone ("trim margins " ^ btos conf.trimmargins)
2242 | 'I' ->
2243 conf.invert <- not conf.invert;
2244 TEdone ("invert colors " ^ btos conf.invert)
2246 | _ ->
2247 state.text <- Printf.sprintf "bad option %d `%c'" key c;
2248 TEstop
2251 class type lvsource = object
2252 method getitemcount : int
2253 method getitem : int -> (string * int)
2254 method hasaction : int -> bool
2255 method exit :
2256 uioh:uioh ->
2257 cancel:bool ->
2258 active:int ->
2259 first:int ->
2260 pan:int ->
2261 qsearch:string ->
2262 uioh option
2263 method getactive : int
2264 method getfirst : int
2265 method getqsearch : string
2266 method setqsearch : string -> unit
2267 method getpan : int
2268 end;;
2270 class virtual lvsourcebase = object
2271 val mutable m_active = 0
2272 val mutable m_first = 0
2273 val mutable m_qsearch = ""
2274 val mutable m_pan = 0
2275 method getactive = m_active
2276 method getfirst = m_first
2277 method getqsearch = m_qsearch
2278 method getpan = m_pan
2279 method setqsearch s = m_qsearch <- s
2280 end;;
2282 let textentryspecial key = function
2283 | ((c, _, (Some (action, _) as onhist), onkey, ondone), mode) ->
2284 let s =
2285 match key with
2286 | Glut.KEY_UP -> action HCprev
2287 | Glut.KEY_DOWN -> action HCnext
2288 | Glut.KEY_HOME -> action HCfirst
2289 | Glut.KEY_END -> action HClast
2290 | _ -> state.text
2292 state.mode <- Textentry ((c, s, onhist, onkey, ondone), mode);
2293 G.postRedisplay "special textentry";
2294 | _ -> ()
2297 let textentrykeyboard key ((c, text, opthist, onkey, ondone), onleave) =
2298 let enttext te =
2299 state.mode <- Textentry (te, onleave);
2300 state.text <- "";
2301 enttext ();
2302 G.postRedisplay "textentrykeyboard enttext";
2304 match Char.unsafe_chr key with
2305 | '\008' -> (* backspace *)
2306 let len = String.length text in
2307 if len = 0
2308 then (
2309 onleave Cancel;
2310 G.postRedisplay "textentrykeyboard after cancel";
2312 else (
2313 let s = String.sub text 0 (len - 1) in
2314 enttext (c, s, opthist, onkey, ondone)
2317 | '\r' | '\n' ->
2318 ondone text;
2319 onleave Confirm;
2320 G.postRedisplay "textentrykeyboard after confirm"
2322 | '\007' (* ctrl-g *)
2323 | '\027' -> (* escape *)
2324 if String.length text = 0
2325 then (
2326 begin match opthist with
2327 | None -> ()
2328 | Some (_, onhistcancel) -> onhistcancel ()
2329 end;
2330 onleave Cancel;
2331 state.text <- "";
2332 G.postRedisplay "textentrykeyboard after cancel2"
2334 else (
2335 enttext (c, "", opthist, onkey, ondone)
2338 | '\127' -> () (* delete *)
2340 | _ ->
2341 begin match onkey text key with
2342 | TEdone text ->
2343 ondone text;
2344 onleave Confirm;
2345 G.postRedisplay "textentrykeyboard after confirm2";
2347 | TEcont text ->
2348 enttext (c, text, opthist, onkey, ondone);
2350 | TEstop ->
2351 onleave Cancel;
2352 G.postRedisplay "textentrykeyboard after cancel3"
2354 | TEswitch te ->
2355 state.mode <- Textentry (te, onleave);
2356 G.postRedisplay "textentrykeyboard switch";
2357 end;
2360 let firstof first active =
2361 if first > active || abs (first - active) > fstate.maxrows - 1
2362 then max 0 (active - (fstate.maxrows/2))
2363 else first
2366 let calcfirst first active =
2367 if active > first
2368 then
2369 let rows = active - first in
2370 if rows > fstate.maxrows then active - fstate.maxrows else first
2371 else active
2374 let scrollph y maxy =
2375 let sh = (float (maxy + conf.winh) /. float conf.winh) in
2376 let sh = float conf.winh /. sh in
2377 let sh = max sh (float conf.scrollh) in
2379 let percent =
2380 if y = state.maxy
2381 then 1.0
2382 else float y /. float maxy
2384 let position = (float conf.winh -. sh) *. percent in
2386 let position =
2387 if position +. sh > float conf.winh
2388 then float conf.winh -. sh
2389 else position
2391 position, sh;
2394 let coe s = (s :> uioh);;
2396 class listview ~(source:lvsource) ~trusted =
2397 object (self)
2398 val m_pan = source#getpan
2399 val m_first = source#getfirst
2400 val m_active = source#getactive
2401 val m_qsearch = source#getqsearch
2402 val m_prev_uioh = state.uioh
2404 method private elemunder y =
2405 let n = y / (fstate.fontsize+1) in
2406 if m_first + n < source#getitemcount
2407 then (
2408 if source#hasaction (m_first + n)
2409 then Some (m_first + n)
2410 else None
2412 else None
2414 method display =
2415 Gl.enable `blend;
2416 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
2417 GlDraw.color (0., 0., 0.) ~alpha:0.85;
2418 GlDraw.rect (0., 0.) (float conf.winw, float conf.winh);
2419 GlDraw.color (1., 1., 1.);
2420 Gl.enable `texture_2d;
2421 let fs = fstate.fontsize in
2422 let nfs = fs + 1 in
2423 let ww = fstate.wwidth in
2424 let tabw = 30.0*.ww in
2425 let itemcount = source#getitemcount in
2426 let rec loop row =
2427 if (row - m_first) * nfs > conf.winh
2428 then ()
2429 else (
2430 if row >= 0 && row < itemcount
2431 then (
2432 let (s, level) = source#getitem row in
2433 let y = (row - m_first) * nfs in
2434 let x = 5.0 +. float (level + m_pan) *. ww in
2435 if row = m_active
2436 then (
2437 Gl.disable `texture_2d;
2438 GlDraw.polygon_mode `both `line;
2439 GlDraw.color (1., 1., 1.) ~alpha:0.9;
2440 GlDraw.rect (1., float (y + 1))
2441 (float (conf.winw - conf.scrollbw - 1), float (y + fs + 3));
2442 GlDraw.polygon_mode `both `fill;
2443 GlDraw.color (1., 1., 1.);
2444 Gl.enable `texture_2d;
2447 let drawtabularstring s =
2448 let drawstr x s = drawstring1 fs (truncate x) (y+nfs) s in
2449 if trusted
2450 then
2451 let tabpos = try String.index s '\t' with Not_found -> -1 in
2452 if tabpos > 0
2453 then
2454 let len = String.length s - tabpos - 1 in
2455 let s1 = String.sub s 0 tabpos
2456 and s2 = String.sub s (tabpos + 1) len in
2457 let nx = drawstr x s1 in
2458 let sw = nx -. x in
2459 let x = x +. (max tabw sw) in
2460 drawstr x s2
2461 else
2462 drawstr x s
2463 else
2464 drawstr x s
2466 let _ = drawtabularstring s in
2467 loop (row+1)
2471 loop m_first;
2472 Gl.disable `blend;
2473 Gl.disable `texture_2d;
2475 method updownlevel incr =
2476 let len = source#getitemcount in
2477 let _, curlevel = source#getitem m_active in
2478 let rec flow i =
2479 if i = len then i-1 else if i = -1 then 0 else
2480 let _, l = source#getitem i in
2481 if l != curlevel then i else flow (i+incr)
2483 let active = flow m_active in
2484 let first = calcfirst m_first active in
2485 G.postRedisplay "special outline updownlevel";
2486 {< m_active = active; m_first = first >}
2488 method private key1 key =
2489 let set active first qsearch =
2490 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
2492 let search active pattern incr =
2493 let dosearch re =
2494 let rec loop n =
2495 if n >= 0 && n < source#getitemcount
2496 then (
2497 let s, _ = source#getitem n in
2499 (try ignore (Str.search_forward re s 0); true
2500 with Not_found -> false)
2501 then Some n
2502 else loop (n + incr)
2504 else None
2506 loop active
2509 let re = Str.regexp_case_fold pattern in
2510 dosearch re
2511 with Failure s ->
2512 state.text <- s;
2513 None
2515 match key with
2516 | 18 | 19 -> (* ctrl-r/ctlr-s *)
2517 let incr = if key = 18 then -1 else 1 in
2518 let active, first =
2519 match search (m_active + incr) m_qsearch incr with
2520 | None ->
2521 state.text <- m_qsearch ^ " [not found]";
2522 m_active, m_first
2523 | Some active ->
2524 state.text <- m_qsearch;
2525 active, firstof m_first active
2527 G.postRedisplay "listview ctrl-r/s";
2528 set active first m_qsearch;
2530 | 8 -> (* backspace *)
2531 let len = String.length m_qsearch in
2532 if len = 0
2533 then coe self
2534 else (
2535 if len = 1
2536 then (
2537 state.text <- "";
2538 G.postRedisplay "listview empty qsearch";
2539 set m_active m_first "";
2541 else
2542 let qsearch = String.sub m_qsearch 0 (len - 1) in
2543 let active, first =
2544 match search m_active qsearch ~-1 with
2545 | None ->
2546 state.text <- qsearch ^ " [not found]";
2547 m_active, m_first
2548 | Some active ->
2549 state.text <- qsearch;
2550 active, firstof m_first active
2552 G.postRedisplay "listview backspace qsearch";
2553 set active first qsearch
2556 | _ when key >= 32 && key < 127 ->
2557 let pattern = addchar m_qsearch (Char.chr key) in
2558 let active, first =
2559 match search m_active pattern 1 with
2560 | None ->
2561 state.text <- pattern ^ " [not found]";
2562 m_active, m_first
2563 | Some active ->
2564 state.text <- pattern;
2565 active, firstof m_first active
2567 G.postRedisplay "listview qsearch add";
2568 set active first pattern;
2570 | 27 -> (* escape *)
2571 state.text <- "";
2572 if String.length m_qsearch = 0
2573 then (
2574 G.postRedisplay "list view escape";
2575 begin
2576 match
2577 source#exit (coe self) true m_active m_first m_pan m_qsearch
2578 with
2579 | None -> m_prev_uioh
2580 | Some uioh -> uioh
2583 else (
2584 G.postRedisplay "list view kill qsearch";
2585 source#setqsearch "";
2586 coe {< m_qsearch = "" >}
2589 | 13 -> (* enter *)
2590 state.text <- "";
2591 let self = {< m_qsearch = "" >} in
2592 source#setqsearch "";
2593 let opt =
2594 G.postRedisplay "listview enter";
2595 if m_active >= 0 && m_active < source#getitemcount
2596 then (
2597 source#exit (coe self) false m_active m_first m_pan "";
2599 else (
2600 source#exit (coe self) true m_active m_first m_pan "";
2603 begin match opt with
2604 | None -> m_prev_uioh
2605 | Some uioh -> uioh
2608 | 127 -> (* delete *)
2609 coe self
2611 | _ -> dolog "unknown key %d" key; coe self
2613 method private special1 key =
2614 let itemcount = source#getitemcount in
2615 let find start incr =
2616 let rec find i =
2617 if i = -1 || i = itemcount
2618 then -1
2619 else (
2620 if source#hasaction i
2621 then i
2622 else find (i + incr)
2625 find start
2627 let set active first =
2628 let first = bound first 0 (itemcount - fstate.maxrows) in
2629 state.text <- "";
2630 coe {< m_active = active; m_first = first >}
2632 let navigate incr =
2633 let isvisible first n = n >= first && n - first <= fstate.maxrows in
2634 let active, first =
2635 let incr1 = if incr > 0 then 1 else -1 in
2636 if isvisible m_first m_active
2637 then
2638 let next =
2639 let next = m_active + incr in
2640 let next =
2641 if next < 0 || next >= itemcount
2642 then -1
2643 else find next incr1
2645 if next = -1 || abs (m_active - next) > fstate.maxrows
2646 then -1
2647 else next
2649 if next = -1
2650 then
2651 let first = m_first + incr in
2652 let first = bound first 0 (itemcount - 1) in
2653 let next =
2654 let next = m_active + incr in
2655 let next = bound next 0 (itemcount - 1) in
2656 find next ~-incr1
2658 let active = if next = -1 then m_active else next in
2659 active, first
2660 else
2661 let first = min next m_first in
2662 let first =
2663 if abs (next - first) > fstate.maxrows
2664 then first + incr
2665 else first
2667 next, first
2668 else
2669 let first = m_first + incr in
2670 let first = bound first 0 (itemcount - 1) in
2671 let active =
2672 let next = m_active + incr in
2673 let next = bound next 0 (itemcount - 1) in
2674 let next = find next incr1 in
2675 let active =
2676 if next = -1 || abs (m_active - first) > fstate.maxrows
2677 then (
2678 let active = if m_active = -1 then next else m_active in
2679 active
2681 else next
2683 if isvisible first active
2684 then active
2685 else -1
2687 active, first
2689 G.postRedisplay "listview navigate";
2690 set active first;
2692 begin match key with
2693 | Glut.KEY_UP -> navigate ~-1
2694 | Glut.KEY_DOWN -> navigate 1
2695 | Glut.KEY_PAGE_UP -> navigate ~-(fstate.maxrows)
2696 | Glut.KEY_PAGE_DOWN -> navigate fstate.maxrows
2698 | Glut.KEY_RIGHT ->
2699 state.text <- "";
2700 G.postRedisplay "listview right";
2701 coe {< m_pan = m_pan - 1 >}
2703 | Glut.KEY_LEFT ->
2704 state.text <- "";
2705 G.postRedisplay "listview left";
2706 coe {< m_pan = m_pan + 1 >}
2708 | Glut.KEY_HOME ->
2709 let active = find 0 1 in
2710 G.postRedisplay "listview home";
2711 set active 0;
2713 | Glut.KEY_END ->
2714 let first = max 0 (itemcount - fstate.maxrows) in
2715 let active = find (itemcount - 1) ~-1 in
2716 G.postRedisplay "listview end";
2717 set active first;
2719 | _ -> coe self
2720 end;
2722 method key key =
2723 match state.mode with
2724 | Textentry te -> textentrykeyboard key te; coe self
2725 | _ -> self#key1 key
2727 method special key =
2728 match state.mode with
2729 | Textentry te -> textentryspecial key te; coe self
2730 | _ -> self#special1 key
2732 method button button bstate x y =
2733 let opt =
2734 match button with
2735 | Glut.LEFT_BUTTON when x > conf.winw - conf.scrollbw ->
2736 G.postRedisplay "listview scroll";
2737 if bstate = Glut.DOWN
2738 then
2739 let _, position, sh = self#scrollph in
2740 if y > truncate position && y < truncate (position +. sh)
2741 then (
2742 state.mstate <- Mscrolly;
2743 Some (coe self)
2745 else
2746 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
2747 let first = truncate (s *. float source#getitemcount) in
2748 let first = min source#getitemcount first in
2749 Some (coe {< m_first = first; m_active = first >})
2750 else (
2751 state.mstate <- Mnone;
2752 Some (coe self);
2754 | Glut.LEFT_BUTTON when bstate = Glut.UP ->
2755 begin match self#elemunder y with
2756 | Some n ->
2757 G.postRedisplay "listview click";
2758 source#exit
2759 (coe {< m_active = n >}) false n m_first m_pan m_qsearch
2760 | _ ->
2761 Some (coe self)
2763 | Glut.OTHER_BUTTON n when (n == 3 || n == 4) && bstate = Glut.UP ->
2764 let len = source#getitemcount in
2765 let first =
2766 if n = 4 && m_first + fstate.maxrows >= len
2767 then
2768 m_first
2769 else
2770 let first = m_first + (if n == 3 then -1 else 1) in
2771 bound first 0 (len - 1)
2773 G.postRedisplay "listview wheel";
2774 Some (coe {< m_first = first >})
2775 | _ ->
2776 Some (coe self)
2778 match opt with
2779 | None -> m_prev_uioh
2780 | Some uioh -> uioh
2782 method motion _ y =
2783 match state.mstate with
2784 | Mscrolly ->
2785 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
2786 let first = truncate (s *. float source#getitemcount) in
2787 let first = min source#getitemcount first in
2788 G.postRedisplay "listview motion";
2789 coe {< m_first = first; m_active = first >}
2790 | _ -> coe self
2792 method pmotion x y =
2793 if x < conf.winw - conf.scrollbw
2794 then
2795 let n =
2796 match self#elemunder y with
2797 | None -> Glut.setCursor Glut.CURSOR_INHERIT; m_active
2798 | Some n -> Glut.setCursor Glut.CURSOR_INFO; n
2800 let o =
2801 if n != m_active
2802 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
2803 else self
2805 coe o
2806 else (
2807 Glut.setCursor Glut.CURSOR_INHERIT;
2808 coe self
2811 method infochanged _ = ()
2813 method scrollpw = (0, 0.0, 0.0)
2814 method scrollph =
2815 let nfs = fstate.fontsize + 1 in
2816 let y = m_first * nfs in
2817 let itemcount = source#getitemcount in
2818 let maxi = max 0 (itemcount - fstate.maxrows) in
2819 let maxy = maxi * nfs in
2820 let p, h = scrollph y maxy in
2821 conf.scrollbw, p, h
2822 end;;
2824 class outlinelistview ~source =
2825 object (self)
2826 inherit listview ~source:(source :> lvsource) ~trusted:false as super
2828 method key key =
2829 match key with
2830 | 14 -> (* ctrl-n *)
2831 source#narrow m_qsearch;
2832 G.postRedisplay "outline ctrl-n";
2833 coe {< m_first = 0; m_active = 0 >}
2835 | 21 -> (* ctrl-u *)
2836 source#denarrow;
2837 G.postRedisplay "outline ctrl-u";
2838 state.text <- "";
2839 coe {< m_first = 0; m_active = 0 >}
2841 | 12 -> (* ctrl-l *)
2842 let first = m_active - (fstate.maxrows / 2) in
2843 G.postRedisplay "outline ctrl-l";
2844 coe {< m_first = first >}
2846 | 127 -> (* delete *)
2847 source#remove m_active;
2848 G.postRedisplay "outline delete";
2849 let active = max 0 (m_active-1) in
2850 coe {< m_first = firstof m_first active;
2851 m_active = active >}
2853 | key -> super#key key
2855 method special key =
2856 let calcfirst first active =
2857 if active > first
2858 then
2859 let rows = active - first in
2860 if rows > fstate.maxrows then active - fstate.maxrows else first
2861 else active
2863 let navigate incr =
2864 let active = m_active + incr in
2865 let active = bound active 0 (source#getitemcount - 1) in
2866 let first = calcfirst m_first active in
2867 G.postRedisplay "special outline navigate";
2868 coe {< m_active = active; m_first = first >}
2870 match key with
2871 | Glut.KEY_UP -> navigate ~-1
2872 | Glut.KEY_DOWN -> navigate 1
2873 | Glut.KEY_PAGE_UP -> navigate ~-(fstate.maxrows)
2874 | Glut.KEY_PAGE_DOWN -> navigate fstate.maxrows
2876 | Glut.KEY_RIGHT ->
2877 let o =
2878 if Glut.getModifiers () land Glut.active_ctrl != 0
2879 then (
2880 G.postRedisplay "special outline right";
2881 {< m_pan = m_pan + 1 >}
2883 else self#updownlevel 1
2885 coe o
2887 | Glut.KEY_LEFT ->
2888 let o =
2889 if Glut.getModifiers () land Glut.active_ctrl != 0
2890 then (
2891 G.postRedisplay "special outline left";
2892 {< m_pan = m_pan - 1 >}
2894 else self#updownlevel ~-1
2896 coe o
2898 | Glut.KEY_HOME ->
2899 G.postRedisplay "special outline home";
2900 coe {< m_first = 0; m_active = 0 >}
2902 | Glut.KEY_END ->
2903 let active = source#getitemcount - 1 in
2904 let first = max 0 (active - fstate.maxrows) in
2905 G.postRedisplay "special outline end";
2906 coe {< m_active = active; m_first = first >}
2908 | _ -> super#special key
2911 let outlinesource usebookmarks =
2912 let empty = [||] in
2913 (object
2914 inherit lvsourcebase
2915 val mutable m_items = empty
2916 val mutable m_orig_items = empty
2917 val mutable m_prev_items = empty
2918 val mutable m_narrow_pattern = ""
2919 val mutable m_hadremovals = false
2921 method getitemcount =
2922 Array.length m_items + (if m_hadremovals then 1 else 0)
2924 method getitem n =
2925 if n == Array.length m_items && m_hadremovals
2926 then
2927 ("[Confirm removal]", 0)
2928 else
2929 let s, n, _ = m_items.(n) in
2930 (s, n)
2932 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
2933 ignore (uioh, first, pan, qsearch);
2934 let confrimremoval = m_hadremovals && active = Array.length m_items in
2935 let items =
2936 if String.length m_narrow_pattern = 0
2937 then m_orig_items
2938 else m_items
2940 if not cancel
2941 then (
2942 if not confrimremoval
2943 then(
2944 let _, _, anchor = m_items.(active) in
2945 gotoanchor anchor;
2946 m_items <- items;
2948 else (
2949 state.bookmarks <- Array.to_list m_items;
2950 m_orig_items <- m_items;
2953 else m_items <- items;
2954 None
2956 method hasaction _ = true
2958 method greetmsg =
2959 if Array.length m_items != Array.length m_orig_items
2960 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
2961 else ""
2963 method narrow pattern =
2964 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
2965 match reopt with
2966 | None -> ()
2967 | Some re ->
2968 let rec loop accu n =
2969 if n = -1
2970 then (
2971 m_narrow_pattern <- pattern;
2972 m_items <- Array.of_list accu
2974 else
2975 let (s, _, _) as o = m_items.(n) in
2976 let accu =
2977 if (try ignore (Str.search_forward re s 0); true
2978 with Not_found -> false)
2979 then o :: accu
2980 else accu
2982 loop accu (n-1)
2984 loop [] (Array.length m_items - 1)
2986 method denarrow =
2987 m_orig_items <- (
2988 if usebookmarks
2989 then Array.of_list state.bookmarks
2990 else state.outlines
2992 m_items <- m_orig_items
2994 method remove m =
2995 if usebookmarks
2996 then
2997 if m >= 0 && m < Array.length m_items
2998 then (
2999 m_hadremovals <- true;
3000 m_items <- Array.init (Array.length m_items - 1) (fun n ->
3001 let n = if n >= m then n+1 else n in
3002 m_items.(n)
3006 method reset anchor items =
3007 m_hadremovals <- false;
3008 if m_orig_items == empty || m_prev_items != items
3009 then (
3010 m_orig_items <- items;
3011 if String.length m_narrow_pattern = 0
3012 then m_items <- items;
3014 m_prev_items <- items;
3015 let rely = getanchory anchor in
3016 let active =
3017 let rec loop n best bestd =
3018 if n = Array.length m_items
3019 then best
3020 else
3021 let (_, _, anchor) = m_items.(n) in
3022 let orely = getanchory anchor in
3023 let d = abs (orely - rely) in
3024 if d < bestd
3025 then loop (n+1) n d
3026 else loop (n+1) best bestd
3028 loop 0 ~-1 max_int
3030 m_active <- active;
3031 m_first <- firstof m_first active
3032 end)
3035 let enterselector usebookmarks =
3036 let source = outlinesource usebookmarks in
3037 fun errmsg ->
3038 let outlines =
3039 if usebookmarks
3040 then Array.of_list state.bookmarks
3041 else state.outlines
3043 if Array.length outlines = 0
3044 then (
3045 showtext ' ' errmsg;
3047 else (
3048 state.text <- source#greetmsg;
3049 Glut.setCursor Glut.CURSOR_INHERIT;
3050 let anchor = getanchor () in
3051 source#reset anchor outlines;
3052 state.uioh <- coe (new outlinelistview ~source);
3053 G.postRedisplay "enter selector";
3057 let enteroutlinemode =
3058 let f = enterselector false in
3059 fun ()-> f "Document has no outline";
3062 let enterbookmarkmode =
3063 let f = enterselector true in
3064 fun () -> f "Document has no bookmarks (yet)";
3067 let color_of_string s =
3068 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
3069 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
3073 let color_to_string (r, g, b) =
3074 let r = truncate (r *. 256.0)
3075 and g = truncate (g *. 256.0)
3076 and b = truncate (b *. 256.0) in
3077 Printf.sprintf "%d/%d/%d" r g b
3080 let irect_of_string s =
3081 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
3084 let irect_to_string (x0,y0,x1,y1) =
3085 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
3088 let makecheckers () =
3089 (* Appropriated from lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
3090 following to say:
3091 converted by Issac Trotts. July 25, 2002 *)
3092 let image_height = 64
3093 and image_width = 64 in
3095 let make_image () =
3096 let image =
3097 GlPix.create `ubyte ~format:`rgb ~width:image_width ~height:image_height
3099 for i = 0 to image_width - 1 do
3100 for j = 0 to image_height - 1 do
3101 Raw.sets (GlPix.to_raw image) ~pos:(3*(i*image_height+j))
3102 (if (i land 8 ) lxor (j land 8) = 0
3103 then [|255;255;255|] else [|200;200;200|])
3104 done
3105 done;
3106 image
3108 let image = make_image () in
3109 let id = GlTex.gen_texture () in
3110 GlTex.bind_texture `texture_2d id;
3111 GlPix.store (`unpack_alignment 1);
3112 GlTex.image2d image;
3113 List.iter (GlTex.parameter ~target:`texture_2d)
3114 [ `wrap_s `repeat;
3115 `wrap_t `repeat;
3116 `mag_filter `nearest;
3117 `min_filter `nearest ];
3121 let setcheckers enabled =
3122 match state.texid with
3123 | None ->
3124 if enabled then state.texid <- Some (makecheckers ())
3126 | Some texid ->
3127 if not enabled
3128 then (
3129 GlTex.delete_texture texid;
3130 state.texid <- None;
3134 let int_of_string_with_suffix s =
3135 let l = String.length s in
3136 let s1, shift =
3137 if l > 1
3138 then
3139 let suffix = Char.lowercase s.[l-1] in
3140 match suffix with
3141 | 'k' -> String.sub s 0 (l-1), 10
3142 | 'm' -> String.sub s 0 (l-1), 20
3143 | 'g' -> String.sub s 0 (l-1), 30
3144 | _ -> s, 0
3145 else s, 0
3147 let n = int_of_string s1 in
3148 let m = n lsl shift in
3149 if m < 0 || m < n
3150 then raise (Failure "value too large")
3151 else m
3154 let string_with_suffix_of_int n =
3155 if n = 0
3156 then "0"
3157 else
3158 let n, s =
3159 if n = 0
3160 then 0, ""
3161 else (
3162 if n land ((1 lsl 20) - 1) = 0
3163 then n lsr 20, "M"
3164 else (
3165 if n land ((1 lsl 10) - 1) = 0
3166 then n lsr 10, "K"
3167 else n, ""
3171 let rec loop s n =
3172 let h = n mod 1000 in
3173 let n = n / 1000 in
3174 if n = 0
3175 then string_of_int h ^ s
3176 else (
3177 let s = Printf.sprintf "_%03d%s" h s in
3178 loop s n
3181 loop "" n ^ s;
3184 let defghyllscroll = (40, 8, 32);;
3185 let ghyllscroll_of_string s =
3186 let (n, a, b) as nab =
3187 if s = "default"
3188 then defghyllscroll
3189 else Scanf.sscanf s "%u,%u,%u" (fun n a b -> n, a, b)
3191 if n <= a || n <= b || a >= b
3192 then failwith "invalid ghyll N,A,B (N <= A, A < B, N <= B)";
3193 nab;
3196 let ghyllscroll_to_string ((n, a, b) as nab) =
3197 if nab = defghyllscroll
3198 then "default"
3199 else Printf.sprintf "%d,%d,%d" n a b;
3202 let describe_location () =
3203 let f (fn, _) l =
3204 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
3206 let fn, ln = List.fold_left f (-1, -1) state.layout in
3207 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
3208 let percent =
3209 if maxy <= 0
3210 then 100.
3211 else (100. *. (float state.y /. float maxy))
3213 if fn = ln
3214 then
3215 Printf.sprintf "page %d of %d [%.2f%%]"
3216 (fn+1) state.pagecount percent
3217 else
3218 Printf.sprintf
3219 "pages %d-%d of %d [%.2f%%]"
3220 (fn+1) (ln+1) state.pagecount percent
3223 let enterinfomode =
3224 let btos b = if b then "\xe2\x88\x9a" else "" in
3225 let showextended = ref false in
3226 let leave mode = function
3227 | Confirm -> state.mode <- mode
3228 | Cancel -> state.mode <- mode in
3229 let src =
3230 (object
3231 val mutable m_first_time = true
3232 val mutable m_l = []
3233 val mutable m_a = [||]
3234 val mutable m_prev_uioh = nouioh
3235 val mutable m_prev_mode = View
3237 inherit lvsourcebase
3239 method reset prev_mode prev_uioh =
3240 m_a <- Array.of_list (List.rev m_l);
3241 m_l <- [];
3242 m_prev_mode <- prev_mode;
3243 m_prev_uioh <- prev_uioh;
3244 if m_first_time
3245 then (
3246 let rec loop n =
3247 if n >= Array.length m_a
3248 then ()
3249 else
3250 match m_a.(n) with
3251 | _, _, _, Action _ -> m_active <- n
3252 | _ -> loop (n+1)
3254 loop 0;
3255 m_first_time <- false;
3258 method int name get set =
3259 m_l <-
3260 (name, `int get, 1, Action (
3261 fun u ->
3262 let ondone s =
3263 try set (int_of_string s)
3264 with exn ->
3265 state.text <- Printf.sprintf "bad integer `%s': %s"
3266 s (Printexc.to_string exn)
3268 state.text <- "";
3269 let te = name ^ ": ", "", None, intentry, ondone in
3270 state.mode <- Textentry (te, leave m_prev_mode);
3272 )) :: m_l
3274 method int_with_suffix name get set =
3275 m_l <-
3276 (name, `intws get, 1, Action (
3277 fun u ->
3278 let ondone s =
3279 try set (int_of_string_with_suffix s)
3280 with exn ->
3281 state.text <- Printf.sprintf "bad integer `%s': %s"
3282 s (Printexc.to_string exn)
3284 state.text <- "";
3285 let te =
3286 name ^ ": ", "", None, intentry_with_suffix, ondone
3288 state.mode <- Textentry (te, leave m_prev_mode);
3290 )) :: m_l
3292 method bool ?(offset=1) ?(btos=btos) name get set =
3293 m_l <-
3294 (name, `bool (btos, get), offset, Action (
3295 fun u ->
3296 let v = get () in
3297 set (not v);
3299 )) :: m_l
3301 method color name get set =
3302 m_l <-
3303 (name, `color get, 1, Action (
3304 fun u ->
3305 let invalid = (nan, nan, nan) in
3306 let ondone s =
3307 let c =
3308 try color_of_string s
3309 with exn ->
3310 state.text <- Printf.sprintf "bad color `%s': %s"
3311 s (Printexc.to_string exn);
3312 invalid
3314 if c <> invalid
3315 then set c;
3317 let te = name ^ ": ", "", None, textentry, ondone in
3318 state.text <- color_to_string (get ());
3319 state.mode <- Textentry (te, leave m_prev_mode);
3321 )) :: m_l
3323 method string name get set =
3324 m_l <-
3325 (name, `string get, 1, Action (
3326 fun u ->
3327 let ondone s = set s in
3328 let te = name ^ ": ", "", None, textentry, ondone in
3329 state.mode <- Textentry (te, leave m_prev_mode);
3331 )) :: m_l
3333 method colorspace name get set =
3334 m_l <-
3335 (name, `string get, 1, Action (
3336 fun _ ->
3337 let source =
3338 let vals = [| "rgb"; "bgr"; "gray" |] in
3339 (object
3340 inherit lvsourcebase
3342 initializer
3343 m_active <- int_of_colorspace conf.colorspace;
3344 m_first <- 0;
3346 method getitemcount = Array.length vals
3347 method getitem n = (vals.(n), 0)
3348 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3349 ignore (uioh, first, pan, qsearch);
3350 if not cancel then set active;
3351 None
3352 method hasaction _ = true
3353 end)
3355 state.text <- "";
3356 coe (new listview ~source ~trusted:true)
3357 )) :: m_l
3359 method caption s offset =
3360 m_l <- (s, `empty, offset, Noaction) :: m_l
3362 method caption2 s f offset =
3363 m_l <- (s, `string f, offset, Noaction) :: m_l
3365 method getitemcount = Array.length m_a
3367 method getitem n =
3368 let tostr = function
3369 | `int f -> string_of_int (f ())
3370 | `intws f -> string_with_suffix_of_int (f ())
3371 | `string f -> f ()
3372 | `color f -> color_to_string (f ())
3373 | `bool (btos, f) -> btos (f ())
3374 | `empty -> ""
3376 let name, t, offset, _ = m_a.(n) in
3377 ((let s = tostr t in
3378 if String.length s > 0
3379 then Printf.sprintf "%s\t%s" name s
3380 else name),
3381 offset)
3383 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3384 let uiohopt =
3385 if not cancel
3386 then (
3387 m_qsearch <- qsearch;
3388 let uioh =
3389 match m_a.(active) with
3390 | _, _, _, Action f -> f uioh
3391 | _ -> uioh
3393 Some uioh
3395 else None
3397 m_active <- active;
3398 m_first <- first;
3399 m_pan <- pan;
3400 uiohopt
3402 method hasaction n =
3403 match m_a.(n) with
3404 | _, _, _, Action _ -> true
3405 | _ -> false
3406 end)
3408 let rec fillsrc prevmode prevuioh =
3409 let sep () = src#caption "" 0 in
3410 let colorp name get set =
3411 src#string name
3412 (fun () -> color_to_string (get ()))
3413 (fun v ->
3415 let c = color_of_string v in
3416 set c
3417 with exn ->
3418 state.text <- Printf.sprintf "bad color `%s': %s"
3419 v (Printexc.to_string exn);
3422 let oldmode = state.mode in
3423 let birdseye = isbirdseye state.mode in
3425 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
3427 src#bool "presentation mode"
3428 (fun () -> conf.presentation)
3429 (fun v ->
3430 conf.presentation <- v;
3431 state.anchor <- getanchor ();
3432 represent ());
3434 src#bool "ignore case in searches"
3435 (fun () -> conf.icase)
3436 (fun v -> conf.icase <- v);
3438 src#bool "preload"
3439 (fun () -> conf.preload)
3440 (fun v -> conf.preload <- v);
3442 src#bool "highlight links"
3443 (fun () -> conf.hlinks)
3444 (fun v -> conf.hlinks <- v);
3446 src#bool "under info"
3447 (fun () -> conf.underinfo)
3448 (fun v -> conf.underinfo <- v);
3450 src#bool "persistent bookmarks"
3451 (fun () -> conf.savebmarks)
3452 (fun v -> conf.savebmarks <- v);
3454 src#bool "proportional display"
3455 (fun () -> conf.proportional)
3456 (fun v -> reqlayout conf.angle v);
3458 src#bool "trim margins"
3459 (fun () -> conf.trimmargins)
3460 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
3462 src#bool "persistent location"
3463 (fun () -> conf.jumpback)
3464 (fun v -> conf.jumpback <- v);
3466 sep ();
3467 src#int "vertical margin"
3468 (fun () -> conf.interpagespace)
3469 (fun n ->
3470 conf.interpagespace <- n;
3471 let pageno, py =
3472 match state.layout with
3473 | [] -> 0, 0
3474 | l :: _ ->
3475 l.pageno, l.pagey
3477 state.maxy <- calcheight ();
3478 let y = getpagey pageno in
3479 gotoy (y + py)
3482 src#int "page bias"
3483 (fun () -> conf.pagebias)
3484 (fun v -> conf.pagebias <- v);
3486 src#int "scroll step"
3487 (fun () -> conf.scrollstep)
3488 (fun n -> conf.scrollstep <- n);
3490 src#int "auto scroll step"
3491 (fun () ->
3492 match state.autoscroll with
3493 | Some step -> step
3494 | _ -> conf.autoscrollstep)
3495 (fun n ->
3496 if state.autoscroll <> None
3497 then state.autoscroll <- Some n;
3498 conf.autoscrollstep <- n);
3500 src#int "zoom"
3501 (fun () -> truncate (conf.zoom *. 100.))
3502 (fun v -> setzoom ((float v) /. 100.));
3504 src#int "rotation"
3505 (fun () -> conf.angle)
3506 (fun v -> reqlayout v conf.proportional);
3508 src#int "scroll bar width"
3509 (fun () -> state.scrollw)
3510 (fun v ->
3511 state.scrollw <- v;
3512 conf.scrollbw <- v;
3513 reshape conf.winw conf.winh;
3516 src#int "scroll handle height"
3517 (fun () -> conf.scrollh)
3518 (fun v -> conf.scrollh <- v;);
3520 src#int "thumbnail width"
3521 (fun () -> conf.thumbw)
3522 (fun v ->
3523 conf.thumbw <- min 4096 v;
3524 match oldmode with
3525 | Birdseye beye ->
3526 leavebirdseye beye false;
3527 enterbirdseye ()
3528 | _ -> ()
3531 sep ();
3532 src#caption "Presentation mode" 0;
3533 src#bool "scrollbar visible"
3534 (fun () -> conf.scrollbarinpm)
3535 (fun v ->
3536 if v != conf.scrollbarinpm
3537 then (
3538 conf.scrollbarinpm <- v;
3539 if conf.presentation
3540 then (
3541 state.scrollw <- if v then conf.scrollbw else 0;
3542 reshape conf.winw conf.winh;
3547 sep ();
3548 src#caption "Pixmap cache" 0;
3549 src#int_with_suffix "size (advisory)"
3550 (fun () -> conf.memlimit)
3551 (fun v -> conf.memlimit <- v);
3553 src#caption2 "used"
3554 (fun () -> Printf.sprintf "%s bytes, %d tiles"
3555 (string_with_suffix_of_int state.memused)
3556 (Hashtbl.length state.tilemap)) 1;
3558 sep ();
3559 src#caption "Layout" 0;
3560 src#caption2 "Dimension"
3561 (fun () ->
3562 Printf.sprintf "%dx%d (virtual %dx%d)"
3563 conf.winw conf.winh
3564 state.w state.maxy)
3566 if conf.debug
3567 then
3568 src#caption2 "Position" (fun () ->
3569 Printf.sprintf "%dx%d" state.x state.y
3571 else
3572 src#caption2 "Visible" (fun () -> describe_location ()) 1
3575 sep ();
3576 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
3577 "Save these parameters as global defaults at exit"
3578 (fun () -> conf.bedefault)
3579 (fun v -> conf.bedefault <- v)
3582 sep ();
3583 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
3584 src#bool ~offset:0 ~btos "Extended parameters"
3585 (fun () -> !showextended)
3586 (fun v -> showextended := v; fillsrc prevmode prevuioh);
3587 if !showextended
3588 then (
3589 src#bool "checkers"
3590 (fun () -> conf.checkers)
3591 (fun v -> conf.checkers <- v; setcheckers v);
3592 src#bool "verbose"
3593 (fun () -> conf.verbose)
3594 (fun v -> conf.verbose <- v);
3595 src#bool "invert colors"
3596 (fun () -> conf.invert)
3597 (fun v -> conf.invert <- v);
3598 src#bool "max fit"
3599 (fun () -> conf.maxhfit)
3600 (fun v -> conf.maxhfit <- v);
3601 src#bool "redirect stderr"
3602 (fun () -> conf.redirectstderr)
3603 (fun v -> conf.redirectstderr <- v; redirectstderr ());
3604 src#string "uri launcher"
3605 (fun () -> conf.urilauncher)
3606 (fun v -> conf.urilauncher <- v);
3607 src#string "tile size"
3608 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
3609 (fun v ->
3611 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
3612 conf.tileh <- max 64 w;
3613 conf.tilew <- max 64 h;
3614 flushtiles ();
3615 with exn ->
3616 state.text <- Printf.sprintf "bad tile size `%s': %s"
3617 v (Printexc.to_string exn));
3618 src#int "texture count"
3619 (fun () -> conf.texcount)
3620 (fun v ->
3621 if realloctexts v
3622 then conf.texcount <- v
3623 else showtext '!' " Failed to set texture count please retry later"
3625 src#int "slice height"
3626 (fun () -> conf.sliceheight)
3627 (fun v ->
3628 conf.sliceheight <- v;
3629 wcmd "sliceh" [`i conf.sliceheight];
3631 src#int "anti-aliasing level"
3632 (fun () -> conf.aalevel)
3633 (fun v ->
3634 conf.aalevel <- bound v 0 8;
3635 state.anchor <- getanchor ();
3636 opendoc state.path state.password;
3638 src#int "ui font size"
3639 (fun () -> fstate.fontsize)
3640 (fun v -> setfontsize (bound v 5 100));
3641 colorp "background color"
3642 (fun () -> conf.bgcolor)
3643 (fun v -> conf.bgcolor <- v);
3644 src#bool "crop hack"
3645 (fun () -> conf.crophack)
3646 (fun v -> conf.crophack <- v);
3647 src#string "trim fuzz"
3648 (fun () -> irect_to_string conf.trimfuzz)
3649 (fun v ->
3651 conf.trimfuzz <- irect_of_string v;
3652 if conf.trimmargins
3653 then settrim true conf.trimfuzz;
3654 with exn ->
3655 state.text <- Printf.sprintf "bad irect `%s': %s"
3656 v (Printexc.to_string exn)
3658 src#string "throttle"
3659 (fun () ->
3660 match conf.maxwait with
3661 | None -> "show place holder if page is not ready"
3662 | Some time ->
3663 if time = infinity
3664 then "wait for page to fully render"
3665 else
3666 "wait " ^ string_of_float time
3667 ^ " seconds before showing placeholder"
3669 (fun v ->
3671 let f = float_of_string v in
3672 if f <= 0.0
3673 then conf.maxwait <- None
3674 else conf.maxwait <- Some f
3675 with exn ->
3676 state.text <- Printf.sprintf "bad time `%s': %s"
3677 v (Printexc.to_string exn)
3679 src#string "ghyll scroll"
3680 (fun () ->
3681 match conf.ghyllscroll with
3682 | None -> ""
3683 | Some nab -> ghyllscroll_to_string nab
3685 (fun v ->
3687 let gs =
3688 if String.length v = 0
3689 then None
3690 else Some (ghyllscroll_of_string v)
3692 conf.ghyllscroll <- gs
3693 with exn ->
3694 state.text <- Printf.sprintf "bad ghyll `%s': %s"
3695 v (Printexc.to_string exn)
3697 src#colorspace "color space"
3698 (fun () -> colorspace_to_string conf.colorspace)
3699 (fun v ->
3700 conf.colorspace <- colorspace_of_int v;
3701 wcmd "cs" [`i v];
3702 load state.layout;
3706 sep ();
3707 src#caption "Document" 0;
3708 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
3709 src#caption2 "Pages"
3710 (fun () -> string_of_int state.pagecount) 1;
3711 src#caption2 "Dimensions"
3712 (fun () -> string_of_int (List.length state.pdims)) 1;
3713 if conf.trimmargins
3714 then (
3715 sep ();
3716 src#caption "Trimmed margins" 0;
3717 src#caption2 "Dimensions"
3718 (fun () -> string_of_int (List.length state.pdims)) 1;
3721 src#reset prevmode prevuioh;
3723 fun () ->
3724 state.text <- "";
3725 let prevmode = state.mode
3726 and prevuioh = state.uioh in
3727 fillsrc prevmode prevuioh;
3728 let source = (src :> lvsource) in
3729 state.uioh <- coe (object (self)
3730 inherit listview ~source ~trusted:true as super
3731 val mutable m_prevmemused = 0
3732 method infochanged = function
3733 | Memused ->
3734 if m_prevmemused != state.memused
3735 then (
3736 m_prevmemused <- state.memused;
3737 G.postRedisplay "memusedchanged";
3739 | Pdim -> G.postRedisplay "pdimchanged"
3740 | Docinfo -> fillsrc prevmode prevuioh
3742 method special key =
3743 if Glut.getModifiers () land Glut.active_ctrl = 0
3744 then
3745 match key with
3746 | Glut.KEY_LEFT -> coe (self#updownlevel ~-1)
3747 | Glut.KEY_RIGHT -> coe (self#updownlevel 1)
3748 | _ -> super#special key
3749 else super#special key
3750 end);
3751 G.postRedisplay "info";
3754 let enterhelpmode =
3755 let source =
3756 (object
3757 inherit lvsourcebase
3758 method getitemcount = Array.length state.help
3759 method getitem n =
3760 let s, n, _ = state.help.(n) in
3761 (s, n)
3763 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3764 let optuioh =
3765 if not cancel
3766 then (
3767 m_qsearch <- qsearch;
3768 match state.help.(active) with
3769 | _, _, Action f -> Some (f uioh)
3770 | _ -> Some (uioh)
3772 else None
3774 m_active <- active;
3775 m_first <- first;
3776 m_pan <- pan;
3777 optuioh
3779 method hasaction n =
3780 match state.help.(n) with
3781 | _, _, Action _ -> true
3782 | _ -> false
3784 initializer
3785 m_active <- -1
3786 end)
3787 in fun () ->
3788 state.uioh <- coe (new listview ~source ~trusted:true);
3789 G.postRedisplay "help";
3792 let entermsgsmode =
3793 let msgsource =
3794 let re = Str.regexp "[\r\n]" in
3795 (object
3796 inherit lvsourcebase
3797 val mutable m_items = [||]
3799 method getitemcount = 1 + Array.length m_items
3801 method getitem n =
3802 if n = 0
3803 then "[Clear]", 0
3804 else m_items.(n-1), 0
3806 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3807 ignore uioh;
3808 if not cancel
3809 then (
3810 if active = 0
3811 then Buffer.clear state.errmsgs;
3812 m_qsearch <- qsearch;
3814 m_active <- active;
3815 m_first <- first;
3816 m_pan <- pan;
3817 None
3819 method hasaction n =
3820 n = 0
3822 method reset =
3823 state.newerrmsgs <- false;
3824 let l = Str.split re (Buffer.contents state.errmsgs) in
3825 m_items <- Array.of_list l
3827 initializer
3828 m_active <- 0
3829 end)
3830 in fun () ->
3831 state.text <- "";
3832 msgsource#reset;
3833 let source = (msgsource :> lvsource) in
3834 state.uioh <- coe (object
3835 inherit listview ~source ~trusted:false as super
3836 method display =
3837 if state.newerrmsgs
3838 then msgsource#reset;
3839 super#display
3840 end);
3841 G.postRedisplay "msgs";
3844 let quickbookmark ?title () =
3845 match state.layout with
3846 | [] -> ()
3847 | l :: _ ->
3848 let title =
3849 match title with
3850 | None ->
3851 let sec = Unix.gettimeofday () in
3852 let tm = Unix.localtime sec in
3853 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
3854 (l.pageno+1)
3855 tm.Unix.tm_mday
3856 tm.Unix.tm_mon
3857 (tm.Unix.tm_year + 1900)
3858 tm.Unix.tm_hour
3859 tm.Unix.tm_min
3860 | Some title -> title
3862 state.bookmarks <-
3863 (title, 0, (l.pageno, float l.pagey /. float l.pageh))
3864 :: state.bookmarks
3867 let doreshape w h =
3868 state.fullscreen <- None;
3869 Glut.reshapeWindow w h;
3872 let viewkeyboard key =
3873 let enttext te =
3874 let mode = state.mode in
3875 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
3876 state.text <- "";
3877 enttext ();
3878 G.postRedisplay "view:enttext"
3880 let c = Char.chr key in
3881 match c with
3882 | '\027' | 'q' -> (* escape *)
3883 begin match state.mstate with
3884 | Mzoomrect _ ->
3885 state.mstate <- Mnone;
3886 Glut.setCursor Glut.CURSOR_INHERIT;
3887 G.postRedisplay "kill zoom rect";
3888 | _ ->
3889 raise Quit
3890 end;
3892 | '\008' -> (* backspace *)
3893 let y = getnav ~-1 in
3894 gotoy_and_clear_text y
3896 | 'o' ->
3897 enteroutlinemode ()
3899 | 'u' ->
3900 state.rects <- [];
3901 state.text <- "";
3902 G.postRedisplay "dehighlight";
3904 | '/' | '?' ->
3905 let ondone isforw s =
3906 cbput state.hists.pat s;
3907 state.searchpattern <- s;
3908 search s isforw
3910 let s = String.create 1 in
3911 s.[0] <- c;
3912 enttext (s, "", Some (onhist state.hists.pat),
3913 textentry, ondone (c ='/'))
3915 | '+' when Glut.getModifiers () land Glut.active_ctrl != 0 ->
3916 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
3917 setzoom (conf.zoom +. incr)
3919 | '+' ->
3920 let ondone s =
3921 let n =
3922 try int_of_string s with exc ->
3923 state.text <- Printf.sprintf "bad integer `%s': %s"
3924 s (Printexc.to_string exc);
3925 max_int
3927 if n != max_int
3928 then (
3929 conf.pagebias <- n;
3930 state.text <- "page bias is now " ^ string_of_int n;
3933 enttext ("page bias: ", "", None, intentry, ondone)
3935 | '-' when Glut.getModifiers () land Glut.active_ctrl != 0 ->
3936 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
3937 setzoom (max 0.01 (conf.zoom -. decr))
3939 | '-' ->
3940 let ondone msg = state.text <- msg in
3941 enttext (
3942 "option [acfhilpstvAPRSZTI]: ", "", None,
3943 optentry state.mode, ondone
3946 | '0' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
3947 setzoom 1.0
3949 | '1' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
3950 let zoom = zoomforh conf.winw conf.winh state.scrollw in
3951 if zoom < 1.0
3952 then setzoom zoom
3954 | '9' when (Glut.getModifiers () land Glut.active_ctrl != 0) ->
3955 togglebirdseye ()
3957 | '0' .. '9' ->
3958 let ondone s =
3959 let n =
3960 try int_of_string s with exc ->
3961 state.text <- Printf.sprintf "bad integer `%s': %s"
3962 s (Printexc.to_string exc);
3965 if n >= 0
3966 then (
3967 addnav ();
3968 cbput state.hists.pag (string_of_int n);
3969 gotopage1 (n + conf.pagebias - 1) 0;
3972 let pageentry text key =
3973 match Char.unsafe_chr key with
3974 | 'g' -> TEdone text
3975 | _ -> intentry text key
3977 let text = "x" in text.[0] <- c;
3978 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone)
3980 | 'b' ->
3981 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
3982 reshape conf.winw conf.winh;
3984 | 'l' ->
3985 conf.hlinks <- not conf.hlinks;
3986 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
3987 G.postRedisplay "toggle highlightlinks";
3989 | 'a' ->
3990 begin match state.autoscroll with
3991 | Some step ->
3992 conf.autoscrollstep <- step;
3993 state.autoscroll <- None
3994 | None ->
3995 if conf.autoscrollstep = 0
3996 then state.autoscroll <- Some 1
3997 else state.autoscroll <- Some conf.autoscrollstep
4000 | 'P' ->
4001 conf.presentation <- not conf.presentation;
4002 if conf.presentation
4003 then (
4004 if not conf.scrollbarinpm
4005 then state.scrollw <- 0;
4007 else
4008 state.scrollw <- conf.scrollbw;
4010 showtext ' ' ("presentation mode " ^
4011 if conf.presentation then "on" else "off");
4012 state.anchor <- getanchor ();
4013 represent ()
4015 | 'f' ->
4016 begin match state.fullscreen with
4017 | None ->
4018 state.fullscreen <- Some (conf.winw, conf.winh);
4019 Glut.fullScreen ()
4020 | Some (w, h) ->
4021 state.fullscreen <- None;
4022 doreshape w h
4025 | 'g' ->
4026 gotoy_and_clear_text 0
4028 | 'G' ->
4029 gotopage1 (state.pagecount - 1) 0
4031 | 'n' ->
4032 search state.searchpattern true
4034 | 'p' | 'N' ->
4035 search state.searchpattern false
4037 | 't' ->
4038 begin match state.layout with
4039 | [] -> ()
4040 | l :: _ ->
4041 gotoy_and_clear_text (getpagey l.pageno)
4044 | ' ' ->
4045 begin match List.rev state.layout with
4046 | [] -> ()
4047 | l :: _ ->
4048 let pageno = min (l.pageno+1) (state.pagecount-1) in
4049 gotoy_and_clear_text (getpagey pageno)
4052 | '\127' -> (* del *)
4053 begin match state.layout with
4054 | [] -> ()
4055 | l :: _ ->
4056 let pageno = max 0 (l.pageno-1) in
4057 gotoy_and_clear_text (getpagey pageno)
4060 | '=' ->
4061 showtext ' ' (describe_location ());
4063 | 'w' ->
4064 begin match state.layout with
4065 | [] -> ()
4066 | l :: _ ->
4067 doreshape (l.pagew + state.scrollw) l.pageh;
4068 G.postRedisplay "w"
4071 | '\'' ->
4072 enterbookmarkmode ()
4074 | 'h' ->
4075 enterhelpmode ()
4077 | 'i' ->
4078 enterinfomode ()
4080 | 'e' when conf.redirectstderr ->
4081 entermsgsmode ()
4083 | 'm' ->
4084 let ondone s =
4085 match state.layout with
4086 | l :: _ ->
4087 state.bookmarks <-
4088 (s, 0, (l.pageno, float l.pagey /. float l.pageh))
4089 :: state.bookmarks
4090 | _ -> ()
4092 enttext ("bookmark: ", "", None, textentry, ondone)
4094 | '~' ->
4095 quickbookmark ();
4096 showtext ' ' "Quick bookmark added";
4098 | 'z' ->
4099 begin match state.layout with
4100 | l :: _ ->
4101 let rect = getpdimrect l.pagedimno in
4102 let w, h =
4103 if conf.crophack
4104 then
4105 (truncate (1.8 *. (rect.(1) -. rect.(0))),
4106 truncate (1.2 *. (rect.(3) -. rect.(0))))
4107 else
4108 (truncate (rect.(1) -. rect.(0)),
4109 truncate (rect.(3) -. rect.(0)))
4111 let w = truncate ((float w)*.conf.zoom)
4112 and h = truncate ((float h)*.conf.zoom) in
4113 if w != 0 && h != 0
4114 then (
4115 state.anchor <- getanchor ();
4116 doreshape (w + state.scrollw) (h + conf.interpagespace)
4118 G.postRedisplay "z";
4120 | [] -> ()
4123 | '\000' -> (* ctrl-2 *)
4124 let maxw = getmaxw () in
4125 if maxw > 0.0
4126 then setzoom (maxw /. float conf.winw)
4128 | '<' | '>' ->
4129 reqlayout (conf.angle + (if c = '>' then 30 else -30)) conf.proportional
4131 | '[' | ']' ->
4132 conf.colorscale <-
4133 bound (conf.colorscale +. (if c = ']' then 0.1 else -0.1)) 0.0 1.0
4135 G.postRedisplay "brightness";
4137 | 'k' ->
4138 begin match state.mode with
4139 | Birdseye beye -> upbirdseye beye
4140 | _ -> gotoy (clamp (-conf.scrollstep))
4143 | 'j' ->
4144 begin match state.mode with
4145 | Birdseye beye -> downbirdseye beye
4146 | _ -> gotoy (clamp conf.scrollstep)
4149 | 'r' ->
4150 state.anchor <- getanchor ();
4151 opendoc state.path state.password
4153 | 'v' when conf.debug ->
4154 state.rects <- [];
4155 List.iter (fun l ->
4156 match getopaque l.pageno with
4157 | None -> ()
4158 | Some opaque ->
4159 let x0, y0, x1, y1 = pagebbox opaque in
4160 let a,b = float x0, float y0 in
4161 let c,d = float x1, float y0 in
4162 let e,f = float x1, float y1 in
4163 let h,j = float x0, float y1 in
4164 let rect = (a,b,c,d,e,f,h,j) in
4165 debugrect rect;
4166 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
4167 ) state.layout;
4168 G.postRedisplay "v";
4170 | _ ->
4171 vlog "huh? %d %c" key (Char.chr key);
4174 let birdseyekeyboard key ((_, _, pageno, _, _) as beye) =
4175 match key with
4176 | 27 -> (* escape *)
4177 leavebirdseye beye true
4179 | 12 -> (* ctrl-l *)
4180 let y, h = getpageyh pageno in
4181 let top = (conf.winh - h) / 2 in
4182 gotoy (max 0 (y - top))
4184 | 13 -> (* enter *)
4185 leavebirdseye beye false
4187 | _ ->
4188 viewkeyboard key
4191 let keyboard ~key ~x ~y =
4192 ignore x;
4193 ignore y;
4194 if key = 7 && not (istextentry state.mode) (* ctrl-g *)
4195 then wcmd "interrupt" []
4196 else state.uioh <- state.uioh#key key
4199 let birdseyespecial key ((conf, leftx, _, hooverpageno, anchor) as beye) =
4200 match key with
4201 | Glut.KEY_UP -> upbirdseye beye
4202 | Glut.KEY_DOWN -> downbirdseye beye
4204 | Glut.KEY_PAGE_UP ->
4205 begin match state.layout with
4206 | l :: _ ->
4207 if l.pagey != 0
4208 then (
4209 state.mode <- Birdseye (
4210 conf, leftx, l.pageno, hooverpageno, anchor
4212 gotopage1 l.pageno 0;
4214 else (
4215 let layout = layout (state.y-conf.winh) conf.winh in
4216 match layout with
4217 | [] -> gotoy (clamp (-conf.winh))
4218 | l :: _ ->
4219 state.mode <- Birdseye (
4220 conf, leftx, l.pageno, hooverpageno, anchor
4222 gotopage1 l.pageno 0
4225 | [] -> gotoy (clamp (-conf.winh))
4226 end;
4228 | Glut.KEY_PAGE_DOWN ->
4229 begin match List.rev state.layout with
4230 | l :: _ ->
4231 let layout = layout (state.y + conf.winh) conf.winh in
4232 begin match layout with
4233 | [] ->
4234 let incr = l.pageh - l.pagevh in
4235 if incr = 0
4236 then (
4237 state.mode <-
4238 Birdseye (
4239 conf, leftx, state.pagecount - 1, hooverpageno, anchor
4241 G.postRedisplay "birdseye pagedown";
4243 else gotoy (clamp (incr + conf.interpagespace*2));
4245 | l :: _ ->
4246 state.mode <-
4247 Birdseye (conf, leftx, l.pageno, hooverpageno, anchor);
4248 gotopage1 l.pageno 0;
4251 | [] -> gotoy (clamp conf.winh)
4252 end;
4254 | Glut.KEY_HOME ->
4255 state.mode <- Birdseye (conf, leftx, 0, hooverpageno, anchor);
4256 gotopage1 0 0
4258 | Glut.KEY_END ->
4259 let pageno = state.pagecount - 1 in
4260 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
4261 if not (pagevisible state.layout pageno)
4262 then
4263 let h =
4264 match List.rev state.pdims with
4265 | [] -> conf.winh
4266 | (_, _, h, _) :: _ -> h
4268 gotoy (max 0 (getpagey pageno - (conf.winh - h - conf.interpagespace)))
4269 else G.postRedisplay "birdseye end";
4270 | _ -> ()
4273 let setautoscrollspeed step goingdown =
4274 let incr = max 1 ((abs step) / 2) in
4275 let incr = if goingdown then incr else -incr in
4276 let astep = step + incr in
4277 state.autoscroll <- Some astep;
4280 let special ~key ~x ~y =
4281 ignore x;
4282 ignore y;
4283 state.uioh <- state.uioh#special key
4286 let drawpage l =
4287 let color =
4288 match state.mode with
4289 | Textentry _ -> scalecolor 0.4
4290 | View -> scalecolor 1.0
4291 | Birdseye (_, _, pageno, hooverpageno, _) ->
4292 if l.pageno = hooverpageno
4293 then scalecolor 0.9
4294 else (
4295 if l.pageno = pageno
4296 then scalecolor 1.0
4297 else scalecolor 0.8
4300 drawtiles l color;
4301 begin match getopaque l.pageno with
4302 | Some opaque ->
4303 if tileready l l.pagex l.pagey
4304 then
4305 let x = l.pagedispx - l.pagex
4306 and y = l.pagedispy - l.pagey in
4307 postprocess opaque conf.hlinks x y;
4309 | _ -> ()
4310 end;
4313 let scrollindicator () =
4314 let sbw, ph, sh = state.uioh#scrollph in
4315 let sbh, pw, sw = state.uioh#scrollpw in
4317 GlDraw.color (0.64, 0.64, 0.64);
4318 GlDraw.rect
4319 (float (conf.winw - sbw), 0.)
4320 (float conf.winw, float conf.winh)
4322 GlDraw.rect
4323 (0., float (conf.winh - sbh))
4324 (float (conf.winw - state.scrollw - 1), float conf.winh)
4326 GlDraw.color (0.0, 0.0, 0.0);
4328 GlDraw.rect
4329 (float (conf.winw - sbw), ph)
4330 (float conf.winw, ph +. sh)
4332 GlDraw.rect
4333 (pw, float (conf.winh - sbh))
4334 (pw +. sw, float conf.winh)
4338 let pagetranslatepoint l x y =
4339 let dy = y - l.pagedispy in
4340 let y = dy + l.pagey in
4341 let dx = x - l.pagedispx in
4342 let x = dx + l.pagex in
4343 (x, y);
4346 let showsel () =
4347 match state.mstate with
4348 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
4351 | Msel ((x0, y0), (x1, y1)) ->
4352 let rec loop = function
4353 | l :: ls ->
4354 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
4355 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh)))
4356 then
4357 match getopaque l.pageno with
4358 | Some opaque ->
4359 let dx, dy = pagetranslatepoint l 0 0 in
4360 let x0 = x0 + dx
4361 and y0 = y0 + dy
4362 and x1 = x1 + dx
4363 and y1 = y1 + dy in
4364 GlMat.mode `modelview;
4365 GlMat.push ();
4366 GlMat.translate ~x:(float ~-dx) ~y:(float ~-dy) ();
4367 seltext opaque (x0, y0, x1, y1);
4368 GlMat.pop ();
4369 | _ -> ()
4370 else loop ls
4371 | [] -> ()
4373 loop state.layout
4376 let showrects () =
4377 Gl.enable `blend;
4378 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
4379 GlDraw.polygon_mode `both `fill;
4380 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
4381 List.iter
4382 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
4383 List.iter (fun l ->
4384 if l.pageno = pageno
4385 then (
4386 let dx = float (l.pagedispx - l.pagex) in
4387 let dy = float (l.pagedispy - l.pagey) in
4388 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
4389 GlDraw.begins `quads;
4391 GlDraw.vertex2 (x0+.dx, y0+.dy);
4392 GlDraw.vertex2 (x1+.dx, y1+.dy);
4393 GlDraw.vertex2 (x2+.dx, y2+.dy);
4394 GlDraw.vertex2 (x3+.dx, y3+.dy);
4396 GlDraw.ends ();
4398 ) state.layout
4399 ) state.rects
4401 Gl.disable `blend;
4404 let display () =
4405 GlClear.color (scalecolor2 conf.bgcolor);
4406 GlClear.clear [`color];
4407 List.iter drawpage state.layout;
4408 showrects ();
4409 showsel ();
4410 state.uioh#display;
4411 scrollindicator ();
4412 begin match state.mstate with
4413 | Mzoomrect ((x0, y0), (x1, y1)) ->
4414 Gl.enable `blend;
4415 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
4416 GlDraw.polygon_mode `both `fill;
4417 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
4418 GlDraw.rect (float x0, float y0)
4419 (float x1, float y1);
4420 Gl.disable `blend;
4421 | _ -> ()
4422 end;
4423 enttext ();
4424 Glut.swapBuffers ();
4427 let getunder x y =
4428 let rec f = function
4429 | l :: rest ->
4430 begin match getopaque l.pageno with
4431 | Some opaque ->
4432 let x0 = l.pagedispx in
4433 let x1 = x0 + l.pagevw in
4434 let y0 = l.pagedispy in
4435 let y1 = y0 + l.pagevh in
4436 if y >= y0 && y <= y1 && x >= x0 && x <= x1
4437 then
4438 let px, py = pagetranslatepoint l x y in
4439 match whatsunder opaque px py with
4440 | Unone -> f rest
4441 | under -> under
4442 else f rest
4443 | _ ->
4444 f rest
4446 | [] -> Unone
4448 f state.layout
4451 let zoomrect x y x1 y1 =
4452 let x0 = min x x1
4453 and x1 = max x x1
4454 and y0 = min y y1 in
4455 gotoy (state.y + y0);
4456 state.anchor <- getanchor ();
4457 let zoom = (float conf.winw *. conf.zoom) /. float (x1 - x0) in
4458 let margin =
4459 if state.w < conf.winw - state.scrollw
4460 then (conf.winw - state.scrollw - state.w) / 2
4461 else 0
4463 state.x <- (state.x + margin) - x0;
4464 setzoom zoom;
4465 Glut.setCursor Glut.CURSOR_INHERIT;
4466 state.mstate <- Mnone;
4469 let scrollx x =
4470 let winw = conf.winw - state.scrollw - 1 in
4471 let s = float x /. float winw in
4472 let destx = truncate (float (state.w + winw) *. s) in
4473 state.x <- winw - destx;
4474 gotoy_and_clear_text state.y;
4475 state.mstate <- Mscrollx;
4478 let scrolly y =
4479 let s = float y /. float conf.winh in
4480 let desty = truncate (float (state.maxy - conf.winh) *. s) in
4481 gotoy_and_clear_text desty;
4482 state.mstate <- Mscrolly;
4485 let viewmouse button bstate x y =
4486 match button with
4487 | Glut.OTHER_BUTTON n when (n == 3 || n == 4) && bstate = Glut.UP ->
4488 if Glut.getModifiers () land Glut.active_ctrl != 0
4489 then (
4490 match state.mstate with
4491 | Mzoom (oldn, i) ->
4492 if oldn = n
4493 then (
4494 if i = 2
4495 then
4496 let incr =
4497 match n with
4498 | 4 ->
4499 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
4500 | _ ->
4501 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
4503 let zoom = conf.zoom -. incr in
4504 setzoom zoom;
4505 state.mstate <- Mzoom (n, 0);
4506 else
4507 state.mstate <- Mzoom (n, i+1);
4509 else state.mstate <- Mzoom (n, 0)
4511 | _ -> state.mstate <- Mzoom (n, 0)
4513 else (
4514 match state.autoscroll with
4515 | Some step -> setautoscrollspeed step (n=4)
4516 | None ->
4517 let incr =
4518 if n = 3
4519 then -conf.scrollstep
4520 else conf.scrollstep
4522 let incr = incr * 2 in
4523 let y = clamp incr in
4524 gotoy_and_clear_text y
4527 | Glut.LEFT_BUTTON when Glut.getModifiers () land Glut.active_ctrl != 0 ->
4528 if bstate = Glut.DOWN
4529 then (
4530 Glut.setCursor Glut.CURSOR_CROSSHAIR;
4531 state.mstate <- Mpan (x, y)
4533 else
4534 state.mstate <- Mnone
4536 | Glut.RIGHT_BUTTON ->
4537 if bstate = Glut.DOWN
4538 then (
4539 Glut.setCursor Glut.CURSOR_CYCLE;
4540 let p = (x, y) in
4541 state.mstate <- Mzoomrect (p, p)
4543 else (
4544 match state.mstate with
4545 | Mzoomrect ((x0, y0), _) ->
4546 if abs (x-x0) > 10 && abs (y - y0) > 10
4547 then zoomrect x0 y0 x y
4548 else (
4549 state.mstate <- Mnone;
4550 Glut.setCursor Glut.CURSOR_INHERIT;
4551 G.postRedisplay "kill accidental zoom rect";
4553 | _ ->
4554 Glut.setCursor Glut.CURSOR_INHERIT;
4555 state.mstate <- Mnone
4558 | Glut.LEFT_BUTTON when x > conf.winw - state.scrollw ->
4559 if bstate = Glut.DOWN
4560 then
4561 let _, position, sh = state.uioh#scrollph in
4562 if y > truncate position && y < truncate (position +. sh)
4563 then state.mstate <- Mscrolly
4564 else scrolly y
4565 else
4566 state.mstate <- Mnone
4568 | Glut.LEFT_BUTTON when y > conf.winh - state.hscrollh ->
4569 if bstate = Glut.DOWN
4570 then
4571 let _, position, sw = state.uioh#scrollpw in
4572 if x > truncate position && x < truncate (position +. sw)
4573 then state.mstate <- Mscrollx
4574 else scrollx x
4575 else
4576 state.mstate <- Mnone
4578 | Glut.LEFT_BUTTON ->
4579 let dest = if bstate = Glut.DOWN then getunder x y else Unone in
4580 begin match dest with
4581 | Ulinkgoto (pageno, top) ->
4582 if pageno >= 0
4583 then (
4584 addnav ();
4585 gotopage1 pageno top;
4588 | Ulinkuri s ->
4589 gotouri s
4591 | Unone when bstate = Glut.DOWN ->
4592 Glut.setCursor Glut.CURSOR_CROSSHAIR;
4593 state.mstate <- Mpan (x, y);
4595 | Unone | Utext _ ->
4596 if bstate = Glut.DOWN
4597 then (
4598 if conf.angle mod 360 = 0
4599 then (
4600 state.mstate <- Msel ((x, y), (x, y));
4601 G.postRedisplay "mouse select";
4604 else (
4605 match state.mstate with
4606 | Mnone -> ()
4608 | Mzoom _ | Mscrollx | Mscrolly ->
4609 state.mstate <- Mnone
4611 | Mzoomrect ((x0, y0), _) ->
4612 zoomrect x0 y0 x y
4614 | Mpan _ ->
4615 Glut.setCursor Glut.CURSOR_INHERIT;
4616 state.mstate <- Mnone
4618 | Msel ((_, y0), (_, y1)) ->
4619 let f l =
4620 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
4621 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh)))
4622 then
4623 match getopaque l.pageno with
4624 | Some opaque ->
4625 copysel opaque
4626 | _ -> ()
4628 List.iter f state.layout;
4629 copysel ""; (* ugly *)
4630 Glut.setCursor Glut.CURSOR_INHERIT;
4631 state.mstate <- Mnone;
4635 | _ -> ()
4638 let birdseyemouse button bstate x y
4639 (conf, leftx, _, hooverpageno, anchor) =
4640 match button with
4641 | Glut.LEFT_BUTTON when bstate = Glut.UP ->
4642 let margin = (conf.winw - (state.w + state.scrollw)) / 2 in
4643 let rec loop = function
4644 | [] -> ()
4645 | l :: rest ->
4646 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4647 && x > margin && x < margin + l.pagew
4648 then (
4649 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
4651 else loop rest
4653 loop state.layout
4654 | Glut.OTHER_BUTTON _ -> viewmouse button bstate x y
4655 | _ -> ()
4658 let mouse bstate button x y =
4659 state.uioh <- state.uioh#button button bstate x y;
4662 let mouse ~button ~state ~x ~y = mouse state button x y;;
4664 let motion ~x ~y =
4665 state.uioh <- state.uioh#motion x y
4668 let pmotion ~x ~y =
4669 state.uioh <- state.uioh#pmotion x y;
4672 let uioh = object
4673 method display = ()
4675 method key key =
4676 begin match state.mode with
4677 | Textentry textentry -> textentrykeyboard key textentry
4678 | Birdseye birdseye -> birdseyekeyboard key birdseye
4679 | View -> viewkeyboard key
4680 end;
4681 state.uioh
4683 method special key =
4684 begin match state.mode with
4685 | View | (Birdseye _) when key = Glut.KEY_F9 ->
4686 togglebirdseye ()
4688 | Birdseye vals ->
4689 birdseyespecial key vals
4691 | View when key = Glut.KEY_F1 ->
4692 enterhelpmode ()
4694 | View ->
4695 begin match state.autoscroll with
4696 | Some step when key = Glut.KEY_DOWN || key = Glut.KEY_UP ->
4697 setautoscrollspeed step (key = Glut.KEY_DOWN)
4699 | _ ->
4700 let y =
4701 match key with
4702 | Glut.KEY_F3 -> search state.searchpattern true; state.y
4703 | Glut.KEY_UP ->
4704 if Glut.getModifiers () land Glut.active_ctrl != 0
4705 then
4706 if Glut.getModifiers () land Glut.active_shift != 0
4707 then (setzoom state.prevzoom; state.y)
4708 else clamp (-conf.winh/2)
4709 else clamp (-conf.scrollstep)
4710 | Glut.KEY_DOWN ->
4711 if Glut.getModifiers () land Glut.active_ctrl != 0
4712 then
4713 if Glut.getModifiers () land Glut.active_shift != 0
4714 then (setzoom state.prevzoom; state.y)
4715 else clamp (conf.winh/2)
4716 else clamp (conf.scrollstep)
4717 | Glut.KEY_PAGE_UP ->
4718 if Glut.getModifiers () land Glut.active_ctrl != 0
4719 then
4720 match state.layout with
4721 | [] -> state.y
4722 | l :: _ -> state.y - l.pagey
4723 else
4724 clamp (-conf.winh)
4725 | Glut.KEY_PAGE_DOWN ->
4726 if Glut.getModifiers () land Glut.active_ctrl != 0
4727 then
4728 match List.rev state.layout with
4729 | [] -> state.y
4730 | l :: _ -> getpagey l.pageno
4731 else
4732 clamp conf.winh
4733 | Glut.KEY_HOME ->
4734 addnav ();
4736 | Glut.KEY_END ->
4737 addnav ();
4738 state.maxy - (if conf.maxhfit then conf.winh else 0)
4740 | (Glut.KEY_RIGHT | Glut.KEY_LEFT) when
4741 Glut.getModifiers () land Glut.active_alt != 0 ->
4742 getnav (if key = Glut.KEY_LEFT then 1 else -1)
4744 | Glut.KEY_RIGHT when conf.zoom > 1.0 ->
4745 let dx =
4746 if Glut.getModifiers () land Glut.active_ctrl != 0
4747 then (conf.winw / 2)
4748 else 10
4750 state.x <- state.x - dx;
4751 state.y
4752 | Glut.KEY_LEFT when conf.zoom > 1.0 ->
4753 let dx =
4754 if Glut.getModifiers () land Glut.active_ctrl != 0
4755 then (conf.winw / 2)
4756 else 10
4758 state.x <- state.x + dx;
4759 state.y
4761 | _ -> state.y
4763 if abs (state.y - y) > conf.scrollstep*2
4764 then gotoghyll y
4765 else gotoy_and_clear_text y
4768 | Textentry te -> textentryspecial key te
4769 end;
4770 state.uioh
4772 method button button bstate x y =
4773 begin match state.mode with
4774 | View -> viewmouse button bstate x y
4775 | Birdseye beye -> birdseyemouse button bstate x y beye
4776 | Textentry _ -> ()
4777 end;
4778 state.uioh
4780 method motion x y =
4781 begin match state.mode with
4782 | Textentry _ -> ()
4783 | View | Birdseye _ ->
4784 match state.mstate with
4785 | Mzoom _ | Mnone -> ()
4787 | Mpan (x0, y0) ->
4788 let dx = x - x0
4789 and dy = y0 - y in
4790 state.mstate <- Mpan (x, y);
4791 if conf.zoom > 1.0 then state.x <- state.x + dx;
4792 let y = clamp dy in
4793 gotoy_and_clear_text y
4795 | Msel (a, _) ->
4796 state.mstate <- Msel (a, (x, y));
4797 G.postRedisplay "motion select";
4799 | Mscrolly ->
4800 let y = min conf.winh (max 0 y) in
4801 scrolly y
4803 | Mscrollx ->
4804 let x = min conf.winw (max 0 x) in
4805 scrollx x
4807 | Mzoomrect (p0, _) ->
4808 state.mstate <- Mzoomrect (p0, (x, y));
4809 G.postRedisplay "motion zoomrect";
4810 end;
4811 state.uioh
4813 method pmotion x y =
4814 begin match state.mode with
4815 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
4816 let margin = (conf.winw - (state.w + state.scrollw)) / 2 in
4817 let rec loop = function
4818 | [] ->
4819 if hooverpageno != -1
4820 then (
4821 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
4822 G.postRedisplay "pmotion birdseye no hoover";
4824 | l :: rest ->
4825 if y > l.pagedispy && y < l.pagedispy + l.pagevh
4826 && x > margin && x < margin + l.pagew
4827 then (
4828 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
4829 G.postRedisplay "pmotion birdseye hoover";
4831 else loop rest
4833 loop state.layout
4835 | Textentry _ -> ()
4837 | View ->
4838 match state.mstate with
4839 | Mnone ->
4840 begin match getunder x y with
4841 | Unone -> Glut.setCursor Glut.CURSOR_INHERIT
4842 | Ulinkuri uri ->
4843 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
4844 Glut.setCursor Glut.CURSOR_INFO
4845 | Ulinkgoto (page, _) ->
4846 if conf.underinfo
4847 then showtext 'p' ("age: " ^ string_of_int (page+1));
4848 Glut.setCursor Glut.CURSOR_INFO
4849 | Utext s ->
4850 if conf.underinfo then showtext 'f' ("ont: " ^ s);
4851 Glut.setCursor Glut.CURSOR_TEXT
4854 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
4856 end;
4857 state.uioh
4859 method infochanged _ = ()
4861 method scrollph =
4862 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
4863 let p, h = scrollph state.y maxy in
4864 state.scrollw, p, h
4866 method scrollpw =
4867 let winw = conf.winw - state.scrollw - 1 in
4868 let fwinw = float winw in
4869 let sw =
4870 let sw = fwinw /. float state.w in
4871 let sw = fwinw *. sw in
4872 max sw (float conf.scrollh)
4874 let position, sw =
4875 let f = state.w+winw in
4876 let r = float (winw-state.x) /. float f in
4877 let p = fwinw *. r in
4878 p-.sw/.2., sw
4880 let sw =
4881 if position +. sw > fwinw
4882 then fwinw -. position
4883 else sw
4885 state.hscrollh, position, sw
4886 end;;
4888 module Config =
4889 struct
4890 open Parser
4892 let fontpath = ref "";;
4893 let wmclasshack = ref false;;
4895 let unent s =
4896 let l = String.length s in
4897 let b = Buffer.create l in
4898 unent b s 0 l;
4899 Buffer.contents b;
4902 let home =
4904 match platform with
4905 | Pwindows | Pmingw -> Sys.getenv "HOMEPATH"
4906 | _ -> Sys.getenv "HOME"
4907 with exn ->
4908 prerr_endline
4909 ("Can not determine home directory location: " ^
4910 Printexc.to_string exn);
4914 let config_of c attrs =
4915 let apply c k v =
4917 match k with
4918 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
4919 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
4920 | "case-insensitive-search" -> { c with icase = bool_of_string v }
4921 | "preload" -> { c with preload = bool_of_string v }
4922 | "page-bias" -> { c with pagebias = int_of_string v }
4923 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
4924 | "auto-scroll-step" ->
4925 { c with autoscrollstep = max 0 (int_of_string v) }
4926 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
4927 | "crop-hack" -> { c with crophack = bool_of_string v }
4928 | "throttle" ->
4929 let mw =
4930 match String.lowercase v with
4931 | "true" -> Some infinity
4932 | "false" -> None
4933 | f -> Some (float_of_string f)
4935 { c with maxwait = mw}
4936 | "highlight-links" -> { c with hlinks = bool_of_string v }
4937 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
4938 | "vertical-margin" ->
4939 { c with interpagespace = max 0 (int_of_string v) }
4940 | "zoom" ->
4941 let zoom = float_of_string v /. 100. in
4942 let zoom = max zoom 0.0 in
4943 { c with zoom = zoom }
4944 | "presentation" -> { c with presentation = bool_of_string v }
4945 | "rotation-angle" -> { c with angle = int_of_string v }
4946 | "width" -> { c with winw = max 20 (int_of_string v) }
4947 | "height" -> { c with winh = max 20 (int_of_string v) }
4948 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
4949 | "proportional-display" -> { c with proportional = bool_of_string v }
4950 | "pixmap-cache-size" ->
4951 { c with memlimit = max 2 (int_of_string_with_suffix v) }
4952 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
4953 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
4954 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
4955 | "persistent-location" -> { c with jumpback = bool_of_string v }
4956 | "background-color" -> { c with bgcolor = color_of_string v }
4957 | "scrollbar-in-presentation" ->
4958 { c with scrollbarinpm = bool_of_string v }
4959 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
4960 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
4961 | "mupdf-store-size" ->
4962 { c with mustoresize = max 1024 (int_of_string_with_suffix v) }
4963 | "checkers" -> { c with checkers = bool_of_string v }
4964 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
4965 | "trim-margins" -> { c with trimmargins = bool_of_string v }
4966 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
4967 | "wmclass-hack" -> wmclasshack := bool_of_string v; c
4968 | "uri-launcher" -> { c with urilauncher = unent v }
4969 | "color-space" -> { c with colorspace = colorspace_of_string v }
4970 | "invert-colors" -> { c with invert = bool_of_string v }
4971 | "brightness" -> { c with colorscale = float_of_string v }
4972 | "redirectstderr" -> { c with redirectstderr = bool_of_string v }
4973 | "ghyllscroll" ->
4974 { c with ghyllscroll = Some (ghyllscroll_of_string v) }
4975 | _ -> c
4976 with exn ->
4977 prerr_endline ("Error processing attribute (`" ^
4978 k ^ "'=`" ^ v ^ "'): " ^ Printexc.to_string exn);
4981 let rec fold c = function
4982 | [] -> c
4983 | (k, v) :: rest ->
4984 let c = apply c k v in
4985 fold c rest
4987 fold c attrs;
4990 let fromstring f pos n v d =
4991 try f v
4992 with exn ->
4993 dolog "Error processing attribute (%S=%S) at %d\n%s"
4994 n v pos (Printexc.to_string exn)
4999 let bookmark_of attrs =
5000 let rec fold title page rely = function
5001 | ("title", v) :: rest -> fold v page rely rest
5002 | ("page", v) :: rest -> fold title v rely rest
5003 | ("rely", v) :: rest -> fold title page v rest
5004 | _ :: rest -> fold title page rely rest
5005 | [] -> title, page, rely
5007 fold "invalid" "0" "0" attrs
5010 let doc_of attrs =
5011 let rec fold path page rely pan = function
5012 | ("path", v) :: rest -> fold v page rely pan rest
5013 | ("page", v) :: rest -> fold path v rely pan rest
5014 | ("rely", v) :: rest -> fold path page v pan rest
5015 | ("pan", v) :: rest -> fold path page rely v rest
5016 | _ :: rest -> fold path page rely pan rest
5017 | [] -> path, page, rely, pan
5019 fold "" "0" "0" "0" attrs
5022 let setconf dst src =
5023 dst.scrollbw <- src.scrollbw;
5024 dst.scrollh <- src.scrollh;
5025 dst.icase <- src.icase;
5026 dst.preload <- src.preload;
5027 dst.pagebias <- src.pagebias;
5028 dst.verbose <- src.verbose;
5029 dst.scrollstep <- src.scrollstep;
5030 dst.maxhfit <- src.maxhfit;
5031 dst.crophack <- src.crophack;
5032 dst.autoscrollstep <- src.autoscrollstep;
5033 dst.maxwait <- src.maxwait;
5034 dst.hlinks <- src.hlinks;
5035 dst.underinfo <- src.underinfo;
5036 dst.interpagespace <- src.interpagespace;
5037 dst.zoom <- src.zoom;
5038 dst.presentation <- src.presentation;
5039 dst.angle <- src.angle;
5040 dst.winw <- src.winw;
5041 dst.winh <- src.winh;
5042 dst.savebmarks <- src.savebmarks;
5043 dst.memlimit <- src.memlimit;
5044 dst.proportional <- src.proportional;
5045 dst.texcount <- src.texcount;
5046 dst.sliceheight <- src.sliceheight;
5047 dst.thumbw <- src.thumbw;
5048 dst.jumpback <- src.jumpback;
5049 dst.bgcolor <- src.bgcolor;
5050 dst.scrollbarinpm <- src.scrollbarinpm;
5051 dst.tilew <- src.tilew;
5052 dst.tileh <- src.tileh;
5053 dst.mustoresize <- src.mustoresize;
5054 dst.checkers <- src.checkers;
5055 dst.aalevel <- src.aalevel;
5056 dst.trimmargins <- src.trimmargins;
5057 dst.trimfuzz <- src.trimfuzz;
5058 dst.urilauncher <- src.urilauncher;
5059 dst.colorspace <- src.colorspace;
5060 dst.invert <- src.invert;
5061 dst.colorscale <- src.colorscale;
5062 dst.redirectstderr <- src.redirectstderr;
5063 dst.ghyllscroll <- src.ghyllscroll;
5066 let get s =
5067 let h = Hashtbl.create 10 in
5068 let dc = { defconf with angle = defconf.angle } in
5069 let rec toplevel v t spos _ =
5070 match t with
5071 | Vdata | Vcdata | Vend -> v
5072 | Vopen ("llppconfig", _, closed) ->
5073 if closed
5074 then v
5075 else { v with f = llppconfig }
5076 | Vopen _ ->
5077 error "unexpected subelement at top level" s spos
5078 | Vclose _ -> error "unexpected close at top level" s spos
5080 and llppconfig v t spos _ =
5081 match t with
5082 | Vdata | Vcdata -> v
5083 | Vend -> error "unexpected end of input in llppconfig" s spos
5084 | Vopen ("defaults", attrs, closed) ->
5085 let c = config_of dc attrs in
5086 setconf dc c;
5087 if closed
5088 then v
5089 else { v with f = skip "defaults" (fun () -> v) }
5091 | Vopen ("ui-font", attrs, closed) ->
5092 let rec getsize size = function
5093 | [] -> size
5094 | ("size", v) :: rest ->
5095 let size =
5096 fromstring int_of_string spos "size" v fstate.fontsize in
5097 getsize size rest
5098 | l -> getsize size l
5100 fstate.fontsize <- getsize fstate.fontsize attrs;
5101 if closed
5102 then v
5103 else { v with f = uifont (Buffer.create 10) }
5105 | Vopen ("doc", attrs, closed) ->
5106 let pathent, spage, srely, span = doc_of attrs in
5107 let path = unent pathent
5108 and pageno = fromstring int_of_string spos "page" spage 0
5109 and rely = fromstring float_of_string spos "rely" srely 0.0
5110 and pan = fromstring int_of_string spos "pan" span 0 in
5111 let c = config_of dc attrs in
5112 let anchor = (pageno, rely) in
5113 if closed
5114 then (Hashtbl.add h path (c, [], pan, anchor); v)
5115 else { v with f = doc path pan anchor c [] }
5117 | Vopen _ ->
5118 error "unexpected subelement in llppconfig" s spos
5120 | Vclose "llppconfig" -> { v with f = toplevel }
5121 | Vclose _ -> error "unexpected close in llppconfig" s spos
5123 and uifont b v t spos epos =
5124 match t with
5125 | Vdata | Vcdata ->
5126 Buffer.add_substring b s spos (epos - spos);
5128 | Vopen (_, _, _) ->
5129 error "unexpected subelement in ui-font" s spos
5130 | Vclose "ui-font" ->
5131 if String.length !fontpath = 0
5132 then fontpath := Buffer.contents b;
5133 { v with f = llppconfig }
5134 | Vclose _ -> error "unexpected close in ui-font" s spos
5135 | Vend -> error "unexpected end of input in ui-font" s spos
5137 and doc path pan anchor c bookmarks v t spos _ =
5138 match t with
5139 | Vdata | Vcdata -> v
5140 | Vend -> error "unexpected end of input in doc" s spos
5141 | Vopen ("bookmarks", _, closed) ->
5142 if closed
5143 then v
5144 else { v with f = pbookmarks path pan anchor c bookmarks }
5146 | Vopen (_, _, _) ->
5147 error "unexpected subelement in doc" s spos
5149 | Vclose "doc" ->
5150 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
5151 { v with f = llppconfig }
5153 | Vclose _ -> error "unexpected close in doc" s spos
5155 and pbookmarks path pan anchor c bookmarks v t spos _ =
5156 match t with
5157 | Vdata | Vcdata -> v
5158 | Vend -> error "unexpected end of input in bookmarks" s spos
5159 | Vopen ("item", attrs, closed) ->
5160 let titleent, spage, srely = bookmark_of attrs in
5161 let page = fromstring int_of_string spos "page" spage 0
5162 and rely = fromstring float_of_string spos "rely" srely 0.0 in
5163 let bookmarks = (unent titleent, 0, (page, rely)) :: bookmarks in
5164 if closed
5165 then { v with f = pbookmarks path pan anchor c bookmarks }
5166 else
5167 let f () = v in
5168 { v with f = skip "item" f }
5170 | Vopen _ ->
5171 error "unexpected subelement in bookmarks" s spos
5173 | Vclose "bookmarks" ->
5174 { v with f = doc path pan anchor c bookmarks }
5176 | Vclose _ -> error "unexpected close in bookmarks" s spos
5178 and skip tag f v t spos _ =
5179 match t with
5180 | Vdata | Vcdata -> v
5181 | Vend ->
5182 error ("unexpected end of input in skipped " ^ tag) s spos
5183 | Vopen (tag', _, closed) ->
5184 if closed
5185 then v
5186 else
5187 let f' () = { v with f = skip tag f } in
5188 { v with f = skip tag' f' }
5189 | Vclose ctag ->
5190 if tag = ctag
5191 then f ()
5192 else error ("unexpected close in skipped " ^ tag) s spos
5195 parse { f = toplevel; accu = () } s;
5196 h, dc;
5199 let do_load f ic =
5201 let len = in_channel_length ic in
5202 let s = String.create len in
5203 really_input ic s 0 len;
5204 f s;
5205 with
5206 | Parse_error (msg, s, pos) ->
5207 let subs = subs s pos in
5208 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
5209 failwith ("parse error: " ^ s)
5211 | exn ->
5212 failwith ("config load error: " ^ Printexc.to_string exn)
5215 let defconfpath =
5216 let dir =
5218 let dir = Filename.concat home ".config" in
5219 if Sys.is_directory dir then dir else home
5220 with _ -> home
5222 Filename.concat dir "llpp.conf"
5225 let confpath = ref defconfpath;;
5227 let load1 f =
5228 if Sys.file_exists !confpath
5229 then
5230 match
5231 (try Some (open_in_bin !confpath)
5232 with exn ->
5233 prerr_endline
5234 ("Error opening configuation file `" ^ !confpath ^ "': " ^
5235 Printexc.to_string exn);
5236 None
5238 with
5239 | Some ic ->
5240 begin try
5241 f (do_load get ic)
5242 with exn ->
5243 prerr_endline
5244 ("Error loading configuation from `" ^ !confpath ^ "': " ^
5245 Printexc.to_string exn);
5246 end;
5247 close_in ic;
5249 | None -> ()
5250 else
5251 f (Hashtbl.create 0, defconf)
5254 let load () =
5255 let f (h, dc) =
5256 let pc, pb, px, pa =
5258 Hashtbl.find h (Filename.basename state.path)
5259 with Not_found -> dc, [], 0, (0, 0.0)
5261 setconf defconf dc;
5262 setconf conf pc;
5263 state.bookmarks <- pb;
5264 state.x <- px;
5265 state.scrollw <- conf.scrollbw;
5266 if conf.jumpback
5267 then state.anchor <- pa;
5268 cbput state.hists.nav pa;
5270 load1 f
5273 let add_attrs bb always dc c =
5274 let ob s a b =
5275 if always || a != b
5276 then Printf.bprintf bb "\n %s='%b'" s a
5277 and oi s a b =
5278 if always || a != b
5279 then Printf.bprintf bb "\n %s='%d'" s a
5280 and oI s a b =
5281 if always || a != b
5282 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
5283 and oz s a b =
5284 if always || a <> b
5285 then Printf.bprintf bb "\n %s='%d'" s (truncate (a*.100.))
5286 and oF s a b =
5287 if always || a <> b
5288 then Printf.bprintf bb "\n %s='%f'" s a
5289 and oc s a b =
5290 if always || a <> b
5291 then
5292 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
5293 and oC s a b =
5294 if always || a <> b
5295 then
5296 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
5297 and oR s a b =
5298 if always || a <> b
5299 then
5300 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
5301 and os s a b =
5302 if always || a <> b
5303 then
5304 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
5305 and og s a b =
5306 if always || a <> b
5307 then
5308 match a with
5309 | None -> ()
5310 | Some (_N, _A, _B) ->
5311 Printf.bprintf bb "\n %s='%u,%u,%u'" s _N _A _B
5312 and oW s a b =
5313 if always || a <> b
5314 then
5315 let v =
5316 match a with
5317 | None -> "false"
5318 | Some f ->
5319 if f = infinity
5320 then "true"
5321 else string_of_float f
5323 Printf.bprintf bb "\n %s='%s'" s v
5325 let w, h =
5326 if always
5327 then dc.winw, dc.winh
5328 else
5329 match state.fullscreen with
5330 | Some wh -> wh
5331 | None -> c.winw, c.winh
5333 let zoom, presentation, interpagespace, maxwait =
5334 if always
5335 then dc.zoom, dc.presentation, dc.interpagespace, dc.maxwait
5336 else
5337 match state.mode with
5338 | Birdseye (bc, _, _, _, _) ->
5339 bc.zoom, bc.presentation, bc.interpagespace, bc.maxwait
5340 | _ -> c.zoom, c.presentation, c.interpagespace, c.maxwait
5342 oi "width" w dc.winw;
5343 oi "height" h dc.winh;
5344 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
5345 oi "scroll-handle-height" c.scrollh dc.scrollh;
5346 ob "case-insensitive-search" c.icase dc.icase;
5347 ob "preload" c.preload dc.preload;
5348 oi "page-bias" c.pagebias dc.pagebias;
5349 oi "scroll-step" c.scrollstep dc.scrollstep;
5350 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
5351 ob "max-height-fit" c.maxhfit dc.maxhfit;
5352 ob "crop-hack" c.crophack dc.crophack;
5353 oW "throttle" maxwait dc.maxwait;
5354 ob "highlight-links" c.hlinks dc.hlinks;
5355 ob "under-cursor-info" c.underinfo dc.underinfo;
5356 oi "vertical-margin" interpagespace dc.interpagespace;
5357 oz "zoom" zoom dc.zoom;
5358 ob "presentation" presentation dc.presentation;
5359 oi "rotation-angle" c.angle dc.angle;
5360 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
5361 ob "proportional-display" c.proportional dc.proportional;
5362 oI "pixmap-cache-size" c.memlimit dc.memlimit;
5363 oi "tex-count" c.texcount dc.texcount;
5364 oi "slice-height" c.sliceheight dc.sliceheight;
5365 oi "thumbnail-width" c.thumbw dc.thumbw;
5366 ob "persistent-location" c.jumpback dc.jumpback;
5367 oc "background-color" c.bgcolor dc.bgcolor;
5368 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
5369 oi "tile-width" c.tilew dc.tilew;
5370 oi "tile-height" c.tileh dc.tileh;
5371 oI "mupdf-store-size" c.mustoresize dc.mustoresize;
5372 ob "checkers" c.checkers dc.checkers;
5373 oi "aalevel" c.aalevel dc.aalevel;
5374 ob "trim-margins" c.trimmargins dc.trimmargins;
5375 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
5376 os "uri-launcher" c.urilauncher dc.urilauncher;
5377 oC "color-space" c.colorspace dc.colorspace;
5378 ob "invert-colors" c.invert dc.invert;
5379 oF "brightness" c.colorscale dc.colorscale;
5380 ob "redirectstderr" c.redirectstderr dc.redirectstderr;
5381 og "ghyllscroll" c.ghyllscroll dc.ghyllscroll;
5382 if always
5383 then ob "wmclass-hack" !wmclasshack false;
5386 let save () =
5387 let uifontsize = fstate.fontsize in
5388 let bb = Buffer.create 32768 in
5389 let f (h, dc) =
5390 let dc = if conf.bedefault then conf else dc in
5391 Buffer.add_string bb "<llppconfig>\n";
5393 if String.length !fontpath > 0
5394 then
5395 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
5396 uifontsize
5397 !fontpath
5398 else (
5399 if uifontsize <> 14
5400 then
5401 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
5404 Buffer.add_string bb "<defaults ";
5405 add_attrs bb true dc dc;
5406 Buffer.add_string bb "/>\n";
5408 let adddoc path pan anchor c bookmarks =
5409 if bookmarks == [] && c = dc && anchor = emptyanchor
5410 then ()
5411 else (
5412 Printf.bprintf bb "<doc path='%s'"
5413 (enent path 0 (String.length path));
5415 if anchor <> emptyanchor
5416 then (
5417 let n, y = anchor in
5418 Printf.bprintf bb " page='%d'" n;
5419 if y > 1e-6
5420 then
5421 Printf.bprintf bb " rely='%f'" y
5425 if pan != 0
5426 then Printf.bprintf bb " pan='%d'" pan;
5428 add_attrs bb false dc c;
5430 begin match bookmarks with
5431 | [] -> Buffer.add_string bb "/>\n"
5432 | _ ->
5433 Buffer.add_string bb ">\n<bookmarks>\n";
5434 List.iter (fun (title, _level, (page, rely)) ->
5435 Printf.bprintf bb
5436 "<item title='%s' page='%d'"
5437 (enent title 0 (String.length title))
5438 page
5440 if rely > 1e-6
5441 then
5442 Printf.bprintf bb " rely='%f'" rely
5444 Buffer.add_string bb "/>\n";
5445 ) bookmarks;
5446 Buffer.add_string bb "</bookmarks>\n</doc>\n";
5447 end;
5451 let pan =
5452 match state.mode with
5453 | Birdseye (_, pan, _, _, _) -> pan
5454 | _ -> state.x
5456 let basename = Filename.basename state.path in
5457 adddoc basename pan (getanchor ())
5458 { conf with
5459 autoscrollstep =
5460 match state.autoscroll with
5461 | Some step -> step
5462 | None -> conf.autoscrollstep }
5463 (if conf.savebmarks then state.bookmarks else []);
5465 Hashtbl.iter (fun path (c, bookmarks, x, y) ->
5466 if basename <> path
5467 then adddoc path x y c bookmarks
5468 ) h;
5469 Buffer.add_string bb "</llppconfig>";
5471 load1 f;
5472 if Buffer.length bb > 0
5473 then
5475 let tmp = !confpath ^ ".tmp" in
5476 let oc = open_out_bin tmp in
5477 Buffer.output_buffer oc bb;
5478 close_out oc;
5479 Unix.rename tmp !confpath;
5480 with exn ->
5481 prerr_endline
5482 ("error while saving configuration: " ^ Printexc.to_string exn)
5484 end;;
5486 let () =
5487 Arg.parse
5488 (Arg.align
5489 [("-p", Arg.String (fun s -> state.password <- s) ,
5490 "<password> Set password");
5492 ("-f", Arg.String (fun s -> Config.fontpath := s),
5493 "<path> Set path to the user interface font");
5495 ("-c", Arg.String (fun s -> Config.confpath := s),
5496 "<path> Set path to the configuration file");
5498 ("-v", Arg.Unit (fun () ->
5499 Printf.printf
5500 "%s\nconfiguration path: %s\n"
5501 (version ())
5502 Config.defconfpath
5504 exit 0), " Print version and exit");
5507 (fun s -> state.path <- s)
5508 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
5510 if String.length state.path = 0
5511 then (prerr_endline "file name missing"; exit 1);
5513 Config.load ();
5515 let _ = Glut.init Sys.argv in
5516 let () = Glut.initDisplayMode ~depth:false ~double_buffer:true () in
5517 let () = Glut.initWindowSize conf.winw conf.winh in
5518 let _ = Glut.createWindow ("llpp " ^ Filename.basename state.path) in
5520 if not (Glut.extensionSupported "GL_ARB_texture_rectangle"
5521 || Glut.extensionSupported "GL_EXT_texture_rectangle")
5522 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
5524 let csock, ssock =
5525 if not is_windows
5526 then
5527 Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0
5528 else
5529 let addr = Unix.ADDR_INET (Unix.inet_addr_loopback, 1337) in
5530 let sock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
5531 Unix.setsockopt sock Unix.SO_REUSEADDR true;
5532 Unix.bind sock addr;
5533 Unix.listen sock 1;
5534 let csock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in
5535 Unix.connect csock addr;
5536 let ssock, _ = Unix.accept sock in
5537 Unix.close sock;
5538 let opts sock =
5539 Unix.setsockopt sock Unix.TCP_NODELAY true;
5540 Unix.setsockopt_optint sock Unix.SO_LINGER None;
5542 opts ssock;
5543 opts csock;
5544 ssock, csock
5547 let () = Glut.displayFunc display in
5548 let () = Glut.reshapeFunc reshape in
5549 let () = Glut.keyboardFunc keyboard in
5550 let () = Glut.specialFunc special in
5551 let () = Glut.idleFunc (Some idle) in
5552 let () = Glut.mouseFunc mouse in
5553 let () = Glut.motionFunc motion in
5554 let () = Glut.passiveMotionFunc pmotion in
5556 setcheckers conf.checkers;
5557 init ssock (
5558 conf.angle, conf.proportional, (conf.trimmargins, conf.trimfuzz),
5559 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
5560 !Config.wmclasshack, !Config.fontpath
5562 state.csock <- csock;
5563 state.ssock <- ssock;
5564 state.text <- "Opening " ^ state.path;
5565 setaalevel conf.aalevel;
5566 writeopen state.path state.password;
5567 state.uioh <- uioh;
5568 setfontsize fstate.fontsize;
5570 redirectstderr ();
5572 while true do
5574 Glut.mainLoop ();
5575 with
5576 | Glut.BadEnum "key in special_of_int" ->
5577 showtext '!' " LablGlut bug: special key not recognized";
5579 | Quit ->
5580 wcmd "quit" [];
5581 Config.save ();
5582 exit 0
5584 | exn when conf.redirectstderr ->
5585 let s =
5586 Printf.sprintf "exception %s\n%s"
5587 (Printexc.to_string exn)
5588 (Printexc.get_backtrace ())
5590 ignore (try
5591 Unix.single_write state.stderr s 0 (String.length s);
5592 with _ -> 0);
5593 exit 1
5594 done;