Fix footnote number
[llpp.git] / main.ml
blob972fd01507ea9b4c78a3f5e4e69661467bc1fcea
1 open Format;;
3 let log fmt = Printf.kprintf prerr_endline fmt;;
4 let dolog fmt = Printf.kprintf prerr_endline fmt;;
6 external init : Unix.file_descr -> unit = "ml_init";;
7 external draw : int -> int -> int -> int -> string -> unit = "ml_draw";;
8 external preload : string -> unit = "ml_preload";;
9 external gettext : string -> (int * int * int * int) -> int -> bool -> unit =
10 "ml_gettext";;
11 external checklink : string -> int -> int -> int = "ml_checklink";;
13 type mstate = Msel of ((int * int) * (int * int)) | Mnone;;
16 type te =
17 | TEstop
18 | TEdone of string
19 | TEcont of string
22 type 'a circbuf =
23 { store : 'a array
24 ; mutable rc : int
25 ; mutable wc : int
26 ; mutable len : int
30 let cbnew n v =
31 { store = Array.create n v
32 ; rc = 0
33 ; wc = 0
34 ; len = 0
38 let cblen b = Array.length b.store;;
40 let cbput b v =
41 let len = cblen b in
42 b.store.(b.wc) <- v;
43 b.wc <- (b.wc + 1) mod len;
44 b.len <- (b.len + 1) mod len;
47 let cbpeekw b = b.store.(b.wc);;
49 let cbget b =
50 let v = b.store.(b.rc) in
51 if b.len = 0
52 then
54 else (
55 let rc = if b.rc = 0 then b.len - 1 else b.rc - 1 in
56 b.rc <- rc;
61 let cbrfollowlen b =
62 b.rc <- b.len - 1;
65 type layout =
66 { pageno : int
67 ; pagedimno : int
68 ; pagew : int
69 ; pageh : int
70 ; pagedispy : int
71 ; pagey : int
72 ; pagevh : int
76 type conf =
77 { mutable scrollw : int
78 ; mutable rectsel : bool
79 ; mutable icase : bool
80 ; mutable preload : bool
81 ; mutable titletext : bool
82 ; mutable pagebias : int
83 ; mutable redispimm : bool
84 ; mutable verbose : bool
88 type state =
89 { mutable csock : Unix.file_descr
90 ; mutable ssock : Unix.file_descr
91 ; mutable w : int
92 ; mutable h : int
93 ; mutable y : int
94 ; mutable prevy : int
95 ; mutable maxy : int
96 ; mutable layout : layout list
97 ; pagemap : ((int * int), string) Hashtbl.t
98 ; mutable pages : (int * int * int) list
99 ; mutable pagecount : int
100 ; pagecache : string circbuf
101 ; navhist : float circbuf
102 ; mutable inflight : int
103 ; mutable safe : int
104 ; mutable mstate : mstate
105 ; mutable searchpattern : string
106 ; mutable rects : (int * int * Gl.point2 * Gl.point2) list
107 ; mutable text : string
108 ; mutable fullscreen : (int * int) option
109 ; mutable textentry :
110 (char * string * (string -> int -> te) * (string -> unit)) option
114 let conf =
115 { scrollw = 5
116 ; icase = true
117 ; rectsel = true
118 ; preload = false
119 ; titletext = false
120 ; pagebias = 0
121 ; redispimm = false
122 ; verbose = false
126 let state =
127 { csock = Unix.stdin
128 ; ssock = Unix.stdin
129 ; w = 0
130 ; h = 0
131 ; y = 0
132 ; prevy = 0
133 ; layout = []
134 ; maxy = max_int
135 ; pagemap = Hashtbl.create 10
136 ; pagecache = cbnew 10 ""
137 ; pages = []
138 ; pagecount = 0
139 ; inflight = 0
140 ; safe = 0
141 ; mstate = Mnone
142 ; navhist = cbnew 100 0.0
143 ; rects = []
144 ; text = ""
145 ; fullscreen = None
146 ; textentry = None
147 ; searchpattern = ""
151 let aincr = 17;;
152 let aincr = 21;;
153 (* let aincr = 40;; *)
154 let scrollh = 12;;
156 let vlog fmt =
157 if conf.verbose
158 then
159 Printf.kprintf prerr_endline fmt
160 else
161 Printf.kprintf ignore fmt
164 let writecmd fd s =
165 let len = String.length s in
166 let n = 4 + len in
167 let b = Buffer.create n in
168 Buffer.add_char b (Char.chr ((len lsr 24) land 0xff));
169 Buffer.add_char b (Char.chr ((len lsr 16) land 0xff));
170 Buffer.add_char b (Char.chr ((len lsr 8) land 0xff));
171 Buffer.add_char b (Char.chr ((len lsr 0) land 0xff));
172 Buffer.add_string b s;
173 let s' = Buffer.contents b in
174 let n' = Unix.write fd s' 0 n in
175 if n' != n then failwith "write failed";
178 let readcmd fd =
179 let s = "xxxx" in
180 let n = Unix.read fd s 0 4 in
181 if n != 4 then failwith "incomplete read(len)";
182 let len = 0
183 lor (Char.code s.[0] lsl 24)
184 lor (Char.code s.[1] lsl 16)
185 lor (Char.code s.[2] lsl 8)
186 lor (Char.code s.[3] lsl 0)
188 let s = String.create len in
189 let n = Unix.read fd s 0 len in
190 if n != len then failwith "incomplete read(data)";
194 let yratio y =
195 if y = state.maxy then 1.0
196 else float y /. float state.maxy
199 let wcmd s l =
200 let b = Buffer.create 10 in
201 Buffer.add_string b s;
202 let rec combine = function
203 | [] -> Buffer.contents b
204 | x :: xs ->
205 Buffer.add_char b ' ';
206 let s =
207 match x with
208 | `b b -> if b then "1" else "0"
209 | `s s -> s
210 | `i i -> string_of_int i
211 | `f f -> string_of_float f
212 | `I f -> string_of_int (truncate f)
214 Buffer.add_string b s;
215 combine xs;
217 let s = combine l in
218 writecmd state.csock s;
221 let calcheight () =
222 let rec f pn ph fh l =
223 match l with
224 | (n, _, h) :: rest ->
225 let fh = fh + (n - pn) * ph in
226 f n h fh rest
228 | [] ->
229 let fh = fh + (ph * (state.pagecount - pn)) in
230 max 0 (fh - state.h)
232 let fh = f 0 0 0 state.pages in
236 let getpagey pageno =
237 let rec f pn ph y l =
238 match l with
239 | (n, _, h) :: rest ->
240 if n >= pageno
241 then
242 y + (pageno - pn) * ph
243 else
244 let y = y + (n - pn) * ph in
245 f n h y rest
247 | [] ->
248 y + (pageno - pn) * ph
250 f 0 0 0 state.pages;
253 let layout y sh =
254 let rec f pageno pdimno prev vy py dy l accu =
255 if pageno = state.pagecount
256 then accu
257 else
258 let ((_, w, h) as curr), rest, pdimno =
259 match l with
260 | ((pageno', _, _) as curr) :: rest when pageno' = pageno ->
261 curr, rest, pdimno + 1
262 | _ ->
263 prev, l, pdimno
265 let pageno' = pageno + 1 in
266 if py + h > vy
267 then
268 let py' = vy - py in
269 let vh = h - py' in
270 if dy + vh > sh
271 then
272 let vh = sh - dy in
273 if vh <= 0
274 then
275 accu
276 else
277 let e =
278 { pageno = pageno
279 ; pagedimno = pdimno
280 ; pagew = w
281 ; pageh = h
282 ; pagedispy = dy
283 ; pagey = py'
284 ; pagevh = vh
287 e :: accu
288 else
289 let e =
290 { pageno = pageno
291 ; pagedimno = pdimno
292 ; pagew = w
293 ; pageh = h
294 ; pagedispy = dy
295 ; pagey = py'
296 ; pagevh = vh
299 let accu = e :: accu in
300 f pageno' pdimno curr (vy + vh) (py + h) (dy + vh + 2) rest accu
301 else
302 f pageno' pdimno curr vy (py + h) dy rest accu
304 let accu = f 0 ~-1 (0,0,0) y 0 0 state.pages [] in
305 state.maxy <- calcheight ();
306 List.rev accu
309 let clamp incr =
310 let y = state.y + incr in
311 let y = max 0 y in
312 let y = min y state.maxy in
316 let gotoy y =
317 let y = max 0 y in
318 let y = min state.maxy y in
319 let pages = layout y state.h in
320 state.y <- y;
321 state.layout <- pages;
322 if conf.redispimm
323 then
324 Glut.postRedisplay ()
328 let addnav () =
329 cbput state.navhist (yratio state.y);
330 cbrfollowlen state.navhist;
333 let getnav () =
334 let y = cbget state.navhist in
335 truncate (y *. float state.maxy)
338 let reshape ~w ~h =
339 state.w <- w;
340 state.h <- h;
341 GlDraw.viewport 0 0 w h;
342 GlMat.mode `modelview;
343 GlMat.load_identity ();
344 GlMat.mode `projection;
345 GlMat.load_identity ();
346 GlMat.rotate ~x:1.0 ~angle:180.0 ();
347 GlMat.translate ~x:~-.1.0 ~y:~-.1.0 ();
348 GlMat.scale3 (2.0 /. float w, 2.0 /. float state.h, 1.0);
349 GlClear.color (1., 1., 1.);
350 GlClear.clear [`color];
351 state.layout <- [];
352 state.pages <- [];
353 state.rects <- [];
354 state.text <- "";
355 wcmd "geometry" [`i (state.w - conf.scrollw); `i h];
358 let showtext c s =
359 if not conf.titletext
360 then (
361 GlDraw.color (0.0, 0.0, 0.0);
362 GlDraw.rect
363 (0.0, float (state.h - 18))
364 (float (state.w - conf.scrollw - 1), float state.h)
366 let font = Glut.BITMAP_8_BY_13 in
367 GlDraw.color (1.0, 1.0, 1.0);
368 GlPix.raster_pos ~x:0.0 ~y:(float (state.h - 5)) ();
369 Glut.bitmapCharacter ~font ~c:(Char.code c);
370 String.iter (fun c -> Glut.bitmapCharacter ~font ~c:(Char.code c)) s
372 else
373 let len = String.length s in
374 let dst = String.create (len + 1) in
375 dst.[0] <- c;
376 StringLabels.blit
377 ~src:s
378 ~dst
379 ~src_pos:0
380 ~dst_pos:1
381 ~len
383 Glut.setWindowTitle ~title:dst
386 let act cmd =
387 match cmd.[0] with
388 | 'c' ->
389 state.pages <- []
391 | 'd' ->
392 Glut.postRedisplay ()
394 | 'C' ->
395 let n = Scanf.sscanf cmd "C %d" (fun n -> n) in
396 state.pagecount <- n;
397 let rely = yratio state.y in
398 let maxy = calcheight () in
399 state.y <- truncate (float maxy *. rely);
400 let pages = layout state.y state.h in
401 state.layout <- pages;
402 Glut.postRedisplay ();
404 | 'f' ->
405 state.safe <- state.safe - 1;
406 if state.safe = 0
407 then
408 Glut.postRedisplay ()
411 | 'T' ->
412 let s = Scanf.sscanf cmd "T %S" (fun s -> s) in
413 state.text <- s;
414 showtext ' ' s;
415 Glut.swapBuffers ();
416 (* Glut.postRedisplay () *)
418 | 'F' ->
419 let pageno, c, x0, y0, x1, y1 =
420 Scanf.sscanf cmd "F %d %d %f %f %f %f"
421 (fun p c x0 y0 x1 y1 -> (p, c, x0, y0, x1, y1))
423 let y = (getpagey pageno) + truncate y0 in
424 addnav ();
425 gotoy y;
426 state.rects <- [pageno, c, (x0, y0), (x1, y1)]
428 | 'R' ->
429 let pageno, c, x0, y0, x1, y1 =
430 Scanf.sscanf cmd "R %d %d %f %f %f %f"
431 (fun pageno c x0 y0 x1 y1 -> (pageno, c, x0, y0, x1, y1))
433 state.rects <- (pageno, c, (x0, y0), (x1, y1)) :: state.rects
435 | 'r' ->
436 let n, w, h, p =
437 Scanf.sscanf cmd "r %d %d %d %s"
438 (fun n w h p -> (n, w, h, p))
440 Hashtbl.replace state.pagemap (n, w) p;
441 let evicted = cbpeekw state.pagecache in
442 if String.length evicted > 0
443 then begin
444 state.safe <- state.safe + 1;
445 wcmd "free" [`s evicted];
446 let l = Hashtbl.fold (fun k p a ->
447 if evicted = p then k :: a else a) state.pagemap []
449 List.iter (fun k -> Hashtbl.remove state.pagemap k) l;
450 end;
451 cbput state.pagecache p;
452 state.inflight <- pred state.inflight;
453 Glut.postRedisplay ()
455 | 'l' ->
456 let (n, w, h) as pagelayout =
457 Scanf.sscanf cmd "l %d %d %d" (fun n w h -> n, w, h)
459 state.pages <- pagelayout :: state.pages
461 | _ ->
462 log "unknown cmd `%S'" cmd
465 let getopaque pageno =
466 try Some (Hashtbl.find state.pagemap (pageno + 1, state.w - conf.scrollw))
467 with Not_found -> None
470 let cache pageno opaque =
471 Hashtbl.replace state.pagemap (pageno + 1, state.w - conf.scrollw) opaque
474 let validopaque opaque = String.length opaque > 0;;
476 let preload l =
477 match getopaque l.pageno with
478 | Some opaque when validopaque opaque ->
479 preload opaque
481 | None when state.inflight < 2+0*(cblen state.pagecache) ->
482 state.inflight <- succ state.inflight;
483 cache l.pageno "";
484 wcmd "render" [`i (l.pageno + 1)
485 ;`i l.pagedimno
486 ;`i l.pagew
487 ;`i l.pageh];
489 | _ -> ()
492 let idle () =
493 if not conf.redispimm && state.y != state.prevy
494 then (
495 state.prevy <- state.y;
496 Glut.postRedisplay ();
498 else
499 let r, _, _ = Unix.select [state.csock] [] [] 0.02 in
501 begin match r with
502 | [] ->
503 if conf.preload then begin
504 let h = state.h in
505 let y = if state.y < state.h then 0 else state.y - state.h in
506 let pages = layout y (h*3) in
507 List.iter preload pages;
508 end;
510 | _ ->
511 let cmd = readcmd state.csock in
512 act cmd;
513 end;
516 let search pattern forward =
517 if String.length pattern > 0
518 then
519 let pn, py =
520 match state.layout with
521 | [] -> 0, 0
522 | l :: _ ->
523 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
525 wcmd "search" [`b conf.icase; `i pn; `i py; `i (if forward then 1 else 0)
526 ; `s (pattern ^ "\000")]
529 let intentry text key =
530 let c = Char.unsafe_chr key in
531 match c with
532 | '0' .. '9' ->
533 let s = "x" in s.[0] <- c;
534 let text = text ^ s in
535 TEcont text
537 | _ ->
538 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
539 TEcont text
542 let textentry text key =
543 let c = Char.unsafe_chr key in
544 match c with
545 | _ when key >= 32 && key <= 127 ->
546 let s = "x" in s.[0] <- c;
547 let text = text ^ s in
548 TEcont text
550 | _ ->
551 log "unhandled key %d char `%c'" key (Char.unsafe_chr key);
552 TEcont text
555 let optentry text key =
556 let btos b = if b then "on" else "off" in
557 let c = Char.unsafe_chr key in
558 match c with
559 | 'r' ->
560 conf.rectsel <- not conf.rectsel;
561 TEdone ("rectsel " ^ (btos conf.rectsel))
563 | 'i' ->
564 conf.icase <- not conf.icase;
565 TEdone ("case insensitive search " ^ (btos conf.icase))
567 | 'p' ->
568 conf.preload <- not conf.preload;
569 TEdone ("preload " ^ (btos conf.preload))
571 | 't' ->
572 conf.titletext <- not conf.titletext;
573 TEdone ("titletext " ^ (btos conf.titletext))
575 | 'd' ->
576 conf.redispimm <- not conf.redispimm;
577 TEdone ("immediate redisplay " ^ (btos conf.redispimm))
579 | 'v' ->
580 conf.verbose <- not conf.verbose;
581 TEdone ("verbose " ^ (btos conf.verbose))
583 | _ ->
584 state.text <- Printf.sprintf "bad option %d `%c'" key c;
585 TEstop
588 let keyboard ~key ~x ~y =
589 match state.textentry with
590 | None ->
591 let c = Char.chr key in
592 begin match c with
593 | '\027' | 'q' ->
594 exit 0
596 | '\008' ->
597 let y = getnav () in
598 gotoy y
600 | 'u' ->
601 state.rects <- [];
602 state.text <- "";
603 Glut.postRedisplay ()
605 | '/' | '?' ->
606 let ondone isforw s =
607 state.searchpattern <- s;
608 search s isforw
610 state.textentry <- Some (c, "", textentry, ondone (c ='/'));
611 state.text <- "";
612 Glut.postRedisplay ()
614 | '+' ->
615 let ondone s =
616 let n =
617 try int_of_string s with exc ->
618 state.text <- Printf.sprintf "bad integer `%s': %s"
619 s (Printexc.to_string exc);
620 max_int
622 if n != max_int
623 then (
624 conf.pagebias <- n;
625 state.text <- "page bias is now " ^ string_of_int n;
628 state.textentry <- Some ('+', "", intentry, ondone);
629 state.text <- "";
630 Glut.postRedisplay ()
633 | '-' ->
634 let ondone text =
635 state.text <- text;
636 Glut.postRedisplay ();
638 state.textentry <- Some ('-', "", optentry, ondone);
639 state.text <- "";
640 Glut.postRedisplay ()
642 | '0' .. '9' ->
643 let ondone s =
644 let n =
645 try int_of_string s with exc ->
646 state.text <- Printf.sprintf "bad integer `%s': %s"
647 s (Printexc.to_string exc);
650 if n >= 0
651 then
652 gotoy (getpagey (n + conf.pagebias - 1))
654 let pageentry text key =
655 match Char.unsafe_chr key with
656 | 'g' -> TEdone text
657 | _ -> intentry text key
659 let text = "x" in text.[0] <- c;
660 state.textentry <- Some (':', text, pageentry, ondone);
661 state.text <- "";
662 Glut.postRedisplay ()
664 | 'b' ->
665 conf.scrollw <- if conf.scrollw > 0 then 0 else 5;
666 reshape state.w state.h;
668 | 'f' ->
669 begin match state.fullscreen with
670 | None ->
671 state.fullscreen <- Some (state.w, state.h);
672 Glut.fullScreen ()
673 | Some (w, h) ->
674 state.fullscreen <- None;
675 Glut.reshapeWindow ~w ~h
678 | 'n' ->
679 search state.searchpattern true
681 | 'p' ->
682 search state.searchpattern false
684 | 't' ->
685 begin match state.layout with
686 | [] -> ()
687 | l :: _ ->
688 gotoy (state.y - l.pagey);
691 | ' ' | 'N' ->
692 begin match List.rev state.layout with
693 | [] -> ()
694 | l :: _ ->
695 gotoy (state.y + l.pageh - l.pagey)
698 | '\127' | 'P' ->
699 begin match state.layout with
700 | [] -> ()
701 | l :: _ ->
702 gotoy (state.y-l.pageh);
705 | '=' ->
706 let f (fn, ln) l =
707 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
709 let fn, ln = List.fold_left f (-1, -1) state.layout in
710 let s =
711 let percent = (100. *. yratio state.y) in
712 if fn = ln
713 then
714 Printf.sprintf "Page %d of %d %.2f%%"
715 (fn+1) state.pagecount percent
716 else
717 Printf.sprintf
718 "Pages %d-%d of %d %.2f%%"
719 (fn+1) (ln+1) state.pagecount percent
721 showtext ' ' s;
722 Glut.swapBuffers ()
724 | 'w' ->
725 begin match state.layout with
726 | [] -> ()
727 | l :: _ ->
728 Glut.reshapeWindow (l.pagew + conf.scrollw) l.pageh;
729 Glut.postRedisplay ();
732 | _ ->
733 vlog "huh? %d %c" key (Char.chr key);
736 | Some (c, text, onkey, ondone) when c = '\008' ->
737 let len = String.length text in
738 let te =
739 if len = 0 || len = 1
740 then
741 None
742 else (
743 let s = String.sub text 0 (len - 1) in
744 Some (c, s, onkey, ondone)
747 state.textentry <- te;
748 Glut.postRedisplay ()
750 | Some (c, text, onkey, ondone) ->
751 begin match Char.unsafe_chr key with
752 | '\r' | '\n' ->
753 state.textentry <- None;
754 ondone text;
756 | '\008' ->
757 let len = String.length text in
758 if len < 2
759 then (
760 state.textentry <- None
762 else (
763 let text = String.sub text 0 (len - 1) in
764 state.textentry <- Some (c, text, onkey, ondone)
767 | '\027' ->
768 state.textentry <- None
770 | _ ->
771 begin match onkey text key with
772 | TEdone text ->
773 state.textentry <- None;
774 ondone text;
776 | TEcont text ->
777 state.textentry <- Some (c, text, onkey, ondone);
779 | TEstop ->
780 state.textentry <- None;
781 end;
782 end;
783 Glut.postRedisplay ()
786 let special ~key ~x ~y =
787 let y =
788 match key with
789 | Glut.KEY_F3 -> search state.searchpattern true; state.y
790 | Glut.KEY_UP -> clamp ~-aincr
791 | Glut.KEY_DOWN -> clamp aincr
792 | Glut.KEY_PAGE_UP -> clamp (-state.h)
793 | Glut.KEY_PAGE_DOWN -> clamp state.h
794 | Glut.KEY_HOME -> addnav (); 0
795 | Glut.KEY_END -> addnav (); state.maxy (* - state.h *)
796 | _ -> state.y
798 state.text <- "";
799 gotoy y
802 let drawplaceholder l =
803 if true
804 then (
805 GlDraw.color (0.2, 0.2, 0.2);
806 GlDraw.color (1.0, 1.0, 1.0);
807 GlDraw.rect
808 (0.0, float l.pagedispy)
809 (float l.pagew, float (l.pagedispy + l.pagevh))
811 let x = 0.0
812 and y = float (l.pagedispy + l.pagevh - 5) in
813 let font = Glut.BITMAP_8_BY_13 in
814 GlDraw.color (0.0, 0.0, 0.0);
815 GlPix.raster_pos ~x ~y ();
816 String.iter (fun c -> Glut.bitmapCharacter ~font ~c:(Char.code c))
817 ("Loading " ^ string_of_int l.pageno);
819 else (
820 GlDraw.begins `quads;
821 GlDraw.vertex2 (0.0, float l.pagedispy);
822 GlDraw.vertex2 (float l.pagew, float l.pagedispy);
823 GlDraw.vertex2 (float l.pagew, float (l.pagedispy + l.pagevh));
824 GlDraw.vertex2 (0.0, float (l.pagedispy + l.pagevh));
825 GlDraw.ends ();
829 let now () = Unix.gettimeofday ();;
831 let drawpage i l =
832 begin match getopaque l.pageno with
833 | Some opaque when validopaque opaque ->
834 GlDraw.color (1.0, 1.0, 1.0);
835 let a = now () in
836 if state.safe = 0
837 then
838 draw l.pagedispy l.pagew l.pagevh l.pagey opaque
840 let b = now () in
841 let d = b-.a in
842 vlog "draw %f sec safe %d" d state.safe;
844 | Some _ ->
845 drawplaceholder l
847 | None ->
848 if state.inflight < cblen state.pagecache
849 then (
850 List.iter preload state.layout;
852 else (
853 drawplaceholder l;
854 vlog "inflight %d" state.inflight;
856 end;
857 GlDraw.color (0.5, 0.5, 0.5);
858 GlDraw.rect
859 (0., float i)
860 (float (state.w - conf.scrollw), float (i + (l.pagedispy - i)))
862 l.pagedispy + l.pagevh;
865 let scrollindicator () =
866 GlDraw.color (0.64 , 0.64, 0.64);
867 GlDraw.rect
868 (float (state.w - conf.scrollw), 0.)
869 (float state.w, float state.h)
871 GlDraw.color (0.0, 0.0, 0.0);
872 let sh = (float (state.maxy + state.h) /. float state.h) in
873 let sh = float state.h /. sh in
874 let sh = max sh (float scrollh) in
876 let percent = yratio state.y in
877 let position = (float state.h -. sh) *. percent in
879 let position =
880 if position +. sh > float state.h
881 then
882 float state.h -. sh
883 else
884 position
886 GlDraw.rect
887 (float (state.w - conf.scrollw), position)
888 (float state.w, position +. sh)
892 let showsel () =
893 match state.mstate with
894 | Mnone ->
897 | Msel ((x0, y0), (x1, y1)) ->
898 let y0' = min y0 y1
899 and y1 = max y0 y1 in
900 let y0 = y0' in
901 let f l =
902 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
903 || ((y1 >= l.pagedispy)) (* && y1 <= (dy + vh))) *)
904 then
905 match getopaque l.pageno with
906 | Some opaque when validopaque opaque ->
907 let oy = -l.pagey + l.pagedispy in
908 gettext opaque (min x0 x1, y0, max x1 x0, y1) oy conf.rectsel
909 | _ -> ()
911 List.iter f state.layout
914 let showrects () =
915 Gl.enable `blend;
916 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
917 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
918 List.iter
919 (fun (pageno, c, (x0, y0), (x1, y1)) ->
920 List.iter (fun l ->
921 if l.pageno = pageno
922 then (
923 let d = float (l.pagedispy - l.pagey) in
924 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
925 GlDraw.rect (x0, y0 +. d) (x1, y1 +. d)
927 ) state.layout
928 ) state.rects
930 Gl.disable `blend;
933 let display () =
934 ignore (List.fold_left drawpage 0 (state.layout));
935 showrects ();
936 scrollindicator ();
937 showsel ();
938 let len = String.length state.text in
939 begin match state.textentry with
940 | None ->
941 if len> 0 then showtext ' ' state.text
943 | Some (c, text, _, _) ->
944 let s =
945 if len > 0
946 then
947 text ^ " [" ^ state.text ^ "]"
948 else
949 text
951 showtext c s
952 end;
953 Glut.swapBuffers ();
956 let getlink x y =
957 let rec f = function
958 | l :: rest ->
959 begin match getopaque l.pageno with
960 | Some opaque when validopaque opaque ->
961 let y = y - l.pagedispy in
962 if y > 0
963 then
964 let y = l.pagey + y in
965 let destpage = checklink opaque x y in
966 if destpage < 0
967 then
968 f rest
969 else
970 destpage
971 else
972 f rest
973 | _ ->
974 f rest
976 | [] -> -1
978 f state.layout
981 let gotopage n =
982 let y = getpagey n in
983 addnav ();
984 state.y <- y;
985 gotoy y;
988 let mouse ~button ~bstate ~x ~y =
989 match button with
990 | Glut.OTHER_BUTTON n when n == 3 || n == 4 && bstate = Glut.UP ->
991 let incr =
992 if n = 3
993 then
994 -aincr
995 else
996 aincr
998 let incr = incr * 2 in
999 let y = clamp incr in
1000 gotoy y
1002 | Glut.LEFT_BUTTON ->
1003 let destpage = if bstate = Glut.DOWN then getlink x y else - 1 in
1004 if destpage >= 0
1005 then
1006 gotopage destpage
1007 else (
1008 if bstate = Glut.DOWN
1009 then (
1010 Glut.setCursor Glut.CURSOR_CROSSHAIR;
1011 state.mstate <- Msel ((x, y), (x, y));
1012 Glut.postRedisplay ()
1014 else (
1015 Glut.setCursor Glut.CURSOR_RIGHT_ARROW;
1016 state.mstate <- Mnone;
1020 | _ ->
1023 let mouse ~button ~state ~x ~y = mouse button state x y;;
1025 let motion ~x ~y =
1026 match state.mstate with
1027 | Mnone -> ()
1028 | Msel (a, _) ->
1029 state.mstate <- Msel (a, (x, y));
1030 Glut.postRedisplay ()
1033 let pmotion ~x ~y =
1034 match state.mstate with
1035 | Mnone ->
1036 let pageno = getlink x y in
1037 if pageno = -1
1038 then
1039 Glut.setCursor Glut.CURSOR_RIGHT_ARROW
1040 else
1041 Glut.setCursor Glut.CURSOR_INFO
1043 | Msel (a, _) ->
1047 let () =
1048 let name = ref "" in
1049 Arg.parse [] (fun s -> name := s) "options:";
1050 let name =
1051 if String.length !name = 0
1052 then (prerr_endline "filename missing"; exit 1)
1053 else !name
1056 let w = 900 in
1057 let h = 900 in
1058 let _ = Glut.init Sys.argv in
1059 let () = Glut.initDisplayMode ~depth:false ~double_buffer:true () in
1060 let () = Glut.initWindowSize w h in
1061 let _ = Glut.createWindow ("llpp " ^ Filename.basename name) in
1063 let csock, ssock = Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0 in
1065 init ssock;
1066 state.w <- w;
1067 state.h <- h;
1068 state.csock <- csock;
1069 state.ssock <- ssock;
1070 writecmd csock ("open " ^ name ^ "\000");
1072 let () = Glut.displayFunc display in
1073 let () = Glut.reshapeFunc reshape in
1074 let () = Glut.keyboardFunc keyboard in
1075 let () = Glut.specialFunc special in
1076 let () = Glut.idleFunc (Some idle) in
1077 let () = Glut.mouseFunc mouse in
1078 let () = Glut.motionFunc motion in
1079 let () = Glut.passiveMotionFunc pmotion in
1080 Glut.mainLoop ();