Avoid "funny" results then more pages are visible than fit the cache
[llpp.git] / main.ml
blob6e7f74c27347c6d2e55a04f12ba1f640acb834e9
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 = 18;;
152 let scrollh = 12;;
154 let vlog fmt =
155 if conf.verbose
156 then
157 Printf.kprintf prerr_endline fmt
158 else
159 Printf.kprintf ignore fmt
162 let writecmd fd s =
163 let len = String.length s in
164 let n = 4 + len in
165 let b = Buffer.create n in
166 Buffer.add_char b (Char.chr ((len lsr 24) land 0xff));
167 Buffer.add_char b (Char.chr ((len lsr 16) land 0xff));
168 Buffer.add_char b (Char.chr ((len lsr 8) land 0xff));
169 Buffer.add_char b (Char.chr ((len lsr 0) land 0xff));
170 Buffer.add_string b s;
171 let s' = Buffer.contents b in
172 let n' = Unix.write fd s' 0 n in
173 if n' != n then failwith "write failed";
176 let readcmd fd =
177 let s = "xxxx" in
178 let n = Unix.read fd s 0 4 in
179 if n != 4 then failwith "incomplete read(len)";
180 let len = 0
181 lor (Char.code s.[0] lsl 24)
182 lor (Char.code s.[1] lsl 16)
183 lor (Char.code s.[2] lsl 8)
184 lor (Char.code s.[3] lsl 0)
186 let s = String.create len in
187 let n = Unix.read fd s 0 len in
188 if n != len then failwith "incomplete read(data)";
192 let yratio y =
193 if y = state.maxy then 1.0
194 else float y /. float state.maxy
197 let wcmd s l =
198 let b = Buffer.create 10 in
199 Buffer.add_string b s;
200 let rec combine = function
201 | [] -> Buffer.contents b
202 | x :: xs ->
203 Buffer.add_char b ' ';
204 let s =
205 match x with
206 | `b b -> if b then "1" else "0"
207 | `s s -> s
208 | `i i -> string_of_int i
209 | `f f -> string_of_float f
210 | `I f -> string_of_int (truncate f)
212 Buffer.add_string b s;
213 combine xs;
215 let s = combine l in
216 writecmd state.csock s;
219 let calcheight () =
220 let rec f pn ph fh l =
221 match l with
222 | (n, _, h) :: rest ->
223 let fh = fh + (n - pn) * ph in
224 f n h fh rest
226 | [] ->
227 let fh = fh + (ph * (state.pagecount - pn)) in
228 max 0 (fh - state.h)
230 let fh = f 0 0 0 state.pages in
234 let getpagey pageno =
235 let rec f pn ph y l =
236 match l with
237 | (n, _, h) :: rest ->
238 if n >= pageno
239 then
240 y + (pageno - pn) * ph
241 else
242 let y = y + (n - pn) * ph in
243 f n h y rest
245 | [] ->
246 y + (pageno - pn) * ph
248 f 0 0 0 state.pages;
251 let layout y sh =
252 let rec f pageno pdimno prev vy py dy l cacheleft accu =
253 if pageno = state.pagecount || cacheleft = 0
254 then accu
255 else
256 let ((_, w, h) as curr), rest, pdimno =
257 match l with
258 | ((pageno', _, _) as curr) :: rest when pageno' = pageno ->
259 curr, rest, pdimno + 1
260 | _ ->
261 prev, l, pdimno
263 let pageno' = pageno + 1 in
264 if py + h > vy
265 then
266 let py' = vy - py in
267 let vh = h - py' in
268 if dy + vh > sh
269 then
270 let vh = sh - dy in
271 if vh <= 0
272 then
273 accu
274 else
275 let e =
276 { pageno = pageno
277 ; pagedimno = pdimno
278 ; pagew = w
279 ; pageh = h
280 ; pagedispy = dy
281 ; pagey = py'
282 ; pagevh = vh
285 e :: accu
286 else
287 let e =
288 { pageno = pageno
289 ; pagedimno = pdimno
290 ; pagew = w
291 ; pageh = h
292 ; pagedispy = dy
293 ; pagey = py'
294 ; pagevh = vh
297 let accu = e :: accu in
298 f pageno' pdimno curr
299 (vy + vh) (py + h) (dy + vh + 2) rest
300 (pred cacheleft) accu
301 else
302 f pageno' pdimno curr vy (py + h) dy rest cacheleft accu
304 let accu = f 0 ~-1 (0,0,0) y 0 0 state.pages (cblen state.pagecache) [] 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 enttext () =
387 let len = String.length state.text in
388 match state.textentry with
389 | None ->
390 if len > 0 then showtext ' ' state.text
392 | Some (c, text, _, _) ->
393 let s =
394 if len > 0
395 then
396 text ^ " [" ^ state.text ^ "]"
397 else
398 text
400 showtext c s;
403 let act cmd =
404 match cmd.[0] with
405 | 'c' ->
406 state.pages <- []
408 | 'd' ->
409 Glut.postRedisplay ()
411 | 'C' ->
412 let n = Scanf.sscanf cmd "C %d" (fun n -> n) in
413 state.pagecount <- n;
414 let rely = yratio state.y in
415 let maxy = calcheight () in
416 state.y <- truncate (float maxy *. rely);
417 let pages = layout state.y state.h in
418 state.layout <- pages;
419 Glut.postRedisplay ();
421 | 'f' ->
422 state.safe <- state.safe - 1;
423 if state.safe = 0
424 then
425 Glut.postRedisplay ()
428 | 'T' ->
429 let s = Scanf.sscanf cmd "T %S" (fun s -> s) in
430 state.text <- s;
431 showtext ' ' s;
432 Glut.swapBuffers ();
433 (* Glut.postRedisplay () *)
435 | 'F' ->
436 let pageno, c, x0, y0, x1, y1 =
437 Scanf.sscanf cmd "F %d %d %f %f %f %f"
438 (fun p c x0 y0 x1 y1 -> (p, c, x0, y0, x1, y1))
440 let y = (getpagey pageno) + truncate y0 in
441 addnav ();
442 gotoy y;
443 state.rects <- [pageno, c, (x0, y0), (x1, y1)]
445 | 'R' ->
446 let pageno, c, x0, y0, x1, y1 =
447 Scanf.sscanf cmd "R %d %d %f %f %f %f"
448 (fun pageno c x0 y0 x1 y1 -> (pageno, c, x0, y0, x1, y1))
450 state.rects <- (pageno, c, (x0, y0), (x1, y1)) :: state.rects
452 | 'r' ->
453 let n, w, h, p =
454 Scanf.sscanf cmd "r %d %d %d %s"
455 (fun n w h p -> (n, w, h, p))
457 Hashtbl.replace state.pagemap (n, w) p;
458 let evicted = cbpeekw state.pagecache in
459 if String.length evicted > 0
460 then begin
461 state.safe <- state.safe + 1;
462 wcmd "free" [`s evicted];
463 let l = Hashtbl.fold (fun k p a ->
464 if evicted = p then k :: a else a) state.pagemap []
466 List.iter (fun k -> Hashtbl.remove state.pagemap k) l;
467 end;
468 cbput state.pagecache p;
469 state.inflight <- pred state.inflight;
470 Glut.postRedisplay ()
472 | 'l' ->
473 let (n, w, h) as pagelayout =
474 Scanf.sscanf cmd "l %d %d %d" (fun n w h -> n, w, h)
476 state.pages <- pagelayout :: state.pages
478 | _ ->
479 log "unknown cmd `%S'" cmd
482 let getopaque pageno =
483 try Some (Hashtbl.find state.pagemap (pageno + 1, state.w - conf.scrollw))
484 with Not_found -> None
487 let cache pageno opaque =
488 Hashtbl.replace state.pagemap (pageno + 1, state.w - conf.scrollw) opaque
491 let validopaque opaque = String.length opaque > 0;;
493 let preload l =
494 match getopaque l.pageno with
495 | Some opaque when validopaque opaque ->
496 preload opaque
498 | None when state.inflight < 2+0*(cblen state.pagecache) ->
499 state.inflight <- succ state.inflight;
500 cache l.pageno "";
501 wcmd "render" [`i (l.pageno + 1)
502 ;`i l.pagedimno
503 ;`i l.pagew
504 ;`i l.pageh];
506 | _ -> ()
509 let idle () =
510 if not conf.redispimm && state.y != state.prevy
511 then (
512 state.prevy <- state.y;
513 Glut.postRedisplay ();
515 else
516 let r, _, _ = Unix.select [state.csock] [] [] 0.02 in
518 begin match r with
519 | [] ->
520 if conf.preload then begin
521 let h = state.h in
522 let y = if state.y < state.h then 0 else state.y - state.h in
523 let pages = layout y (h*3) in
524 List.iter preload pages;
525 end;
527 | _ ->
528 let cmd = readcmd state.csock in
529 act cmd;
530 end;
533 let search pattern forward =
534 if String.length pattern > 0
535 then
536 let pn, py =
537 match state.layout with
538 | [] -> 0, 0
539 | l :: _ ->
540 l.pageno, (l.pagey + if forward then 0 else 0*l.pagevh)
542 wcmd "search" [`b conf.icase; `i pn; `i py; `i (if forward then 1 else 0)
543 ; `s (pattern ^ "\000")]
546 let intentry text key =
547 let c = Char.unsafe_chr key in
548 match c with
549 | '0' .. '9' ->
550 let s = "x" in s.[0] <- c;
551 let text = text ^ s in
552 TEcont text
554 | _ ->
555 state.text <- Printf.sprintf "invalid char (%d, `%c')" key c;
556 TEcont text
559 let textentry text key =
560 let c = Char.unsafe_chr key in
561 match c with
562 | _ when key >= 32 && key <= 127 ->
563 let s = "x" in s.[0] <- c;
564 let text = text ^ s in
565 TEcont text
567 | _ ->
568 log "unhandled key %d char `%c'" key (Char.unsafe_chr key);
569 TEcont text
572 let optentry text key =
573 let btos b = if b then "on" else "off" in
574 let c = Char.unsafe_chr key in
575 match c with
576 | 'r' ->
577 conf.rectsel <- not conf.rectsel;
578 TEdone ("rectsel " ^ (btos conf.rectsel))
580 | 'i' ->
581 conf.icase <- not conf.icase;
582 TEdone ("case insensitive search " ^ (btos conf.icase))
584 | 'p' ->
585 conf.preload <- not conf.preload;
586 TEdone ("preload " ^ (btos conf.preload))
588 | 't' ->
589 conf.titletext <- not conf.titletext;
590 TEdone ("titletext " ^ (btos conf.titletext))
592 | 'd' ->
593 conf.redispimm <- not conf.redispimm;
594 TEdone ("immediate redisplay " ^ (btos conf.redispimm))
596 | 'v' ->
597 conf.verbose <- not conf.verbose;
598 TEdone ("verbose " ^ (btos conf.verbose))
600 | _ ->
601 state.text <- Printf.sprintf "bad option %d `%c'" key c;
602 TEstop
605 let keyboard ~key ~x ~y =
606 let enttext te =
607 state.textentry <- te;
608 state.text <- "";
609 enttext ();
610 Glut.swapBuffers ()
612 match state.textentry with
613 | None ->
614 let c = Char.chr key in
615 begin match c with
616 | '\027' | 'q' ->
617 exit 0
619 | '\008' ->
620 let y = getnav () in
621 gotoy y
623 | 'u' ->
624 state.rects <- [];
625 state.text <- "";
626 Glut.postRedisplay ()
628 | '/' | '?' ->
629 let ondone isforw s =
630 state.searchpattern <- s;
631 search s isforw
633 enttext (Some (c, "", textentry, ondone (c ='/')))
635 | '+' ->
636 let ondone s =
637 let n =
638 try int_of_string s with exc ->
639 state.text <- Printf.sprintf "bad integer `%s': %s"
640 s (Printexc.to_string exc);
641 max_int
643 if n != max_int
644 then (
645 conf.pagebias <- n;
646 state.text <- "page bias is now " ^ string_of_int n;
649 enttext (Some ('+', "", intentry, ondone))
651 | '-' ->
652 let ondone msg =
653 state.text <- msg;
655 enttext (Some ('-', "", optentry, ondone))
657 | '0' .. '9' ->
658 let ondone s =
659 let n =
660 try int_of_string s with exc ->
661 state.text <- Printf.sprintf "bad integer `%s': %s"
662 s (Printexc.to_string exc);
665 if n >= 0
666 then
667 gotoy (getpagey (n + conf.pagebias - 1))
669 let pageentry text key =
670 match Char.unsafe_chr key with
671 | 'g' -> TEdone text
672 | _ -> intentry text key
674 let text = "x" in text.[0] <- c;
675 enttext (Some (':', text, pageentry, ondone))
677 | 'b' ->
678 conf.scrollw <- if conf.scrollw > 0 then 0 else 5;
679 reshape state.w state.h;
681 | 'f' ->
682 begin match state.fullscreen with
683 | None ->
684 state.fullscreen <- Some (state.w, state.h);
685 Glut.fullScreen ()
686 | Some (w, h) ->
687 state.fullscreen <- None;
688 Glut.reshapeWindow ~w ~h
691 | 'n' ->
692 search state.searchpattern true
694 | 'p' ->
695 search state.searchpattern false
697 | 't' ->
698 begin match state.layout with
699 | [] -> ()
700 | l :: _ ->
701 gotoy (state.y - l.pagey);
704 | ' ' | 'N' ->
705 begin match List.rev state.layout with
706 | [] -> ()
707 | l :: _ ->
708 gotoy (state.y + l.pageh - l.pagey)
711 | '\127' | 'P' ->
712 begin match state.layout with
713 | [] -> ()
714 | l :: _ ->
715 gotoy (state.y-l.pageh);
718 | '=' ->
719 let f (fn, ln) l =
720 if fn = -1 then l.pageno, l.pageno else fn, l.pageno
722 let fn, ln = List.fold_left f (-1, -1) state.layout in
723 let s =
724 let percent = (100. *. yratio state.y) in
725 if fn = ln
726 then
727 Printf.sprintf "Page %d of %d %.2f%%"
728 (fn+1) state.pagecount percent
729 else
730 Printf.sprintf
731 "Pages %d-%d of %d %.2f%%"
732 (fn+1) (ln+1) state.pagecount percent
734 showtext ' ' s;
735 Glut.swapBuffers ()
737 | 'w' ->
738 begin match state.layout with
739 | [] -> ()
740 | l :: _ ->
741 Glut.reshapeWindow (l.pagew + conf.scrollw) l.pageh;
742 Glut.postRedisplay ();
745 | _ ->
746 vlog "huh? %d %c" key (Char.chr key);
749 | Some (c, text, onkey, ondone) when c = '\008' ->
750 let len = String.length text in
751 let te =
752 if len = 0 || len = 1
753 then
754 None
755 else (
756 let s = String.sub text 0 (len - 1) in
757 Some (c, s, onkey, ondone)
760 enttext te
762 | Some (c, text, onkey, ondone) ->
763 begin match Char.unsafe_chr key with
764 | '\r' | '\n' ->
765 ondone text;
766 state.textentry <- None;
767 Glut.postRedisplay ()
769 | '\027' ->
770 state.textentry <- None;
771 Glut.postRedisplay ()
773 | _ ->
774 begin match onkey text key with
775 | TEdone text ->
776 state.textentry <- None;
777 ondone text;
778 Glut.postRedisplay ()
780 | TEcont text ->
781 enttext (Some (c, text, onkey, ondone));
783 | TEstop ->
784 state.textentry <- None;
785 Glut.postRedisplay ()
786 end;
787 end;
790 let special ~key ~x ~y =
791 let y =
792 match key with
793 | Glut.KEY_F3 -> search state.searchpattern true; state.y
794 | Glut.KEY_UP -> clamp ~-aincr
795 | Glut.KEY_DOWN -> clamp aincr
796 | Glut.KEY_PAGE_UP -> clamp (-state.h)
797 | Glut.KEY_PAGE_DOWN -> clamp state.h
798 | Glut.KEY_HOME -> addnav (); 0
799 | Glut.KEY_END -> addnav (); state.maxy (* - state.h *)
800 | _ -> state.y
802 state.text <- "";
803 gotoy y
806 let drawplaceholder l =
807 if true
808 then (
809 GlDraw.color (0.2, 0.2, 0.2);
810 GlDraw.color (1.0, 1.0, 1.0);
811 GlDraw.rect
812 (0.0, float l.pagedispy)
813 (float l.pagew, float (l.pagedispy + l.pagevh))
815 let x = 0.0
816 and y = float (l.pagedispy + l.pagevh - 5) in
817 let font = Glut.BITMAP_8_BY_13 in
818 GlDraw.color (0.0, 0.0, 0.0);
819 GlPix.raster_pos ~x ~y ();
820 String.iter (fun c -> Glut.bitmapCharacter ~font ~c:(Char.code c))
821 ("Loading " ^ string_of_int l.pageno);
823 else (
824 GlDraw.begins `quads;
825 GlDraw.vertex2 (0.0, float l.pagedispy);
826 GlDraw.vertex2 (float l.pagew, float l.pagedispy);
827 GlDraw.vertex2 (float l.pagew, float (l.pagedispy + l.pagevh));
828 GlDraw.vertex2 (0.0, float (l.pagedispy + l.pagevh));
829 GlDraw.ends ();
833 let now () = Unix.gettimeofday ();;
835 let drawpage i l =
836 begin match getopaque l.pageno with
837 | Some opaque when validopaque opaque ->
838 GlDraw.color (1.0, 1.0, 1.0);
839 let a = now () in
840 if state.safe = 0
841 then
842 draw l.pagedispy l.pagew l.pagevh l.pagey opaque
844 let b = now () in
845 let d = b-.a in
846 vlog "draw %f sec safe %d" d state.safe;
848 | Some _ ->
849 drawplaceholder l
851 | None ->
852 if state.inflight < cblen state.pagecache
853 then (
854 List.iter preload state.layout;
856 else (
857 drawplaceholder l;
858 vlog "inflight %d" state.inflight;
860 end;
861 GlDraw.color (0.5, 0.5, 0.5);
862 GlDraw.rect
863 (0., float i)
864 (float (state.w - conf.scrollw), float (i + (l.pagedispy - i)))
866 l.pagedispy + l.pagevh;
869 let scrollindicator () =
870 GlDraw.color (0.64 , 0.64, 0.64);
871 GlDraw.rect
872 (float (state.w - conf.scrollw), 0.)
873 (float state.w, float state.h)
875 GlDraw.color (0.0, 0.0, 0.0);
876 let sh = (float (state.maxy + state.h) /. float state.h) in
877 let sh = float state.h /. sh in
878 let sh = max sh (float scrollh) in
880 let percent = yratio state.y in
881 let position = (float state.h -. sh) *. percent in
883 let position =
884 if position +. sh > float state.h
885 then
886 float state.h -. sh
887 else
888 position
890 GlDraw.rect
891 (float (state.w - conf.scrollw), position)
892 (float state.w, position +. sh)
896 let showsel () =
897 match state.mstate with
898 | Mnone ->
901 | Msel ((x0, y0), (x1, y1)) ->
902 let y0' = min y0 y1
903 and y1 = max y0 y1 in
904 let y0 = y0' in
905 let f l =
906 if (y0 >= l.pagedispy && y0 <= (l.pagedispy + l.pagevh))
907 || ((y1 >= l.pagedispy)) (* && y1 <= (dy + vh))) *)
908 then
909 match getopaque l.pageno with
910 | Some opaque when validopaque opaque ->
911 let oy = -l.pagey + l.pagedispy in
912 gettext opaque (min x0 x1, y0, max x1 x0, y1) oy conf.rectsel
913 | _ -> ()
915 List.iter f state.layout
918 let showrects () =
919 Gl.enable `blend;
920 GlDraw.color (0.0, 0.0, 1.0) ~alpha:0.5;
921 GlFunc.blend_func `src_alpha `one_minus_src_alpha;
922 List.iter
923 (fun (pageno, c, (x0, y0), (x1, y1)) ->
924 List.iter (fun l ->
925 if l.pageno = pageno
926 then (
927 let d = float (l.pagedispy - l.pagey) in
928 GlDraw.color (0.0, 0.0, 1.0 /. float c) ~alpha:0.5;
929 GlDraw.rect (x0, y0 +. d) (x1, y1 +. d)
931 ) state.layout
932 ) state.rects
934 Gl.disable `blend;
937 let display () =
938 ignore (List.fold_left drawpage 0 (state.layout));
939 showrects ();
940 scrollindicator ();
941 showsel ();
942 enttext ();
943 Glut.swapBuffers ();
946 let getlink x y =
947 let rec f = function
948 | l :: rest ->
949 begin match getopaque l.pageno with
950 | Some opaque when validopaque opaque ->
951 let y = y - l.pagedispy in
952 if y > 0
953 then
954 let y = l.pagey + y in
955 let destpage = checklink opaque x y in
956 if destpage < 0
957 then
958 f rest
959 else
960 destpage
961 else
962 f rest
963 | _ ->
964 f rest
966 | [] -> -1
968 f state.layout
971 let gotopage n =
972 let y = getpagey n in
973 addnav ();
974 state.y <- y;
975 gotoy y;
978 let mouse ~button ~bstate ~x ~y =
979 match button with
980 | Glut.OTHER_BUTTON n when n == 3 || n == 4 && bstate = Glut.UP ->
981 let incr =
982 if n = 3
983 then
984 -aincr
985 else
986 aincr
988 let incr = incr * 2 in
989 let y = clamp incr in
990 gotoy y
992 | Glut.LEFT_BUTTON ->
993 let destpage = if bstate = Glut.DOWN then getlink x y else - 1 in
994 if destpage >= 0
995 then
996 gotopage destpage
997 else (
998 if bstate = Glut.DOWN
999 then (
1000 Glut.setCursor Glut.CURSOR_CROSSHAIR;
1001 state.mstate <- Msel ((x, y), (x, y));
1002 Glut.postRedisplay ()
1004 else (
1005 Glut.setCursor Glut.CURSOR_RIGHT_ARROW;
1006 state.mstate <- Mnone;
1010 | _ ->
1013 let mouse ~button ~state ~x ~y = mouse button state x y;;
1015 let motion ~x ~y =
1016 match state.mstate with
1017 | Mnone -> ()
1018 | Msel (a, _) ->
1019 state.mstate <- Msel (a, (x, y));
1020 Glut.postRedisplay ()
1023 let pmotion ~x ~y =
1024 match state.mstate with
1025 | Mnone ->
1026 let pageno = getlink x y in
1027 if pageno = -1
1028 then
1029 Glut.setCursor Glut.CURSOR_RIGHT_ARROW
1030 else
1031 Glut.setCursor Glut.CURSOR_INFO
1033 | Msel (a, _) ->
1037 let () =
1038 let name = ref "" in
1039 Arg.parse [] (fun s -> name := s) "options:";
1040 let name =
1041 if String.length !name = 0
1042 then (prerr_endline "filename missing"; exit 1)
1043 else !name
1046 let w = 900 in
1047 let h = 900 in
1048 let _ = Glut.init Sys.argv in
1049 let () = Glut.initDisplayMode ~depth:false ~double_buffer:true () in
1050 let () = Glut.initWindowSize w h in
1051 let _ = Glut.createWindow ("llpp " ^ Filename.basename name) in
1053 let csock, ssock = Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM 0 in
1055 init ssock;
1056 state.w <- w;
1057 state.h <- h;
1058 state.csock <- csock;
1059 state.ssock <- ssock;
1060 writecmd csock ("open " ^ name ^ "\000");
1062 let () = Glut.displayFunc display in
1063 let () = Glut.reshapeFunc reshape in
1064 let () = Glut.keyboardFunc keyboard in
1065 let () = Glut.specialFunc special in
1066 let () = Glut.idleFunc (Some idle) in
1067 let () = Glut.mouseFunc mouse in
1068 let () = Glut.motionFunc motion in
1069 let () = Glut.passiveMotionFunc pmotion in
1070 Glut.mainLoop ();