baa12b04e3d96ac0186b21d13a8dc38b496b26bd
[llpp.git] / main.ml
blobbaa12b04e3d96ac0186b21d13a8dc38b496b26bd
1 exception Quit;;
3 type under =
4 | Unone
5 | Ulinkuri of string
6 | Ulinkgoto of (int * int)
7 | Utext of facename
8 | Uunexpected of string
9 | Ulaunch of string
10 | Unamed of string
11 | Uremote of (string * int)
12 and facename = string;;
14 let dolog fmt = Printf.kprintf prerr_endline fmt;;
15 let now = Unix.gettimeofday;;
17 type params = (angle * proportional * trimparams
18 * texcount * sliceheight * memsize
19 * colorspace * fontpath * trimcachepath)
20 and pageno = int
21 and width = int
22 and height = int
23 and leftx = int
24 and opaque = string
25 and recttype = int
26 and pixmapsize = int
27 and angle = int
28 and proportional = bool
29 and trimmargins = bool
30 and interpagespace = int
31 and texcount = int
32 and sliceheight = int
33 and gen = int
34 and top = float
35 and dtop = float
36 and fontpath = string
37 and trimcachepath = string
38 and memsize = int
39 and aalevel = int
40 and irect = (int * int * int * int)
41 and trimparams = (trimmargins * irect)
42 and colorspace = | Rgb | Bgr | Gray
45 type link =
46 | Lnotfound
47 | Lfound of int
48 and linkdir =
49 | LDfirst
50 | LDlast
51 | LDfirstvisible of (int * int * int)
52 | LDleft of int
53 | LDright of int
54 | LDdown of int
55 | LDup of int
58 type pagewithlinks =
59 | Pwlnotfound
60 | Pwl of int
63 type keymap =
64 | KMinsrt of key
65 | KMinsrl of key list
66 | KMmulti of key list * key list
67 and key = int * int
68 and keyhash = (key, keymap) Hashtbl.t
69 and keystate =
70 | KSnone
71 | KSinto of (key list * key list)
74 type platform = | Punknown | Plinux | Posx | Psun | Pfreebsd
75 | Pdragonflybsd | Popenbsd | Pnetbsd | Pcygwin;;
77 type pipe = (Unix.file_descr * Unix.file_descr);;
79 external init : pipe -> params -> unit = "ml_init";;
80 external seltext : string -> (int * int * int * int) -> unit = "ml_seltext";;
81 external copysel : Unix.file_descr -> opaque -> unit = "ml_copysel";;
82 external getpdimrect : int -> float array = "ml_getpdimrect";;
83 external whatsunder : string -> int -> int -> under = "ml_whatsunder";;
84 external zoomforh : int -> int -> int -> int -> float = "ml_zoom_for_height";;
85 external drawstr : int -> int -> int -> string -> float = "ml_draw_string";;
86 external measurestr : int -> string -> float = "ml_measure_string";;
87 external getmaxw : unit -> float = "ml_getmaxw";;
88 external postprocess :
89 opaque -> int -> int -> int -> (int * string * int) -> int = "ml_postprocess";;
90 external pagebbox : opaque -> (int * int * int * int) = "ml_getpagebox";;
91 external platform : unit -> platform = "ml_platform";;
92 external setaalevel : int -> unit = "ml_setaalevel";;
93 external realloctexts : int -> bool = "ml_realloctexts";;
94 external cloexec : Unix.file_descr -> unit = "ml_cloexec";;
95 external findlink : opaque -> linkdir -> link = "ml_findlink";;
96 external getlink : opaque -> int -> under = "ml_getlink";;
97 external getlinkrect : opaque -> int -> irect = "ml_getlinkrect";;
98 external getlinkcount : opaque -> int = "ml_getlinkcount";;
99 external findpwl: int -> int -> pagewithlinks = "ml_find_page_with_links"
100 external popen : string -> (Unix.file_descr * int) list -> unit = "ml_popen";;
101 external mbtoutf8 : string -> string = "ml_mbtoutf8";;
103 let platform_to_string = function
104 | Punknown -> "unknown"
105 | Plinux -> "Linux"
106 | Posx -> "OSX"
107 | Psun -> "Sun"
108 | Pfreebsd -> "FreeBSD"
109 | Pdragonflybsd -> "DragonflyBSD"
110 | Popenbsd -> "OpenBSD"
111 | Pnetbsd -> "NetBSD"
112 | Pcygwin -> "Cygwin"
115 let platform = platform ();;
117 let popen cmd fda =
118 if platform = Pcygwin
119 then (
120 let sh = "/bin/sh" in
121 let args = [|sh; "-c"; cmd|] in
122 let rec std si so se = function
123 | [] -> si, so, se
124 | (fd, 0) :: rest -> std fd so se rest
125 | (fd, -1) :: rest ->
126 Unix.set_close_on_exec fd;
127 std si so se rest
128 | (_, n) :: _ ->
129 failwith ("unexpected fdn in cygwin popen " ^ string_of_int n)
131 let si, so, se = std Unix.stdin Unix.stdout Unix.stderr fda in
132 ignore (Unix.create_process sh args si so se)
134 else popen cmd fda;
137 type x = int
138 and y = int
139 and tilex = int
140 and tiley = int
141 and tileparams = (x * y * width * height * tilex * tiley)
144 external drawtile : tileparams -> opaque -> unit = "ml_drawtile";;
146 type mpos = int * int
147 and mstate =
148 | Msel of (mpos * mpos)
149 | Mpan of mpos
150 | Mscrolly | Mscrollx
151 | Mzoom of (int * int)
152 | Mzoomrect of (mpos * mpos)
153 | Mnone
156 type textentry = string * string * onhist option * onkey * ondone * cancelonempty
157 and onkey = string -> int -> te
158 and ondone = string -> unit
159 and histcancel = unit -> unit
160 and onhist = ((histcmd -> string) * histcancel)
161 and histcmd = HCnext | HCprev | HCfirst | HClast
162 and cancelonempty = bool
163 and te =
164 | TEstop
165 | TEdone of string
166 | TEcont of string
167 | TEswitch of textentry
170 type 'a circbuf =
171 { store : 'a array
172 ; mutable rc : int
173 ; mutable wc : int
174 ; mutable len : int
178 let bound v minv maxv =
179 max minv (min maxv v);
182 let cbnew n v =
183 { store = Array.create n v
184 ; rc = 0
185 ; wc = 0
186 ; len = 0
190 let cbcap b = Array.length b.store;;
192 let cbput b v =
193 let cap = cbcap b in
194 b.store.(b.wc) <- v;
195 b.wc <- (b.wc + 1) mod cap;
196 b.rc <- b.wc;
197 b.len <- min (b.len + 1) cap;
200 let cbempty b = b.len = 0;;
202 let cbgetg b circular dir =
203 if cbempty b
204 then b.store.(0)
205 else
206 let rc = b.rc + dir in
207 let rc =
208 if circular
209 then (
210 if rc = -1
211 then b.len-1
212 else (
213 if rc >= b.len
214 then 0
215 else rc
218 else bound rc 0 (b.len-1)
220 b.rc <- rc;
221 b.store.(rc);
224 let cbget b = cbgetg b false;;
225 let cbgetc b = cbgetg b true;;
227 let drawstring size x y s =
228 Gl.enable `blend;
229 Gl.enable `texture_2d;
230 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
231 ignore (drawstr size x y s);
232 Gl.disable `blend;
233 Gl.disable `texture_2d;
236 let drawstring1 size x y s =
237 drawstr size x y s;
240 let drawstring2 size x y fmt =
241 Printf.kprintf (drawstring size (x+1) (y+size+1)) fmt
244 type page =
245 { pageno : int
246 ; pagedimno : int
247 ; pagew : int
248 ; pageh : int
249 ; pagex : int
250 ; pagey : int
251 ; pagevw : int
252 ; pagevh : int
253 ; pagedispx : int
254 ; pagedispy : int
255 ; pagecol : int
259 let debugl l =
260 dolog "l %d dim=%d {" l.pageno l.pagedimno;
261 dolog " WxH %dx%d" l.pagew l.pageh;
262 dolog " vWxH %dx%d" l.pagevw l.pagevh;
263 dolog " pagex,y %d,%d" l.pagex l.pagey;
264 dolog " dispx,y %d,%d" l.pagedispx l.pagedispy;
265 dolog " column %d" l.pagecol;
266 dolog "}";
269 let debugrect (x0, y0, x1, y1, x2, y2, x3, y3) =
270 dolog "rect {";
271 dolog " x0,y0=(% f, % f)" x0 y0;
272 dolog " x1,y1=(% f, % f)" x1 y1;
273 dolog " x2,y2=(% f, % f)" x2 y2;
274 dolog " x3,y3=(% f, % f)" x3 y3;
275 dolog "}";
278 type multicolumns = multicol * pagegeom
279 and singlecolumn = pagegeom
280 and splitcolumns = columncount * pagegeom
281 and pagegeom = ((pdimno * x * y * (pageno * width * height * leftx)) array)
282 and multicol = columncount * covercount * covercount
283 and pdimno = int
284 and columncount = int
285 and covercount = int;;
287 type conf =
288 { mutable scrollbw : int
289 ; mutable scrollh : int
290 ; mutable icase : bool
291 ; mutable preload : bool
292 ; mutable pagebias : int
293 ; mutable verbose : bool
294 ; mutable debug : bool
295 ; mutable scrollstep : int
296 ; mutable hscrollstep : int
297 ; mutable maxhfit : bool
298 ; mutable crophack : bool
299 ; mutable autoscrollstep : int
300 ; mutable maxwait : float option
301 ; mutable hlinks : bool
302 ; mutable underinfo : bool
303 ; mutable interpagespace : interpagespace
304 ; mutable zoom : float
305 ; mutable presentation : bool
306 ; mutable angle : angle
307 ; mutable winw : int
308 ; mutable winh : int
309 ; mutable savebmarks : bool
310 ; mutable proportional : proportional
311 ; mutable trimmargins : trimmargins
312 ; mutable trimfuzz : irect
313 ; mutable memlimit : memsize
314 ; mutable texcount : texcount
315 ; mutable sliceheight : sliceheight
316 ; mutable thumbw : width
317 ; mutable jumpback : bool
318 ; mutable bgcolor : float * float * float
319 ; mutable bedefault : bool
320 ; mutable scrollbarinpm : bool
321 ; mutable tilew : int
322 ; mutable tileh : int
323 ; mutable mustoresize : memsize
324 ; mutable checkers : bool
325 ; mutable aalevel : int
326 ; mutable urilauncher : string
327 ; mutable pathlauncher : string
328 ; mutable colorspace : colorspace
329 ; mutable invert : bool
330 ; mutable colorscale : float
331 ; mutable redirectstderr : bool
332 ; mutable ghyllscroll : (int * int * int) option
333 ; mutable columns : columns
334 ; mutable beyecolumns : columncount option
335 ; mutable selcmd : string
336 ; mutable updatecurs : bool
337 ; mutable keyhashes : (string * keyhash) list
338 ; mutable hfsize : int
339 ; mutable pgscale : float
341 and columns =
342 | Csingle of singlecolumn
343 | Cmulti of multicolumns
344 | Csplit of splitcolumns
347 type anchor = pageno * top * dtop;;
349 type outline = string * int * anchor;;
351 type rect = float * float * float * float * float * float * float * float;;
353 type tile = opaque * pixmapsize * elapsed
354 and elapsed = float;;
355 type pagemapkey = pageno * gen;;
356 type tilemapkey = pageno * gen * colorspace * angle * width * height * col * row
357 and row = int
358 and col = int;;
360 let emptyanchor = (0, 0.0, 0.0);;
362 type infochange = | Memused | Docinfo | Pdim;;
364 class type uioh = object
365 method display : unit
366 method key : int -> int -> uioh
367 method button : int -> bool -> int -> int -> int -> uioh
368 method motion : int -> int -> uioh
369 method pmotion : int -> int -> uioh
370 method infochanged : infochange -> unit
371 method scrollpw : (int * float * float)
372 method scrollph : (int * float * float)
373 method modehash : keyhash
374 end;;
376 type mode =
377 | Birdseye of (conf * leftx * pageno * pageno * anchor)
378 | Textentry of (textentry * onleave)
379 | View
380 | LinkNav of linktarget
381 and onleave = leavetextentrystatus -> unit
382 and leavetextentrystatus = | Cancel | Confirm
383 and helpitem = string * int * action
384 and action =
385 | Noaction
386 | Action of (uioh -> uioh)
387 and linktarget =
388 | Ltexact of (pageno * int)
389 | Ltgendir of int
392 let isbirdseye = function Birdseye _ -> true | _ -> false;;
393 let istextentry = function Textentry _ -> true | _ -> false;;
395 type currently =
396 | Idle
397 | Loading of (page * gen)
398 | Tiling of (
399 page * opaque * colorspace * angle * gen * col * row * width * height
401 | Outlining of outline list
404 let emptykeyhash = Hashtbl.create 0;;
405 let nouioh : uioh = object (self)
406 method display = ()
407 method key _ _ = self
408 method button _ _ _ _ _ = self
409 method motion _ _ = self
410 method pmotion _ _ = self
411 method infochanged _ = ()
412 method scrollpw = (0, nan, nan)
413 method scrollph = (0, nan, nan)
414 method modehash = emptykeyhash
415 end;;
417 type state =
418 { mutable sr : Unix.file_descr
419 ; mutable sw : Unix.file_descr
420 ; mutable wsfd : Unix.file_descr
421 ; mutable errfd : Unix.file_descr option
422 ; mutable stderr : Unix.file_descr
423 ; mutable errmsgs : Buffer.t
424 ; mutable newerrmsgs : bool
425 ; mutable w : int
426 ; mutable x : int
427 ; mutable y : int
428 ; mutable scrollw : int
429 ; mutable hscrollh : int
430 ; mutable anchor : anchor
431 ; mutable ranchors : (string * string * anchor) list
432 ; mutable maxy : int
433 ; mutable layout : page list
434 ; pagemap : (pagemapkey, opaque) Hashtbl.t
435 ; tilemap : (tilemapkey, tile) Hashtbl.t
436 ; tilelru : (tilemapkey * opaque * pixmapsize) Queue.t
437 ; mutable pdims : (pageno * width * height * leftx) list
438 ; mutable pagecount : int
439 ; mutable currently : currently
440 ; mutable mstate : mstate
441 ; mutable searchpattern : string
442 ; mutable rects : (pageno * recttype * rect) list
443 ; mutable rects1 : (pageno * recttype * rect) list
444 ; mutable text : string
445 ; mutable fullscreen : (width * height) option
446 ; mutable mode : mode
447 ; mutable uioh : uioh
448 ; mutable outlines : outline array
449 ; mutable bookmarks : outline list
450 ; mutable path : string
451 ; mutable password : string
452 ; mutable geomcmds : (string * ((string * (unit -> unit)) list))
453 ; mutable memused : memsize
454 ; mutable gen : gen
455 ; mutable throttle : (page list * int * float) option
456 ; mutable autoscroll : int option
457 ; mutable ghyll : (int option -> unit)
458 ; mutable help : helpitem array
459 ; mutable docinfo : (int * string) list
460 ; mutable texid : GlTex.texture_id option
461 ; hists : hists
462 ; mutable prevzoom : float
463 ; mutable progress : float
464 ; mutable redisplay : bool
465 ; mutable mpos : mpos
466 ; mutable keystate : keystate
467 ; mutable glinks : bool
468 ; mutable prevcolumns : (columns * float) option
470 and hists =
471 { pat : string circbuf
472 ; pag : string circbuf
473 ; nav : anchor circbuf
474 ; sel : string circbuf
478 let defconf =
479 { scrollbw = 7
480 ; scrollh = 12
481 ; icase = true
482 ; preload = true
483 ; pagebias = 0
484 ; verbose = false
485 ; debug = false
486 ; scrollstep = 24
487 ; hscrollstep = 24
488 ; maxhfit = true
489 ; crophack = false
490 ; autoscrollstep = 2
491 ; maxwait = None
492 ; hlinks = false
493 ; underinfo = false
494 ; interpagespace = 2
495 ; zoom = 1.0
496 ; presentation = false
497 ; angle = 0
498 ; winw = 900
499 ; winh = 900
500 ; savebmarks = true
501 ; proportional = true
502 ; trimmargins = false
503 ; trimfuzz = (0,0,0,0)
504 ; memlimit = 32 lsl 20
505 ; texcount = 256
506 ; sliceheight = 24
507 ; thumbw = 76
508 ; jumpback = true
509 ; bgcolor = (0.5, 0.5, 0.5)
510 ; bedefault = false
511 ; scrollbarinpm = true
512 ; tilew = 2048
513 ; tileh = 2048
514 ; mustoresize = 256 lsl 20
515 ; checkers = true
516 ; aalevel = 8
517 ; urilauncher =
518 (match platform with
519 | Plinux | Pfreebsd | Pdragonflybsd
520 | Popenbsd | Pnetbsd | Psun -> "xdg-open \"%s\""
521 | Posx -> "open \"%s\""
522 | Pcygwin -> "cygstart \"%s\""
523 | Punknown -> "echo %s")
524 ; pathlauncher = "lp \"%s\""
525 ; selcmd =
526 (match platform with
527 | Plinux | Pfreebsd | Pdragonflybsd
528 | Popenbsd | Pnetbsd | Psun -> "xsel -i"
529 | Posx -> "pbcopy"
530 | Pcygwin -> "wsel"
531 | Punknown -> "cat")
532 ; colorspace = Rgb
533 ; invert = false
534 ; colorscale = 1.0
535 ; redirectstderr = false
536 ; ghyllscroll = None
537 ; columns = Csingle [||]
538 ; beyecolumns = None
539 ; updatecurs = false
540 ; hfsize = 12
541 ; pgscale = 1.0
542 ; keyhashes =
543 let mk n = (n, Hashtbl.create 1) in
544 [ mk "global"
545 ; mk "info"
546 ; mk "help"
547 ; mk "outline"
548 ; mk "listview"
549 ; mk "birdseye"
550 ; mk "textentry"
551 ; mk "links"
552 ; mk "view"
557 let findkeyhash c name =
558 try List.assoc name c.keyhashes
559 with Not_found -> failwith ("invalid mode name `" ^ name ^ "'")
562 let conf = { defconf with angle = defconf.angle };;
564 let pgscale h = truncate (float h *. conf.pgscale);;
566 type fontstate =
567 { mutable fontsize : int
568 ; mutable wwidth : float
569 ; mutable maxrows : int
573 let fstate =
574 { fontsize = 14
575 ; wwidth = nan
576 ; maxrows = -1
580 let setfontsize n =
581 fstate.fontsize <- n;
582 fstate.wwidth <- measurestr fstate.fontsize "w";
583 fstate.maxrows <- (conf.winh - fstate.fontsize - 1) / (fstate.fontsize + 1);
586 let geturl s =
587 let colonpos = try String.index s ':' with Not_found -> -1 in
588 let len = String.length s in
589 if colonpos >= 0 && colonpos + 3 < len
590 then (
591 if s.[colonpos+1] = '/' && s.[colonpos+2] = '/'
592 then
593 let schemestartpos =
594 try String.rindex_from s colonpos ' '
595 with Not_found -> -1
597 let scheme =
598 String.sub s (schemestartpos+1) (colonpos-1-schemestartpos)
600 match scheme with
601 | "http" | "ftp" | "mailto" ->
602 let epos =
603 try String.index_from s colonpos ' '
604 with Not_found -> len
606 String.sub s (schemestartpos+1) (epos-1-schemestartpos)
607 | _ -> ""
608 else ""
610 else ""
613 let gotouri uri =
614 if String.length conf.urilauncher = 0
615 then print_endline uri
616 else (
617 let url = geturl uri in
618 if String.length url = 0
619 then print_endline uri
620 else
621 let re = Str.regexp "%s" in
622 let command = Str.global_replace re url conf.urilauncher in
623 try popen command []
624 with exn ->
625 Printf.eprintf
626 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
627 flush stderr;
631 let version () =
632 Printf.sprintf "llpp version %s (%s/%dbit, ocaml %s)" Help.version
633 (platform_to_string platform) Sys.word_size Sys.ocaml_version
636 let makehelp () =
637 let strings = version () :: "" :: Help.keys in
638 Array.of_list (
639 List.map (fun s ->
640 let url = geturl s in
641 if String.length url > 0
642 then (s, 0, Action (fun u -> gotouri url; u))
643 else (s, 0, Noaction)
644 ) strings);
647 let noghyll _ = ();;
648 let firstgeomcmds = "", [];;
650 let state =
651 { sr = Unix.stdin
652 ; sw = Unix.stdin
653 ; wsfd = Unix.stdin
654 ; errfd = None
655 ; stderr = Unix.stderr
656 ; errmsgs = Buffer.create 0
657 ; newerrmsgs = false
658 ; x = 0
659 ; y = 0
660 ; w = 0
661 ; scrollw = 0
662 ; hscrollh = 0
663 ; anchor = emptyanchor
664 ; ranchors = []
665 ; layout = []
666 ; maxy = max_int
667 ; tilelru = Queue.create ()
668 ; pagemap = Hashtbl.create 10
669 ; tilemap = Hashtbl.create 10
670 ; pdims = []
671 ; pagecount = 0
672 ; currently = Idle
673 ; mstate = Mnone
674 ; rects = []
675 ; rects1 = []
676 ; text = ""
677 ; mode = View
678 ; fullscreen = None
679 ; searchpattern = ""
680 ; outlines = [||]
681 ; bookmarks = []
682 ; path = ""
683 ; password = ""
684 ; geomcmds = firstgeomcmds
685 ; hists =
686 { nav = cbnew 10 emptyanchor
687 ; pat = cbnew 10 ""
688 ; pag = cbnew 10 ""
689 ; sel = cbnew 10 ""
691 ; memused = 0
692 ; gen = 0
693 ; throttle = None
694 ; autoscroll = None
695 ; ghyll = noghyll
696 ; help = makehelp ()
697 ; docinfo = []
698 ; texid = None
699 ; prevzoom = 1.0
700 ; progress = -1.0
701 ; uioh = nouioh
702 ; redisplay = true
703 ; mpos = (-1, -1)
704 ; keystate = KSnone
705 ; glinks = false
706 ; prevcolumns = None
710 let vlog fmt =
711 if conf.verbose
712 then
713 Printf.kprintf prerr_endline fmt
714 else
715 Printf.kprintf ignore fmt
718 let launchpath () =
719 if String.length conf.pathlauncher = 0
720 then print_endline state.path
721 else (
722 let re = Str.regexp "%s" in
723 let command = Str.global_replace re state.path conf.pathlauncher in
724 try popen command []
725 with exn ->
726 Printf.eprintf
727 "failed to execute `%s': %s\n" command (Printexc.to_string exn);
728 flush stderr;
732 module Ne = struct
733 type 'a t = | Res of 'a | Exn of exn;;
735 let pipe () =
736 try Res (Unix.pipe ())
737 with exn -> Exn exn
740 let clo fd f =
741 try Unix.close fd
742 with exn -> f (Printexc.to_string exn)
745 let dup fd =
746 try Res (Unix.dup fd)
747 with exn -> Exn exn
750 let dup2 fd1 fd2 =
751 try Res (Unix.dup2 fd1 fd2)
752 with exn -> Exn exn
754 end;;
756 let redirectstderr () =
757 let clofail what errmsg = dolog "failed to close %s: %s" what errmsg in
758 if conf.redirectstderr
759 then
760 match Ne.pipe () with
761 | Ne.Exn exn ->
762 dolog "failed to create stderr redirection pipes: %s"
763 (Printexc.to_string exn)
765 | Ne.Res (r, w) ->
766 begin match Ne.dup Unix.stderr with
767 | Ne.Exn exn ->
768 dolog "failed to dup stderr: %s" (Printexc.to_string exn);
769 Ne.clo r (clofail "pipe/r");
770 Ne.clo w (clofail "pipe/w");
772 | Ne.Res dupstderr ->
773 begin match Ne.dup2 w Unix.stderr with
774 | Ne.Exn exn ->
775 dolog "failed to dup2 to stderr: %s"
776 (Printexc.to_string exn);
777 Ne.clo dupstderr (clofail "stderr duplicate");
778 Ne.clo r (clofail "redir pipe/r");
779 Ne.clo w (clofail "redir pipe/w");
781 | Ne.Res () ->
782 state.stderr <- dupstderr;
783 state.errfd <- Some r;
784 end;
786 else (
787 state.newerrmsgs <- false;
788 begin match state.errfd with
789 | Some fd ->
790 begin match Ne.dup2 state.stderr Unix.stderr with
791 | Ne.Exn exn ->
792 dolog "failed to dup2 original stderr: %s"
793 (Printexc.to_string exn)
794 | Ne.Res () ->
795 Ne.clo fd (clofail "dup of stderr");
796 Unix.dup2 state.stderr Unix.stderr;
797 state.errfd <- None;
798 end;
799 | None -> ()
800 end;
801 prerr_string (Buffer.contents state.errmsgs);
802 flush stderr;
803 Buffer.clear state.errmsgs;
807 module G =
808 struct
809 let postRedisplay who =
810 if conf.verbose
811 then prerr_endline ("redisplay for " ^ who);
812 state.redisplay <- true;
814 end;;
816 let getopaque pageno =
817 try Some (Hashtbl.find state.pagemap (pageno, state.gen))
818 with Not_found -> None
821 let putopaque pageno opaque =
822 Hashtbl.replace state.pagemap (pageno, state.gen) opaque
825 let pagetranslatepoint l x y =
826 let dy = y - l.pagedispy in
827 let y = dy + l.pagey in
828 let dx = x - l.pagedispx in
829 let x = dx + l.pagex in
830 (x, y);
833 let getunder x y =
834 let rec f = function
835 | l :: rest ->
836 begin match getopaque l.pageno with
837 | Some opaque ->
838 let x0 = l.pagedispx in
839 let x1 = x0 + l.pagevw in
840 let y0 = l.pagedispy in
841 let y1 = y0 + l.pagevh in
842 if y >= y0 && y <= y1 && x >= x0 && x <= x1
843 then
844 let px, py = pagetranslatepoint l x y in
845 match whatsunder opaque px py with
846 | Unone -> f rest
847 | under -> under
848 else f rest
849 | _ ->
850 f rest
852 | [] -> Unone
854 f state.layout
857 let showtext c s =
858 state.text <- Printf.sprintf "%c%s" c s;
859 G.postRedisplay "showtext";
862 let undertext = function
863 | Unone -> "none"
864 | Ulinkuri s -> s
865 | Ulinkgoto (pageno, _) -> Printf.sprintf "%s: page %d" state.path (pageno+1)
866 | Utext s -> "font: " ^ s
867 | Uunexpected s -> "unexpected: " ^ s
868 | Ulaunch s -> "launch: " ^ s
869 | Unamed s -> "named: " ^ s
870 | Uremote (filename, pageno) ->
871 Printf.sprintf "%s: page %d" filename (pageno+1)
874 let updateunder x y =
875 match getunder x y with
876 | Unone -> Wsi.setcursor Wsi.CURSOR_INHERIT
877 | Ulinkuri uri ->
878 if conf.underinfo then showtext 'u' ("ri: " ^ uri);
879 Wsi.setcursor Wsi.CURSOR_INFO
880 | Ulinkgoto (pageno, _) ->
881 if conf.underinfo
882 then showtext 'p' ("age: " ^ string_of_int (pageno+1));
883 Wsi.setcursor Wsi.CURSOR_INFO
884 | Utext s ->
885 if conf.underinfo then showtext 'f' ("ont: " ^ s);
886 Wsi.setcursor Wsi.CURSOR_TEXT
887 | Uunexpected s ->
888 if conf.underinfo then showtext 'u' ("nexpected: " ^ s);
889 Wsi.setcursor Wsi.CURSOR_INHERIT
890 | Ulaunch s ->
891 if conf.underinfo then showtext 'l' ("aunch: " ^ s);
892 Wsi.setcursor Wsi.CURSOR_INHERIT
893 | Unamed s ->
894 if conf.underinfo then showtext 'n' ("amed: " ^ s);
895 Wsi.setcursor Wsi.CURSOR_INHERIT
896 | Uremote (filename, pageno) ->
897 if conf.underinfo then showtext 'r'
898 (Printf.sprintf "emote: %s (%d)" filename (pageno+1));
899 Wsi.setcursor Wsi.CURSOR_INFO
902 let showlinktype under =
903 if conf.underinfo
904 then
905 match under with
906 | Unone -> ()
907 | under ->
908 let s = undertext under in
909 showtext ' ' s
912 let addchar s c =
913 let b = Buffer.create (String.length s + 1) in
914 Buffer.add_string b s;
915 Buffer.add_char b c;
916 Buffer.contents b;
919 let colorspace_of_string s =
920 match String.lowercase s with
921 | "rgb" -> Rgb
922 | "bgr" -> Bgr
923 | "gray" -> Gray
924 | _ -> failwith "invalid colorspace"
927 let int_of_colorspace = function
928 | Rgb -> 0
929 | Bgr -> 1
930 | Gray -> 2
933 let colorspace_of_int = function
934 | 0 -> Rgb
935 | 1 -> Bgr
936 | 2 -> Gray
937 | n -> failwith ("invalid colorspace index " ^ string_of_int n)
940 let colorspace_to_string = function
941 | Rgb -> "rgb"
942 | Bgr -> "bgr"
943 | Gray -> "gray"
946 let intentry_with_suffix text key =
947 let c =
948 if key >= 32 && key < 127
949 then Char.chr key
950 else '\000'
952 match Char.lowercase c with
953 | '0' .. '9' ->
954 let text = addchar text c in
955 TEcont text
957 | 'k' | 'm' | 'g' ->
958 let text = addchar text c in
959 TEcont text
961 | _ ->
962 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
963 TEcont text
966 let multicolumns_to_string (n, a, b) =
967 if a = 0 && b = 0
968 then Printf.sprintf "%d" n
969 else Printf.sprintf "%d,%d,%d" n a b;
972 let multicolumns_of_string s =
974 (int_of_string s, 0, 0)
975 with _ ->
976 Scanf.sscanf s "%u,%u,%u" (fun n a b ->
977 if a > 1 || b > 1
978 then failwith "subtly broken"; (n, a, b)
982 let readcmd fd =
983 let s = "xxxx" in
984 let n = Unix.read fd s 0 4 in
985 if n != 4 then failwith "incomplete read(len)";
986 let len = 0
987 lor (Char.code s.[0] lsl 24)
988 lor (Char.code s.[1] lsl 16)
989 lor (Char.code s.[2] lsl 8)
990 lor (Char.code s.[3] lsl 0)
992 let s = String.create len in
993 let n = Unix.read fd s 0 len in
994 if n != len then failwith "incomplete read(data)";
998 let btod b = if b then 1 else 0;;
1000 let wcmd fmt =
1001 let b = Buffer.create 16 in
1002 Buffer.add_string b "llll";
1003 Printf.kbprintf
1004 (fun b ->
1005 let s = Buffer.contents b in
1006 let n = String.length s in
1007 let len = n - 4 in
1008 (* dolog "wcmd %S" (String.sub s 4 len); *)
1009 s.[0] <- Char.chr ((len lsr 24) land 0xff);
1010 s.[1] <- Char.chr ((len lsr 16) land 0xff);
1011 s.[2] <- Char.chr ((len lsr 8) land 0xff);
1012 s.[3] <- Char.chr (len land 0xff);
1013 let n' = Unix.write state.sw s 0 n in
1014 if n' != n then failwith "write failed";
1015 ) b fmt;
1018 let calcips h =
1019 let d = conf.winh - h in
1020 max conf.interpagespace ((d + 1) / 2)
1023 let rowyh (c, coverA, coverB) b n =
1024 if c = 1 || (n < coverA || n >= state.pagecount - coverB)
1025 then
1026 let _, _, vy, (_, _, h, _) = b.(n) in
1027 (vy, h)
1028 else
1029 let n' = n - coverA in
1030 let d = n' mod c in
1031 let s = n - d in
1032 let e = min state.pagecount (s + c) in
1033 let rec find m miny maxh = if m = e then miny, maxh else
1034 let _, _, y, (_, _, h, _) = b.(m) in
1035 let miny = min miny y in
1036 let maxh = max maxh h in
1037 find (m+1) miny maxh
1038 in find s max_int 0
1041 let calcheight () =
1042 match conf.columns with
1043 | Cmulti ((_, _, _) as cl, b) ->
1044 if Array.length b > 0
1045 then
1046 let y, h = rowyh cl b (Array.length b - 1) in
1047 y + h + (if conf.presentation then calcips h else 0)
1048 else 0
1049 | Csingle b ->
1050 if Array.length b > 0
1051 then
1052 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
1053 y + h + (if conf.presentation then calcips h else 0)
1054 else 0
1055 | Csplit (_, b) ->
1056 if Array.length b > 0
1057 then
1058 let (_, _, y, (_, _, h, _)) = b.(Array.length b - 1) in
1059 y + h
1060 else 0
1063 let getpageyh pageno =
1064 let pageno = bound pageno 0 (state.pagecount-1) in
1065 match conf.columns with
1066 | Csingle b ->
1067 if Array.length b = 0
1068 then 0, 0
1069 else
1070 let (_, _, y, (_, _, h, _)) = b.(pageno) in
1071 let y =
1072 if conf.presentation
1073 then y - calcips h
1074 else y
1076 y, h
1077 | Cmulti (cl, b) ->
1078 if Array.length b = 0
1079 then 0, 0
1080 else
1081 let y, h = rowyh cl b pageno in
1082 let y =
1083 if conf.presentation
1084 then y - calcips h
1085 else y
1087 y, h
1088 | Csplit (c, b) ->
1089 if Array.length b = 0
1090 then 0, 0
1091 else
1092 let n = pageno*c in
1093 let (_, _, y, (_, _, h, _)) = b.(n) in
1094 y, h
1097 let getpagedim pageno =
1098 let rec f ppdim l =
1099 match l with
1100 | (n, _, _, _) as pdim :: rest ->
1101 if n >= pageno
1102 then (if n = pageno then pdim else ppdim)
1103 else f pdim rest
1105 | [] -> ppdim
1107 f (-1, -1, -1, -1) state.pdims
1110 let getpagey pageno = fst (getpageyh pageno);;
1112 let nogeomcmds cmds =
1113 match cmds with
1114 | s, [] -> String.length s = 0
1115 | _ -> false
1118 let page_of_y y =
1119 let ((c, coverA, coverB) as cl), b =
1120 match conf.columns with
1121 | Csingle b -> (1, 0, 0), b
1122 | Cmulti (c, b) -> c, b
1123 | Csplit (_, b) -> (1, 0, 0), b
1125 let rec bsearch nmin nmax =
1126 if nmin > nmax
1127 then bound nmin 0 (state.pagecount-1)
1128 else
1129 let n = (nmax + nmin) / 2 in
1130 let vy, h = rowyh cl b n in
1131 let y0, y1 =
1132 if conf.presentation
1133 then
1134 let ips = calcips h in
1135 let y0 = vy - ips in
1136 let y1 = vy + h + ips in
1137 y0, y1
1138 else (
1139 if n = 0
1140 then 0, vy + h + conf.interpagespace
1141 else
1142 let y0 = vy - conf.interpagespace in
1143 y0, y0 + h + conf.interpagespace
1146 if y >= y0 && y < y1
1147 then (
1148 if c = 1
1149 then n
1150 else (
1151 if n > coverA
1152 then
1153 if n < state.pagecount - coverB
1154 then ((n-coverA)/c)*c + coverA
1155 else n
1156 else n
1159 else (
1160 if y > y0
1161 then bsearch (n+1) nmax
1162 else bsearch nmin (n-1)
1165 let r = bsearch 0 (state.pagecount-1) in
1169 let layoutN ((columns, coverA, coverB), b) y sh =
1170 let sh = sh - state.hscrollh in
1171 let rec fold accu n =
1172 if n = Array.length b
1173 then accu
1174 else
1175 let pdimno, dx, vy, (_, w, h, xoff) = b.(n) in
1176 if (vy - y) > sh &&
1177 (n = coverA - 1
1178 || n = state.pagecount - coverB
1179 || (n - coverA) mod columns = columns - 1)
1180 then accu
1181 else
1182 let accu =
1183 if vy + h > y
1184 then
1185 let pagey = max 0 (y - vy) in
1186 let pagedispy = if pagey > 0 then 0 else vy - y in
1187 let pagedispx, pagex =
1188 let pdx =
1189 if n = coverA - 1 || n = state.pagecount - coverB
1190 then state.x + (conf.winw - state.scrollw - w) / 2
1191 else dx + xoff + state.x
1193 if pdx < 0
1194 then 0, -pdx
1195 else pdx, 0
1197 let pagevw =
1198 let vw = conf.winw - state.scrollw - pagedispx in
1199 let pw = w - pagex in
1200 min vw pw
1202 let pagevh = min (h - pagey) (sh - pagedispy) in
1203 if pagevw > 0 && pagevh > 0
1204 then
1205 let e =
1206 { pageno = n
1207 ; pagedimno = pdimno
1208 ; pagew = w
1209 ; pageh = h
1210 ; pagex = pagex
1211 ; pagey = pagey
1212 ; pagevw = pagevw
1213 ; pagevh = pagevh
1214 ; pagedispx = pagedispx
1215 ; pagedispy = pagedispy
1216 ; pagecol = 0
1219 e :: accu
1220 else
1221 accu
1222 else
1223 accu
1225 fold accu (n+1)
1227 List.rev (fold [] (page_of_y y));
1230 let layoutS (columns, b) y sh =
1231 let sh = sh - state.hscrollh in
1232 let rec fold accu n =
1233 if n = Array.length b
1234 then accu
1235 else
1236 let pdimno, px, vy, (_, pagew, pageh, xoff) = b.(n) in
1237 if (vy - y) > sh
1238 then accu
1239 else
1240 let accu =
1241 if vy + pageh > y
1242 then
1243 let x = xoff + state.x in
1244 let pagey = max 0 (y - vy) in
1245 let pagedispy = if pagey > 0 then 0 else vy - y in
1246 let pagedispx, pagex =
1247 if px = 0
1248 then (
1249 if x < 0
1250 then 0, -x
1251 else x, 0
1253 else (
1254 let px = px - x in
1255 if px < 0
1256 then -px, 0
1257 else 0, px
1260 let pagecolw = pagew/columns in
1261 let pagedispx =
1262 if pagecolw < conf.winw
1263 then pagedispx + ((conf.winw - state.scrollw - pagecolw) / 2)
1264 else pagedispx
1266 let pagevw =
1267 let vw = conf.winw - pagedispx - state.scrollw in
1268 let pw = pagew - pagex in
1269 min vw pw
1271 let pagevw = min pagevw pagecolw in
1272 let pagevh = min (pageh - pagey) (sh - pagedispy) in
1273 if pagevw > 0 && pagevh > 0
1274 then
1275 let e =
1276 { pageno = n/columns
1277 ; pagedimno = pdimno
1278 ; pagew = pagew
1279 ; pageh = pageh
1280 ; pagex = pagex
1281 ; pagey = pagey
1282 ; pagevw = pagevw
1283 ; pagevh = pagevh
1284 ; pagedispx = pagedispx
1285 ; pagedispy = pagedispy
1286 ; pagecol = n mod columns
1289 e :: accu
1290 else
1291 accu
1292 else
1293 accu
1295 fold accu (n+1)
1297 List.rev (fold [] 0)
1300 let layout y sh =
1301 if nogeomcmds state.geomcmds
1302 then
1303 match conf.columns with
1304 | Csingle b -> layoutN ((1, 0, 0), b) y sh
1305 | Cmulti c -> layoutN c y sh
1306 | Csplit s -> layoutS s y sh
1307 else []
1310 let clamp incr =
1311 let y = state.y + incr in
1312 let y = max 0 y in
1313 let y = min y (state.maxy - (if conf.maxhfit then conf.winh else 0)) in
1317 let itertiles l f =
1318 let tilex = l.pagex mod conf.tilew in
1319 let tiley = l.pagey mod conf.tileh in
1321 let col = l.pagex / conf.tilew in
1322 let row = l.pagey / conf.tileh in
1324 let rec rowloop row y0 dispy h =
1325 if h = 0
1326 then ()
1327 else (
1328 let dh = conf.tileh - y0 in
1329 let dh = min h dh in
1330 let rec colloop col x0 dispx w =
1331 if w = 0
1332 then ()
1333 else (
1334 let dw = conf.tilew - x0 in
1335 let dw = min w dw in
1337 f col row dispx dispy x0 y0 dw dh;
1338 colloop (col+1) 0 (dispx+dw) (w-dw)
1341 colloop col tilex l.pagedispx l.pagevw;
1342 rowloop (row+1) 0 (dispy+dh) (h-dh)
1345 if l.pagevw > 0 && l.pagevh > 0
1346 then rowloop row tiley l.pagedispy l.pagevh;
1349 let gettileopaque l col row =
1350 let key =
1351 l.pageno, state.gen, conf.colorspace, conf.angle, l.pagew, l.pageh, col, row
1353 try Some (Hashtbl.find state.tilemap key)
1354 with Not_found -> None
1357 let puttileopaque l col row gen colorspace angle opaque size elapsed =
1358 let key = l.pageno, gen, colorspace, angle, l.pagew, l.pageh, col, row in
1359 Hashtbl.add state.tilemap key (opaque, size, elapsed)
1362 let drawtiles l color =
1363 GlDraw.color color;
1364 let f col row x y tilex tiley w h =
1365 match gettileopaque l col row with
1366 | Some (opaque, _, t) ->
1367 let params = x, y, w, h, tilex, tiley in
1368 if conf.invert
1369 then (
1370 Gl.enable `blend;
1371 GlFunc.blend_func `zero `one_minus_src_color;
1373 drawtile params opaque;
1374 if conf.invert
1375 then Gl.disable `blend;
1376 if conf.debug
1377 then (
1378 let s = Printf.sprintf
1379 "%d[%d,%d] %f sec"
1380 l.pageno col row t
1382 let w = measurestr fstate.fontsize s in
1383 GlMisc.push_attrib [`current];
1384 GlDraw.color (0.0, 0.0, 0.0);
1385 GlDraw.rect
1386 (float (x-2), float (y-2))
1387 (float (x+2) +. w, float (y + fstate.fontsize + 2));
1388 GlDraw.color (1.0, 1.0, 1.0);
1389 drawstring fstate.fontsize x (y + fstate.fontsize - 1) s;
1390 GlMisc.pop_attrib ();
1393 | _ ->
1394 let w =
1395 let lw = conf.winw - state.scrollw - x in
1396 min lw w
1397 and h =
1398 let lh = conf.winh - y in
1399 min lh h
1401 begin match state.texid with
1402 | Some id ->
1403 Gl.enable `texture_2d;
1404 GlTex.bind_texture `texture_2d id;
1405 let x0 = float x
1406 and y0 = float y
1407 and x1 = float (x+w)
1408 and y1 = float (y+h) in
1410 let tw = float w /. 16.0
1411 and th = float h /. 16.0 in
1412 let tx0 = float tilex /. 16.0
1413 and ty0 = float tiley /. 16.0 in
1414 let tx1 = tx0 +. tw
1415 and ty1 = ty0 +. th in
1416 GlDraw.begins `quads;
1417 GlTex.coord2 (tx0, ty0); GlDraw.vertex2 (x0, y0);
1418 GlTex.coord2 (tx0, ty1); GlDraw.vertex2 (x0, y1);
1419 GlTex.coord2 (tx1, ty1); GlDraw.vertex2 (x1, y1);
1420 GlTex.coord2 (tx1, ty0); GlDraw.vertex2 (x1, y0);
1421 GlDraw.ends ();
1423 Gl.disable `texture_2d;
1424 | None ->
1425 GlDraw.color (1.0, 1.0, 1.0);
1426 GlDraw.rect
1427 (float x, float y)
1428 (float (x+w), float (y+h));
1429 end;
1430 if w > 128 && h > fstate.fontsize + 10
1431 then (
1432 GlDraw.color (0.0, 0.0, 0.0);
1433 let c, r =
1434 if conf.verbose
1435 then (col*conf.tilew, row*conf.tileh)
1436 else col, row
1438 drawstring2 fstate.fontsize x y "Loading %d [%d,%d]" l.pageno c r;
1440 GlDraw.color color;
1442 itertiles l f
1445 let pagevisible layout n = List.exists (fun l -> l.pageno = n) layout;;
1447 let tilevisible1 l x y =
1448 let ax0 = l.pagex
1449 and ax1 = l.pagex + l.pagevw
1450 and ay0 = l.pagey
1451 and ay1 = l.pagey + l.pagevh in
1453 let bx0 = x
1454 and by0 = y in
1455 let bx1 = min (bx0 + conf.tilew) l.pagew
1456 and by1 = min (by0 + conf.tileh) l.pageh in
1458 let rx0 = max ax0 bx0
1459 and ry0 = max ay0 by0
1460 and rx1 = min ax1 bx1
1461 and ry1 = min ay1 by1 in
1463 let nonemptyintersection = rx1 > rx0 && ry1 > ry0 in
1464 nonemptyintersection
1467 let tilevisible layout n x y =
1468 let rec findpageinlayout m = function
1469 | l :: rest when l.pageno = n ->
1470 tilevisible1 l x y || (
1471 match conf.columns with
1472 | Csplit (c, _) when c > m -> findpageinlayout (m+1) rest
1473 | _ -> false
1475 | _ :: rest -> findpageinlayout 0 rest
1476 | [] -> false
1478 findpageinlayout 0 layout;
1481 let tileready l x y =
1482 tilevisible1 l x y &&
1483 gettileopaque l (x/conf.tilew) (y/conf.tileh) != None
1486 let tilepage n p layout =
1487 let rec loop = function
1488 | l :: rest ->
1489 if l.pageno = n
1490 then
1491 let f col row _ _ _ _ _ _ =
1492 if state.currently = Idle
1493 then
1494 match gettileopaque l col row with
1495 | Some _ -> ()
1496 | None ->
1497 let x = col*conf.tilew
1498 and y = row*conf.tileh in
1499 let w =
1500 let w = l.pagew - x in
1501 min w conf.tilew
1503 let h =
1504 let h = l.pageh - y in
1505 min h conf.tileh
1507 wcmd "tile %s %d %d %d %d" p x y w h;
1508 state.currently <-
1509 Tiling (
1510 l, p, conf.colorspace, conf.angle, state.gen, col, row,
1511 conf.tilew, conf.tileh
1514 itertiles l f;
1515 else
1516 loop rest
1518 | [] -> ()
1520 if nogeomcmds state.geomcmds
1521 then loop layout;
1524 let preloadlayout y =
1525 let y = if y < conf.winh then 0 else y - conf.winh in
1526 let h = conf.winh*3 in
1527 layout y h;
1530 let load pages =
1531 let rec loop pages =
1532 if state.currently != Idle
1533 then ()
1534 else
1535 match pages with
1536 | l :: rest ->
1537 begin match getopaque l.pageno with
1538 | None ->
1539 wcmd "page %d %d" l.pageno l.pagedimno;
1540 state.currently <- Loading (l, state.gen);
1541 | Some opaque ->
1542 tilepage l.pageno opaque pages;
1543 loop rest
1544 end;
1545 | _ -> ()
1547 if nogeomcmds state.geomcmds
1548 then loop pages
1551 let preload pages =
1552 load pages;
1553 if conf.preload && state.currently = Idle
1554 then load (preloadlayout state.y);
1557 let layoutready layout =
1558 let rec fold all ls =
1559 all && match ls with
1560 | l :: rest ->
1561 let seen = ref false in
1562 let allvisible = ref true in
1563 let foo col row _ _ _ _ _ _ =
1564 seen := true;
1565 allvisible := !allvisible &&
1566 begin match gettileopaque l col row with
1567 | Some _ -> true
1568 | None -> false
1571 itertiles l foo;
1572 fold (!seen && !allvisible) rest
1573 | [] -> true
1575 let alltilesvisible = fold true layout in
1576 alltilesvisible;
1579 let gotoy y =
1580 let y = bound y 0 state.maxy in
1581 let y, layout, proceed =
1582 match conf.maxwait with
1583 | Some time when state.ghyll == noghyll ->
1584 begin match state.throttle with
1585 | None ->
1586 let layout = layout y conf.winh in
1587 let ready = layoutready layout in
1588 if not ready
1589 then (
1590 load layout;
1591 state.throttle <- Some (layout, y, now ());
1593 else G.postRedisplay "gotoy showall (None)";
1594 y, layout, ready
1595 | Some (_, _, started) ->
1596 let dt = now () -. started in
1597 if dt > time
1598 then (
1599 state.throttle <- None;
1600 let layout = layout y conf.winh in
1601 load layout;
1602 G.postRedisplay "maxwait";
1603 y, layout, true
1605 else -1, [], false
1608 | _ ->
1609 let layout = layout y conf.winh in
1610 if true || layoutready layout
1611 then G.postRedisplay "gotoy ready";
1612 y, layout, true
1614 if proceed
1615 then (
1616 state.y <- y;
1617 state.layout <- layout;
1618 begin match state.mode with
1619 | LinkNav (Ltexact (pageno, linkno)) ->
1620 let rec loop = function
1621 | [] ->
1622 state.mode <- LinkNav (Ltgendir 0)
1623 | l :: _ when l.pageno = pageno ->
1624 begin match getopaque pageno with
1625 | None ->
1626 state.mode <- LinkNav (Ltgendir 0)
1627 | Some opaque ->
1628 let x0, y0, x1, y1 = getlinkrect opaque linkno in
1629 if not (x0 >= l.pagex && x1 <= l.pagex + l.pagevw
1630 && y0 >= l.pagey && y1 <= l.pagey + l.pagevh)
1631 then state.mode <- LinkNav (Ltgendir 0)
1633 | _ :: rest -> loop rest
1635 loop layout
1636 | _ -> ()
1637 end;
1638 begin match state.mode with
1639 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
1640 if not (pagevisible layout pageno)
1641 then (
1642 match state.layout with
1643 | [] -> ()
1644 | l :: _ ->
1645 state.mode <- Birdseye (
1646 conf, leftx, l.pageno, hooverpageno, anchor
1649 | LinkNav (Ltgendir dir as lt) ->
1650 let linknav =
1651 let rec loop = function
1652 | [] -> lt
1653 | l :: rest ->
1654 match getopaque l.pageno with
1655 | None -> loop rest
1656 | Some opaque ->
1657 let link =
1658 let ld =
1659 if dir = 0
1660 then LDfirstvisible (l.pagex, l.pagey, dir)
1661 else (
1662 if dir > 0 then LDfirst else LDlast
1665 findlink opaque ld
1667 match link with
1668 | Lnotfound -> loop rest
1669 | Lfound n ->
1670 showlinktype (getlink opaque n);
1671 Ltexact (l.pageno, n)
1673 loop state.layout
1675 state.mode <- LinkNav linknav
1676 | _ -> ()
1677 end;
1678 preload layout;
1680 state.ghyll <- noghyll;
1681 if conf.updatecurs
1682 then (
1683 let mx, my = state.mpos in
1684 updateunder mx my;
1688 let conttiling pageno opaque =
1689 tilepage pageno opaque
1690 (if conf.preload then preloadlayout state.y else state.layout)
1693 let gotoy_and_clear_text y =
1694 if not conf.verbose then state.text <- "";
1695 gotoy y;
1698 let getanchor1 l =
1699 let top =
1700 let coloff = l.pagecol * l.pageh in
1701 float (l.pagey + coloff) /. float l.pageh
1703 let dtop =
1704 if l.pagedispy = 0
1705 then
1707 else
1708 if conf.presentation
1709 then float l.pagedispy /. float (calcips l.pageh)
1710 else float l.pagedispy /. float conf.interpagespace
1712 (l.pageno, top, dtop)
1715 let getanchor () =
1716 match state.layout with
1717 | l :: _ -> getanchor1 l
1718 | [] ->
1719 let n = page_of_y state.y in
1720 let y, h = getpageyh n in
1721 let dy = y - state.y in
1722 let dtop =
1723 if conf.presentation
1724 then
1725 let ips = calcips h in
1726 float (dy + ips) /. float ips
1727 else
1728 float dy /. float conf.interpagespace
1730 (n, 0.0, dtop)
1733 let getanchory (n, top, dtop) =
1734 let y, h = getpageyh n in
1735 if conf.presentation
1736 then
1737 let ips = calcips h in
1738 y + truncate (top*.float h -. dtop*.float ips) + ips;
1739 else
1740 y + truncate (top*.float h -. dtop*.float conf.interpagespace)
1743 let gotoanchor anchor =
1744 gotoy (getanchory anchor);
1747 let addnav () =
1748 cbput state.hists.nav (getanchor ());
1751 let getnav dir =
1752 let anchor = cbgetc state.hists.nav dir in
1753 getanchory anchor;
1756 let gotoghyll y =
1757 let scroll f n a b =
1758 (* http://devmaster.net/forums/topic/9796-ease-in-ease-out-algorithm/ *)
1759 let snake f a b =
1760 let s x = 3.0*.x**2.0 -. 2.0*.x**3.0 in
1761 if f < a
1762 then s (float f /. float a)
1763 else (
1764 if f > b
1765 then 1.0 -. s ((float (f-b) /. float (n-b)))
1766 else 1.0
1769 snake f a b
1770 and summa f n a b =
1771 (* courtesy:
1772 http://integrals.wolfram.com/index.jsp?expr=3x%5E2-2x%5E3&random=false *)
1773 let iv x = -.((-.2.0 +. x)*.x**3.0)/.2.0 in
1774 let iv1 = iv f in
1775 let ins = float a *. iv1
1776 and outs = float (n-b) *. iv1 in
1777 let ones = b - a in
1778 ins +. outs +. float ones
1780 let rec set (_N, _A, _B) y sy =
1781 let sum = summa 1.0 _N _A _B in
1782 let dy = float (y - sy) in
1783 state.ghyll <- (
1784 let rec gf n y1 o =
1785 if n >= _N
1786 then state.ghyll <- noghyll
1787 else
1788 let go n =
1789 let s = scroll n _N _A _B in
1790 let y1 = y1 +. ((s *. dy) /. sum) in
1791 gotoy_and_clear_text (truncate y1);
1792 state.ghyll <- gf (n+1) y1;
1794 match o with
1795 | None -> go n
1796 | Some y' -> set (_N/2, 1, 1) y' state.y
1798 gf 0 (float state.y)
1801 match conf.ghyllscroll with
1802 | None ->
1803 gotoy_and_clear_text y
1804 | Some nab ->
1805 if state.ghyll == noghyll
1806 then set nab y state.y
1807 else state.ghyll (Some y)
1810 let gotopage n top =
1811 let y, h = getpageyh n in
1812 let y = y + (truncate (top *. float h)) in
1813 gotoghyll y
1816 let gotopage1 n top =
1817 let y = getpagey n in
1818 let y = y + top in
1819 gotoghyll y
1822 let invalidate s f =
1823 state.layout <- [];
1824 state.pdims <- [];
1825 state.rects <- [];
1826 state.rects1 <- [];
1827 match state.geomcmds with
1828 | ps, [] when String.length ps = 0 ->
1829 f ();
1830 state.geomcmds <- s, [];
1832 | ps, [] ->
1833 state.geomcmds <- ps, [s, f];
1835 | ps, (s', _) :: rest when s' = s ->
1836 state.geomcmds <- ps, ((s, f) :: rest);
1838 | ps, cmds ->
1839 state.geomcmds <- ps, ((s, f) :: cmds);
1842 let flushpages () =
1843 Hashtbl.iter (fun _ opaque ->
1844 wcmd "freepage %s" opaque;
1845 ) state.pagemap;
1846 Hashtbl.clear state.pagemap;
1849 let opendoc path password =
1850 state.path <- path;
1851 state.password <- password;
1852 state.gen <- state.gen + 1;
1853 state.docinfo <- [];
1855 flushpages ();
1856 setaalevel conf.aalevel;
1857 Wsi.settitle ("llpp " ^ (mbtoutf8 (Filename.basename path)));
1858 wcmd "open %s\000%s\000" path password;
1859 invalidate "reqlayout"
1860 (fun () ->
1861 wcmd "reqlayout %d %d" conf.angle (btod conf.proportional));
1864 let reload () =
1865 state.anchor <- getanchor ();
1866 opendoc state.path state.password;
1869 let scalecolor c =
1870 let c = c *. conf.colorscale in
1871 (c, c, c);
1874 let scalecolor2 (r, g, b) =
1875 (r *. conf.colorscale, g *. conf.colorscale, b *. conf.colorscale);
1878 let docolumns = function
1879 | Csingle _ ->
1880 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
1881 let rec loop pageno pdimno pdim y ph pdims =
1882 if pageno = state.pagecount
1883 then ()
1884 else
1885 let pdimno, ((_, w, h, xoff) as pdim), pdims =
1886 match pdims with
1887 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1888 pdimno+1, pdim, rest
1889 | _ ->
1890 pdimno, pdim, pdims
1892 let x = max 0 (((conf.winw - state.scrollw - w) / 2) - xoff) in
1893 let y = y +
1894 (if conf.presentation
1895 then (if pageno = 0 then calcips h else calcips ph + calcips h)
1896 else (if pageno = 0 then 0 else conf.interpagespace)
1899 a.(pageno) <- (pdimno, x, y, pdim);
1900 loop (pageno+1) pdimno pdim (y + h) h pdims
1902 loop 0 ~-1 (-1,-1,-1,-1) 0 0 state.pdims;
1903 conf.columns <- Csingle a;
1905 | Cmulti ((columns, coverA, coverB), _) ->
1906 let a = Array.make state.pagecount (-1, -1, -1, (-1, -1, -1, -1)) in
1907 let rec loop pageno pdimno pdim x y rowh pdims =
1908 let rec fixrow m = if m = pageno then () else
1909 let (pdimno, x, y, ((_, _, h, _) as pdim)) = a.(m) in
1910 if h < rowh
1911 then (
1912 let y = y + (rowh - h) / 2 in
1913 a.(m) <- (pdimno, x, y, pdim);
1915 fixrow (m+1)
1917 if pageno = state.pagecount
1918 then fixrow (((pageno - 1) / columns) * columns)
1919 else
1920 let pdimno, ((_, w, h, xoff) as pdim), pdims =
1921 match pdims with
1922 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1923 pdimno+1, pdim, rest
1924 | _ ->
1925 pdimno, pdim, pdims
1927 let x, y, rowh' =
1928 if pageno = coverA - 1 || pageno = state.pagecount - coverB
1929 then (
1930 let x = (conf.winw - state.scrollw - w) / 2 in
1931 let ips =
1932 if conf.presentation then calcips h else conf.interpagespace in
1933 x, y + ips + rowh, h
1935 else (
1936 if (pageno - coverA) mod columns = 0
1937 then (
1938 let x = max 0 (conf.winw - state.scrollw - state.w) / 2 in
1939 let y =
1940 if conf.presentation
1941 then
1942 let ips = calcips h in
1943 y + (if pageno = 0 then 0 else calcips rowh + ips)
1944 else
1945 y + (if pageno = 0 then 0 else conf.interpagespace)
1947 x, y + rowh, h
1949 else x, y, max rowh h
1952 let y =
1953 if pageno > 1 && (pageno - coverA) mod columns = 0
1954 then (
1955 let y =
1956 if pageno = columns && conf.presentation
1957 then (
1958 let ips = calcips rowh in
1959 for i = 0 to pred columns
1961 let (pdimno, x, y, pdim) = a.(i) in
1962 a.(i) <- (pdimno, x, y+ips, pdim)
1963 done;
1964 y+ips;
1966 else y
1968 fixrow (pageno - columns);
1971 else y
1973 a.(pageno) <- (pdimno, x, y, pdim);
1974 let x = x + w + xoff*2 + conf.interpagespace in
1975 loop (pageno+1) pdimno pdim x y rowh' pdims
1977 loop 0 ~-1 (-1,-1,-1,-1) 0 0 0 state.pdims;
1978 conf.columns <- Cmulti ((columns, coverA, coverB), a);
1980 | Csplit (c, _) ->
1981 let a = Array.make (state.pagecount*c) (-1, -1, -1, (-1, -1, -1, -1)) in
1982 let rec loop pageno pdimno pdim y pdims =
1983 if pageno = state.pagecount
1984 then ()
1985 else
1986 let pdimno, ((_, w, h, _) as pdim), pdims =
1987 match pdims with
1988 | ((pageno', _, _, _) as pdim) :: rest when pageno' = pageno ->
1989 pdimno+1, pdim, rest
1990 | _ ->
1991 pdimno, pdim, pdims
1993 let cw = w / c in
1994 let rec loop1 n x y =
1995 if n = c then y else (
1996 a.(pageno*c + n) <- (pdimno, x, y, pdim);
1997 loop1 (n+1) (x+cw) (y + h + conf.interpagespace)
2000 let y = loop1 0 0 y in
2001 loop (pageno+1) pdimno pdim y pdims
2003 loop 0 ~-1 (-1,-1,-1,-1) 0 state.pdims;
2004 conf.columns <- Csplit (c, a);
2007 let represent () =
2008 docolumns conf.columns;
2009 state.maxy <- calcheight ();
2010 state.hscrollh <-
2011 if state.w <= conf.winw - state.scrollw
2012 then 0
2013 else state.scrollw
2015 match state.mode with
2016 | Birdseye (_, _, pageno, _, _) ->
2017 let y, h = getpageyh pageno in
2018 let top = (conf.winh - h) / 2 in
2019 gotoy (max 0 (y - top))
2020 | _ -> gotoanchor state.anchor
2023 let reshape w h =
2024 GlDraw.viewport 0 0 w h;
2025 let firsttime = state.geomcmds == firstgeomcmds in
2026 if not firsttime && nogeomcmds state.geomcmds
2027 then state.anchor <- getanchor ();
2029 conf.winw <- w;
2030 let w = truncate (float w *. conf.zoom) - state.scrollw in
2031 let w = max w 2 in
2032 conf.winh <- h;
2033 setfontsize fstate.fontsize;
2034 GlMat.mode `modelview;
2035 GlMat.load_identity ();
2037 GlMat.mode `projection;
2038 GlMat.load_identity ();
2039 GlMat.rotate ~x:1.0 ~angle:180.0 ();
2040 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
2041 GlMat.scale3 (2.0 /. float conf.winw, 2.0 /. float conf.winh, 1.0);
2043 let relx =
2044 if conf.zoom <= 1.0
2045 then 0.0
2046 else float state.x /. float state.w
2048 invalidate "geometry"
2049 (fun () ->
2050 state.w <- w;
2051 if not firsttime
2052 then state.x <- truncate (relx *. float w);
2053 let w =
2054 match conf.columns with
2055 | Csingle _ -> w
2056 | Cmulti ((c, _, _), _) -> (w - (c-1)*conf.interpagespace) / c
2057 | Csplit (c, _) -> w * c
2059 wcmd "geometry %d %d" w h);
2062 let enttext () =
2063 let len = String.length state.text in
2064 let drawstring s =
2065 let hscrollh =
2066 match state.mode with
2067 | Textentry _
2068 | View ->
2069 let h, _, _ = state.uioh#scrollpw in
2071 | _ -> 0
2073 let rect x w =
2074 GlDraw.rect
2075 (x, float (conf.winh - (fstate.fontsize + 4) - hscrollh))
2076 (x+.w, float (conf.winh - hscrollh))
2079 let w = float (conf.winw - state.scrollw - 1) in
2080 if state.progress >= 0.0 && state.progress < 1.0
2081 then (
2082 GlDraw.color (0.3, 0.3, 0.3);
2083 let w1 = w *. state.progress in
2084 rect 0.0 w1;
2085 GlDraw.color (0.0, 0.0, 0.0);
2086 rect w1 (w-.w1)
2088 else (
2089 GlDraw.color (0.0, 0.0, 0.0);
2090 rect 0.0 w;
2093 GlDraw.color (1.0, 1.0, 1.0);
2094 drawstring fstate.fontsize
2095 (if len > 0 then 8 else 2) (conf.winh - hscrollh - 5) s;
2097 let s =
2098 match state.mode with
2099 | Textentry ((prefix, text, _, _, _, _), _) ->
2100 let s =
2101 if len > 0
2102 then
2103 Printf.sprintf "%s%s_ [%s]" prefix text state.text
2104 else
2105 Printf.sprintf "%s%s_" prefix text
2109 | _ -> state.text
2111 let s =
2112 if state.newerrmsgs
2113 then (
2114 if not (istextentry state.mode)
2115 then
2116 let s1 = "(press 'e' to review error messasges)" in
2117 if String.length s > 0 then s ^ " " ^ s1 else s1
2118 else s
2120 else s
2122 if String.length s > 0
2123 then drawstring s
2126 let gctiles () =
2127 let len = Queue.length state.tilelru in
2128 let layout = lazy (
2129 match state.throttle with
2130 | None ->
2131 if conf.preload
2132 then preloadlayout state.y
2133 else state.layout
2134 | Some (layout, _, _) ->
2135 layout
2136 ) in
2137 let rec loop qpos =
2138 if state.memused <= conf.memlimit
2139 then ()
2140 else (
2141 if qpos < len
2142 then
2143 let (k, p, s) as lruitem = Queue.pop state.tilelru in
2144 let n, gen, colorspace, angle, pagew, pageh, col, row = k in
2145 let (_, pw, ph, _) = getpagedim n in
2147 gen = state.gen
2148 && colorspace = conf.colorspace
2149 && angle = conf.angle
2150 && pagew = pw
2151 && pageh = ph
2152 && (
2153 let x = col*conf.tilew
2154 and y = row*conf.tileh in
2155 tilevisible (Lazy.force_val layout) n x y
2157 then Queue.push lruitem state.tilelru
2158 else (
2159 wcmd "freetile %s" p;
2160 state.memused <- state.memused - s;
2161 state.uioh#infochanged Memused;
2162 Hashtbl.remove state.tilemap k;
2164 loop (qpos+1)
2167 loop 0
2170 let flushtiles () =
2171 Queue.iter (fun (k, p, s) ->
2172 wcmd "freetile %s" p;
2173 state.memused <- state.memused - s;
2174 state.uioh#infochanged Memused;
2175 Hashtbl.remove state.tilemap k;
2176 ) state.tilelru;
2177 Queue.clear state.tilelru;
2178 load state.layout;
2181 let logcurrently = function
2182 | Idle -> dolog "Idle"
2183 | Loading (l, gen) ->
2184 dolog "Loading %d gen=%d curgen=%d" l.pageno gen state.gen
2185 | Tiling (l, pageopaque, colorspace, angle, gen, col, row, tilew, tileh) ->
2186 dolog
2187 "Tiling %d[%d,%d] page=%s cs=%s angle"
2188 l.pageno col row pageopaque
2189 (colorspace_to_string colorspace)
2191 dolog "gen=(%d,%d) (%d,%d) tile=(%d,%d) (%d,%d)"
2192 angle gen conf.angle state.gen
2193 tilew tileh
2194 conf.tilew conf.tileh
2196 | Outlining _ ->
2197 dolog "outlining"
2200 let act cmds =
2201 (* dolog "%S" cmds; *)
2202 let op, args =
2203 let spacepos =
2204 try String.index cmds ' '
2205 with Not_found -> -1
2207 if spacepos = -1
2208 then cmds, ""
2209 else
2210 let l = String.length cmds in
2211 let op = String.sub cmds 0 spacepos in
2212 op, begin
2213 if l - spacepos < 2 then ""
2214 else String.sub cmds (spacepos+1) (l-spacepos-1)
2217 match op with
2218 | "clear" ->
2219 state.uioh#infochanged Pdim;
2220 state.pdims <- [];
2222 | "clearrects" ->
2223 state.rects <- state.rects1;
2224 G.postRedisplay "clearrects";
2226 | "continue" ->
2227 let n =
2228 try Scanf.sscanf args "%u" (fun n -> n)
2229 with exn ->
2230 dolog "error processing 'continue' %S: %s"
2231 cmds (Printexc.to_string exn);
2232 exit 1;
2234 state.pagecount <- n;
2235 begin match state.currently with
2236 | Outlining l ->
2237 state.currently <- Idle;
2238 state.outlines <- Array.of_list (List.rev l)
2239 | _ -> ()
2240 end;
2242 let cur, cmds = state.geomcmds in
2243 if String.length cur = 0
2244 then failwith "umpossible";
2246 begin match List.rev cmds with
2247 | [] ->
2248 state.geomcmds <- "", [];
2249 represent ();
2250 | (s, f) :: rest ->
2251 f ();
2252 state.geomcmds <- s, List.rev rest;
2253 end;
2254 if conf.maxwait = None
2255 then G.postRedisplay "continue";
2257 | "title" ->
2258 Wsi.settitle args
2260 | "msg" ->
2261 showtext ' ' args
2263 | "vmsg" ->
2264 if conf.verbose
2265 then showtext ' ' args
2267 | "progress" ->
2268 let progress, text =
2270 Scanf.sscanf args "%f %n"
2271 (fun f pos ->
2272 f, String.sub args pos (String.length args - pos))
2273 with exn ->
2274 dolog "error processing 'progress' %S: %s"
2275 cmds (Printexc.to_string exn);
2276 exit 1;
2278 state.text <- text;
2279 state.progress <- progress;
2280 G.postRedisplay "progress"
2282 | "firstmatch" ->
2283 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2285 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
2286 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2287 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2288 with exn ->
2289 dolog "error processing 'firstmatch' %S: %s"
2290 cmds (Printexc.to_string exn);
2291 exit 1;
2293 let y = (getpagey pageno) + truncate y0 in
2294 addnav ();
2295 gotoy y;
2296 state.rects1 <- [pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)]
2298 | "match" ->
2299 let pageno, c, x0, y0, x1, y1, x2, y2, x3, y3 =
2301 Scanf.sscanf args "%u %d %f %f %f %f %f %f %f %f"
2302 (fun p c x0 y0 x1 y1 x2 y2 x3 y3 ->
2303 (p, c, x0, y0, x1, y1, x2, y2, x3, y3))
2304 with exn ->
2305 dolog "error processing 'match' %S: %s"
2306 cmds (Printexc.to_string exn);
2307 exit 1;
2309 state.rects1 <-
2310 (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) :: state.rects1
2312 | "page" ->
2313 let pageopaque, t =
2315 Scanf.sscanf args "%s %f" (fun p t -> p, t)
2316 with exn ->
2317 dolog "error processing 'page' %S: %s"
2318 cmds (Printexc.to_string exn);
2319 exit 1;
2321 begin match state.currently with
2322 | Loading (l, gen) ->
2323 vlog "page %d took %f sec" l.pageno t;
2324 Hashtbl.replace state.pagemap (l.pageno, gen) pageopaque;
2325 begin match state.throttle with
2326 | None ->
2327 let preloadedpages =
2328 if conf.preload
2329 then preloadlayout state.y
2330 else state.layout
2332 let evict () =
2333 let module IntSet =
2334 Set.Make (struct type t = int let compare = (-) end) in
2335 let set =
2336 List.fold_left (fun s l -> IntSet.add l.pageno s)
2337 IntSet.empty preloadedpages
2339 let evictedpages =
2340 Hashtbl.fold (fun ((pageno, _) as key) opaque accu ->
2341 if not (IntSet.mem pageno set)
2342 then (
2343 wcmd "freepage %s" opaque;
2344 key :: accu
2346 else accu
2347 ) state.pagemap []
2349 List.iter (Hashtbl.remove state.pagemap) evictedpages;
2351 evict ();
2352 state.currently <- Idle;
2353 if gen = state.gen
2354 then (
2355 tilepage l.pageno pageopaque state.layout;
2356 load state.layout;
2357 load preloadedpages;
2358 if pagevisible state.layout l.pageno
2359 && layoutready state.layout
2360 then G.postRedisplay "page";
2363 | Some (layout, _, _) ->
2364 state.currently <- Idle;
2365 tilepage l.pageno pageopaque layout;
2366 load state.layout
2367 end;
2369 | _ ->
2370 dolog "Inconsistent loading state";
2371 logcurrently state.currently;
2372 exit 1
2375 | "tile" ->
2376 let (x, y, opaque, size, t) =
2378 Scanf.sscanf args "%u %u %s %u %f"
2379 (fun x y p size t -> (x, y, p, size, t))
2380 with exn ->
2381 dolog "error processing 'tile' %S: %s"
2382 cmds (Printexc.to_string exn);
2383 exit 1;
2385 begin match state.currently with
2386 | Tiling (l, pageopaque, cs, angle, gen, col, row, tilew, tileh) ->
2387 vlog "tile %d [%d,%d] took %f sec" l.pageno col row t;
2389 if tilew != conf.tilew || tileh != conf.tileh
2390 then (
2391 wcmd "freetile %s" opaque;
2392 state.currently <- Idle;
2393 load state.layout;
2395 else (
2396 puttileopaque l col row gen cs angle opaque size t;
2397 state.memused <- state.memused + size;
2398 state.uioh#infochanged Memused;
2399 gctiles ();
2400 Queue.push ((l.pageno, gen, cs, angle, l.pagew, l.pageh, col, row),
2401 opaque, size) state.tilelru;
2403 let layout =
2404 match state.throttle with
2405 | None -> state.layout
2406 | Some (layout, _, _) -> layout
2409 state.currently <- Idle;
2410 if gen = state.gen
2411 && conf.colorspace = cs
2412 && conf.angle = angle
2413 && tilevisible layout l.pageno x y
2414 then conttiling l.pageno pageopaque;
2416 begin match state.throttle with
2417 | None ->
2418 preload state.layout;
2419 if gen = state.gen
2420 && conf.colorspace = cs
2421 && conf.angle = angle
2422 && tilevisible state.layout l.pageno x y
2423 then G.postRedisplay "tile nothrottle";
2425 | Some (layout, y, _) ->
2426 let ready = layoutready layout in
2427 if ready
2428 then (
2429 state.y <- y;
2430 state.layout <- layout;
2431 state.throttle <- None;
2432 G.postRedisplay "throttle";
2434 else load layout;
2435 end;
2438 | _ ->
2439 dolog "Inconsistent tiling state";
2440 logcurrently state.currently;
2441 exit 1
2444 | "pdim" ->
2445 let pdim =
2447 Scanf.sscanf args "%u %u %u %u" (fun n w h x -> n, w, h, x)
2448 with exn ->
2449 dolog "error processing 'pdim' %S: %s"
2450 cmds (Printexc.to_string exn);
2451 exit 1;
2453 state.uioh#infochanged Pdim;
2454 state.pdims <- pdim :: state.pdims
2456 | "o" ->
2457 let (l, n, t, h, pos) =
2459 Scanf.sscanf args "%u %u %d %u %n"
2460 (fun l n t h pos -> l, n, t, h, pos)
2461 with exn ->
2462 dolog "error processing 'o' %S: %s"
2463 cmds (Printexc.to_string exn);
2464 exit 1;
2466 let s = String.sub args pos (String.length args - pos) in
2467 let outline = (s, l, (n, float t /. float h, 0.0)) in
2468 begin match state.currently with
2469 | Outlining outlines ->
2470 state.currently <- Outlining (outline :: outlines)
2471 | Idle ->
2472 state.currently <- Outlining [outline]
2473 | currently ->
2474 dolog "invalid outlining state";
2475 logcurrently currently
2478 | "info" ->
2479 state.docinfo <- (1, args) :: state.docinfo
2481 | "infoend" ->
2482 state.uioh#infochanged Docinfo;
2483 state.docinfo <- List.rev state.docinfo
2485 | _ ->
2486 dolog "unknown cmd `%S'" cmds
2489 let onhist cb =
2490 let rc = cb.rc in
2491 let action = function
2492 | HCprev -> cbget cb ~-1
2493 | HCnext -> cbget cb 1
2494 | HCfirst -> cbget cb ~-(cb.rc)
2495 | HClast -> cbget cb (cb.len - 1 - cb.rc)
2496 and cancel () = cb.rc <- rc
2497 in (action, cancel)
2500 let search pattern forward =
2501 if String.length pattern > 0
2502 then
2503 let pn, py =
2504 match state.layout with
2505 | [] -> 0, 0
2506 | l :: _ ->
2507 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
2509 wcmd "search %d %d %d %d,%s\000"
2510 (btod conf.icase) pn py (btod forward) pattern;
2513 let intentry text key =
2514 let c =
2515 if key >= 32 && key < 127
2516 then Char.chr key
2517 else '\000'
2519 match c with
2520 | '0' .. '9' ->
2521 let text = addchar text c in
2522 TEcont text
2524 | _ ->
2525 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2526 TEcont text
2529 let linknentry text key =
2530 let c =
2531 if key >= 32 && key < 127
2532 then Char.chr key
2533 else '\000'
2535 match c with
2536 | 'a' .. 'z' ->
2537 let text = addchar text c in
2538 TEcont text
2540 | _ ->
2541 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
2542 TEcont text
2545 let linkndone f s =
2546 if String.length s > 0
2547 then (
2548 let n =
2549 let l = String.length s in
2550 let rec loop pos n = if pos = l then n else
2551 let m = Char.code s.[pos] - (if pos = 0 && l > 1 then 96 else 97) in
2552 loop (pos+1) (n*26 + m)
2553 in loop 0 0
2555 let rec loop n = function
2556 | [] -> ()
2557 | l :: rest ->
2558 match getopaque l.pageno with
2559 | None -> loop n rest
2560 | Some opaque ->
2561 let m = getlinkcount opaque in
2562 if n < m
2563 then (
2564 let under = getlink opaque n in
2565 f under
2567 else loop (n-m) rest
2569 loop n state.layout;
2573 let textentry text key =
2574 if key land 0xff00 = 0xff00
2575 then TEcont text
2576 else TEcont (text ^ Wsi.toutf8 key)
2579 let reqlayout angle proportional =
2580 match state.throttle with
2581 | None ->
2582 if nogeomcmds state.geomcmds
2583 then state.anchor <- getanchor ();
2584 conf.angle <- angle mod 360;
2585 if conf.angle != 0
2586 then (
2587 match state.mode with
2588 | LinkNav _ -> state.mode <- View
2589 | _ -> ()
2591 conf.proportional <- proportional;
2592 invalidate "reqlayout"
2593 (fun () -> wcmd "reqlayout %d %d" conf.angle (btod proportional));
2594 | _ -> ()
2597 let settrim trimmargins trimfuzz =
2598 if nogeomcmds state.geomcmds
2599 then state.anchor <- getanchor ();
2600 conf.trimmargins <- trimmargins;
2601 conf.trimfuzz <- trimfuzz;
2602 let x0, y0, x1, y1 = trimfuzz in
2603 invalidate "settrim"
2604 (fun () ->
2605 wcmd "settrim %d %d %d %d %d" (btod conf.trimmargins) x0 y0 x1 y1);
2606 flushpages ();
2609 let setzoom zoom =
2610 match state.throttle with
2611 | None ->
2612 let zoom = max 0.01 zoom in
2613 if zoom <> conf.zoom
2614 then (
2615 state.prevzoom <- conf.zoom;
2616 conf.zoom <- zoom;
2617 reshape conf.winw conf.winh;
2618 state.text <- Printf.sprintf "zoom is now %-5.1f" (zoom *. 100.0);
2621 | Some (layout, y, started) ->
2622 let time =
2623 match conf.maxwait with
2624 | None -> 0.0
2625 | Some t -> t
2627 let dt = now () -. started in
2628 if dt > time
2629 then (
2630 state.y <- y;
2631 load layout;
2635 let setcolumns mode columns coverA coverB =
2636 state.prevcolumns <- Some (conf.columns, conf.zoom);
2637 if columns < 0
2638 then (
2639 if isbirdseye mode
2640 then showtext '!' "split mode doesn't work in bird's eye"
2641 else (
2642 conf.columns <- Csplit (-columns, [||]);
2643 state.x <- 0;
2644 conf.zoom <- 1.0;
2647 else (
2648 if columns < 2
2649 then (
2650 conf.columns <- Csingle [||];
2651 state.x <- 0;
2652 setzoom 1.0;
2654 else (
2655 conf.columns <- Cmulti ((columns, coverA, coverB), [||]);
2656 conf.zoom <- 1.0;
2659 reshape conf.winw conf.winh;
2662 let enterbirdseye () =
2663 let zoom = float conf.thumbw /. float conf.winw in
2664 let birdseyepageno =
2665 let cy = conf.winh / 2 in
2666 let fold = function
2667 | [] -> 0
2668 | l :: rest ->
2669 let rec fold best = function
2670 | [] -> best.pageno
2671 | l :: rest ->
2672 let d = cy - (l.pagedispy + l.pagevh/2)
2673 and dbest = cy - (best.pagedispy + best.pagevh/2) in
2674 if abs d < abs dbest
2675 then fold l rest
2676 else best.pageno
2677 in fold l rest
2679 fold state.layout
2681 state.mode <- Birdseye (
2682 { conf with zoom = conf.zoom }, state.x, birdseyepageno, -1, getanchor ()
2684 conf.zoom <- zoom;
2685 conf.presentation <- false;
2686 conf.interpagespace <- 10;
2687 conf.hlinks <- false;
2688 state.x <- 0;
2689 state.mstate <- Mnone;
2690 conf.maxwait <- None;
2691 conf.columns <- (
2692 match conf.beyecolumns with
2693 | Some c ->
2694 conf.zoom <- 1.0;
2695 Cmulti ((c, 0, 0), [||])
2696 | None -> Csingle [||]
2698 Wsi.setcursor Wsi.CURSOR_INHERIT;
2699 if conf.verbose
2700 then
2701 state.text <- Printf.sprintf "birds eye mode on (zoom %3.1f%%)"
2702 (100.0*.zoom)
2703 else
2704 state.text <- ""
2706 reshape conf.winw conf.winh;
2709 let leavebirdseye (c, leftx, pageno, _, anchor) goback =
2710 state.mode <- View;
2711 conf.zoom <- c.zoom;
2712 conf.presentation <- c.presentation;
2713 conf.interpagespace <- c.interpagespace;
2714 conf.maxwait <- c.maxwait;
2715 conf.hlinks <- c.hlinks;
2716 conf.beyecolumns <- (
2717 match conf.columns with
2718 | Cmulti ((c, _, _), _) -> Some c
2719 | Csingle _ -> None
2720 | Csplit _ -> failwith "leaving bird's eye split mode"
2722 conf.columns <- (
2723 match c.columns with
2724 | Cmulti (c, _) -> Cmulti (c, [||])
2725 | Csingle _ -> Csingle [||]
2726 | Csplit (c, _) -> Csplit (c, [||])
2728 state.x <- leftx;
2729 if conf.verbose
2730 then
2731 state.text <- Printf.sprintf "birds eye mode off (zoom %3.1f%%)"
2732 (100.0*.conf.zoom)
2734 reshape conf.winw conf.winh;
2735 state.anchor <- if goback then anchor else (pageno, 0.0, 1.0);
2738 let togglebirdseye () =
2739 match state.mode with
2740 | Birdseye vals -> leavebirdseye vals true
2741 | View -> enterbirdseye ()
2742 | _ -> ()
2745 let upbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2746 let pageno = max 0 (pageno - incr) in
2747 let rec loop = function
2748 | [] -> gotopage1 pageno 0
2749 | l :: _ when l.pageno = pageno ->
2750 if l.pagedispy >= 0 && l.pagey = 0
2751 then G.postRedisplay "upbirdseye"
2752 else gotopage1 pageno 0
2753 | _ :: rest -> loop rest
2755 loop state.layout;
2756 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor)
2759 let downbirdseye incr (conf, leftx, pageno, hooverpageno, anchor) =
2760 let pageno = min (state.pagecount - 1) (pageno + incr) in
2761 state.mode <- Birdseye (conf, leftx, pageno, hooverpageno, anchor);
2762 let rec loop = function
2763 | [] ->
2764 let y, h = getpageyh pageno in
2765 let dy = (y - state.y) - (conf.winh - h - conf.interpagespace) in
2766 gotoy (clamp dy)
2767 | l :: _ when l.pageno = pageno ->
2768 if l.pagevh != l.pageh
2769 then gotoy (clamp (l.pageh - l.pagevh + conf.interpagespace))
2770 else G.postRedisplay "downbirdseye"
2771 | _ :: rest -> loop rest
2773 loop state.layout
2776 let optentry mode _ key =
2777 let btos b = if b then "on" else "off" in
2778 if key >= 32 && key < 127
2779 then
2780 let c = Char.chr key in
2781 match c with
2782 | 's' ->
2783 let ondone s =
2784 try conf.scrollstep <- int_of_string s with exc ->
2785 state.text <- Printf.sprintf "bad integer `%s': %s"
2786 s (Printexc.to_string exc)
2788 TEswitch ("scroll step: ", "", None, intentry, ondone, true)
2790 | 'A' ->
2791 let ondone s =
2793 conf.autoscrollstep <- int_of_string s;
2794 if state.autoscroll <> None
2795 then state.autoscroll <- Some conf.autoscrollstep
2796 with exc ->
2797 state.text <- Printf.sprintf "bad integer `%s': %s"
2798 s (Printexc.to_string exc)
2800 TEswitch ("auto scroll step: ", "", None, intentry, ondone, true)
2802 | 'C' ->
2803 let ondone s =
2805 let n, a, b = multicolumns_of_string s in
2806 setcolumns mode n a b;
2807 with exc ->
2808 state.text <- Printf.sprintf "bad columns `%s': %s"
2809 s (Printexc.to_string exc)
2811 TEswitch ("columns: ", "", None, textentry, ondone, true)
2813 | 'Z' ->
2814 let ondone s =
2816 let zoom = float (int_of_string s) /. 100.0 in
2817 setzoom zoom
2818 with exc ->
2819 state.text <- Printf.sprintf "bad integer `%s': %s"
2820 s (Printexc.to_string exc)
2822 TEswitch ("zoom: ", "", None, intentry, ondone, true)
2824 | 't' ->
2825 let ondone s =
2827 conf.thumbw <- bound (int_of_string s) 2 4096;
2828 state.text <-
2829 Printf.sprintf "thumbnail width is set to %d" conf.thumbw;
2830 begin match mode with
2831 | Birdseye beye ->
2832 leavebirdseye beye false;
2833 enterbirdseye ();
2834 | _ -> ();
2836 with exc ->
2837 state.text <- Printf.sprintf "bad integer `%s': %s"
2838 s (Printexc.to_string exc)
2840 TEswitch ("thumbnail width: ", "", None, intentry, ondone, true)
2842 | 'R' ->
2843 let ondone s =
2844 match try
2845 Some (int_of_string s)
2846 with exc ->
2847 state.text <- Printf.sprintf "bad integer `%s': %s"
2848 s (Printexc.to_string exc);
2849 None
2850 with
2851 | Some angle -> reqlayout angle conf.proportional
2852 | None -> ()
2854 TEswitch ("rotation: ", "", None, intentry, ondone, true)
2856 | 'i' ->
2857 conf.icase <- not conf.icase;
2858 TEdone ("case insensitive search " ^ (btos conf.icase))
2860 | 'p' ->
2861 conf.preload <- not conf.preload;
2862 gotoy state.y;
2863 TEdone ("preload " ^ (btos conf.preload))
2865 | 'v' ->
2866 conf.verbose <- not conf.verbose;
2867 TEdone ("verbose " ^ (btos conf.verbose))
2869 | 'd' ->
2870 conf.debug <- not conf.debug;
2871 TEdone ("debug " ^ (btos conf.debug))
2873 | 'h' ->
2874 conf.maxhfit <- not conf.maxhfit;
2875 state.maxy <- calcheight ();
2876 TEdone ("maxhfit " ^ (btos conf.maxhfit))
2878 | 'c' ->
2879 conf.crophack <- not conf.crophack;
2880 TEdone ("crophack " ^ btos conf.crophack)
2882 | 'a' ->
2883 let s =
2884 match conf.maxwait with
2885 | None ->
2886 conf.maxwait <- Some infinity;
2887 "always wait for page to complete"
2888 | Some _ ->
2889 conf.maxwait <- None;
2890 "show placeholder if page is not ready"
2892 TEdone s
2894 | 'f' ->
2895 conf.underinfo <- not conf.underinfo;
2896 TEdone ("underinfo " ^ btos conf.underinfo)
2898 | 'P' ->
2899 conf.savebmarks <- not conf.savebmarks;
2900 TEdone ("persistent bookmarks " ^ btos conf.savebmarks)
2902 | 'S' ->
2903 let ondone s =
2905 let pageno, py =
2906 match state.layout with
2907 | [] -> 0, 0
2908 | l :: _ ->
2909 l.pageno, l.pagey
2911 conf.interpagespace <- int_of_string s;
2912 docolumns conf.columns;
2913 state.maxy <- calcheight ();
2914 let y = getpagey pageno in
2915 gotoy (y + py)
2916 with exc ->
2917 state.text <- Printf.sprintf "bad integer `%s': %s"
2918 s (Printexc.to_string exc)
2920 TEswitch ("vertical margin: ", "", None, intentry, ondone, true)
2922 | 'l' ->
2923 reqlayout conf.angle (not conf.proportional);
2924 TEdone ("proportional display " ^ btos conf.proportional)
2926 | 'T' ->
2927 settrim (not conf.trimmargins) conf.trimfuzz;
2928 TEdone ("trim margins " ^ btos conf.trimmargins)
2930 | 'I' ->
2931 conf.invert <- not conf.invert;
2932 TEdone ("invert colors " ^ btos conf.invert)
2934 | 'x' ->
2935 let ondone s =
2936 cbput state.hists.sel s;
2937 conf.selcmd <- s;
2939 TEswitch ("selection command: ", "", Some (onhist state.hists.sel),
2940 textentry, ondone, true)
2942 | _ ->
2943 state.text <- Printf.sprintf "bad option %d `%c'" key c;
2944 TEstop
2945 else
2946 TEcont state.text
2949 class type lvsource = object
2950 method getitemcount : int
2951 method getitem : int -> (string * int)
2952 method hasaction : int -> bool
2953 method exit :
2954 uioh:uioh ->
2955 cancel:bool ->
2956 active:int ->
2957 first:int ->
2958 pan:int ->
2959 qsearch:string ->
2960 uioh option
2961 method getactive : int
2962 method getfirst : int
2963 method getqsearch : string
2964 method setqsearch : string -> unit
2965 method getpan : int
2966 end;;
2968 class virtual lvsourcebase = object
2969 val mutable m_active = 0
2970 val mutable m_first = 0
2971 val mutable m_qsearch = ""
2972 val mutable m_pan = 0
2973 method getactive = m_active
2974 method getfirst = m_first
2975 method getqsearch = m_qsearch
2976 method getpan = m_pan
2977 method setqsearch s = m_qsearch <- s
2978 end;;
2980 let withoutlastutf8 s =
2981 let len = String.length s in
2982 if len = 0
2983 then s
2984 else
2985 let rec find pos =
2986 if pos = 0
2987 then pos
2988 else
2989 let b = Char.code s.[pos] in
2990 if b land 0b110000 = 0b11000000
2991 then find (pos-1)
2992 else pos-1
2994 let first =
2995 if Char.code s.[len-1] land 0x80 = 0
2996 then len-1
2997 else find (len-1)
2999 String.sub s 0 first;
3002 let textentrykeyboard
3003 key _mask ((c, text, opthist, onkey, ondone, cancelonempty), onleave) =
3004 let enttext te =
3005 state.mode <- Textentry (te, onleave);
3006 state.text <- "";
3007 enttext ();
3008 G.postRedisplay "textentrykeyboard enttext";
3010 let histaction cmd =
3011 match opthist with
3012 | None -> ()
3013 | Some (action, _) ->
3014 state.mode <- Textentry (
3015 (c, action cmd, opthist, onkey, ondone, cancelonempty), onleave
3017 G.postRedisplay "textentry histaction"
3019 match key with
3020 | 0xff08 -> (* backspace *)
3021 let s = withoutlastutf8 text in
3022 let len = String.length s in
3023 if cancelonempty && len = 0
3024 then (
3025 onleave Cancel;
3026 G.postRedisplay "textentrykeyboard after cancel";
3028 else (
3029 enttext (c, s, opthist, onkey, ondone, cancelonempty)
3032 | 0xff0d ->
3033 ondone text;
3034 onleave Confirm;
3035 G.postRedisplay "textentrykeyboard after confirm"
3037 | 0xff52 -> histaction HCprev
3038 | 0xff54 -> histaction HCnext
3039 | 0xff50 -> histaction HCfirst
3040 | 0xff57 -> histaction HClast
3042 | 0xff1b -> (* escape*)
3043 if String.length text = 0
3044 then (
3045 begin match opthist with
3046 | None -> ()
3047 | Some (_, onhistcancel) -> onhistcancel ()
3048 end;
3049 onleave Cancel;
3050 state.text <- "";
3051 G.postRedisplay "textentrykeyboard after cancel2"
3053 else (
3054 enttext (c, "", opthist, onkey, ondone, cancelonempty)
3057 | 0xff9f | 0xffff -> () (* delete *)
3059 | _ when key != 0 && key land 0xff00 != 0xff00 ->
3060 begin match onkey text key with
3061 | TEdone text ->
3062 ondone text;
3063 onleave Confirm;
3064 G.postRedisplay "textentrykeyboard after confirm2";
3066 | TEcont text ->
3067 enttext (c, text, opthist, onkey, ondone, cancelonempty);
3069 | TEstop ->
3070 onleave Cancel;
3071 G.postRedisplay "textentrykeyboard after cancel3"
3073 | TEswitch te ->
3074 state.mode <- Textentry (te, onleave);
3075 G.postRedisplay "textentrykeyboard switch";
3076 end;
3078 | _ ->
3079 vlog "unhandled key %s" (Wsi.keyname key)
3082 let firstof first active =
3083 if first > active || abs (first - active) > fstate.maxrows - 1
3084 then max 0 (active - (fstate.maxrows/2))
3085 else first
3088 let calcfirst first active =
3089 if active > first
3090 then
3091 let rows = active - first in
3092 if rows > fstate.maxrows then active - fstate.maxrows else first
3093 else active
3096 let scrollph y maxy =
3097 let sh = (float (maxy + conf.winh) /. float conf.winh) in
3098 let sh = float conf.winh /. sh in
3099 let sh = max sh (float conf.scrollh) in
3101 let percent =
3102 if y = state.maxy
3103 then 1.0
3104 else float y /. float maxy
3106 let position = (float conf.winh -. sh) *. percent in
3108 let position =
3109 if position +. sh > float conf.winh
3110 then float conf.winh -. sh
3111 else position
3113 position, sh;
3116 let coe s = (s :> uioh);;
3118 class listview ~(source:lvsource) ~trusted ~modehash =
3119 object (self)
3120 val m_pan = source#getpan
3121 val m_first = source#getfirst
3122 val m_active = source#getactive
3123 val m_qsearch = source#getqsearch
3124 val m_prev_uioh = state.uioh
3126 method private elemunder y =
3127 let n = y / (fstate.fontsize+1) in
3128 if m_first + n < source#getitemcount
3129 then (
3130 if source#hasaction (m_first + n)
3131 then Some (m_first + n)
3132 else None
3134 else None
3136 method display =
3137 Gl.enable `blend;
3138 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
3139 GlDraw.color (0., 0., 0.) ~alpha:0.85;
3140 GlDraw.rect (0., 0.) (float conf.winw, float conf.winh);
3141 GlDraw.color (1., 1., 1.);
3142 Gl.enable `texture_2d;
3143 let fs = fstate.fontsize in
3144 let nfs = fs + 1 in
3145 let ww = fstate.wwidth in
3146 let tabw = 30.0*.ww in
3147 let itemcount = source#getitemcount in
3148 let rec loop row =
3149 if (row - m_first) > fstate.maxrows
3150 then ()
3151 else (
3152 if row >= 0 && row < itemcount
3153 then (
3154 let (s, level) = source#getitem row in
3155 let y = (row - m_first) * nfs in
3156 let x = 5.0 +. float (level + m_pan) *. ww in
3157 if row = m_active
3158 then (
3159 Gl.disable `texture_2d;
3160 GlDraw.polygon_mode `both `line;
3161 GlDraw.color (1., 1., 1.) ~alpha:0.9;
3162 GlDraw.rect (1., float (y + 1))
3163 (float (conf.winw - conf.scrollbw - 1), float (y + fs + 3));
3164 GlDraw.polygon_mode `both `fill;
3165 GlDraw.color (1., 1., 1.);
3166 Gl.enable `texture_2d;
3169 let drawtabularstring s =
3170 let drawstr x s = drawstring1 fs (truncate x) (y+nfs) s in
3171 if trusted
3172 then
3173 let tabpos = try String.index s '\t' with Not_found -> -1 in
3174 if tabpos > 0
3175 then
3176 let len = String.length s - tabpos - 1 in
3177 let s1 = String.sub s 0 tabpos
3178 and s2 = String.sub s (tabpos + 1) len in
3179 let nx = drawstr x s1 in
3180 let sw = nx -. x in
3181 let x = x +. (max tabw sw) in
3182 drawstr x s2
3183 else
3184 drawstr x s
3185 else
3186 drawstr x s
3188 let _ = drawtabularstring s in
3189 loop (row+1)
3193 loop m_first;
3194 Gl.disable `blend;
3195 Gl.disable `texture_2d;
3197 method updownlevel incr =
3198 let len = source#getitemcount in
3199 let curlevel =
3200 if m_active >= 0 && m_active < len
3201 then snd (source#getitem m_active)
3202 else -1
3204 let rec flow i =
3205 if i = len then i-1 else if i = -1 then 0 else
3206 let _, l = source#getitem i in
3207 if l != curlevel then i else flow (i+incr)
3209 let active = flow m_active in
3210 let first = calcfirst m_first active in
3211 G.postRedisplay "outline updownlevel";
3212 {< m_active = active; m_first = first >}
3214 method private key1 key mask =
3215 let set1 active first qsearch =
3216 coe {< m_active = active; m_first = first; m_qsearch = qsearch >}
3218 let search active pattern incr =
3219 let dosearch re =
3220 let rec loop n =
3221 if n >= 0 && n < source#getitemcount
3222 then (
3223 let s, _ = source#getitem n in
3225 (try ignore (Str.search_forward re s 0); true
3226 with Not_found -> false)
3227 then Some n
3228 else loop (n + incr)
3230 else None
3232 loop active
3235 let re = Str.regexp_case_fold pattern in
3236 dosearch re
3237 with Failure s ->
3238 state.text <- s;
3239 None
3241 let itemcount = source#getitemcount in
3242 let find start incr =
3243 let rec find i =
3244 if i = -1 || i = itemcount
3245 then -1
3246 else (
3247 if source#hasaction i
3248 then i
3249 else find (i + incr)
3252 find start
3254 let set active first =
3255 let first = bound first 0 (itemcount - fstate.maxrows) in
3256 state.text <- "";
3257 coe {< m_active = active; m_first = first >}
3259 let navigate incr =
3260 let isvisible first n = n >= first && n - first <= fstate.maxrows in
3261 let active, first =
3262 let incr1 = if incr > 0 then 1 else -1 in
3263 if isvisible m_first m_active
3264 then
3265 let next =
3266 let next = m_active + incr in
3267 let next =
3268 if next < 0 || next >= itemcount
3269 then -1
3270 else find next incr1
3272 if next = -1 || abs (m_active - next) > fstate.maxrows
3273 then -1
3274 else next
3276 if next = -1
3277 then
3278 let first = m_first + incr in
3279 let first = bound first 0 (itemcount - 1) in
3280 let next =
3281 let next = m_active + incr in
3282 let next = bound next 0 (itemcount - 1) in
3283 find next ~-incr1
3285 let active = if next = -1 then m_active else next in
3286 active, first
3287 else
3288 let first = min next m_first in
3289 let first =
3290 if abs (next - first) > fstate.maxrows
3291 then first + incr
3292 else first
3294 next, first
3295 else
3296 let first = m_first + incr in
3297 let first = bound first 0 (itemcount - 1) in
3298 let active =
3299 let next = m_active + incr in
3300 let next = bound next 0 (itemcount - 1) in
3301 let next = find next incr1 in
3302 let active =
3303 if next = -1 || abs (m_active - first) > fstate.maxrows
3304 then (
3305 let active = if m_active = -1 then next else m_active in
3306 active
3308 else next
3310 if isvisible first active
3311 then active
3312 else -1
3314 active, first
3316 G.postRedisplay "listview navigate";
3317 set active first;
3319 match key with
3320 | (0x72|0x73) when Wsi.withctrl mask -> (* ctrl-r/ctlr-s *)
3321 let incr = if key = 0x72 then -1 else 1 in
3322 let active, first =
3323 match search (m_active + incr) m_qsearch incr with
3324 | None ->
3325 state.text <- m_qsearch ^ " [not found]";
3326 m_active, m_first
3327 | Some active ->
3328 state.text <- m_qsearch;
3329 active, firstof m_first active
3331 G.postRedisplay "listview ctrl-r/s";
3332 set1 active first m_qsearch;
3334 | 0xff08 -> (* backspace *)
3335 if String.length m_qsearch = 0
3336 then coe self
3337 else (
3338 let qsearch = withoutlastutf8 m_qsearch in
3339 let len = String.length qsearch in
3340 if len = 0
3341 then (
3342 state.text <- "";
3343 G.postRedisplay "listview empty qsearch";
3344 set1 m_active m_first "";
3346 else
3347 let active, first =
3348 match search m_active qsearch ~-1 with
3349 | None ->
3350 state.text <- qsearch ^ " [not found]";
3351 m_active, m_first
3352 | Some active ->
3353 state.text <- qsearch;
3354 active, firstof m_first active
3356 G.postRedisplay "listview backspace qsearch";
3357 set1 active first qsearch
3360 | key when (key != 0 && key land 0xff00 != 0xff00) ->
3361 let pattern = m_qsearch ^ Wsi.toutf8 key in
3362 let active, first =
3363 match search m_active pattern 1 with
3364 | None ->
3365 state.text <- pattern ^ " [not found]";
3366 m_active, m_first
3367 | Some active ->
3368 state.text <- pattern;
3369 active, firstof m_first active
3371 G.postRedisplay "listview qsearch add";
3372 set1 active first pattern;
3374 | 0xff1b -> (* escape *)
3375 state.text <- "";
3376 if String.length m_qsearch = 0
3377 then (
3378 G.postRedisplay "list view escape";
3379 begin
3380 match
3381 source#exit (coe self) true m_active m_first m_pan m_qsearch
3382 with
3383 | None -> m_prev_uioh
3384 | Some uioh -> uioh
3387 else (
3388 G.postRedisplay "list view kill qsearch";
3389 source#setqsearch "";
3390 coe {< m_qsearch = "" >}
3393 | 0xff0d -> (* return *)
3394 state.text <- "";
3395 let self = {< m_qsearch = "" >} in
3396 source#setqsearch "";
3397 let opt =
3398 G.postRedisplay "listview enter";
3399 if m_active >= 0 && m_active < source#getitemcount
3400 then (
3401 source#exit (coe self) false m_active m_first m_pan "";
3403 else (
3404 source#exit (coe self) true m_active m_first m_pan "";
3407 begin match opt with
3408 | None -> m_prev_uioh
3409 | Some uioh -> uioh
3412 | 0xff9f | 0xffff -> (* delete *)
3413 coe self
3415 | 0xff52 -> navigate ~-1 (* up *)
3416 | 0xff54 -> navigate 1 (* down *)
3417 | 0xff55 -> navigate ~-(fstate.maxrows) (* prior *)
3418 | 0xff56 -> navigate fstate.maxrows (* next *)
3420 | 0xff53 -> (* right *)
3421 state.text <- "";
3422 G.postRedisplay "listview right";
3423 coe {< m_pan = m_pan - 1 >}
3425 | 0xff51 -> (* left *)
3426 state.text <- "";
3427 G.postRedisplay "listview left";
3428 coe {< m_pan = m_pan + 1 >}
3430 | 0xff50 -> (* home *)
3431 let active = find 0 1 in
3432 G.postRedisplay "listview home";
3433 set active 0;
3435 | 0xff57 -> (* end *)
3436 let first = max 0 (itemcount - fstate.maxrows) in
3437 let active = find (itemcount - 1) ~-1 in
3438 G.postRedisplay "listview end";
3439 set active first;
3441 | key when (key = 0 || key land 0xff00 = 0xff00) ->
3442 coe self
3444 | _ ->
3445 dolog "listview unknown key %#x" key; coe self
3447 method key key mask =
3448 match state.mode with
3449 | Textentry te -> textentrykeyboard key mask te; coe self
3450 | _ -> self#key1 key mask
3452 method button button down x y _ =
3453 let opt =
3454 match button with
3455 | 1 when x > conf.winw - conf.scrollbw ->
3456 G.postRedisplay "listview scroll";
3457 if down
3458 then
3459 let _, position, sh = self#scrollph in
3460 if y > truncate position && y < truncate (position +. sh)
3461 then (
3462 state.mstate <- Mscrolly;
3463 Some (coe self)
3465 else
3466 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3467 let first = truncate (s *. float source#getitemcount) in
3468 let first = min source#getitemcount first in
3469 Some (coe {< m_first = first; m_active = first >})
3470 else (
3471 state.mstate <- Mnone;
3472 Some (coe self);
3474 | 1 when not down ->
3475 begin match self#elemunder y with
3476 | Some n ->
3477 G.postRedisplay "listview click";
3478 source#exit
3479 (coe {< m_active = n >}) false n m_first m_pan m_qsearch
3480 | _ ->
3481 Some (coe self)
3483 | n when (n == 4 || n == 5) && not down ->
3484 let len = source#getitemcount in
3485 let first =
3486 if n = 5 && m_first + fstate.maxrows >= len
3487 then
3488 m_first
3489 else
3490 let first = m_first + (if n == 4 then -1 else 1) in
3491 bound first 0 (len - 1)
3493 G.postRedisplay "listview wheel";
3494 Some (coe {< m_first = first >})
3495 | n when (n = 6 || n = 7) && not down ->
3496 let inc = m_first + (if n = 7 then -1 else 1) in
3497 G.postRedisplay "listview hwheel";
3498 Some (coe {< m_pan = m_pan + inc >})
3499 | _ ->
3500 Some (coe self)
3502 match opt with
3503 | None -> m_prev_uioh
3504 | Some uioh -> uioh
3506 method motion _ y =
3507 match state.mstate with
3508 | Mscrolly ->
3509 let s = float (max 0 (y - conf.scrollh)) /. float conf.winh in
3510 let first = truncate (s *. float source#getitemcount) in
3511 let first = min source#getitemcount first in
3512 G.postRedisplay "listview motion";
3513 coe {< m_first = first; m_active = first >}
3514 | _ -> coe self
3516 method pmotion x y =
3517 if x < conf.winw - conf.scrollbw
3518 then
3519 let n =
3520 match self#elemunder y with
3521 | None -> Wsi.setcursor Wsi.CURSOR_INHERIT; m_active
3522 | Some n -> Wsi.setcursor Wsi.CURSOR_INFO; n
3524 let o =
3525 if n != m_active
3526 then (G.postRedisplay "listview pmotion"; {< m_active = n >})
3527 else self
3529 coe o
3530 else (
3531 Wsi.setcursor Wsi.CURSOR_INHERIT;
3532 coe self
3535 method infochanged _ = ()
3537 method scrollpw = (0, 0.0, 0.0)
3538 method scrollph =
3539 let nfs = fstate.fontsize + 1 in
3540 let y = m_first * nfs in
3541 let itemcount = source#getitemcount in
3542 let maxi = max 0 (itemcount - fstate.maxrows) in
3543 let maxy = maxi * nfs in
3544 let p, h = scrollph y maxy in
3545 conf.scrollbw, p, h
3547 method modehash = modehash
3548 end;;
3550 class outlinelistview ~source =
3551 object (self)
3552 inherit listview
3553 ~source:(source :> lvsource)
3554 ~trusted:false
3555 ~modehash:(findkeyhash conf "outline")
3556 as super
3558 method key key mask =
3559 let calcfirst first active =
3560 if active > first
3561 then
3562 let rows = active - first in
3563 let maxrows =
3564 if String.length state.text = 0
3565 then fstate.maxrows
3566 else fstate.maxrows - 2
3568 if rows > maxrows then active - maxrows else first
3569 else active
3571 let navigate incr =
3572 let active = m_active + incr in
3573 let active = bound active 0 (source#getitemcount - 1) in
3574 let first = calcfirst m_first active in
3575 G.postRedisplay "outline navigate";
3576 coe {< m_active = active; m_first = first >}
3578 let ctrl = Wsi.withctrl mask in
3579 match key with
3580 | 110 when ctrl -> (* ctrl-n *)
3581 source#narrow m_qsearch;
3582 G.postRedisplay "outline ctrl-n";
3583 coe {< m_first = 0; m_active = 0 >}
3585 | 117 when ctrl -> (* ctrl-u *)
3586 source#denarrow;
3587 G.postRedisplay "outline ctrl-u";
3588 state.text <- "";
3589 coe {< m_first = 0; m_active = 0 >}
3591 | 108 when ctrl -> (* ctrl-l *)
3592 let first = max 0 (m_active - (fstate.maxrows / 2)) in
3593 G.postRedisplay "outline ctrl-l";
3594 coe {< m_first = first >}
3596 | 0xff9f | 0xffff -> (* delete *)
3597 source#remove m_active;
3598 G.postRedisplay "outline delete";
3599 let active = max 0 (m_active-1) in
3600 coe {< m_first = firstof m_first active;
3601 m_active = active >}
3603 | 0xff52 -> navigate ~-1 (* up *)
3604 | 0xff54 -> navigate 1 (* down *)
3605 | 0xff55 -> (* prior *)
3606 navigate ~-(fstate.maxrows)
3607 | 0xff56 -> (* next *)
3608 navigate fstate.maxrows
3610 | 0xff53 -> (* [ctrl-]right *)
3611 let o =
3612 if ctrl
3613 then (
3614 G.postRedisplay "outline ctrl right";
3615 {< m_pan = m_pan + 1 >}
3617 else self#updownlevel 1
3619 coe o
3621 | 0xff51 -> (* [ctrl-]left *)
3622 let o =
3623 if ctrl
3624 then (
3625 G.postRedisplay "outline ctrl left";
3626 {< m_pan = m_pan - 1 >}
3628 else self#updownlevel ~-1
3630 coe o
3632 | 0xff50 -> (* home *)
3633 G.postRedisplay "outline home";
3634 coe {< m_first = 0; m_active = 0 >}
3636 | 0xff57 -> (* end *)
3637 let active = source#getitemcount - 1 in
3638 let first = max 0 (active - fstate.maxrows) in
3639 G.postRedisplay "outline end";
3640 coe {< m_active = active; m_first = first >}
3642 | _ -> super#key key mask
3645 let outlinesource usebookmarks =
3646 let empty = [||] in
3647 (object
3648 inherit lvsourcebase
3649 val mutable m_items = empty
3650 val mutable m_orig_items = empty
3651 val mutable m_prev_items = empty
3652 val mutable m_narrow_pattern = ""
3653 val mutable m_hadremovals = false
3655 method getitemcount =
3656 Array.length m_items + (if m_hadremovals then 1 else 0)
3658 method getitem n =
3659 if n == Array.length m_items && m_hadremovals
3660 then
3661 ("[Confirm removal]", 0)
3662 else
3663 let s, n, _ = m_items.(n) in
3664 (s, n)
3666 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
3667 ignore (uioh, first, qsearch);
3668 let confrimremoval = m_hadremovals && active = Array.length m_items in
3669 let items =
3670 if String.length m_narrow_pattern = 0
3671 then m_orig_items
3672 else m_items
3674 if not cancel
3675 then (
3676 if not confrimremoval
3677 then(
3678 let _, _, anchor = m_items.(active) in
3679 gotoghyll (getanchory anchor);
3680 m_items <- items;
3682 else (
3683 state.bookmarks <- Array.to_list m_items;
3684 m_orig_items <- m_items;
3687 else m_items <- items;
3688 m_pan <- pan;
3689 None
3691 method hasaction _ = true
3693 method greetmsg =
3694 if Array.length m_items != Array.length m_orig_items
3695 then "Narrowed to " ^ m_narrow_pattern ^ " (ctrl-u to restore)"
3696 else ""
3698 method narrow pattern =
3699 let reopt = try Some (Str.regexp_case_fold pattern) with _ -> None in
3700 match reopt with
3701 | None -> ()
3702 | Some re ->
3703 let rec loop accu n =
3704 if n = -1
3705 then (
3706 m_narrow_pattern <- pattern;
3707 m_items <- Array.of_list accu
3709 else
3710 let (s, _, _) as o = m_items.(n) in
3711 let accu =
3712 if (try ignore (Str.search_forward re s 0); true
3713 with Not_found -> false)
3714 then o :: accu
3715 else accu
3717 loop accu (n-1)
3719 loop [] (Array.length m_items - 1)
3721 method denarrow =
3722 m_orig_items <- (
3723 if usebookmarks
3724 then Array.of_list state.bookmarks
3725 else state.outlines
3727 m_items <- m_orig_items
3729 method remove m =
3730 if usebookmarks
3731 then
3732 if m >= 0 && m < Array.length m_items
3733 then (
3734 m_hadremovals <- true;
3735 m_items <- Array.init (Array.length m_items - 1) (fun n ->
3736 let n = if n >= m then n+1 else n in
3737 m_items.(n)
3741 method reset anchor items =
3742 m_hadremovals <- false;
3743 if m_orig_items == empty || m_prev_items != items
3744 then (
3745 m_orig_items <- items;
3746 if String.length m_narrow_pattern = 0
3747 then m_items <- items;
3749 m_prev_items <- items;
3750 let rely = getanchory anchor in
3751 let active =
3752 let rec loop n best bestd =
3753 if n = Array.length m_items
3754 then best
3755 else
3756 let (_, _, anchor) = m_items.(n) in
3757 let orely = getanchory anchor in
3758 let d = abs (orely - rely) in
3759 if d < bestd
3760 then loop (n+1) n d
3761 else loop (n+1) best bestd
3763 loop 0 ~-1 max_int
3765 m_active <- active;
3766 m_first <- firstof m_first active
3767 end)
3770 let enterselector usebookmarks =
3771 let source = outlinesource usebookmarks in
3772 fun errmsg ->
3773 let outlines =
3774 if usebookmarks
3775 then Array.of_list state.bookmarks
3776 else state.outlines
3778 if Array.length outlines = 0
3779 then (
3780 showtext ' ' errmsg;
3782 else (
3783 state.text <- source#greetmsg;
3784 Wsi.setcursor Wsi.CURSOR_INHERIT;
3785 let anchor = getanchor () in
3786 source#reset anchor outlines;
3787 state.uioh <- coe (new outlinelistview ~source);
3788 G.postRedisplay "enter selector";
3792 let enteroutlinemode =
3793 let f = enterselector false in
3794 fun ()-> f "Document has no outline";
3797 let enterbookmarkmode =
3798 let f = enterselector true in
3799 fun () -> f "Document has no bookmarks (yet)";
3802 let color_of_string s =
3803 Scanf.sscanf s "%d/%d/%d" (fun r g b ->
3804 (float r /. 256.0, float g /. 256.0, float b /. 256.0)
3808 let color_to_string (r, g, b) =
3809 let r = truncate (r *. 256.0)
3810 and g = truncate (g *. 256.0)
3811 and b = truncate (b *. 256.0) in
3812 Printf.sprintf "%d/%d/%d" r g b
3815 let irect_of_string s =
3816 Scanf.sscanf s "%d/%d/%d/%d" (fun x0 y0 x1 y1 -> (x0,y0,x1,y1))
3819 let irect_to_string (x0,y0,x1,y1) =
3820 Printf.sprintf "%d/%d/%d/%d" x0 y0 x1 y1
3823 let makecheckers () =
3824 (* Appropriated from lablGL-1.04/LablGlut/examples/lablGL/checker.ml which had
3825 following to say:
3826 converted by Issac Trotts. July 25, 2002 *)
3827 let image = GlPix.create `ubyte ~format:`luminance ~width:2 ~height:2 in
3828 Raw.sets_string (GlPix.to_raw image) ~pos:0 "\255\200\200\255";
3829 let id = GlTex.gen_texture () in
3830 GlTex.bind_texture `texture_2d id;
3831 GlPix.store (`unpack_alignment 1);
3832 GlTex.image2d image;
3833 List.iter (GlTex.parameter ~target:`texture_2d)
3834 [ `wrap_s `repeat;
3835 `wrap_t `repeat;
3836 `mag_filter `nearest;
3837 `min_filter `nearest ];
3841 let setcheckers enabled =
3842 match state.texid with
3843 | None ->
3844 if enabled then state.texid <- Some (makecheckers ())
3846 | Some texid ->
3847 if not enabled
3848 then (
3849 GlTex.delete_texture texid;
3850 state.texid <- None;
3854 let int_of_string_with_suffix s =
3855 let l = String.length s in
3856 let s1, shift =
3857 if l > 1
3858 then
3859 let suffix = Char.lowercase s.[l-1] in
3860 match suffix with
3861 | 'k' -> String.sub s 0 (l-1), 10
3862 | 'm' -> String.sub s 0 (l-1), 20
3863 | 'g' -> String.sub s 0 (l-1), 30
3864 | _ -> s, 0
3865 else s, 0
3867 let n = int_of_string s1 in
3868 let m = n lsl shift in
3869 if m < 0 || m < n
3870 then raise (Failure "value too large")
3871 else m
3874 let string_with_suffix_of_int n =
3875 if n = 0
3876 then "0"
3877 else
3878 let n, s =
3879 if n land ((1 lsl 20) - 1) = 0
3880 then n lsr 20, "M"
3881 else (
3882 if n land ((1 lsl 10) - 1) = 0
3883 then n lsr 10, "K"
3884 else n, ""
3887 let rec loop s n =
3888 let h = n mod 1000 in
3889 let n = n / 1000 in
3890 if n = 0
3891 then string_of_int h ^ s
3892 else (
3893 let s = Printf.sprintf "_%03d%s" h s in
3894 loop s n
3897 loop "" n ^ s;
3900 let defghyllscroll = (40, 8, 32);;
3901 let ghyllscroll_of_string s =
3902 let (n, a, b) as nab =
3903 if s = "default"
3904 then defghyllscroll
3905 else Scanf.sscanf s "%u,%u,%u" (fun n a b -> n, a, b)
3907 if n <= a || n <= b || a >= b
3908 then failwith "invalid ghyll N,A,B (N <= A, A < B, N <= B)";
3909 nab;
3912 let ghyllscroll_to_string ((n, a, b) as nab) =
3913 if nab = defghyllscroll
3914 then "default"
3915 else Printf.sprintf "%d,%d,%d" n a b;
3918 let describe_location () =
3919 let f (fn, _) l =
3920 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
3922 let fn, ln = List.fold_left f (-1, -1) state.layout in
3923 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
3924 let percent =
3925 if maxy <= 0
3926 then 100.
3927 else (100. *. (float state.y /. float maxy))
3929 if fn = ln
3930 then
3931 Printf.sprintf "page %d of %d [%.2f%%]"
3932 (fn+1) state.pagecount percent
3933 else
3934 Printf.sprintf
3935 "pages %d-%d of %d [%.2f%%]"
3936 (fn+1) (ln+1) state.pagecount percent
3939 let setpresentationmode v =
3940 let (n, _, _) = getanchor () in
3941 let _, h = getpageyh n in
3942 let ips = if conf.presentation then calcips h else conf.interpagespace in
3943 state.anchor <- (n, 0.0, float ips);
3944 conf.presentation <- v;
3945 if conf.presentation
3946 then (
3947 if not conf.scrollbarinpm
3948 then state.scrollw <- 0;
3950 else state.scrollw <- conf.scrollbw;
3951 represent ();
3954 let enterinfomode =
3955 let btos b = if b then "\xe2\x88\x9a" else "" in
3956 let showextended = ref false in
3957 let leave mode = function
3958 | Confirm -> state.mode <- mode
3959 | Cancel -> state.mode <- mode in
3960 let src =
3961 (object
3962 val mutable m_first_time = true
3963 val mutable m_l = []
3964 val mutable m_a = [||]
3965 val mutable m_prev_uioh = nouioh
3966 val mutable m_prev_mode = View
3968 inherit lvsourcebase
3970 method reset prev_mode prev_uioh =
3971 m_a <- Array.of_list (List.rev m_l);
3972 m_l <- [];
3973 m_prev_mode <- prev_mode;
3974 m_prev_uioh <- prev_uioh;
3975 if m_first_time
3976 then (
3977 let rec loop n =
3978 if n >= Array.length m_a
3979 then ()
3980 else
3981 match m_a.(n) with
3982 | _, _, _, Action _ -> m_active <- n
3983 | _ -> loop (n+1)
3985 loop 0;
3986 m_first_time <- false;
3989 method int name get set =
3990 m_l <-
3991 (name, `int get, 1, Action (
3992 fun u ->
3993 let ondone s =
3994 try set (int_of_string s)
3995 with exn ->
3996 state.text <- Printf.sprintf "bad integer `%s': %s"
3997 s (Printexc.to_string exn)
3999 state.text <- "";
4000 let te = name ^ ": ", "", None, intentry, ondone, true in
4001 state.mode <- Textentry (te, leave m_prev_mode);
4003 )) :: m_l
4005 method int_with_suffix name get set =
4006 m_l <-
4007 (name, `intws get, 1, Action (
4008 fun u ->
4009 let ondone s =
4010 try set (int_of_string_with_suffix s)
4011 with exn ->
4012 state.text <- Printf.sprintf "bad integer `%s': %s"
4013 s (Printexc.to_string exn)
4015 state.text <- "";
4016 let te =
4017 name ^ ": ", "", None, intentry_with_suffix, ondone, true
4019 state.mode <- Textentry (te, leave m_prev_mode);
4021 )) :: m_l
4023 method bool ?(offset=1) ?(btos=btos) name get set =
4024 m_l <-
4025 (name, `bool (btos, get), offset, Action (
4026 fun u ->
4027 let v = get () in
4028 set (not v);
4030 )) :: m_l
4032 method color name get set =
4033 m_l <-
4034 (name, `color get, 1, Action (
4035 fun u ->
4036 let invalid = (nan, nan, nan) in
4037 let ondone s =
4038 let c =
4039 try color_of_string s
4040 with exn ->
4041 state.text <- Printf.sprintf "bad color `%s': %s"
4042 s (Printexc.to_string exn);
4043 invalid
4045 if c <> invalid
4046 then set c;
4048 let te = name ^ ": ", "", None, textentry, ondone, true in
4049 state.text <- color_to_string (get ());
4050 state.mode <- Textentry (te, leave m_prev_mode);
4052 )) :: m_l
4054 method string name get set =
4055 m_l <-
4056 (name, `string get, 1, Action (
4057 fun u ->
4058 let ondone s = set s in
4059 let te = name ^ ": ", "", None, textentry, ondone, true in
4060 state.mode <- Textentry (te, leave m_prev_mode);
4062 )) :: m_l
4064 method colorspace name get set =
4065 m_l <-
4066 (name, `string get, 1, Action (
4067 fun _ ->
4068 let source =
4069 let vals = [| "rgb"; "bgr"; "gray" |] in
4070 (object
4071 inherit lvsourcebase
4073 initializer
4074 m_active <- int_of_colorspace conf.colorspace;
4075 m_first <- 0;
4077 method getitemcount = Array.length vals
4078 method getitem n = (vals.(n), 0)
4079 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4080 ignore (uioh, first, pan, qsearch);
4081 if not cancel then set active;
4082 None
4083 method hasaction _ = true
4084 end)
4086 state.text <- "";
4087 let modehash = findkeyhash conf "info" in
4088 coe (new listview ~source ~trusted:true ~modehash)
4089 )) :: m_l
4091 method caption s offset =
4092 m_l <- (s, `empty, offset, Noaction) :: m_l
4094 method caption2 s f offset =
4095 m_l <- (s, `string f, offset, Noaction) :: m_l
4097 method getitemcount = Array.length m_a
4099 method getitem n =
4100 let tostr = function
4101 | `int f -> string_of_int (f ())
4102 | `intws f -> string_with_suffix_of_int (f ())
4103 | `string f -> f ()
4104 | `color f -> color_to_string (f ())
4105 | `bool (btos, f) -> btos (f ())
4106 | `empty -> ""
4108 let name, t, offset, _ = m_a.(n) in
4109 ((let s = tostr t in
4110 if String.length s > 0
4111 then Printf.sprintf "%s\t%s" name s
4112 else name),
4113 offset)
4115 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4116 let uiohopt =
4117 if not cancel
4118 then (
4119 m_qsearch <- qsearch;
4120 let uioh =
4121 match m_a.(active) with
4122 | _, _, _, Action f -> f uioh
4123 | _ -> uioh
4125 Some uioh
4127 else None
4129 m_active <- active;
4130 m_first <- first;
4131 m_pan <- pan;
4132 uiohopt
4134 method hasaction n =
4135 match m_a.(n) with
4136 | _, _, _, Action _ -> true
4137 | _ -> false
4138 end)
4140 let rec fillsrc prevmode prevuioh =
4141 let sep () = src#caption "" 0 in
4142 let colorp name get set =
4143 src#string name
4144 (fun () -> color_to_string (get ()))
4145 (fun v ->
4147 let c = color_of_string v in
4148 set c
4149 with exn ->
4150 state.text <- Printf.sprintf "bad color `%s': %s"
4151 v (Printexc.to_string exn);
4154 let oldmode = state.mode in
4155 let birdseye = isbirdseye state.mode in
4157 src#caption (if birdseye then "Setup (Bird's eye)" else "Setup") 0;
4159 src#bool "presentation mode"
4160 (fun () -> conf.presentation)
4161 (fun v -> setpresentationmode v);
4163 src#bool "ignore case in searches"
4164 (fun () -> conf.icase)
4165 (fun v -> conf.icase <- v);
4167 src#bool "preload"
4168 (fun () -> conf.preload)
4169 (fun v -> conf.preload <- v);
4171 src#bool "highlight links"
4172 (fun () -> conf.hlinks)
4173 (fun v -> conf.hlinks <- v);
4175 src#bool "under info"
4176 (fun () -> conf.underinfo)
4177 (fun v -> conf.underinfo <- v);
4179 src#bool "persistent bookmarks"
4180 (fun () -> conf.savebmarks)
4181 (fun v -> conf.savebmarks <- v);
4183 src#bool "proportional display"
4184 (fun () -> conf.proportional)
4185 (fun v -> reqlayout conf.angle v);
4187 src#bool "trim margins"
4188 (fun () -> conf.trimmargins)
4189 (fun v -> settrim v conf.trimfuzz; fillsrc prevmode prevuioh);
4191 src#bool "persistent location"
4192 (fun () -> conf.jumpback)
4193 (fun v -> conf.jumpback <- v);
4195 sep ();
4196 src#int "inter-page space"
4197 (fun () -> conf.interpagespace)
4198 (fun n ->
4199 conf.interpagespace <- n;
4200 docolumns conf.columns;
4201 let pageno, py =
4202 match state.layout with
4203 | [] -> 0, 0
4204 | l :: _ ->
4205 l.pageno, l.pagey
4207 state.maxy <- calcheight ();
4208 let y = getpagey pageno in
4209 gotoy (y + py)
4212 src#int "page bias"
4213 (fun () -> conf.pagebias)
4214 (fun v -> conf.pagebias <- v);
4216 src#int "scroll step"
4217 (fun () -> conf.scrollstep)
4218 (fun n -> conf.scrollstep <- n);
4220 src#int "horizontal scroll step"
4221 (fun () -> conf.hscrollstep)
4222 (fun v -> conf.hscrollstep <- v);
4224 src#int "auto scroll step"
4225 (fun () ->
4226 match state.autoscroll with
4227 | Some step -> step
4228 | _ -> conf.autoscrollstep)
4229 (fun n ->
4230 if state.autoscroll <> None
4231 then state.autoscroll <- Some n;
4232 conf.autoscrollstep <- n);
4234 src#int "zoom"
4235 (fun () -> truncate (conf.zoom *. 100.))
4236 (fun v -> setzoom ((float v) /. 100.));
4238 src#int "rotation"
4239 (fun () -> conf.angle)
4240 (fun v -> reqlayout v conf.proportional);
4242 src#int "scroll bar width"
4243 (fun () -> state.scrollw)
4244 (fun v ->
4245 state.scrollw <- v;
4246 conf.scrollbw <- v;
4247 reshape conf.winw conf.winh;
4250 src#int "scroll handle height"
4251 (fun () -> conf.scrollh)
4252 (fun v -> conf.scrollh <- v;);
4254 src#int "thumbnail width"
4255 (fun () -> conf.thumbw)
4256 (fun v ->
4257 conf.thumbw <- min 4096 v;
4258 match oldmode with
4259 | Birdseye beye ->
4260 leavebirdseye beye false;
4261 enterbirdseye ()
4262 | _ -> ()
4265 let mode = state.mode in
4266 src#string "columns"
4267 (fun () ->
4268 match conf.columns with
4269 | Csingle _ -> "1"
4270 | Cmulti (multi, _) -> multicolumns_to_string multi
4271 | Csplit (count, _) -> "-" ^ string_of_int count
4273 (fun v ->
4274 let n, a, b = multicolumns_of_string v in
4275 setcolumns mode n a b);
4277 sep ();
4278 src#caption "Presentation mode" 0;
4279 src#bool "scrollbar visible"
4280 (fun () -> conf.scrollbarinpm)
4281 (fun v ->
4282 if v != conf.scrollbarinpm
4283 then (
4284 conf.scrollbarinpm <- v;
4285 if conf.presentation
4286 then (
4287 state.scrollw <- if v then conf.scrollbw else 0;
4288 reshape conf.winw conf.winh;
4293 sep ();
4294 src#caption "Pixmap cache" 0;
4295 src#int_with_suffix "size (advisory)"
4296 (fun () -> conf.memlimit)
4297 (fun v -> conf.memlimit <- v);
4299 src#caption2 "used"
4300 (fun () -> Printf.sprintf "%s bytes, %d tiles"
4301 (string_with_suffix_of_int state.memused)
4302 (Hashtbl.length state.tilemap)) 1;
4304 sep ();
4305 src#caption "Layout" 0;
4306 src#caption2 "Dimension"
4307 (fun () ->
4308 Printf.sprintf "%dx%d (virtual %dx%d)"
4309 conf.winw conf.winh
4310 state.w state.maxy)
4312 if conf.debug
4313 then
4314 src#caption2 "Position" (fun () ->
4315 Printf.sprintf "%dx%d" state.x state.y
4317 else
4318 src#caption2 "Visible" (fun () -> describe_location ()) 1
4321 sep ();
4322 src#bool ~offset:0 ~btos:(fun v -> if v then "(on)" else "(off)")
4323 "Save these parameters as global defaults at exit"
4324 (fun () -> conf.bedefault)
4325 (fun v -> conf.bedefault <- v)
4328 sep ();
4329 let btos b = if b then "\xc2\xab" else "\xc2\xbb" in
4330 src#bool ~offset:0 ~btos "Extended parameters"
4331 (fun () -> !showextended)
4332 (fun v -> showextended := v; fillsrc prevmode prevuioh);
4333 if !showextended
4334 then (
4335 src#bool "checkers"
4336 (fun () -> conf.checkers)
4337 (fun v -> conf.checkers <- v; setcheckers v);
4338 src#bool "update cursor"
4339 (fun () -> conf.updatecurs)
4340 (fun v -> conf.updatecurs <- v);
4341 src#bool "verbose"
4342 (fun () -> conf.verbose)
4343 (fun v -> conf.verbose <- v);
4344 src#bool "invert colors"
4345 (fun () -> conf.invert)
4346 (fun v -> conf.invert <- v);
4347 src#bool "max fit"
4348 (fun () -> conf.maxhfit)
4349 (fun v -> conf.maxhfit <- v);
4350 src#bool "redirect stderr"
4351 (fun () -> conf.redirectstderr)
4352 (fun v -> conf.redirectstderr <- v; redirectstderr ());
4353 src#string "uri launcher"
4354 (fun () -> conf.urilauncher)
4355 (fun v -> conf.urilauncher <- v);
4356 src#string "path launcher"
4357 (fun () -> conf.pathlauncher)
4358 (fun v -> conf.pathlauncher <- v);
4359 src#string "tile size"
4360 (fun () -> Printf.sprintf "%dx%d" conf.tilew conf.tileh)
4361 (fun v ->
4363 let w, h = Scanf.sscanf v "%dx%d" (fun w h -> w, h) in
4364 conf.tilew <- max 64 w;
4365 conf.tileh <- max 64 h;
4366 flushtiles ();
4367 with exn ->
4368 state.text <- Printf.sprintf "bad tile size `%s': %s"
4369 v (Printexc.to_string exn));
4370 src#int "texture count"
4371 (fun () -> conf.texcount)
4372 (fun v ->
4373 if realloctexts v
4374 then conf.texcount <- v
4375 else showtext '!' " Failed to set texture count please retry later"
4377 src#int "slice height"
4378 (fun () -> conf.sliceheight)
4379 (fun v ->
4380 conf.sliceheight <- v;
4381 wcmd "sliceh %d" conf.sliceheight;
4383 src#int "anti-aliasing level"
4384 (fun () -> conf.aalevel)
4385 (fun v ->
4386 conf.aalevel <- bound v 0 8;
4387 state.anchor <- getanchor ();
4388 opendoc state.path state.password;
4390 src#string "page scroll scaling factor"
4391 (fun () -> string_of_float conf.pgscale)
4392 (fun v ->
4394 let s = float_of_string v in
4395 conf.pgscale <- s
4396 with exn ->
4397 state.text <- Printf.sprintf
4398 "bad page scroll scaling factor `%s': %s"
4399 v (Printexc.to_string exn)
4402 src#int "ui font size"
4403 (fun () -> fstate.fontsize)
4404 (fun v -> setfontsize (bound v 5 100));
4405 src#int "hint font size"
4406 (fun () -> conf.hfsize)
4407 (fun v -> conf.hfsize <- bound v 5 100);
4408 colorp "background color"
4409 (fun () -> conf.bgcolor)
4410 (fun v -> conf.bgcolor <- v);
4411 src#bool "crop hack"
4412 (fun () -> conf.crophack)
4413 (fun v -> conf.crophack <- v);
4414 src#string "trim fuzz"
4415 (fun () -> irect_to_string conf.trimfuzz)
4416 (fun v ->
4418 conf.trimfuzz <- irect_of_string v;
4419 if conf.trimmargins
4420 then settrim true conf.trimfuzz;
4421 with exn ->
4422 state.text <- Printf.sprintf "bad irect `%s': %s"
4423 v (Printexc.to_string exn)
4425 src#string "throttle"
4426 (fun () ->
4427 match conf.maxwait with
4428 | None -> "show place holder if page is not ready"
4429 | Some time ->
4430 if time = infinity
4431 then "wait for page to fully render"
4432 else
4433 "wait " ^ string_of_float time
4434 ^ " seconds before showing placeholder"
4436 (fun v ->
4438 let f = float_of_string v in
4439 if f <= 0.0
4440 then conf.maxwait <- None
4441 else conf.maxwait <- Some f
4442 with exn ->
4443 state.text <- Printf.sprintf "bad time `%s': %s"
4444 v (Printexc.to_string exn)
4446 src#string "ghyll scroll"
4447 (fun () ->
4448 match conf.ghyllscroll with
4449 | None -> ""
4450 | Some nab -> ghyllscroll_to_string nab
4452 (fun v ->
4454 let gs =
4455 if String.length v = 0
4456 then None
4457 else Some (ghyllscroll_of_string v)
4459 conf.ghyllscroll <- gs
4460 with exn ->
4461 state.text <- Printf.sprintf "bad ghyll `%s': %s"
4462 v (Printexc.to_string exn)
4464 src#string "selection command"
4465 (fun () -> conf.selcmd)
4466 (fun v -> conf.selcmd <- v);
4467 src#colorspace "color space"
4468 (fun () -> colorspace_to_string conf.colorspace)
4469 (fun v ->
4470 conf.colorspace <- colorspace_of_int v;
4471 wcmd "cs %d" v;
4472 load state.layout;
4476 sep ();
4477 src#caption "Document" 0;
4478 List.iter (fun (_, s) -> src#caption s 1) state.docinfo;
4479 src#caption2 "Pages"
4480 (fun () -> string_of_int state.pagecount) 1;
4481 src#caption2 "Dimensions"
4482 (fun () -> string_of_int (List.length state.pdims)) 1;
4483 if conf.trimmargins
4484 then (
4485 sep ();
4486 src#caption "Trimmed margins" 0;
4487 src#caption2 "Dimensions"
4488 (fun () -> string_of_int (List.length state.pdims)) 1;
4491 sep ();
4492 src#caption "OpenGL" 0;
4493 src#caption (Printf.sprintf "Vendor\t%s" (GlMisc.get_string `vendor)) 1;
4494 src#caption (Printf.sprintf "Renderer\t%s" (GlMisc.get_string `renderer)) 1;
4495 src#reset prevmode prevuioh;
4497 fun () ->
4498 state.text <- "";
4499 let prevmode = state.mode
4500 and prevuioh = state.uioh in
4501 fillsrc prevmode prevuioh;
4502 let source = (src :> lvsource) in
4503 let modehash = findkeyhash conf "info" in
4504 state.uioh <- coe (object (self)
4505 inherit listview ~source ~trusted:true ~modehash as super
4506 val mutable m_prevmemused = 0
4507 method infochanged = function
4508 | Memused ->
4509 if m_prevmemused != state.memused
4510 then (
4511 m_prevmemused <- state.memused;
4512 G.postRedisplay "memusedchanged";
4514 | Pdim -> G.postRedisplay "pdimchanged"
4515 | Docinfo -> fillsrc prevmode prevuioh
4517 method key key mask =
4518 if not (Wsi.withctrl mask)
4519 then
4520 match key with
4521 | 0xff51 -> coe (self#updownlevel ~-1)
4522 | 0xff53 -> coe (self#updownlevel 1)
4523 | _ -> super#key key mask
4524 else super#key key mask
4525 end);
4526 G.postRedisplay "info";
4529 let enterhelpmode =
4530 let source =
4531 (object
4532 inherit lvsourcebase
4533 method getitemcount = Array.length state.help
4534 method getitem n =
4535 let s, l, _ = state.help.(n) in
4536 (s, l)
4538 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4539 let optuioh =
4540 if not cancel
4541 then (
4542 m_qsearch <- qsearch;
4543 match state.help.(active) with
4544 | _, _, Action f -> Some (f uioh)
4545 | _ -> Some (uioh)
4547 else None
4549 m_active <- active;
4550 m_first <- first;
4551 m_pan <- pan;
4552 optuioh
4554 method hasaction n =
4555 match state.help.(n) with
4556 | _, _, Action _ -> true
4557 | _ -> false
4559 initializer
4560 m_active <- -1
4561 end)
4562 in fun () ->
4563 let modehash = findkeyhash conf "help" in
4564 state.uioh <- coe (new listview ~source ~trusted:true ~modehash);
4565 G.postRedisplay "help";
4568 let entermsgsmode =
4569 let msgsource =
4570 let re = Str.regexp "[\r\n]" in
4571 (object
4572 inherit lvsourcebase
4573 val mutable m_items = [||]
4575 method getitemcount = 1 + Array.length m_items
4577 method getitem n =
4578 if n = 0
4579 then "[Clear]", 0
4580 else m_items.(n-1), 0
4582 method exit ~uioh ~cancel ~active ~first ~pan ~qsearch =
4583 ignore uioh;
4584 if not cancel
4585 then (
4586 if active = 0
4587 then Buffer.clear state.errmsgs;
4588 m_qsearch <- qsearch;
4590 m_active <- active;
4591 m_first <- first;
4592 m_pan <- pan;
4593 None
4595 method hasaction n =
4596 n = 0
4598 method reset =
4599 state.newerrmsgs <- false;
4600 let l = Str.split re (Buffer.contents state.errmsgs) in
4601 m_items <- Array.of_list l
4603 initializer
4604 m_active <- 0
4605 end)
4606 in fun () ->
4607 state.text <- "";
4608 msgsource#reset;
4609 let source = (msgsource :> lvsource) in
4610 let modehash = findkeyhash conf "listview" in
4611 state.uioh <- coe (object
4612 inherit listview ~source ~trusted:false ~modehash as super
4613 method display =
4614 if state.newerrmsgs
4615 then msgsource#reset;
4616 super#display
4617 end);
4618 G.postRedisplay "msgs";
4621 let quickbookmark ?title () =
4622 match state.layout with
4623 | [] -> ()
4624 | l :: _ ->
4625 let title =
4626 match title with
4627 | None ->
4628 let sec = Unix.gettimeofday () in
4629 let tm = Unix.localtime sec in
4630 Printf.sprintf "Quick (page %d) (bookmarked at %d/%d/%d %d:%d)"
4631 (l.pageno+1)
4632 tm.Unix.tm_mday
4633 tm.Unix.tm_mon
4634 (tm.Unix.tm_year + 1900)
4635 tm.Unix.tm_hour
4636 tm.Unix.tm_min
4637 | Some title -> title
4639 state.bookmarks <- (title, 0, getanchor1 l) :: state.bookmarks
4642 let doreshape w h =
4643 state.fullscreen <- None;
4644 Wsi.reshape w h;
4647 let setautoscrollspeed step goingdown =
4648 let incr = max 1 ((abs step) / 2) in
4649 let incr = if goingdown then incr else -incr in
4650 let astep = step + incr in
4651 state.autoscroll <- Some astep;
4654 let gotounder = function
4655 | Ulinkgoto (pageno, top) ->
4656 if pageno >= 0
4657 then (
4658 addnav ();
4659 gotopage1 pageno top;
4662 | Ulinkuri s ->
4663 gotouri s
4665 | Uremote (filename, pageno) ->
4666 let path =
4667 if Sys.file_exists filename
4668 then filename
4669 else
4670 let dir = Filename.dirname state.path in
4671 let path = Filename.concat dir filename in
4672 if Sys.file_exists path
4673 then path
4674 else ""
4676 if String.length path > 0
4677 then (
4678 let anchor = getanchor () in
4679 let ranchor = state.path, state.password, anchor in
4680 state.anchor <- (pageno, 0.0, 0.0);
4681 state.ranchors <- ranchor :: state.ranchors;
4682 opendoc path "";
4684 else showtext '!' ("Could not find " ^ filename)
4686 | Uunexpected _ | Ulaunch _ | Unamed _ | Utext _ | Unone -> ()
4689 let canpan () =
4690 match conf.columns with
4691 | Csplit _ -> true
4692 | _ -> conf.zoom > 1.0
4695 let viewkeyboard key mask =
4696 let enttext te =
4697 let mode = state.mode in
4698 state.mode <- Textentry (te, fun _ -> state.mode <- mode);
4699 state.text <- "";
4700 enttext ();
4701 G.postRedisplay "view:enttext"
4703 let ctrl = Wsi.withctrl mask in
4704 let existsinrow pageno (columns, coverA, coverB) p =
4705 let last = ((pageno - coverA) mod columns) + columns in
4706 let rec any = function
4707 | [] -> false
4708 | l :: rest ->
4709 if l.pageno = coverA - 1 || l.pageno = state.pagecount - coverB
4710 then p l
4711 else (
4712 if not (p l)
4713 then (if l.pageno = last then false else any rest)
4714 else true
4717 any state.layout
4719 match key with
4720 | 81 -> (* Q *)
4721 exit 0
4723 | 0xff63 -> (* insert *)
4724 if conf.angle mod 360 = 0 && not (isbirdseye state.mode)
4725 then (
4726 state.mode <- LinkNav (Ltgendir 0);
4727 gotoy state.y;
4729 else showtext '!' "Keyboard link navigation does not work under rotation"
4731 | 0xff1b | 113 -> (* escape / q *)
4732 begin match state.mstate with
4733 | Mzoomrect _ ->
4734 state.mstate <- Mnone;
4735 Wsi.setcursor Wsi.CURSOR_INHERIT;
4736 G.postRedisplay "kill zoom rect";
4737 | _ ->
4738 begin match state.mode with
4739 | LinkNav _ ->
4740 state.mode <- View;
4741 G.postRedisplay "esc leave linknav"
4742 | _ ->
4743 match state.ranchors with
4744 | [] -> raise Quit
4745 | (path, password, anchor) :: rest ->
4746 state.ranchors <- rest;
4747 state.anchor <- anchor;
4748 opendoc path password
4749 end;
4750 end;
4752 | 0xff08 -> (* backspace *)
4753 gotoghyll (getnav ~-1)
4755 | 111 -> (* o *)
4756 enteroutlinemode ()
4758 | 117 -> (* u *)
4759 state.rects <- [];
4760 state.text <- "";
4761 G.postRedisplay "dehighlight";
4763 | 47 | 63 -> (* / ? *)
4764 let ondone isforw s =
4765 cbput state.hists.pat s;
4766 state.searchpattern <- s;
4767 search s isforw
4769 let s = String.create 1 in
4770 s.[0] <- Char.chr key;
4771 enttext (s, "", Some (onhist state.hists.pat),
4772 textentry, ondone (key = 47), true)
4774 | 43 | 0xffab | 61 when ctrl -> (* ctrl-+ or ctrl-= *)
4775 let incr = if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01 in
4776 setzoom (conf.zoom +. incr)
4778 | 43 | 0xffab -> (* + *)
4779 let ondone s =
4780 let n =
4781 try int_of_string s with exc ->
4782 state.text <- Printf.sprintf "bad integer `%s': %s"
4783 s (Printexc.to_string exc);
4784 max_int
4786 if n != max_int
4787 then (
4788 conf.pagebias <- n;
4789 state.text <- "page bias is now " ^ string_of_int n;
4792 enttext ("page bias: ", "", None, intentry, ondone, true)
4794 | 45 | 0xffad when ctrl -> (* ctrl-- *)
4795 let decr = if conf.zoom -. 0.1 < 0.1 then 0.01 else 0.1 in
4796 setzoom (max 0.01 (conf.zoom -. decr))
4798 | 45 | 0xffad -> (* - *)
4799 let ondone msg = state.text <- msg in
4800 enttext (
4801 "option [acfhilpstvxACFPRSZTIS]: ", "", None,
4802 optentry state.mode, ondone, true
4805 | 48 when ctrl -> (* ctrl-0 *)
4806 setzoom 1.0
4808 | 49 when ctrl -> (* ctrl-1 *)
4809 let cols =
4810 match conf.columns with
4811 | Csingle _ | Cmulti _ -> 1
4812 | Csplit (n, _) -> n
4814 let zoom = zoomforh conf.winw conf.winh state.scrollw cols in
4815 if zoom < 1.0
4816 then setzoom zoom
4818 | 0xffc6 -> (* f9 *)
4819 togglebirdseye ()
4821 | 57 when ctrl -> (* ctrl-9 *)
4822 togglebirdseye ()
4824 | (48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57)
4825 when not ctrl -> (* 0..9 *)
4826 let ondone s =
4827 let n =
4828 try int_of_string s with exc ->
4829 state.text <- Printf.sprintf "bad integer `%s': %s"
4830 s (Printexc.to_string exc);
4833 if n >= 0
4834 then (
4835 addnav ();
4836 cbput state.hists.pag (string_of_int n);
4837 gotopage1 (n + conf.pagebias - 1) 0;
4840 let pageentry text key =
4841 match Char.unsafe_chr key with
4842 | 'g' -> TEdone text
4843 | _ -> intentry text key
4845 let text = "x" in text.[0] <- Char.chr key;
4846 enttext (":", text, Some (onhist state.hists.pag), pageentry, ondone, true)
4848 | 98 -> (* b *)
4849 state.scrollw <- if state.scrollw > 0 then 0 else conf.scrollbw;
4850 reshape conf.winw conf.winh;
4852 | 108 -> (* l *)
4853 conf.hlinks <- not conf.hlinks;
4854 state.text <- "highlightlinks " ^ if conf.hlinks then "on" else "off";
4855 G.postRedisplay "toggle highlightlinks";
4857 | 70 -> (* F *)
4858 state.glinks <- true;
4859 let mode = state.mode in
4860 state.mode <- Textentry (
4861 (":", "", None, linknentry, linkndone gotounder, false),
4862 (fun _ ->
4863 state.glinks <- false;
4864 state.mode <- mode)
4866 state.text <- "";
4867 G.postRedisplay "view:linkent(F)"
4869 | 121 -> (* y *)
4870 state.glinks <- true;
4871 let mode = state.mode in
4872 state.mode <- Textentry (
4873 (":", "", None, linknentry, linkndone (fun under ->
4874 match Ne.pipe () with
4875 | Ne.Exn exn ->
4876 showtext '!' (Printf.sprintf "pipe failed: %s"
4877 (Printexc.to_string exn));
4878 | Ne.Res (r, w) ->
4879 let popened =
4880 try popen conf.selcmd [r, 0; w, -1]; true
4881 with exn ->
4882 showtext '!'
4883 (Printf.sprintf "failed to execute %s: %s"
4884 conf.selcmd (Printexc.to_string exn));
4885 false
4887 let clo cap fd =
4888 Ne.clo fd (fun msg ->
4889 showtext '!' (Printf.sprintf "failed to close %s: %s" cap msg)
4892 let s = undertext under in
4893 if popened
4894 then
4895 (try
4896 let l = String.length s in
4897 let n = Unix.write w s 0 l in
4898 if n != l
4899 then
4900 showtext '!'
4901 (Printf.sprintf
4902 "failed to write %d characters to sel pipe, wrote %d"
4905 with exn ->
4906 showtext '!'
4907 (Printf.sprintf "failed to write to sel pipe: %s"
4908 (Printexc.to_string exn)
4911 else dolog "%s" s;
4912 clo "pipe/r" r;
4913 clo "pipe/w" w;
4914 ), false
4916 fun _ ->
4917 state.glinks <- false;
4918 state.mode <- mode
4920 state.text <- "";
4921 G.postRedisplay "view:linkent"
4923 | 97 -> (* a *)
4924 begin match state.autoscroll with
4925 | Some step ->
4926 conf.autoscrollstep <- step;
4927 state.autoscroll <- None
4928 | None ->
4929 if conf.autoscrollstep = 0
4930 then state.autoscroll <- Some 1
4931 else state.autoscroll <- Some conf.autoscrollstep
4934 | 112 when ctrl -> (* ctrl-p *)
4935 launchpath ()
4937 | 80 -> (* P *)
4938 setpresentationmode (not conf.presentation);
4939 showtext ' ' ("presentation mode " ^
4940 if conf.presentation then "on" else "off");
4942 | 102 -> (* f *)
4943 begin match state.fullscreen with
4944 | None ->
4945 state.fullscreen <- Some (conf.winw, conf.winh);
4946 Wsi.fullscreen ()
4947 | Some (w, h) ->
4948 state.fullscreen <- None;
4949 doreshape w h
4952 | 112 | 78 -> (* p|N *)
4953 search state.searchpattern false
4955 | 110 | 0xffc0 -> (* n|F3 *)
4956 search state.searchpattern true
4958 | 116 -> (* t *)
4959 begin match state.layout with
4960 | [] -> ()
4961 | l :: _ ->
4962 gotoy_and_clear_text (getpagey l.pageno)
4965 | 32 -> (* space *)
4966 begin match state.layout with
4967 | [] -> ()
4968 | l :: rest ->
4969 match conf.columns with
4970 | Csingle _ ->
4971 if conf.presentation && rest == [] && l.pageh > l.pagey + l.pagevh
4972 then
4973 let y = clamp (pgscale conf.winh) in
4974 gotoghyll y
4975 else
4976 let pageno = min (l.pageno+1) (state.pagecount-1) in
4977 gotoghyll (getpagey pageno)
4978 | Cmulti ((c, _, _) as cl, _) ->
4979 if conf.presentation
4980 && (existsinrow l.pageno cl
4981 (fun l -> l.pageh > l.pagey + l.pagevh))
4982 then
4983 let y = clamp (pgscale conf.winh) in
4984 gotoghyll y
4985 else
4986 let pageno = min (l.pageno+c) (state.pagecount-1) in
4987 gotoghyll (getpagey pageno)
4988 | Csplit (n, _) ->
4989 if l.pageno < state.pagecount - 1 || l.pagecol < n - 1
4990 then
4991 let pagey, pageh = getpageyh l.pageno in
4992 let pagey = pagey + pageh * l.pagecol in
4993 let ips = if l.pagecol = 0 then 0 else conf.interpagespace in
4994 gotoghyll (pagey + pageh + ips)
4997 | 0xff9f | 0xffff -> (* delete *)
4998 begin match state.layout with
4999 | [] -> ()
5000 | l :: _ ->
5001 match conf.columns with
5002 | Csingle _ ->
5003 if conf.presentation && l.pagey != 0
5004 then
5005 gotoghyll (clamp (pgscale ~-(conf.winh)))
5006 else
5007 let pageno = max 0 (l.pageno-1) in
5008 gotoghyll (getpagey pageno)
5009 | Cmulti ((c, _, coverB) as cl, _) ->
5010 if conf.presentation &&
5011 (existsinrow l.pageno cl (fun l -> l.pagey != 0))
5012 then
5013 gotoghyll (clamp (pgscale ~-(conf.winh)))
5014 else
5015 let decr =
5016 if l.pageno = state.pagecount - coverB
5017 then 1
5018 else c
5020 let pageno = max 0 (l.pageno-decr) in
5021 gotoghyll (getpagey pageno)
5022 | Csplit (n, _) ->
5023 let y =
5024 if l.pagecol = 0
5025 then
5026 if l.pageno = 0
5027 then l.pagey
5028 else
5029 let pageno = max 0 (l.pageno-1) in
5030 let pagey, pageh = getpageyh pageno in
5031 pagey + (n-1)*pageh
5032 else
5033 let pagey, pageh = getpageyh l.pageno in
5034 pagey + pageh * (l.pagecol-1) - conf.interpagespace
5036 gotoghyll y
5039 | 61 -> (* = *)
5040 showtext ' ' (describe_location ());
5042 | 119 -> (* w *)
5043 begin match state.layout with
5044 | [] -> ()
5045 | l :: _ ->
5046 doreshape (l.pagew + state.scrollw) l.pageh;
5047 G.postRedisplay "w"
5050 | 39 -> (* ' *)
5051 enterbookmarkmode ()
5053 | 104 | 0xffbe -> (* h|F1 *)
5054 enterhelpmode ()
5056 | 105 -> (* i *)
5057 enterinfomode ()
5059 | 101 when conf.redirectstderr -> (* e *)
5060 entermsgsmode ()
5062 | 109 -> (* m *)
5063 let ondone s =
5064 match state.layout with
5065 | l :: _ -> state.bookmarks <- (s, 0, getanchor1 l) :: state.bookmarks
5066 | _ -> ()
5068 enttext ("bookmark: ", "", None, textentry, ondone, true)
5070 | 126 -> (* ~ *)
5071 quickbookmark ();
5072 showtext ' ' "Quick bookmark added";
5074 | 122 -> (* z *)
5075 begin match state.layout with
5076 | l :: _ ->
5077 let rect = getpdimrect l.pagedimno in
5078 let w, h =
5079 if conf.crophack
5080 then
5081 (truncate (1.8 *. (rect.(1) -. rect.(0))),
5082 truncate (1.2 *. (rect.(3) -. rect.(0))))
5083 else
5084 (truncate (rect.(1) -. rect.(0)),
5085 truncate (rect.(3) -. rect.(0)))
5087 let w = truncate ((float w)*.conf.zoom)
5088 and h = truncate ((float h)*.conf.zoom) in
5089 if w != 0 && h != 0
5090 then (
5091 state.anchor <- getanchor ();
5092 doreshape (w + state.scrollw) (h + conf.interpagespace)
5094 G.postRedisplay "z";
5096 | [] -> ()
5099 | 50 when ctrl -> (* ctrl-2 *)
5100 let maxw = getmaxw () in
5101 if maxw > 0.0
5102 then setzoom (maxw /. float conf.winw)
5104 | 60 | 62 -> (* < > *)
5105 reqlayout (conf.angle + (if key = 62 then 30 else -30)) conf.proportional
5107 | 91 | 93 -> (* [ ] *)
5108 conf.colorscale <-
5109 bound (conf.colorscale +. (if key = 93 then 0.1 else -0.1)) 0.0 1.0
5111 G.postRedisplay "brightness";
5113 | 99 when state.mode = View -> (* c *)
5114 let (c, a, b), z =
5115 match state.prevcolumns with
5116 | None -> (1, 0, 0), 1.0
5117 | Some (columns, z) ->
5118 let cab =
5119 match columns with
5120 | Csplit (c, _) -> -c, 0, 0
5121 | Cmulti ((c, a, b), _) -> c, a, b
5122 | Csingle _ -> 1, 0, 0
5124 cab, z
5126 setcolumns View c a b;
5127 setzoom z;
5129 | 0xff54 | 0xff52 when ctrl && Wsi.withshift mask ->
5130 setzoom state.prevzoom
5132 | 107 | 0xff52 -> (* k up *)
5133 begin match state.autoscroll with
5134 | None ->
5135 begin match state.mode with
5136 | Birdseye beye -> upbirdseye 1 beye
5137 | _ ->
5138 if ctrl
5139 then gotoy_and_clear_text (clamp ~-(conf.winh/2))
5140 else gotoy_and_clear_text (clamp (-conf.scrollstep))
5142 | Some n ->
5143 setautoscrollspeed n false
5146 | 106 | 0xff54 -> (* j down *)
5147 begin match state.autoscroll with
5148 | None ->
5149 begin match state.mode with
5150 | Birdseye beye -> downbirdseye 1 beye
5151 | _ ->
5152 if ctrl
5153 then gotoy_and_clear_text (clamp (conf.winh/2))
5154 else gotoy_and_clear_text (clamp conf.scrollstep)
5156 | Some n ->
5157 setautoscrollspeed n true
5160 | 0xff51 | 0xff53 when not (Wsi.withalt mask) -> (* left / right *)
5161 if canpan ()
5162 then
5163 let dx =
5164 if ctrl
5165 then conf.winw / 2
5166 else conf.hscrollstep
5168 let dx = if key = 0xff51 then dx else -dx in
5169 state.x <- state.x + dx;
5170 gotoy_and_clear_text state.y
5171 else (
5172 state.text <- "";
5173 G.postRedisplay "lef/right"
5176 | 0xff55 -> (* prior *)
5177 let y =
5178 if ctrl
5179 then
5180 match state.layout with
5181 | [] -> state.y
5182 | l :: _ -> state.y - l.pagey
5183 else
5184 clamp (pgscale (-conf.winh))
5186 gotoghyll y
5188 | 0xff56 -> (* next *)
5189 let y =
5190 if ctrl
5191 then
5192 match List.rev state.layout with
5193 | [] -> state.y
5194 | l :: _ -> getpagey l.pageno
5195 else
5196 clamp (pgscale conf.winh)
5198 gotoghyll y
5200 | 103 | 0xff50 -> (* g home *)
5201 gotoghyll 0
5202 | 71 | 0xff57 -> (* G end *)
5203 gotoghyll (clamp state.maxy)
5205 | 0xff53 when Wsi.withalt mask -> (* alt-right *)
5206 gotoghyll (getnav 1)
5207 | 0xff51 when Wsi.withalt mask -> (* alt-left *)
5208 gotoghyll (getnav ~-1)
5210 | 114 -> (* r *)
5211 reload ()
5213 | 118 when conf.debug -> (* v *)
5214 state.rects <- [];
5215 List.iter (fun l ->
5216 match getopaque l.pageno with
5217 | None -> ()
5218 | Some opaque ->
5219 let x0, y0, x1, y1 = pagebbox opaque in
5220 let a,b = float x0, float y0 in
5221 let c,d = float x1, float y0 in
5222 let e,f = float x1, float y1 in
5223 let h,j = float x0, float y1 in
5224 let rect = (a,b,c,d,e,f,h,j) in
5225 debugrect rect;
5226 state.rects <- (l.pageno, l.pageno mod 3, rect) :: state.rects;
5227 ) state.layout;
5228 G.postRedisplay "v";
5230 | _ ->
5231 vlog "huh? %s" (Wsi.keyname key)
5234 let linknavkeyboard key mask linknav =
5235 let getpage pageno =
5236 let rec loop = function
5237 | [] -> None
5238 | l :: _ when l.pageno = pageno -> Some l
5239 | _ :: rest -> loop rest
5240 in loop state.layout
5242 let doexact (pageno, n) =
5243 match getopaque pageno, getpage pageno with
5244 | Some opaque, Some l ->
5245 if key = 0xff0d
5246 then
5247 let under = getlink opaque n in
5248 G.postRedisplay "link gotounder";
5249 gotounder under;
5250 state.mode <- View;
5251 else
5252 let opt, dir =
5253 match key with
5254 | 0xff50 -> (* home *)
5255 Some (findlink opaque LDfirst), -1
5257 | 0xff57 -> (* end *)
5258 Some (findlink opaque LDlast), 1
5260 | 0xff51 -> (* left *)
5261 Some (findlink opaque (LDleft n)), -1
5263 | 0xff53 -> (* right *)
5264 Some (findlink opaque (LDright n)), 1
5266 | 0xff52 -> (* up *)
5267 Some (findlink opaque (LDup n)), -1
5269 | 0xff54 -> (* down *)
5270 Some (findlink opaque (LDdown n)), 1
5272 | _ -> None, 0
5274 let pwl l dir =
5275 begin match findpwl l.pageno dir with
5276 | Pwlnotfound -> ()
5277 | Pwl pageno ->
5278 let notfound dir =
5279 state.mode <- LinkNav (Ltgendir dir);
5280 let y, h = getpageyh pageno in
5281 let y =
5282 if dir < 0
5283 then y + h - conf.winh
5284 else y
5286 gotoy y
5288 begin match getopaque pageno, getpage pageno with
5289 | Some opaque, Some _ ->
5290 let link =
5291 let ld = if dir > 0 then LDfirst else LDlast in
5292 findlink opaque ld
5294 begin match link with
5295 | Lfound m ->
5296 showlinktype (getlink opaque m);
5297 state.mode <- LinkNav (Ltexact (pageno, m));
5298 G.postRedisplay "linknav jpage";
5299 | _ -> notfound dir
5300 end;
5301 | _ -> notfound dir
5302 end;
5303 end;
5305 begin match opt with
5306 | Some Lnotfound -> pwl l dir;
5307 | Some (Lfound m) ->
5308 if m = n
5309 then pwl l dir
5310 else (
5311 let _, y0, _, y1 = getlinkrect opaque m in
5312 if y0 < l.pagey
5313 then gotopage1 l.pageno y0
5314 else (
5315 let d = fstate.fontsize + 1 in
5316 if y1 - l.pagey > l.pagevh - d
5317 then gotopage1 l.pageno (y1 - conf.winh - state.hscrollh + d)
5318 else G.postRedisplay "linknav";
5320 showlinktype (getlink opaque m);
5321 state.mode <- LinkNav (Ltexact (l.pageno, m));
5324 | None -> viewkeyboard key mask
5325 end;
5326 | _ -> viewkeyboard key mask
5328 if key = 0xff63
5329 then (
5330 state.mode <- View;
5331 G.postRedisplay "leave linknav"
5333 else
5334 match linknav with
5335 | Ltgendir _ -> viewkeyboard key mask
5336 | Ltexact exact -> doexact exact
5339 let keyboard key mask =
5340 if (key = 103 && Wsi.withctrl mask) && not (istextentry state.mode)
5341 then wcmd "interrupt"
5342 else state.uioh <- state.uioh#key key mask
5345 let birdseyekeyboard key mask
5346 ((oconf, leftx, pageno, hooverpageno, anchor) as beye) =
5347 let incr =
5348 match conf.columns with
5349 | Csingle _ -> 1
5350 | Cmulti ((c, _, _), _) -> c
5351 | Csplit _ -> failwith "bird's eye split mode"
5353 let pgh layout = List.fold_left (fun m l -> max l.pageh m) conf.winh layout in
5354 match key with
5355 | 108 when Wsi.withctrl mask -> (* ctrl-l *)
5356 let y, h = getpageyh pageno in
5357 let top = (conf.winh - h) / 2 in
5358 gotoy (max 0 (y - top))
5359 | 0xff0d -> leavebirdseye beye false
5360 | 0xff1b -> leavebirdseye beye true (* escape *)
5361 | 0xff52 -> upbirdseye incr beye (* up *)
5362 | 0xff54 -> downbirdseye incr beye (* down *)
5363 | 0xff51 -> upbirdseye 1 beye (* left *)
5364 | 0xff53 -> downbirdseye 1 beye (* right *)
5366 | 0xff55 -> (* prior *)
5367 begin match state.layout with
5368 | l :: _ ->
5369 if l.pagey != 0
5370 then (
5371 state.mode <- Birdseye (
5372 oconf, leftx, l.pageno, hooverpageno, anchor
5374 gotopage1 l.pageno 0;
5376 else (
5377 let layout = layout (state.y-conf.winh) (pgh state.layout) in
5378 match layout with
5379 | [] -> gotoy (clamp (-conf.winh))
5380 | l :: _ ->
5381 state.mode <- Birdseye (
5382 oconf, leftx, l.pageno, hooverpageno, anchor
5384 gotopage1 l.pageno 0
5387 | [] -> gotoy (clamp (-conf.winh))
5388 end;
5390 | 0xff56 -> (* next *)
5391 begin match List.rev state.layout with
5392 | l :: _ ->
5393 let layout = layout (state.y + (pgh state.layout)) conf.winh in
5394 begin match layout with
5395 | [] ->
5396 let incr = l.pageh - l.pagevh in
5397 if incr = 0
5398 then (
5399 state.mode <-
5400 Birdseye (
5401 oconf, leftx, state.pagecount - 1, hooverpageno, anchor
5403 G.postRedisplay "birdseye pagedown";
5405 else gotoy (clamp (incr + conf.interpagespace*2));
5407 | l :: _ ->
5408 state.mode <-
5409 Birdseye (oconf, leftx, l.pageno, hooverpageno, anchor);
5410 gotopage1 l.pageno 0;
5413 | [] -> gotoy (clamp conf.winh)
5414 end;
5416 | 0xff50 -> (* home *)
5417 state.mode <- Birdseye (oconf, leftx, 0, hooverpageno, anchor);
5418 gotopage1 0 0
5420 | 0xff57 -> (* end *)
5421 let pageno = state.pagecount - 1 in
5422 state.mode <- Birdseye (oconf, leftx, pageno, hooverpageno, anchor);
5423 if not (pagevisible state.layout pageno)
5424 then
5425 let h =
5426 match List.rev state.pdims with
5427 | [] -> conf.winh
5428 | (_, _, h, _) :: _ -> h
5430 gotoy (max 0 (getpagey pageno - (conf.winh - h - conf.interpagespace)))
5431 else G.postRedisplay "birdseye end";
5432 | _ -> viewkeyboard key mask
5435 let drawpage l linkindexbase =
5436 let color =
5437 match state.mode with
5438 | Textentry _ -> scalecolor 0.4
5439 | LinkNav _
5440 | View -> scalecolor 1.0
5441 | Birdseye (_, _, pageno, hooverpageno, _) ->
5442 if l.pageno = hooverpageno
5443 then scalecolor 0.9
5444 else (
5445 if l.pageno = pageno
5446 then scalecolor 1.0
5447 else scalecolor 0.8
5450 drawtiles l color;
5451 begin match getopaque l.pageno with
5452 | Some opaque ->
5453 if tileready l l.pagex l.pagey
5454 then
5455 let x = l.pagedispx - l.pagex
5456 and y = l.pagedispy - l.pagey in
5457 let hlmask =
5458 match conf.columns with
5459 | Csingle _ | Cmulti _ ->
5460 (if conf.hlinks then 1 else 0)
5461 + (if state.glinks
5462 && not (isbirdseye state.mode) then 2 else 0)
5463 | _ -> 0
5465 let s =
5466 match state.mode with
5467 | Textentry ((_, s, _, _, _, _), _) when state.glinks -> s
5468 | _ -> ""
5470 postprocess opaque hlmask x y (linkindexbase, s, conf.hfsize);
5471 else 0
5473 | _ -> 0
5474 end;
5477 let scrollindicator () =
5478 let sbw, ph, sh = state.uioh#scrollph in
5479 let sbh, pw, sw = state.uioh#scrollpw in
5481 GlDraw.color (0.64, 0.64, 0.64);
5482 GlDraw.rect
5483 (float (conf.winw - sbw), 0.)
5484 (float conf.winw, float conf.winh)
5486 GlDraw.rect
5487 (0., float (conf.winh - sbh))
5488 (float (conf.winw - state.scrollw - 1), float conf.winh)
5490 GlDraw.color (0.0, 0.0, 0.0);
5492 GlDraw.rect
5493 (float (conf.winw - sbw), ph)
5494 (float conf.winw, ph +. sh)
5496 GlDraw.rect
5497 (pw, float (conf.winh - sbh))
5498 (pw +. sw, float conf.winh)
5502 let showsel () =
5503 match state.mstate with
5504 | Mnone | Mscrolly | Mscrollx | Mpan _ | Mzoom _ | Mzoomrect _ ->
5507 | Msel ((x0, y0), (x1, y1)) ->
5508 let rec loop = function
5509 | l :: ls ->
5510 if ((y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
5511 || ((y1 >= l.pagedispy && y1 <= (l.pagedispy + l.pagevh))))
5512 && ((x0 >= l.pagedispx && x0 <= (l.pagedispx + l.pagevw))
5513 || ((x1 >= l.pagedispx && x1 <= (l.pagedispx + l.pagevw))))
5514 then
5515 match getopaque l.pageno with
5516 | Some opaque ->
5517 let x0, y0 = pagetranslatepoint l x0 y0 in
5518 let x1, y1 = pagetranslatepoint l x1 y1 in
5519 seltext opaque (x0, y0, x1, y1);
5520 | _ -> ()
5521 else loop ls
5522 | [] -> ()
5524 loop state.layout
5527 let showrects rects =
5528 Gl.enable `blend;
5529 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
5530 GlDraw.polygon_mode `both `fill;
5531 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5532 List.iter
5533 (fun (pageno, c, (x0, y0, x1, y1, x2, y2, x3, y3)) ->
5534 List.iter (fun l ->
5535 if l.pageno = pageno
5536 then (
5537 let dx = float (l.pagedispx - l.pagex) in
5538 let dy = float (l.pagedispy - l.pagey) in
5539 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
5540 GlDraw.begins `quads;
5542 GlDraw.vertex2 (x0+.dx, y0+.dy);
5543 GlDraw.vertex2 (x1+.dx, y1+.dy);
5544 GlDraw.vertex2 (x2+.dx, y2+.dy);
5545 GlDraw.vertex2 (x3+.dx, y3+.dy);
5547 GlDraw.ends ();
5549 ) state.layout
5550 ) rects
5552 Gl.disable `blend;
5555 let display () =
5556 GlClear.color (scalecolor2 conf.bgcolor);
5557 GlClear.clear [`color];
5558 let rec loop linkindexbase = function
5559 | l :: rest ->
5560 let linkindexbase = linkindexbase + drawpage l linkindexbase in
5561 loop linkindexbase rest
5562 | [] -> ()
5564 loop 0 state.layout;
5565 let rects =
5566 match state.mode with
5567 | LinkNav (Ltexact (pageno, linkno)) ->
5568 begin match getopaque pageno with
5569 | Some opaque ->
5570 let x0, y0, x1, y1 = getlinkrect opaque linkno in
5571 (pageno, 5, (
5572 float x0, float y0,
5573 float x1, float y0,
5574 float x1, float y1,
5575 float x0, float y1)
5576 ) :: state.rects
5577 | None -> state.rects
5579 | _ -> state.rects
5581 showrects rects;
5582 showsel ();
5583 state.uioh#display;
5584 begin match state.mstate with
5585 | Mzoomrect ((x0, y0), (x1, y1)) ->
5586 Gl.enable `blend;
5587 GlDraw.color (0.3, 0.3, 0.3) ~alpha:0.5;
5588 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
5589 GlDraw.rect (float x0, float y0)
5590 (float x1, float y1);
5591 Gl.disable `blend;
5592 | _ -> ()
5593 end;
5594 enttext ();
5595 scrollindicator ();
5596 Wsi.swapb ();
5599 let zoomrect x y x1 y1 =
5600 let x0 = min x x1
5601 and x1 = max x x1
5602 and y0 = min y y1 in
5603 gotoy (state.y + y0);
5604 state.anchor <- getanchor ();
5605 let zoom = (float conf.winw *. conf.zoom) /. float (x1 - x0) in
5606 let margin =
5607 if state.w < conf.winw - state.scrollw
5608 then (conf.winw - state.scrollw - state.w) / 2
5609 else 0
5611 state.x <- (state.x + margin) - x0;
5612 setzoom zoom;
5613 Wsi.setcursor Wsi.CURSOR_INHERIT;
5614 state.mstate <- Mnone;
5617 let scrollx x =
5618 let winw = conf.winw - state.scrollw - 1 in
5619 let s = float x /. float winw in
5620 let destx = truncate (float (state.w + winw) *. s) in
5621 state.x <- winw - destx;
5622 gotoy_and_clear_text state.y;
5623 state.mstate <- Mscrollx;
5626 let scrolly y =
5627 let s = float y /. float conf.winh in
5628 let desty = truncate (float (state.maxy - conf.winh) *. s) in
5629 gotoy_and_clear_text desty;
5630 state.mstate <- Mscrolly;
5633 let viewmouse button down x y mask =
5634 match button with
5635 | n when (n == 4 || n == 5) && not down ->
5636 if Wsi.withctrl mask
5637 then (
5638 match state.mstate with
5639 | Mzoom (oldn, i) ->
5640 if oldn = n
5641 then (
5642 if i = 2
5643 then
5644 let incr =
5645 match n with
5646 | 5 ->
5647 if conf.zoom +. 0.01 > 0.1 then 0.1 else 0.01
5648 | _ ->
5649 if conf.zoom -. 0.1 < 0.1 then -0.01 else -0.1
5651 let zoom = conf.zoom -. incr in
5652 setzoom zoom;
5653 state.mstate <- Mzoom (n, 0);
5654 else
5655 state.mstate <- Mzoom (n, i+1);
5657 else state.mstate <- Mzoom (n, 0)
5659 | _ -> state.mstate <- Mzoom (n, 0)
5661 else (
5662 match state.autoscroll with
5663 | Some step -> setautoscrollspeed step (n=4)
5664 | None ->
5665 let incr =
5666 if n = 4
5667 then -conf.scrollstep
5668 else conf.scrollstep
5670 let incr = incr * 2 in
5671 let y = clamp incr in
5672 gotoy_and_clear_text y
5675 | n when (n = 6 || n = 7) && not down && canpan () ->
5676 state.x <- state.x + (if n = 7 then -2 else 2) * conf.hscrollstep;
5677 gotoy_and_clear_text state.y
5679 | 1 when Wsi.withctrl mask ->
5680 if down
5681 then (
5682 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5683 state.mstate <- Mpan (x, y)
5685 else
5686 state.mstate <- Mnone
5688 | 3 ->
5689 if down
5690 then (
5691 Wsi.setcursor Wsi.CURSOR_CYCLE;
5692 let p = (x, y) in
5693 state.mstate <- Mzoomrect (p, p)
5695 else (
5696 match state.mstate with
5697 | Mzoomrect ((x0, y0), _) ->
5698 if abs (x-x0) > 10 && abs (y - y0) > 10
5699 then zoomrect x0 y0 x y
5700 else (
5701 state.mstate <- Mnone;
5702 Wsi.setcursor Wsi.CURSOR_INHERIT;
5703 G.postRedisplay "kill accidental zoom rect";
5705 | _ ->
5706 Wsi.setcursor Wsi.CURSOR_INHERIT;
5707 state.mstate <- Mnone
5710 | 1 when x > conf.winw - state.scrollw ->
5711 if down
5712 then
5713 let _, position, sh = state.uioh#scrollph in
5714 if y > truncate position && y < truncate (position +. sh)
5715 then state.mstate <- Mscrolly
5716 else scrolly y
5717 else
5718 state.mstate <- Mnone
5720 | 1 when y > conf.winh - state.hscrollh ->
5721 if down
5722 then
5723 let _, position, sw = state.uioh#scrollpw in
5724 if x > truncate position && x < truncate (position +. sw)
5725 then state.mstate <- Mscrollx
5726 else scrollx x
5727 else
5728 state.mstate <- Mnone
5730 | 1 ->
5731 let dest = if down then getunder x y else Unone in
5732 begin match dest with
5733 | Ulinkgoto _
5734 | Ulinkuri _
5735 | Uremote _
5736 | Uunexpected _ | Ulaunch _ | Unamed _ ->
5737 gotounder dest
5739 | Unone when down ->
5740 Wsi.setcursor Wsi.CURSOR_CROSSHAIR;
5741 state.mstate <- Mpan (x, y);
5743 | Unone | Utext _ ->
5744 if down
5745 then (
5746 if conf.angle mod 360 = 0
5747 then (
5748 state.mstate <- Msel ((x, y), (x, y));
5749 G.postRedisplay "mouse select";
5752 else (
5753 match state.mstate with
5754 | Mnone -> ()
5756 | Mzoom _ | Mscrollx | Mscrolly ->
5757 state.mstate <- Mnone
5759 | Mzoomrect ((x0, y0), _) ->
5760 zoomrect x0 y0 x y
5762 | Mpan _ ->
5763 Wsi.setcursor Wsi.CURSOR_INHERIT;
5764 state.mstate <- Mnone
5766 | Msel ((x0, y0), (x1, y1)) ->
5767 let rec loop = function
5768 | [] -> ()
5769 | l :: rest ->
5770 let inside =
5771 let a0 = l.pagedispy in
5772 let a1 = a0 + l.pagevh in
5773 let b0 = l.pagedispx in
5774 let b1 = b0 + l.pagevw in
5775 ((y0 >= a0 && y0 <= a1) || (y1 >= a0 && y1 <= a1))
5776 && ((x0 >= b0 && x0 <= b1) || (x1 >= b0 && x1 <= b1))
5778 if inside
5779 then
5780 match getopaque l.pageno with
5781 | Some opaque ->
5782 begin
5783 match Ne.pipe () with
5784 | Ne.Exn exn ->
5785 showtext '!'
5786 (Printf.sprintf
5787 "can not create sel pipe: %s"
5788 (Printexc.to_string exn));
5789 | Ne.Res (r, w) ->
5790 let doclose what fd =
5791 Ne.clo fd (fun msg ->
5792 dolog "%s close failed: %s" what msg)
5795 popen conf.selcmd [r, 0; w, -1];
5796 copysel w opaque;
5797 doclose "pipe/r" r;
5798 G.postRedisplay "copysel";
5799 with exn ->
5800 dolog "can not execute %S: %s"
5801 conf.selcmd (Printexc.to_string exn);
5802 doclose "pipe/r" r;
5803 doclose "pipe/w" w;
5805 | None -> ()
5806 else loop rest
5808 loop state.layout;
5809 Wsi.setcursor Wsi.CURSOR_INHERIT;
5810 state.mstate <- Mnone;
5814 | _ -> ()
5817 let birdseyemouse button down x y mask
5818 (conf, leftx, _, hooverpageno, anchor) =
5819 match button with
5820 | 1 when down ->
5821 let rec loop = function
5822 | [] -> ()
5823 | l :: rest ->
5824 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5825 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5826 then (
5827 leavebirdseye (conf, leftx, l.pageno, hooverpageno, anchor) false;
5829 else loop rest
5831 loop state.layout
5832 | 3 -> ()
5833 | _ -> viewmouse button down x y mask
5836 let mouse button down x y mask =
5837 state.uioh <- state.uioh#button button down x y mask;
5840 let motion ~x ~y =
5841 state.uioh <- state.uioh#motion x y
5844 let pmotion ~x ~y =
5845 state.uioh <- state.uioh#pmotion x y;
5848 let uioh = object
5849 method display = ()
5851 method key key mask =
5852 begin match state.mode with
5853 | Textentry textentry -> textentrykeyboard key mask textentry
5854 | Birdseye birdseye -> birdseyekeyboard key mask birdseye
5855 | View -> viewkeyboard key mask
5856 | LinkNav linknav -> linknavkeyboard key mask linknav
5857 end;
5858 state.uioh
5860 method button button bstate x y mask =
5861 begin match state.mode with
5862 | LinkNav _
5863 | View -> viewmouse button bstate x y mask
5864 | Birdseye beye -> birdseyemouse button bstate x y mask beye
5865 | Textentry _ -> ()
5866 end;
5867 state.uioh
5869 method motion x y =
5870 begin match state.mode with
5871 | Textentry _ -> ()
5872 | View | Birdseye _ | LinkNav _ ->
5873 match state.mstate with
5874 | Mzoom _ | Mnone -> ()
5876 | Mpan (x0, y0) ->
5877 let dx = x - x0
5878 and dy = y0 - y in
5879 state.mstate <- Mpan (x, y);
5880 if canpan ()
5881 then state.x <- state.x + dx;
5882 let y = clamp dy in
5883 gotoy_and_clear_text y
5885 | Msel (a, _) ->
5886 state.mstate <- Msel (a, (x, y));
5887 G.postRedisplay "motion select";
5889 | Mscrolly ->
5890 let y = min conf.winh (max 0 y) in
5891 scrolly y
5893 | Mscrollx ->
5894 let x = min conf.winw (max 0 x) in
5895 scrollx x
5897 | Mzoomrect (p0, _) ->
5898 state.mstate <- Mzoomrect (p0, (x, y));
5899 G.postRedisplay "motion zoomrect";
5900 end;
5901 state.uioh
5903 method pmotion x y =
5904 begin match state.mode with
5905 | Birdseye (conf, leftx, pageno, hooverpageno, anchor) ->
5906 let rec loop = function
5907 | [] ->
5908 if hooverpageno != -1
5909 then (
5910 state.mode <- Birdseye (conf, leftx, pageno, -1, anchor);
5911 G.postRedisplay "pmotion birdseye no hoover";
5913 | l :: rest ->
5914 if y > l.pagedispy && y < l.pagedispy + l.pagevh
5915 && x > l.pagedispx && x < l.pagedispx + l.pagevw
5916 then (
5917 state.mode <- Birdseye (conf, leftx, pageno, l.pageno, anchor);
5918 G.postRedisplay "pmotion birdseye hoover";
5920 else loop rest
5922 loop state.layout
5924 | Textentry _ -> ()
5926 | LinkNav _
5927 | View ->
5928 match state.mstate with
5929 | Mnone -> updateunder x y
5930 | Mpan _ | Msel _ | Mzoom _ | Mscrolly | Mscrollx | Mzoomrect _ ->
5932 end;
5933 state.uioh
5935 method infochanged _ = ()
5937 method scrollph =
5938 let maxy = state.maxy - (if conf.maxhfit then conf.winh else 0) in
5939 let p, h = scrollph state.y maxy in
5940 state.scrollw, p, h
5942 method scrollpw =
5943 let winw = conf.winw - state.scrollw - 1 in
5944 let fwinw = float winw in
5945 let sw =
5946 let sw = fwinw /. float state.w in
5947 let sw = fwinw *. sw in
5948 max sw (float conf.scrollh)
5950 let position, sw =
5951 let f = state.w+winw in
5952 let r = float (winw-state.x) /. float f in
5953 let p = fwinw *. r in
5954 p-.sw/.2., sw
5956 let sw =
5957 if position +. sw > fwinw
5958 then fwinw -. position
5959 else sw
5961 state.hscrollh, position, sw
5963 method modehash =
5964 let modename =
5965 match state.mode with
5966 | LinkNav _ -> "links"
5967 | Textentry _ -> "textentry"
5968 | Birdseye _ -> "birdseye"
5969 | View -> "view"
5971 findkeyhash conf modename
5972 end;;
5974 module Config =
5975 struct
5976 open Parser
5978 let fontpath = ref "";;
5980 module KeyMap =
5981 Map.Make (struct type t = (int * int) let compare = compare end);;
5983 let unent s =
5984 let l = String.length s in
5985 let b = Buffer.create l in
5986 unent b s 0 l;
5987 Buffer.contents b;
5990 let home =
5991 try Sys.getenv "HOME"
5992 with exn ->
5993 prerr_endline
5994 ("Can not determine home directory location: " ^
5995 Printexc.to_string exn);
5999 let modifier_of_string = function
6000 | "alt" -> Wsi.altmask
6001 | "shift" -> Wsi.shiftmask
6002 | "ctrl" | "control" -> Wsi.ctrlmask
6003 | "meta" -> Wsi.metamask
6004 | _ -> 0
6007 let key_of_string =
6008 let r = Str.regexp "-" in
6009 fun s ->
6010 let elems = Str.full_split r s in
6011 let f n k m =
6012 let g s =
6013 let m1 = modifier_of_string s in
6014 if m1 = 0
6015 then (Wsi.namekey s, m)
6016 else (k, m lor m1)
6017 in function
6018 | Str.Delim s when n land 1 = 0 -> g s
6019 | Str.Text s -> g s
6020 | Str.Delim _ -> (k, m)
6022 let rec loop n k m = function
6023 | [] -> (k, m)
6024 | x :: xs ->
6025 let k, m = f n k m x in
6026 loop (n+1) k m xs
6028 loop 0 0 0 elems
6031 let keys_of_string =
6032 let r = Str.regexp "[ \t]" in
6033 fun s ->
6034 let elems = Str.split r s in
6035 List.map key_of_string elems
6038 let copykeyhashes c =
6039 List.map (fun (k, v) -> k, Hashtbl.copy v) c.keyhashes;
6042 let config_of c attrs =
6043 let apply c k v =
6045 match k with
6046 | "scroll-bar-width" -> { c with scrollbw = max 0 (int_of_string v) }
6047 | "scroll-handle-height" -> { c with scrollh = max 0 (int_of_string v) }
6048 | "case-insensitive-search" -> { c with icase = bool_of_string v }
6049 | "preload" -> { c with preload = bool_of_string v }
6050 | "page-bias" -> { c with pagebias = int_of_string v }
6051 | "scroll-step" -> { c with scrollstep = max 1 (int_of_string v) }
6052 | "horizontal-scroll-step" ->
6053 { c with hscrollstep = max (int_of_string v) 1 }
6054 | "auto-scroll-step" ->
6055 { c with autoscrollstep = max 0 (int_of_string v) }
6056 | "max-height-fit" -> { c with maxhfit = bool_of_string v }
6057 | "crop-hack" -> { c with crophack = bool_of_string v }
6058 | "throttle" ->
6059 let mw =
6060 match String.lowercase v with
6061 | "true" -> Some infinity
6062 | "false" -> None
6063 | f -> Some (float_of_string f)
6065 { c with maxwait = mw}
6066 | "highlight-links" -> { c with hlinks = bool_of_string v }
6067 | "under-cursor-info" -> { c with underinfo = bool_of_string v }
6068 | "vertical-margin" ->
6069 { c with interpagespace = max 0 (int_of_string v) }
6070 | "zoom" ->
6071 let zoom = float_of_string v /. 100. in
6072 let zoom = max zoom 0.0 in
6073 { c with zoom = zoom }
6074 | "presentation" -> { c with presentation = bool_of_string v }
6075 | "rotation-angle" -> { c with angle = int_of_string v }
6076 | "width" -> { c with winw = max 20 (int_of_string v) }
6077 | "height" -> { c with winh = max 20 (int_of_string v) }
6078 | "persistent-bookmarks" -> { c with savebmarks = bool_of_string v }
6079 | "proportional-display" -> { c with proportional = bool_of_string v }
6080 | "pixmap-cache-size" ->
6081 { c with memlimit = max 2 (int_of_string_with_suffix v) }
6082 | "tex-count" -> { c with texcount = max 1 (int_of_string v) }
6083 | "slice-height" -> { c with sliceheight = max 2 (int_of_string v) }
6084 | "thumbnail-width" -> { c with thumbw = max 2 (int_of_string v) }
6085 | "persistent-location" -> { c with jumpback = bool_of_string v }
6086 | "background-color" -> { c with bgcolor = color_of_string v }
6087 | "scrollbar-in-presentation" ->
6088 { c with scrollbarinpm = bool_of_string v }
6089 | "tile-width" -> { c with tilew = max 2 (int_of_string v) }
6090 | "tile-height" -> { c with tileh = max 2 (int_of_string v) }
6091 | "mupdf-store-size" ->
6092 { c with mustoresize = max 1024 (int_of_string_with_suffix v) }
6093 | "checkers" -> { c with checkers = bool_of_string v }
6094 | "aalevel" -> { c with aalevel = max 0 (int_of_string v) }
6095 | "trim-margins" -> { c with trimmargins = bool_of_string v }
6096 | "trim-fuzz" -> { c with trimfuzz = irect_of_string v }
6097 | "uri-launcher" -> { c with urilauncher = unent v }
6098 | "path-launcher" -> { c with pathlauncher = unent v }
6099 | "color-space" -> { c with colorspace = colorspace_of_string v }
6100 | "invert-colors" -> { c with invert = bool_of_string v }
6101 | "brightness" -> { c with colorscale = float_of_string v }
6102 | "redirectstderr" -> { c with redirectstderr = bool_of_string v }
6103 | "ghyllscroll" ->
6104 { c with ghyllscroll = Some (ghyllscroll_of_string v) }
6105 | "columns" ->
6106 let (n, _, _) as nab = multicolumns_of_string v in
6107 if n < 0
6108 then { c with columns = Csplit (-n, [||]) }
6109 else { c with columns = Cmulti (nab, [||]) }
6110 | "birds-eye-columns" ->
6111 { c with beyecolumns = Some (max (int_of_string v) 2) }
6112 | "selection-command" -> { c with selcmd = unent v }
6113 | "update-cursor" -> { c with updatecurs = bool_of_string v }
6114 | "hint-font-size" -> { c with hfsize = bound (int_of_string v) 5 100 }
6115 | "page-scroll-scale" -> { c with pgscale = float_of_string v }
6116 | _ -> c
6117 with exn ->
6118 prerr_endline ("Error processing attribute (`" ^
6119 k ^ "'=`" ^ v ^ "'): " ^ Printexc.to_string exn);
6122 let rec fold c = function
6123 | [] -> c
6124 | (k, v) :: rest ->
6125 let c = apply c k v in
6126 fold c rest
6128 fold { c with keyhashes = copykeyhashes c } attrs;
6131 let fromstring f pos n v d =
6132 try f v
6133 with exn ->
6134 dolog "Error processing attribute (%S=%S) at %d\n%s"
6135 n v pos (Printexc.to_string exn)
6140 let bookmark_of attrs =
6141 let rec fold title page rely visy = function
6142 | ("title", v) :: rest -> fold v page rely visy rest
6143 | ("page", v) :: rest -> fold title v rely visy rest
6144 | ("rely", v) :: rest -> fold title page v visy rest
6145 | ("visy", v) :: rest -> fold title page rely v rest
6146 | _ :: rest -> fold title page rely visy rest
6147 | [] -> title, page, rely, visy
6149 fold "invalid" "0" "0" "0" attrs
6152 let doc_of attrs =
6153 let rec fold path page rely pan visy = function
6154 | ("path", v) :: rest -> fold v page rely pan visy rest
6155 | ("page", v) :: rest -> fold path v rely pan visy rest
6156 | ("rely", v) :: rest -> fold path page v pan visy rest
6157 | ("pan", v) :: rest -> fold path page rely v visy rest
6158 | ("visy", v) :: rest -> fold path page rely pan v rest
6159 | _ :: rest -> fold path page rely pan visy rest
6160 | [] -> path, page, rely, pan, visy
6162 fold "" "0" "0" "0" "0" attrs
6165 let map_of attrs =
6166 let rec fold rs ls = function
6167 | ("out", v) :: rest -> fold v ls rest
6168 | ("in", v) :: rest -> fold rs v rest
6169 | _ :: rest -> fold ls rs rest
6170 | [] -> ls, rs
6172 fold "" "" attrs
6175 let setconf dst src =
6176 dst.scrollbw <- src.scrollbw;
6177 dst.scrollh <- src.scrollh;
6178 dst.icase <- src.icase;
6179 dst.preload <- src.preload;
6180 dst.pagebias <- src.pagebias;
6181 dst.verbose <- src.verbose;
6182 dst.scrollstep <- src.scrollstep;
6183 dst.maxhfit <- src.maxhfit;
6184 dst.crophack <- src.crophack;
6185 dst.autoscrollstep <- src.autoscrollstep;
6186 dst.maxwait <- src.maxwait;
6187 dst.hlinks <- src.hlinks;
6188 dst.underinfo <- src.underinfo;
6189 dst.interpagespace <- src.interpagespace;
6190 dst.zoom <- src.zoom;
6191 dst.presentation <- src.presentation;
6192 dst.angle <- src.angle;
6193 dst.winw <- src.winw;
6194 dst.winh <- src.winh;
6195 dst.savebmarks <- src.savebmarks;
6196 dst.memlimit <- src.memlimit;
6197 dst.proportional <- src.proportional;
6198 dst.texcount <- src.texcount;
6199 dst.sliceheight <- src.sliceheight;
6200 dst.thumbw <- src.thumbw;
6201 dst.jumpback <- src.jumpback;
6202 dst.bgcolor <- src.bgcolor;
6203 dst.scrollbarinpm <- src.scrollbarinpm;
6204 dst.tilew <- src.tilew;
6205 dst.tileh <- src.tileh;
6206 dst.mustoresize <- src.mustoresize;
6207 dst.checkers <- src.checkers;
6208 dst.aalevel <- src.aalevel;
6209 dst.trimmargins <- src.trimmargins;
6210 dst.trimfuzz <- src.trimfuzz;
6211 dst.urilauncher <- src.urilauncher;
6212 dst.colorspace <- src.colorspace;
6213 dst.invert <- src.invert;
6214 dst.colorscale <- src.colorscale;
6215 dst.redirectstderr <- src.redirectstderr;
6216 dst.ghyllscroll <- src.ghyllscroll;
6217 dst.columns <- src.columns;
6218 dst.beyecolumns <- src.beyecolumns;
6219 dst.selcmd <- src.selcmd;
6220 dst.updatecurs <- src.updatecurs;
6221 dst.pathlauncher <- src.pathlauncher;
6222 dst.keyhashes <- copykeyhashes src;
6223 dst.hfsize <- src.hfsize;
6224 dst.hscrollstep <- src.hscrollstep;
6225 dst.pgscale <- src.pgscale;
6228 let get s =
6229 let h = Hashtbl.create 10 in
6230 let dc = { defconf with angle = defconf.angle } in
6231 let rec toplevel v t spos _ =
6232 match t with
6233 | Vdata | Vcdata | Vend -> v
6234 | Vopen ("llppconfig", _, closed) ->
6235 if closed
6236 then v
6237 else { v with f = llppconfig }
6238 | Vopen _ ->
6239 error "unexpected subelement at top level" s spos
6240 | Vclose _ -> error "unexpected close at top level" s spos
6242 and llppconfig v t spos _ =
6243 match t with
6244 | Vdata | Vcdata -> v
6245 | Vend -> error "unexpected end of input in llppconfig" s spos
6246 | Vopen ("defaults", attrs, closed) ->
6247 let c = config_of dc attrs in
6248 setconf dc c;
6249 if closed
6250 then v
6251 else { v with f = defaults }
6253 | Vopen ("ui-font", attrs, closed) ->
6254 let rec getsize size = function
6255 | [] -> size
6256 | ("size", v) :: rest ->
6257 let size =
6258 fromstring int_of_string spos "size" v fstate.fontsize in
6259 getsize size rest
6260 | l -> getsize size l
6262 fstate.fontsize <- getsize fstate.fontsize attrs;
6263 if closed
6264 then v
6265 else { v with f = uifont (Buffer.create 10) }
6267 | Vopen ("doc", attrs, closed) ->
6268 let pathent, spage, srely, span, svisy = doc_of attrs in
6269 let path = unent pathent
6270 and pageno = fromstring int_of_string spos "page" spage 0
6271 and rely = fromstring float_of_string spos "rely" srely 0.0
6272 and pan = fromstring int_of_string spos "pan" span 0
6273 and visy = fromstring float_of_string spos "visy" svisy 0.0 in
6274 let c = config_of dc attrs in
6275 let anchor = (pageno, rely, visy) in
6276 if closed
6277 then (Hashtbl.add h path (c, [], pan, anchor); v)
6278 else { v with f = doc path pan anchor c [] }
6280 | Vopen _ ->
6281 error "unexpected subelement in llppconfig" s spos
6283 | Vclose "llppconfig" -> { v with f = toplevel }
6284 | Vclose _ -> error "unexpected close in llppconfig" s spos
6286 and defaults v t spos _ =
6287 match t with
6288 | Vdata | Vcdata -> v
6289 | Vend -> error "unexpected end of input in defaults" s spos
6290 | Vopen ("keymap", attrs, closed) ->
6291 let modename =
6292 try List.assoc "mode" attrs
6293 with Not_found -> "global" in
6294 if closed
6295 then v
6296 else
6297 let ret keymap =
6298 let h = findkeyhash dc modename in
6299 KeyMap.iter (Hashtbl.replace h) keymap;
6300 defaults
6302 { v with f = pkeymap ret KeyMap.empty }
6304 | Vopen (_, _, _) ->
6305 error "unexpected subelement in defaults" s spos
6307 | Vclose "defaults" ->
6308 { v with f = llppconfig }
6310 | Vclose _ -> error "unexpected close in defaults" s spos
6312 and uifont b v t spos epos =
6313 match t with
6314 | Vdata | Vcdata ->
6315 Buffer.add_substring b s spos (epos - spos);
6317 | Vopen (_, _, _) ->
6318 error "unexpected subelement in ui-font" s spos
6319 | Vclose "ui-font" ->
6320 if String.length !fontpath = 0
6321 then fontpath := Buffer.contents b;
6322 { v with f = llppconfig }
6323 | Vclose _ -> error "unexpected close in ui-font" s spos
6324 | Vend -> error "unexpected end of input in ui-font" s spos
6326 and doc path pan anchor c bookmarks v t spos _ =
6327 match t with
6328 | Vdata | Vcdata -> v
6329 | Vend -> error "unexpected end of input in doc" s spos
6330 | Vopen ("bookmarks", _, closed) ->
6331 if closed
6332 then v
6333 else { v with f = pbookmarks path pan anchor c bookmarks }
6335 | Vopen ("keymap", attrs, closed) ->
6336 let modename =
6337 try List.assoc "mode" attrs
6338 with Not_found -> "global"
6340 if closed
6341 then v
6342 else
6343 let ret keymap =
6344 let h = findkeyhash c modename in
6345 KeyMap.iter (Hashtbl.replace h) keymap;
6346 doc path pan anchor c bookmarks
6348 { v with f = pkeymap ret KeyMap.empty }
6350 | Vopen (_, _, _) ->
6351 error "unexpected subelement in doc" s spos
6353 | Vclose "doc" ->
6354 Hashtbl.add h path (c, List.rev bookmarks, pan, anchor);
6355 { v with f = llppconfig }
6357 | Vclose _ -> error "unexpected close in doc" s spos
6359 and pkeymap ret keymap v t spos _ =
6360 match t with
6361 | Vdata | Vcdata -> v
6362 | Vend -> error "unexpected end of input in keymap" s spos
6363 | Vopen ("map", attrs, closed) ->
6364 let r, l = map_of attrs in
6365 let kss = fromstring keys_of_string spos "in" r [] in
6366 let lss = fromstring keys_of_string spos "out" l [] in
6367 let keymap =
6368 match kss with
6369 | [] -> keymap
6370 | ks :: [] -> KeyMap.add ks (KMinsrl lss) keymap
6371 | ks :: rest -> KeyMap.add ks (KMmulti (rest, lss)) keymap
6373 if closed
6374 then { v with f = pkeymap ret keymap }
6375 else
6376 let f () = v in
6377 { v with f = skip "map" f }
6379 | Vopen _ ->
6380 error "unexpected subelement in keymap" s spos
6382 | Vclose "keymap" ->
6383 { v with f = ret keymap }
6385 | Vclose _ -> error "unexpected close in keymap" s spos
6387 and pbookmarks path pan anchor c bookmarks v t spos _ =
6388 match t with
6389 | Vdata | Vcdata -> v
6390 | Vend -> error "unexpected end of input in bookmarks" s spos
6391 | Vopen ("item", attrs, closed) ->
6392 let titleent, spage, srely, svisy = bookmark_of attrs in
6393 let page = fromstring int_of_string spos "page" spage 0
6394 and rely = fromstring float_of_string spos "rely" srely 0.0
6395 and visy = fromstring float_of_string spos "visy" svisy 0.0 in
6396 let bookmarks =
6397 (unent titleent, 0, (page, rely, visy)) :: bookmarks
6399 if closed
6400 then { v with f = pbookmarks path pan anchor c bookmarks }
6401 else
6402 let f () = v in
6403 { v with f = skip "item" f }
6405 | Vopen _ ->
6406 error "unexpected subelement in bookmarks" s spos
6408 | Vclose "bookmarks" ->
6409 { v with f = doc path pan anchor c bookmarks }
6411 | Vclose _ -> error "unexpected close in bookmarks" s spos
6413 and skip tag f v t spos _ =
6414 match t with
6415 | Vdata | Vcdata -> v
6416 | Vend ->
6417 error ("unexpected end of input in skipped " ^ tag) s spos
6418 | Vopen (tag', _, closed) ->
6419 if closed
6420 then v
6421 else
6422 let f' () = { v with f = skip tag f } in
6423 { v with f = skip tag' f' }
6424 | Vclose ctag ->
6425 if tag = ctag
6426 then f ()
6427 else error ("unexpected close in skipped " ^ tag) s spos
6430 parse { f = toplevel; accu = () } s;
6431 h, dc;
6434 let do_load f ic =
6436 let len = in_channel_length ic in
6437 let s = String.create len in
6438 really_input ic s 0 len;
6439 f s;
6440 with
6441 | Parse_error (msg, s, pos) ->
6442 let subs = subs s pos in
6443 let s = Printf.sprintf "%s: at %d [..%s..]" msg pos subs in
6444 failwith ("parse error: " ^ s)
6446 | exn ->
6447 failwith ("config load error: " ^ Printexc.to_string exn)
6450 let defconfpath =
6451 let dir =
6453 let dir = Filename.concat home ".config" in
6454 if Sys.is_directory dir then dir else home
6455 with _ -> home
6457 Filename.concat dir "llpp.conf"
6460 let confpath = ref defconfpath;;
6462 let load1 f =
6463 if Sys.file_exists !confpath
6464 then
6465 match
6466 (try Some (open_in_bin !confpath)
6467 with exn ->
6468 prerr_endline
6469 ("Error opening configuation file `" ^ !confpath ^ "': " ^
6470 Printexc.to_string exn);
6471 None
6473 with
6474 | Some ic ->
6475 let success =
6477 f (do_load get ic)
6478 with exn ->
6479 prerr_endline
6480 ("Error loading configuation from `" ^ !confpath ^ "': " ^
6481 Printexc.to_string exn);
6482 false
6484 close_in ic;
6485 success
6487 | None -> false
6488 else
6489 f (Hashtbl.create 0, defconf)
6492 let load () =
6493 let f (h, dc) =
6494 let pc, pb, px, pa =
6496 Hashtbl.find h (Filename.basename state.path)
6497 with Not_found -> dc, [], 0, emptyanchor
6499 setconf defconf dc;
6500 setconf conf pc;
6501 state.bookmarks <- pb;
6502 state.x <- px;
6503 state.scrollw <- conf.scrollbw;
6504 if conf.jumpback
6505 then state.anchor <- pa;
6506 cbput state.hists.nav pa;
6507 true
6509 load1 f
6512 let add_attrs bb always dc c =
6513 let ob s a b =
6514 if always || a != b
6515 then Printf.bprintf bb "\n %s='%b'" s a
6516 and oi s a b =
6517 if always || a != b
6518 then Printf.bprintf bb "\n %s='%d'" s a
6519 and oI s a b =
6520 if always || a != b
6521 then Printf.bprintf bb "\n %s='%s'" s (string_with_suffix_of_int a)
6522 and oz s a b =
6523 if always || a <> b
6524 then Printf.bprintf bb "\n %s='%g'" s (a*.100.)
6525 and oF s a b =
6526 if always || a <> b
6527 then Printf.bprintf bb "\n %s='%f'" s a
6528 and oc s a b =
6529 if always || a <> b
6530 then
6531 Printf.bprintf bb "\n %s='%s'" s (color_to_string a)
6532 and oC s a b =
6533 if always || a <> b
6534 then
6535 Printf.bprintf bb "\n %s='%s'" s (colorspace_to_string a)
6536 and oR s a b =
6537 if always || a <> b
6538 then
6539 Printf.bprintf bb "\n %s='%s'" s (irect_to_string a)
6540 and os s a b =
6541 if always || a <> b
6542 then
6543 Printf.bprintf bb "\n %s='%s'" s (enent a 0 (String.length a))
6544 and og s a b =
6545 if always || a <> b
6546 then
6547 match a with
6548 | None -> ()
6549 | Some (_N, _A, _B) ->
6550 Printf.bprintf bb "\n %s='%u,%u,%u'" s _N _A _B
6551 and oW s a b =
6552 if always || a <> b
6553 then
6554 let v =
6555 match a with
6556 | None -> "false"
6557 | Some f ->
6558 if f = infinity
6559 then "true"
6560 else string_of_float f
6562 Printf.bprintf bb "\n %s='%s'" s v
6563 and oco s a b =
6564 if always || a <> b
6565 then
6566 match a with
6567 | Cmulti ((n, a, b), _) when n > 1 ->
6568 Printf.bprintf bb "\n %s='%d,%d,%d'" s n a b
6569 | Csplit (n, _) when n > 1 ->
6570 Printf.bprintf bb "\n %s='%d'" s ~-n
6571 | _ -> ()
6572 and obeco s a b =
6573 if always || a <> b
6574 then
6575 match a with
6576 | Some c when c > 1 -> Printf.bprintf bb "\n %s='%d'" s c
6577 | _ -> ()
6579 let w, h =
6580 if always
6581 then dc.winw, dc.winh
6582 else
6583 match state.fullscreen with
6584 | Some wh -> wh
6585 | None -> c.winw, c.winh
6587 oi "width" w dc.winw;
6588 oi "height" h dc.winh;
6589 oi "scroll-bar-width" c.scrollbw dc.scrollbw;
6590 oi "scroll-handle-height" c.scrollh dc.scrollh;
6591 ob "case-insensitive-search" c.icase dc.icase;
6592 ob "preload" c.preload dc.preload;
6593 oi "page-bias" c.pagebias dc.pagebias;
6594 oi "scroll-step" c.scrollstep dc.scrollstep;
6595 oi "auto-scroll-step" c.autoscrollstep dc.autoscrollstep;
6596 ob "max-height-fit" c.maxhfit dc.maxhfit;
6597 ob "crop-hack" c.crophack dc.crophack;
6598 oW "throttle" c.maxwait dc.maxwait;
6599 ob "highlight-links" c.hlinks dc.hlinks;
6600 ob "under-cursor-info" c.underinfo dc.underinfo;
6601 oi "vertical-margin" c.interpagespace dc.interpagespace;
6602 oz "zoom" c.zoom dc.zoom;
6603 ob "presentation" c.presentation dc.presentation;
6604 oi "rotation-angle" c.angle dc.angle;
6605 ob "persistent-bookmarks" c.savebmarks dc.savebmarks;
6606 ob "proportional-display" c.proportional dc.proportional;
6607 oI "pixmap-cache-size" c.memlimit dc.memlimit;
6608 oi "tex-count" c.texcount dc.texcount;
6609 oi "slice-height" c.sliceheight dc.sliceheight;
6610 oi "thumbnail-width" c.thumbw dc.thumbw;
6611 ob "persistent-location" c.jumpback dc.jumpback;
6612 oc "background-color" c.bgcolor dc.bgcolor;
6613 ob "scrollbar-in-presentation" c.scrollbarinpm dc.scrollbarinpm;
6614 oi "tile-width" c.tilew dc.tilew;
6615 oi "tile-height" c.tileh dc.tileh;
6616 oI "mupdf-store-size" c.mustoresize dc.mustoresize;
6617 ob "checkers" c.checkers dc.checkers;
6618 oi "aalevel" c.aalevel dc.aalevel;
6619 ob "trim-margins" c.trimmargins dc.trimmargins;
6620 oR "trim-fuzz" c.trimfuzz dc.trimfuzz;
6621 os "uri-launcher" c.urilauncher dc.urilauncher;
6622 os "path-launcher" c.pathlauncher dc.pathlauncher;
6623 oC "color-space" c.colorspace dc.colorspace;
6624 ob "invert-colors" c.invert dc.invert;
6625 oF "brightness" c.colorscale dc.colorscale;
6626 ob "redirectstderr" c.redirectstderr dc.redirectstderr;
6627 og "ghyllscroll" c.ghyllscroll dc.ghyllscroll;
6628 oco "columns" c.columns dc.columns;
6629 obeco "birds-eye-columns" c.beyecolumns dc.beyecolumns;
6630 os "selection-command" c.selcmd dc.selcmd;
6631 ob "update-cursor" c.updatecurs dc.updatecurs;
6632 oi "hint-font-size" c.hfsize dc.hfsize;
6633 oi "horizontal-scroll-step" c.hscrollstep dc.hscrollstep;
6634 oF "page-scroll-scale" c.pgscale dc.pgscale;
6637 let keymapsbuf always dc c =
6638 let bb = Buffer.create 16 in
6639 let rec loop = function
6640 | [] -> ()
6641 | (modename, h) :: rest ->
6642 let dh = findkeyhash dc modename in
6643 if always || h <> dh
6644 then (
6645 if Hashtbl.length h > 0
6646 then (
6647 if Buffer.length bb > 0
6648 then Buffer.add_char bb '\n';
6649 Printf.bprintf bb "<keymap mode='%s'>\n" modename;
6650 Hashtbl.iter (fun i o ->
6651 let isdifferent = always ||
6653 let dO = Hashtbl.find dh i in
6654 dO <> o
6655 with Not_found -> true
6657 if isdifferent
6658 then
6659 let addkm (k, m) =
6660 if Wsi.withctrl m then Buffer.add_string bb "ctrl-";
6661 if Wsi.withalt m then Buffer.add_string bb "alt-";
6662 if Wsi.withshift m then Buffer.add_string bb "shift-";
6663 if Wsi.withmeta m then Buffer.add_string bb "meta-";
6664 Buffer.add_string bb (Wsi.keyname k);
6666 let addkms l =
6667 let rec loop = function
6668 | [] -> ()
6669 | km :: [] -> addkm km
6670 | km :: rest -> addkm km; Buffer.add_char bb ' '; loop rest
6672 loop l
6674 Buffer.add_string bb "<map in='";
6675 addkm i;
6676 match o with
6677 | KMinsrt km ->
6678 Buffer.add_string bb "' out='";
6679 addkm km;
6680 Buffer.add_string bb "'/>\n"
6682 | KMinsrl kms ->
6683 Buffer.add_string bb "' out='";
6684 addkms kms;
6685 Buffer.add_string bb "'/>\n"
6687 | KMmulti (ins, kms) ->
6688 Buffer.add_char bb ' ';
6689 addkms ins;
6690 Buffer.add_string bb "' out='";
6691 addkms kms;
6692 Buffer.add_string bb "'/>\n"
6693 ) h;
6694 Buffer.add_string bb "</keymap>";
6697 loop rest
6699 loop c.keyhashes;
6703 let save () =
6704 let uifontsize = fstate.fontsize in
6705 let bb = Buffer.create 32768 in
6706 let f (h, dc) =
6707 let dc = if conf.bedefault then conf else dc in
6708 Buffer.add_string bb "<llppconfig>\n";
6710 if String.length !fontpath > 0
6711 then
6712 Printf.bprintf bb "<ui-font size='%d'><![CDATA[%s]]></ui-font>\n"
6713 uifontsize
6714 !fontpath
6715 else (
6716 if uifontsize <> 14
6717 then
6718 Printf.bprintf bb "<ui-font size='%d'/>\n" uifontsize
6721 Buffer.add_string bb "<defaults ";
6722 add_attrs bb true dc dc;
6723 let kb = keymapsbuf true dc dc in
6724 if Buffer.length kb > 0
6725 then (
6726 Buffer.add_string bb ">\n";
6727 Buffer.add_buffer bb kb;
6728 Buffer.add_string bb "\n</defaults>\n";
6730 else Buffer.add_string bb "/>\n";
6732 let adddoc path pan anchor c bookmarks =
6733 if bookmarks == [] && c = dc && anchor = emptyanchor
6734 then ()
6735 else (
6736 Printf.bprintf bb "<doc path='%s'"
6737 (enent path 0 (String.length path));
6739 if anchor <> emptyanchor
6740 then (
6741 let n, rely, visy = anchor in
6742 Printf.bprintf bb " page='%d'" n;
6743 if rely > 1e-6
6744 then
6745 Printf.bprintf bb " rely='%f'" rely
6747 if abs_float visy > 1e-6
6748 then
6749 Printf.bprintf bb " visy='%f'" visy
6753 if pan != 0
6754 then Printf.bprintf bb " pan='%d'" pan;
6756 add_attrs bb false dc c;
6757 let kb = keymapsbuf false dc c in
6759 begin match bookmarks with
6760 | [] ->
6761 if Buffer.length kb > 0
6762 then (
6763 Buffer.add_string bb ">\n";
6764 Buffer.add_buffer bb kb;
6765 Buffer.add_string bb "\n</doc>\n";
6767 else Buffer.add_string bb "/>\n"
6768 | _ ->
6769 Buffer.add_string bb ">\n<bookmarks>\n";
6770 List.iter (fun (title, _level, (page, rely, visy)) ->
6771 Printf.bprintf bb
6772 "<item title='%s' page='%d'"
6773 (enent title 0 (String.length title))
6774 page
6776 if rely > 1e-6
6777 then
6778 Printf.bprintf bb " rely='%f'" rely
6780 if abs_float visy > 1e-6
6781 then
6782 Printf.bprintf bb " visy='%f'" visy
6784 Buffer.add_string bb "/>\n";
6785 ) bookmarks;
6786 Buffer.add_string bb "</bookmarks>";
6787 if Buffer.length kb > 0
6788 then (
6789 Buffer.add_string bb "\n";
6790 Buffer.add_buffer bb kb;
6792 Buffer.add_string bb "\n</doc>\n";
6793 end;
6797 let pan, conf =
6798 match state.mode with
6799 | Birdseye (c, pan, _, _, _) ->
6800 let beyecolumns =
6801 match conf.columns with
6802 | Cmulti ((c, _, _), _) -> Some c
6803 | Csingle _ -> None
6804 | Csplit _ -> None
6805 and columns =
6806 match c.columns with
6807 | Cmulti (c, _) -> Cmulti (c, [||])
6808 | Csingle _ -> Csingle [||]
6809 | Csplit _ -> failwith "quit from bird's eye while split"
6811 pan, { c with beyecolumns = beyecolumns; columns = columns }
6812 | _ -> state.x, conf
6814 let basename = Filename.basename state.path in
6815 adddoc basename pan (getanchor ())
6816 (let conf =
6817 let autoscrollstep =
6818 match state.autoscroll with
6819 | Some step -> step
6820 | None -> conf.autoscrollstep
6822 match state.mode with
6823 | Birdseye (bc, _, _, _, _) ->
6824 { conf with
6825 zoom = bc.zoom;
6826 presentation = bc.presentation;
6827 interpagespace = bc.interpagespace;
6828 maxwait = bc.maxwait;
6829 autoscrollstep = autoscrollstep }
6830 | _ -> { conf with autoscrollstep = autoscrollstep }
6831 in conf)
6832 (if conf.savebmarks then state.bookmarks else []);
6834 Hashtbl.iter (fun path (c, bookmarks, x, anchor) ->
6835 if basename <> path
6836 then adddoc path x anchor c bookmarks
6837 ) h;
6838 Buffer.add_string bb "</llppconfig>\n";
6839 true;
6841 if load1 f && Buffer.length bb > 0
6842 then
6844 let tmp = !confpath ^ ".tmp" in
6845 let oc = open_out_bin tmp in
6846 Buffer.output_buffer oc bb;
6847 close_out oc;
6848 Unix.rename tmp !confpath;
6849 with exn ->
6850 prerr_endline
6851 ("error while saving configuration: " ^ Printexc.to_string exn)
6853 end;;
6855 let () =
6856 let trimcachepath = ref "" in
6857 Arg.parse
6858 (Arg.align
6859 [("-p", Arg.String (fun s -> state.password <- s) ,
6860 "<password> Set password");
6862 ("-f", Arg.String (fun s -> Config.fontpath := s),
6863 "<path> Set path to the user interface font");
6865 ("-c", Arg.String (fun s -> Config.confpath := s),
6866 "<path> Set path to the configuration file");
6868 ("-tcf", Arg.String (fun s -> trimcachepath := s),
6869 "<path> Set path to the trim cache file");
6871 ("-v", Arg.Unit (fun () ->
6872 Printf.printf
6873 "%s\nconfiguration path: %s\n"
6874 (version ())
6875 Config.defconfpath
6877 exit 0), " Print version and exit");
6880 (fun s -> state.path <- s)
6881 ("Usage: " ^ Sys.argv.(0) ^ " [options] some.pdf\nOptions:")
6883 if String.length state.path = 0
6884 then (prerr_endline "file name missing"; exit 1);
6886 if not (Config.load ())
6887 then prerr_endline "failed to load configuration";
6889 let globalkeyhash = findkeyhash conf "global" in
6890 let wsfd, winw, winh = Wsi.init (object
6891 method expose =
6892 if nogeomcmds state.geomcmds || platform == Posx
6893 then display ()
6894 else (
6895 GlClear.color (scalecolor2 conf.bgcolor);
6896 GlClear.clear [`color];
6898 method display = display ()
6899 method reshape w h = reshape w h
6900 method mouse b d x y m = mouse b d x y m
6901 method motion x y = state.mpos <- (x, y); motion x y
6902 method pmotion x y = state.mpos <- (x, y); pmotion x y
6903 method key k m =
6904 let mascm = m land (
6905 Wsi.altmask + Wsi.shiftmask + Wsi.ctrlmask + Wsi.metamask
6906 ) in
6907 match state.keystate with
6908 | KSnone ->
6909 let km = k, mascm in
6910 begin
6911 match
6912 let modehash = state.uioh#modehash in
6913 try Hashtbl.find modehash km
6914 with Not_found ->
6915 try Hashtbl.find globalkeyhash km
6916 with Not_found -> KMinsrt (k, m)
6917 with
6918 | KMinsrt (k, m) -> keyboard k m
6919 | KMinsrl l -> List.iter (fun (k, m) -> keyboard k m) l
6920 | KMmulti (l, r) -> state.keystate <- KSinto (l, r)
6922 | KSinto ((k', m') :: [], insrt) when k'=k && m' land mascm = m' ->
6923 List.iter (fun (k, m) -> keyboard k m) insrt;
6924 state.keystate <- KSnone
6925 | KSinto ((k', m') :: keys, insrt) when k'=k && m' land mascm = m' ->
6926 state.keystate <- KSinto (keys, insrt)
6927 | _ ->
6928 state.keystate <- KSnone
6930 method enter x y = state.mpos <- (x, y); pmotion x y
6931 method leave = state.mpos <- (-1, -1)
6932 method quit = raise Quit
6933 end) conf.winw conf.winh (platform = Posx) in
6935 state.wsfd <- wsfd;
6937 if not (
6938 List.exists GlMisc.check_extension
6939 [ "GL_ARB_texture_rectangle"
6940 ; "GL_EXT_texture_recangle"
6941 ; "GL_NV_texture_rectangle" ]
6943 then (prerr_endline "OpenGL does not suppport rectangular textures"; exit 1);
6945 let cr, sw =
6946 match Ne.pipe () with
6947 | Ne.Exn exn ->
6948 Printf.eprintf "pipe/crsw failed: %s" (Printexc.to_string exn);
6949 exit 1
6950 | Ne.Res rw -> rw
6951 and sr, cw =
6952 match Ne.pipe () with
6953 | Ne.Exn exn ->
6954 Printf.eprintf "pipe/srcw failed: %s" (Printexc.to_string exn);
6955 exit 1
6956 | Ne.Res rw -> rw
6959 cloexec cr;
6960 cloexec sw;
6961 cloexec sr;
6962 cloexec cw;
6964 setcheckers conf.checkers;
6965 redirectstderr ();
6967 init (cr, cw) (
6968 conf.angle, conf.proportional, (conf.trimmargins, conf.trimfuzz),
6969 conf.texcount, conf.sliceheight, conf.mustoresize, conf.colorspace,
6970 !Config.fontpath, !trimcachepath
6972 state.sr <- sr;
6973 state.sw <- sw;
6974 state.text <- "Opening " ^ state.path;
6975 reshape winw winh;
6976 opendoc state.path state.password;
6977 state.uioh <- uioh;
6979 Sys.set_signal Sys.sighup (Sys.Signal_handle (fun _ -> reload ()));
6981 let rec loop deadline =
6982 let r =
6983 match state.errfd with
6984 | None -> [state.sr; state.wsfd]
6985 | Some fd -> [state.sr; state.wsfd; fd]
6987 if state.redisplay
6988 then (
6989 state.redisplay <- false;
6990 display ();
6992 let timeout =
6993 let now = now () in
6994 if deadline > now
6995 then (
6996 if deadline = infinity
6997 then ~-.1.0
6998 else max 0.0 (deadline -. now)
7000 else 0.0
7002 let r, _, _ =
7003 try Unix.select r [] [] timeout
7004 with Unix.Unix_error (Unix.EINTR, _, _) -> [], [], []
7006 begin match r with
7007 | [] ->
7008 state.ghyll None;
7009 let newdeadline =
7010 if state.ghyll == noghyll
7011 then
7012 match state.autoscroll with
7013 | Some step when step != 0 ->
7014 let y = state.y + step in
7015 let y =
7016 if y < 0
7017 then state.maxy
7018 else if y >= state.maxy then 0 else y
7020 gotoy y;
7021 if state.mode = View
7022 then state.text <- "";
7023 deadline +. 0.01
7024 | _ -> infinity
7025 else deadline +. 0.01
7027 loop newdeadline
7029 | l ->
7030 let rec checkfds = function
7031 | [] -> ()
7032 | fd :: rest when fd = state.sr ->
7033 let cmd = readcmd state.sr in
7034 act cmd;
7035 checkfds rest
7037 | fd :: rest when fd = state.wsfd ->
7038 Wsi.readresp fd;
7039 checkfds rest
7041 | fd :: rest ->
7042 let s = String.create 80 in
7043 let n = Unix.read fd s 0 80 in
7044 if conf.redirectstderr
7045 then (
7046 Buffer.add_substring state.errmsgs s 0 n;
7047 state.newerrmsgs <- true;
7048 state.redisplay <- true;
7050 else (
7051 prerr_string (String.sub s 0 n);
7052 flush stderr;
7054 checkfds rest
7056 checkfds l;
7057 let newdeadline =
7058 let deadline1 =
7059 if deadline = infinity
7060 then now () +. 0.01
7061 else deadline
7063 match state.autoscroll with
7064 | Some step when step != 0 -> deadline1
7065 | _ -> if state.ghyll == noghyll then infinity else deadline1
7067 loop newdeadline
7068 end;
7071 loop infinity;
7072 with Quit ->
7073 Config.save ();