···257257258258 type semantic_key =
259259 [ (* Clipboard *)
260260- `Copy
260260+ `Copy
261261 | `Paste
262262 | (* Focus management *)
263263- `Focus of
264264- [ `Out | `Next | `Prev | `Left | `Right | `Up | `Down ]
263263+ `Focus of [ `Out | `Next | `Prev | `Left | `Right | `Up | `Down ]
265264 ]
266265267266 type key =
···653652 let has_permanent_sensor flags = flags land flag_permanent_sensor <> 0
654653655654 let rec update_sensors ox oy sw sh mw mh ui =
656656- if has_transient_sensor ui.flags
657657- || (has_permanent_sensor ui.flags
658658- &&
659659- match ui.sensor_cache with
660660- | None -> true
661661- | Some (ox', oy', sw', sh') ->
662662- not (ox = ox' && oy = oy' && sw = sw' && sh = sh'))
655655+ if
656656+ has_transient_sensor ui.flags
657657+ || (has_permanent_sensor ui.flags
658658+ &&
659659+ match ui.sensor_cache with
660660+ | None -> true
661661+ | Some (ox', oy', sw', sh') -> not (ox = ox' && oy = oy' && sw = sw' && sh = sh')
662662+ )
663663 then (
664664 ui.flags <- ui.flags land lnot flag_transient_sensor;
665665 if has_permanent_sensor ui.flags then ui.sensor_cache <- Some (ox, oy, sw, sh);
···765765 ;;
766766767767 let dispatch_mouse t (event, (x, y), _mods) =
768768- if match event with
769769- | `Press btn ->
770770- release_grab t x y;
771771- let w, h = t.size in
772772- dispatch_mouse t x y btn w h t.view
773773- | `Drag ->
774774- (match t.mouse_grab with
775775- | None -> false
776776- | Some (drag, _) ->
777777- drag ~x ~y;
778778- true)
779779- | `Release ->
780780- release_grab t x y;
781781- true
768768+ if
769769+ match event with
770770+ | `Press btn ->
771771+ release_grab t x y;
772772+ let w, h = t.size in
773773+ dispatch_mouse t x y btn w h t.view
774774+ | `Drag ->
775775+ (match t.mouse_grab with
776776+ | None -> false
777777+ | Some (drag, _) ->
778778+ drag ~x ~y;
779779+ true)
780780+ | `Release ->
781781+ release_grab t x y;
782782+ true
782783 then `Handled
783784 else `Unhandled
784785 ;;
···798799 let same_size w h image = w = I.width image && h = I.height image
799800800801 let rec render_node vx1 vy1 vx2 vy2 sw sh t : cache =
801801- if let cache = t.cache in
802802- vx1 >= Interval.fst cache.vx
803803- && vy1 >= Interval.fst cache.vy
804804- && vx2 <= Interval.snd cache.vx
805805- && vy2 <= Interval.snd cache.vy
806806- && same_size sw sh cache.image
802802+ if
803803+ let cache = t.cache in
804804+ vx1 >= Interval.fst cache.vx
805805+ && vy1 >= Interval.fst cache.vy
806806+ && vx2 <= Interval.snd cache.vx
807807+ && vy2 <= Interval.snd cache.vy
808808+ && same_size sw sh cache.image
807809 then t.cache
808810 else if vx2 < 0 || vy2 < 0 || sw < vx1 || sh < vy1
809811 then (
···10671069 -> ui Lwd.root
10681070 -> unit
1069107110701070- let await_read_unix fd timeout : [ `Ready | `NotReady ] =
10721072+ let await_read_unix fd timeout : [ `Ready | `NotReady | `LwdStateUpdate ] =
10711073 let rec select () =
10721074 match Unix.select [ fd ] [] [ fd ] timeout with
10731075 | [], [], [] -> `NotReady
···10801082 (* FIXME Uses of [quick_sample] and [quick_release] should be replaced by
10811083 [sample] and [release] with the appropriate release management. *)
1082108410851085+ let cache = ref None
10861086+10831087 let step
10841084- ?(await_read = await_read_unix)
10851085- ?(process_event = true)
10861086- ?(timeout = -1.0)
10871087- ~renderer
10881088- term
10891089- root
10881088+ ?(await_read = await_read_unix)
10891089+ ?(process_event = true)
10901090+ ?(timeout = -1.0)
10911091+ ~renderer
10921092+ term
10931093+ root
10901094 =
10911095 let size = Term.size term in
10921096 let image =
10931093- let rec stabilize () =
10941094- let tree = Lwd.quick_sample root in
10951095- Renderer.update renderer size tree;
10961096- let image = Renderer.image renderer in
10971097- if Lwd.is_damaged root then stabilize () else image
10981098- in
10991099- stabilize ()
10971097+ if (not (Lwd.is_damaged root)) && !cache |> Option.is_some
10981098+ then !cache |> Option.get
10991099+ else (
11001100+ let rec stabilize () =
11011101+ let tree = Lwd.quick_sample root in
11021102+ Renderer.update renderer size tree;
11031103+ let image = Renderer.image renderer in
11041104+ (* If we are already damaged then we should re-calculate*)
11051105+ if Lwd.is_damaged root then stabilize () else image
11061106+ in
11071107+ stabilize ())
11001108 in
11091109+ cache := Some image;
11011110 Term.image term image;
11111111+ (* Now we wait for another event or the timeout*)
11021112 if process_event
11031113 then (
11041114 let wait_for_event () =
11051115 let i, _ = Term.fds term in
11061116 match await_read i timeout with
11071107- | `NotReady -> Term.pending term
11171117+ | `NotReady -> false
11081118 | `Ready -> true
11191119+ | `LwdStateUpdate -> false
11091120 in
11101110- let has_event = timeout < 0.0 || Term.pending term || wait_for_event () in
11111111- if has_event
11211121+ (* for async I should extend this to include changed lwd.var values*)
11221122+ (* let has_event =Term.pending term in *)
11231123+ if wait_for_event ()
11121124 then (
11251125+ Printf.eprintf "getting term event\n";
11131126 match Term.event term with
11141127 | `End -> ()
11151128 | `Resize _ -> ()
···11211134 type run_with_term_intern =
11221135 step:step
11231136 -> Term.t
11371137+ -> ?on_invalidate:(ui -> unit)
11241138 -> ?tick_period:float
11251139 -> ?tick:(unit -> unit)
11261140 -> renderer:Renderer.t
···1130114411311145 type run_with_term =
11321146 Term.t
11471147+ -> ?on_invalidate:(ui -> unit)
11331148 -> ?tick_period:float
11341149 -> ?tick:(unit -> unit)
11351150 -> renderer:Renderer.t
···11381153 -> unit
1139115411401155 let run_with_term : run_with_term_intern =
11411141- fun ~(step : step) term ?tick_period ?(tick = ignore) ~renderer quit t ->
11561156+ fun ~(step : step)
11571157+ term
11581158+ ?(on_invalidate = fun _ -> ())
11591159+ ?tick_period
11601160+ ?(tick = ignore)
11611161+ ~renderer
11621162+ quit
11631163+ t ->
11421164 let quit = Lwd.observe (Lwd.get quit) in
11431143- let root = Lwd.observe t in
11651165+ let root = Lwd.observe ~on_invalidate t in
11441166 let rec loop () =
11451167 let quit = Lwd.quick_sample quit in
11461168 if not quit
···11551177 ;;
1156117811571179 let run
11581158- ~(run_with_term : run_with_term)
11591159- ?tick_period
11601160- ?tick
11611161- ?term
11621162- ?(renderer = Renderer.make ())
11631163- ?quit
11641164- ?(quit_on_escape = true)
11651165- ?(quit_on_ctrl_q = true)
11661166- t
11801180+ ~(run_with_term : run_with_term)
11811181+ ?on_invalidate
11821182+ ?tick_period
11831183+ ?tick
11841184+ ?term
11851185+ ?(renderer = Renderer.make ())
11861186+ ?quit
11871187+ ?(quit_on_escape = true)
11881188+ ?(quit_on_ctrl_q = true)
11891189+ t
11671190 =
11681191 let quit =
11691192 match quit with
···11751198 t
11761199 ~f:
11771200 (Ui.event_filter (function
11781178- | `Key (`ASCII 'Q', [ `Ctrl ]) when quit_on_ctrl_q ->
11791179- Lwd.set quit true;
11801180- `Handled
11811181- | `Key (`Escape, []) when quit_on_escape ->
11821182- Lwd.set quit true;
11831183- `Handled
11841184- | _ -> `Unhandled))
12011201+ | `Key (`ASCII 'Q', [ `Ctrl ]) when quit_on_ctrl_q ->
12021202+ Lwd.set quit true;
12031203+ `Handled
12041204+ | `Key (`Escape, []) when quit_on_escape ->
12051205+ Lwd.set quit true;
12061206+ `Handled
12071207+ | _ -> `Unhandled))
11851208 in
11861209 match term with
11871210 | Some term -> run_with_term term ?tick_period ?tick ~renderer quit t
11881211 | None ->
11891212 let term = Term.create () in
11901190- run_with_term term ?tick_period ?tick ~renderer quit t;
12131213+ run_with_term term ?on_invalidate ?tick_period ?tick ~renderer quit t;
11911214 Term.release term
11921215 ;;
11931216 end
+15-11
forks/nottui/lib/nottui/nottui_main.mli
···188188 Copy and paste, as well as focus movements. *)
189189 type semantic_key =
190190 [ (* Clipboard *)
191191- `Copy
191191+ `Copy
192192 | `Paste
193193 | (* Focus management *)
194194- `Focus of
195195- [ `Out | `Next | `Prev | `Left | `Right | `Up | `Down ]
194194+ `Focus of [ `Out | `Next | `Prev | `Left | `Right | `Up | `Down ]
196195 ]
197196198197 (** A key is the pair of a main key and a list of modifiers *)
···391390 To simulate concurrency in a polling fashion, tick function and period
392391 can be provided. Use the [Lwt] backend for real concurrency. *)
393392 val run
394394- : ?tick_period:float
393393+ : ?on_invalidate:(ui -> unit)
394394+ -> ?tick_period:float
395395 -> ?tick:(unit -> unit)
396396 -> ?term:Term.t
397397 -> ?renderer:Renderer.t
···416416 -> ui Lwd.root
417417 -> unit
418418419419- val await_read_unix : Unix.file_descr -> float -> [ `NotReady | `Ready ]
419419+ val await_read_unix : Unix.file_descr -> float -> [ `NotReady | `Ready | `LwdStateUpdate]
420420421421 (** Run one step of the main loop.
422422···426426427427 [?await_read]- A function that waits for the file handle to be ready for reading for up to the provided timeout (-1.0 for no timeout). This exists entirely so this waiting can be overriden to interoperate with an effects based async system. *)
428428 val step
429429- : ?await_read:(Unix.file_descr -> float -> [ `Ready | `NotReady ])
429429+ : ?await_read:(Unix.file_descr -> float -> [ `Ready | `NotReady | `LwdStateUpdate ])
430430 -> ?process_event:bool
431431 -> ?timeout:float
432432 -> renderer:Renderer.t
···434434 -> ui Lwd.root
435435 -> unit
436436437437- type run_with_term_intern=
437437+ type run_with_term_intern =
438438 step:step
439439 -> Term.t
440440+ -> ?on_invalidate:(ui -> unit)
440441 -> ?tick_period:float
441442 -> ?tick:(unit -> unit)
442443 -> renderer:Renderer.t
···444445 -> ui Lwd.t
445446 -> unit
446447447447- type run_with_term=
448448+ type run_with_term =
448449 Term.t
450450+ -> ?on_invalidate:(ui -> unit)
449451 -> ?tick_period:float
450452 -> ?tick:(unit -> unit)
451453 -> renderer:Renderer.t
452454 -> bool Lwd.var
453455 -> ui Lwd.t
454456 -> unit
455455- val run_with_term:run_with_term_intern
456457457457- val run:
458458- run_with_term:run_with_term
458458+ val run_with_term : run_with_term_intern
459459+460460+ val run
461461+ : run_with_term:run_with_term
462462+ -> ?on_invalidate:(ui -> unit)
459463 -> ?tick_period:float
460464 -> ?tick:(unit -> unit)
461465 -> ?term:Term.t
···22open Nottui
33open Picos
44open Picos_std_structured
55-open Picos_std_finally
55+open Picos_std_finally
66+open Picos_std_event
77+open Picos_std_sync
6879module Ui_loop = struct
88-let step in_fd =
99- Ui_loop.Internal.step ~await_read:(fun _ timeout ->
1010- (*await the read inside a promise*)
1111- let rec select ()=
1212- match Picos_io.Unix.select [in_fd] [] [in_fd ] timeout with
1313- | [], [], [] -> `NotReady
1414- | _ -> `Ready
1515- | exception Unix.Unix_error (Unix.EINTR, _, _) -> select ()
1616-1010+ let step computation in_fd =
1111+ Ui_loop.Internal.step ~await_read:(fun _ timeout ->
1212+ let rec select () =
1313+ Printf.eprintf "waiting on events\n";
1414+ computation := Ivar.create ();
1515+ let cancelEvent = Ivar.read_evt !computation in
1616+ let ret =
1717+ Event.select
1818+ [ Picos_io_select.on in_fd `R |> Event.map (fun x -> `Ready)
1919+ ; Picos_io_select.on in_fd `W |> Event.map (fun _ -> `Ready)
2020+ ; cancelEvent
2121+ |> Event.map (fun x ->
2222+ Printf.eprintf "rerun-invalidation\n";
2323+ `LwdStateUpdate)
2424+ ; Picos_io_select.timeout ~seconds:10.0
2525+ |> Event.map (fun x -> `NotReady)
2626+ ]
2727+ in
2828+ Printf.eprintf "finished waiting\n";
2929+ ret
3030+ (* match Picos_io.Unix.select [ in_fd ] [] [ in_fd ] timeout with *)
3131+ (* | [], [], [] -> `NotReady *)
3232+ (* | _ -> `Ready *)
3333+ (* | exception Unix.Unix_error (Unix.EINTR, _, _) -> select () *)
1734 in
1818- select ()
1919- )
2020-;;
3535+ select ())
3636+ ;;
21372222-let run_with_term term =
2323- let in_fd,out_fd= Notty_unix.Term.fds term in
2424- let in_fd=Picos_io_fd.create in_fd in
2525- let step= step in_fd in
2626-2727- Ui_loop.Internal.run_with_term ~step term
2828-;;
3838+ let a = ref 0
29394040+ let run_with_term term ?on_invalidate =
4141+ let in_fd, out_fd = Notty_unix.Term.fds term in
4242+ (* the term will likely be attached to stdin so we check to make sure we don't recreate that file handle, because picos creates this handle at startup*)
4343+ let in_picos_fd =
4444+ if in_fd = (Picos_io.Unix.stdin |> Picos_io_fd.unsafe_get)
4545+ then Picos_io.Unix.stdin
4646+ else Picos_io_fd.create ~dispose:false in_fd
4747+ in
4848+ let trigger = ref (Ivar.create ()) in
4949+ let step = step trigger in_picos_fd in
5050+ a := !a + 1;
5151+ Ui_loop.Internal.run_with_term
5252+ ~on_invalidate:(fun _ ->
5353+ Printf.eprintf "invalidated\n";
5454+ Ivar.fill !trigger ())
5555+ ~step
5656+ term
5757+ ;;
30583131-let run = Ui_loop.Internal.run ~run_with_term
5959+ let run = Ui_loop.Internal.run ~run_with_term
3260end
+10-4
forks/nottui/lib/nottui_picos/nottui_picos.mli
···11-21open Notty
32open Nottui
43open Picos
44+55module Ui_loop : sig
66 open Notty_unix
77-8798 (** Run one step of the main loop.
109···1312 consume and dispatch it.
14131514 *)
1616- val step:
1515+ (*
1616+ val step:
1717 Picos_io_fd.t
1818 -> ?process_event:bool
1919 -> ?timeout:float
···2121 -> Term.t
2222 -> ui Lwd.root
2323 -> unit
2424+ *)
24252626+ (*
2727+ NOTE: Currently we use a tick and a timeout, this is essentially how long we will wait to respond to events that happened
2828+ *)
2529 (** Repeatedly run steps of the main loop, until either:
2630 - [quit] becomes true,
2731 - the ui computation raises an exception,
···3438 Uses Picos for concurrency.
3539 The only change vs the normal version is this yields whenever waiting for input
3640 *)
4141+3742 val run
3838- : ?tick_period:float
4343+ : ?on_invalidate:(ui -> unit)
4444+ -> ?tick_period:float
3945 -> ?tick:(unit -> unit)
4046 -> ?term:Term.t
4147 -> ?renderer:Renderer.t
+7-21
jj_tui/bin/main.ml
···4040 | _ ->
4141 `Unhandled)
4242 in
4343- let rec loop () =
4444- if not (Lwd.peek quit)
4545- then (
4646- (* let start_time = Unix.gettimeofday() in *)
4747- let term_width, term_height = Notty_unix.Term.size (Vars.get_term ()) in
4848- let prev_term_width, prev_term_height = Lwd.peek Vars.term_width_height in
4949- if term_width <> prev_term_width || term_height <> prev_term_height
5050- then Lwd.set Vars.term_width_height (term_width, term_height);
5151- Nottui_picos.step
5252- ~await_read:(fun fd timeout ->
5353- (*await the read inside a promise*)
5454- Promise.await @@ Flock.fork_as_promise (fun _ -> await_read_unix fd timeout))
5555- ~process_event:true
5656- ~timeout:0.01
5757- ~renderer
5858- term
5959- (Lwd.observe @@ root);
6060- loop ())
4343+ let tick () =
4444+ let term_width, term_height = Notty_unix.Term.size (Vars.get_term ()) in
4545+ let prev_term_width, prev_term_height = Lwd.peek Vars.term_width_height in
4646+ if term_width <> prev_term_width || term_height <> prev_term_height
4747+ then Lwd.set Vars.term_width_height (term_width, term_height)
6148 in
6262- loop ()
4949+ Nottui_picos.Ui_loop.run ~tick ~term ~renderer ~quit root
6350;;
64516552let start_ui () =
···7057 Flock.terminate ()
7158;;
72597373-Picos_io.Unix
7460let start () =
7575- Picos_mux_multififo.run_on ~n_domains:1 (fun _ ->
6161+ Picos_mux_multififo.run_on ~n_domains:4 (fun _ ->
7662 Flock.join_after @@ fun () ->
7763 init_logging ();
7864 start_ui ())