···8787 ;;
88888989 let release_reversable ((v, _) : handle) =
9090- Log.debug (fun m -> m "Maybe release or remove %d from reversable focus stack" (Lwd.peek v));
9090+ Log.debug (fun m ->
9191+ m "Maybe release or remove %d from reversable focus stack" (Lwd.peek v));
9192 (* we should only release if we actually have the focus*)
9293 if var_equal !currently_focused v
9394 then (
···9596 match !focus_stack with
9697 | hd :: tl ->
9798 request_var hd;
9898- Log.debug (fun m -> m "Released reversable focus %d in echange form %d" (Lwd.peek v) (Lwd.peek v));
9999+ Log.debug (fun m ->
100100+ m "Released reversable focus %d in echange form %d" (Lwd.peek v) (Lwd.peek v));
99101 focus_stack := tl
100102 | _ -> ())
101103 else (
···10561058module Ui_loop = struct
10571059 open Notty_unix
1058106010591059- let await_read_unix fd timeout: [`Ready|`NotReady]=
10601060- let rec select ()=
10611061- match Unix.select[fd] [] [fd] timeout with
10621062- | [], [], []-> `NotReady
10631063- | _-> `Ready
10641064- | exception Unix.Unix_error (Unix.EINTR, _, _) -> select ()
10611061+ module Internal = struct
10621062+ type step =
10631063+ ?process_event:bool
10641064+ -> ?timeout:float
10651065+ -> renderer:Renderer.t
10661066+ -> Term.t
10671067+ -> ui Lwd.root
10681068+ -> unit
10691069+10701070+ let await_read_unix fd timeout : [ `Ready | `NotReady ] =
10711071+ let rec select () =
10721072+ match Unix.select [ fd ] [] [ fd ] timeout with
10731073+ | [], [], [] -> `NotReady
10741074+ | _ -> `Ready
10751075+ | exception Unix.Unix_error (Unix.EINTR, _, _) -> select ()
10651076 in
10661066- select ()
10771077+ select ()
10781078+ ;;
1067107910801080+ (* FIXME Uses of [quick_sample] and [quick_release] should be replaced by
10811081+ [sample] and [release] with the appropriate release management. *)
1068108210831083+ let step
10841084+ ?(await_read = await_read_unix)
10851085+ ?(process_event = true)
10861086+ ?(timeout = -1.0)
10871087+ ~renderer
10881088+ term
10891089+ root
10901090+ =
10911091+ let size = Term.size term in
10921092+ 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 ()
11001100+ in
11011101+ Term.image term image;
11021102+ if process_event
11031103+ then (
11041104+ let wait_for_event () =
11051105+ let i, _ = Term.fds term in
11061106+ match await_read i timeout with
11071107+ | `NotReady -> Term.pending term
11081108+ | `Ready -> true
11091109+ in
11101110+ let has_event = timeout < 0.0 || Term.pending term || wait_for_event () in
11111111+ if has_event
11121112+ then (
11131113+ match Term.event term with
11141114+ | `End -> ()
11151115+ | `Resize _ -> ()
11161116+ | #Unescape.event as event ->
11171117+ let event = (event : Unescape.event :> Ui.event) in
11181118+ ignore (Renderer.dispatch_event renderer event : [ `Handled | `Unhandled ])))
11191119+ ;;
1069112010701070- (* FIXME Uses of [quick_sample] and [quick_release] should be replaced by
10711071- [sample] and [release] with the appropriate release management. *)
11211121+ type run_with_term_intern =
11221122+ step:step
11231123+ -> Term.t
11241124+ -> ?tick_period:float
11251125+ -> ?tick:(unit -> unit)
11261126+ -> renderer:Renderer.t
11271127+ -> bool Lwd.var
11281128+ -> ui Lwd.t
11291129+ -> unit
11301130+11311131+ type run_with_term =
11321132+ Term.t
11331133+ -> ?tick_period:float
11341134+ -> ?tick:(unit -> unit)
11351135+ -> renderer:Renderer.t
11361136+ -> bool Lwd.var
11371137+ -> ui Lwd.t
11381138+ -> unit
11391139+11401140+ let run_with_term : run_with_term_intern =
11411141+ fun ~(step : step) term ?tick_period ?(tick = ignore) ~renderer quit t ->
11421142+ let quit = Lwd.observe (Lwd.get quit) in
11431143+ let root = Lwd.observe t in
11441144+ let rec loop () =
11451145+ let quit = Lwd.quick_sample quit in
11461146+ if not quit
11471147+ then (
11481148+ step ~process_event:true ?timeout:tick_period ~renderer term root;
11491149+ tick ();
11501150+ loop ())
11511151+ in
11521152+ loop ();
11531153+ ignore (Lwd.quick_release root);
11541154+ ignore (Lwd.quick_release quit)
11551155+ ;;
1072115610731073- let step ?(await_read=await_read_unix) ?(process_event = true) ?(timeout = -1.0) ~renderer term root =
10741074- let size = Term.size term in
10751075- let image =
10761076- let rec stabilize () =
10771077- let tree = Lwd.quick_sample root in
10781078- Renderer.update renderer size tree;
10791079- let image = Renderer.image renderer in
10801080- if Lwd.is_damaged root then stabilize () else image
11571157+ 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
11671167+ =
11681168+ let quit =
11691169+ match quit with
11701170+ | Some quit -> quit
11711171+ | None -> Lwd.var false
10811172 in
10821082- stabilize ()
10831083- in
10841084- Term.image term image;
10851085- if process_event
10861086- then (
10871087- let wait_for_event () =
10881088- let i, _ = Term.fds term in
10891089- match await_read i timeout with
10901090- | `NotReady -> Term.pending term
10911091- | `Ready-> true
11731173+ let t =
11741174+ Lwd.map
11751175+ t
11761176+ ~f:
11771177+ (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))
10921185 in
10931093- let has_event = timeout < 0.0 || Term.pending term || wait_for_event () in
10941094- if has_event
10951095- then (
10961096- match Term.event term with
10971097- | `End -> ()
10981098- | `Resize _ -> ()
10991099- | #Unescape.event as event ->
11001100- let event = (event : Unescape.event :> Ui.event) in
11011101- ignore (Renderer.dispatch_event renderer event : [ `Handled | `Unhandled ])))
11021102- ;;
11861186+ match term with
11871187+ | Some term -> run_with_term term ?tick_period ?tick ~renderer quit t
11881188+ | None ->
11891189+ let term = Term.create () in
11901190+ run_with_term term ?tick_period ?tick ~renderer quit t;
11911191+ Term.release term
11921192+ ;;
11931193+ end
1103119411041104- let run_with_term term ?tick_period ?(tick = ignore) ~renderer quit t =
11051105- let quit = Lwd.observe (Lwd.get quit) in
11061106- let root = Lwd.observe t in
11071107- let rec loop () =
11081108- let quit = Lwd.quick_sample quit in
11091109- if not quit
11101110- then (
11111111- step ~process_event:true ?timeout:tick_period ~renderer term root;
11121112- tick ();
11131113- loop ())
11141114- in
11151115- loop ();
11161116- ignore (Lwd.quick_release root);
11171117- ignore (Lwd.quick_release quit)
11181118- ;;
11951195+ let step = Internal.step ~await_read:Internal.await_read_unix
11961196+ let run_with_term = Internal.run_with_term ~step
1119119711201120- let run
11211121- ?tick_period
11221122- ?tick
11231123- ?term
11241124- ?(renderer = Renderer.make ())
11251125- ?quit
11261126- ?(quit_on_escape = true)
11271127- ?(quit_on_ctrl_q = true)
11281128- t
11291129- =
11301130- let quit =
11311131- match quit with
11321132- | Some quit -> quit
11331133- | None -> Lwd.var false
11341134- in
11351135- let t =
11361136- Lwd.map
11371137- t
11381138- ~f:
11391139- (Ui.event_filter (function
11401140- | `Key (`ASCII 'Q', [ `Ctrl ]) when quit_on_ctrl_q ->
11411141- Lwd.set quit true;
11421142- `Handled
11431143- | `Key (`Escape, []) when quit_on_escape ->
11441144- Lwd.set quit true;
11451145- `Handled
11461146- | _ -> `Unhandled))
11471147- in
11481148- match term with
11491149- | Some term -> run_with_term term ?tick_period ?tick ~renderer quit t
11501150- | None ->
11511151- let term = Term.create () in
11521152- run_with_term term ?tick_period ?tick ~renderer quit t;
11531153- Term.release term
11981198+ let run =
11991199+ Internal.run
12001200+ ~run_with_term:
12011201+ (Internal.run_with_term
12021202+ ~step:(Internal.step ~await_read:Internal.await_read_unix))
11541203 ;;
11551204end
+67-3
forks/nottui/lib/nottui/nottui_main.mli
···366366module Ui_loop : sig
367367 open Notty_unix
368368369369-370369 (** Run one step of the main loop.
371370372371 Update output image describe by the provided [root].
373372 If [process_event], wait up to [timeout] seconds for an input event, then
374373 consume and dispatch it. *)
375374 val step
376376- : ?await_read:(Unix.file_descr -> float -> [ `Ready | `NotReady ])
377377- -> ?process_event:bool
375375+ : ?process_event:bool
378376 -> ?timeout:float
379377 -> renderer:Renderer.t
380378 -> Term.t
···402400 -> ?quit_on_ctrl_q:bool
403401 -> ui Lwd.t
404402 -> unit
403403+404404+ module Internal : sig
405405+ (** Provides slightly more powerful interfaces as compared to the Ui_llop module.
406406+407407+ Allows you to override the step/ await_read function.
408408+409409+ This should allow you to implement your own concurrency framework and modify how stepping is done. *)
410410+411411+ type step =
412412+ ?process_event:bool
413413+ -> ?timeout:float
414414+ -> renderer:Renderer.t
415415+ -> Term.t
416416+ -> ui Lwd.root
417417+ -> unit
418418+419419+ val await_read_unix : Unix.file_descr -> float -> [ `NotReady | `Ready ]
420420+421421+ (** Run one step of the main loop.
422422+423423+ Update output image describe by the provided [root].
424424+ If [process_event], wait up to [timeout] seconds for an input event, then
425425+ consume and dispatch it.
426426+427427+ [?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 ])
430430+ -> ?process_event:bool
431431+ -> ?timeout:float
432432+ -> renderer:Renderer.t
433433+ -> Term.t
434434+ -> ui Lwd.root
435435+ -> unit
436436+437437+ type run_with_term_intern=
438438+ step:step
439439+ -> Term.t
440440+ -> ?tick_period:float
441441+ -> ?tick:(unit -> unit)
442442+ -> renderer:Renderer.t
443443+ -> bool Lwd.var
444444+ -> ui Lwd.t
445445+ -> unit
446446+447447+ type run_with_term=
448448+ Term.t
449449+ -> ?tick_period:float
450450+ -> ?tick:(unit -> unit)
451451+ -> renderer:Renderer.t
452452+ -> bool Lwd.var
453453+ -> ui Lwd.t
454454+ -> unit
455455+ val run_with_term:run_with_term_intern
456456+457457+ val run:
458458+ run_with_term:run_with_term
459459+ -> ?tick_period:float
460460+ -> ?tick:(unit -> unit)
461461+ -> ?term:Term.t
462462+ -> ?renderer:Renderer.t
463463+ -> ?quit:bool Lwd.var
464464+ -> ?quit_on_escape:bool
465465+ -> ?quit_on_ctrl_q:bool
466466+ -> ui Lwd.t
467467+ -> unit
468468+ end
405469end
···11+open Notty
22+open Nottui
33+open Picos
44+open Picos_std_structured
55+open Picos_std_finally
66+77+module 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+1717+ in
1818+ select ()
1919+ )
2020+;;
2121+2222+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+;;
2929+3030+3131+let run = Ui_loop.Internal.run ~run_with_term
3232+end
+47
forks/nottui/lib/nottui_picos/nottui_picos.mli
···11+22+open Notty
33+open Nottui
44+open Picos
55+module Ui_loop : sig
66+ open Notty_unix
77+88+99+ (** Run one step of the main loop.
1010+1111+ Update output image describe by the provided [root].
1212+ If [process_event], wait up to [timeout] seconds for an input event, then
1313+ consume and dispatch it.
1414+1515+ *)
1616+ val step:
1717+ Picos_io_fd.t
1818+ -> ?process_event:bool
1919+ -> ?timeout:float
2020+ -> renderer:Renderer.t
2121+ -> Term.t
2222+ -> ui Lwd.root
2323+ -> unit
2424+2525+ (** Repeatedly run steps of the main loop, until either:
2626+ - [quit] becomes true,
2727+ - the ui computation raises an exception,
2828+ - if [quit_on_ctrl_q] was true or not provided, wait for Ctrl-Q event
2929+ - if [quit_on_escape] was true or not provided, wait for Escape event
3030+3131+ Specific [term] or [renderer] instances can be provided, otherwise new
3232+ ones will be allocated and released.
3333+3434+ Uses Picos for concurrency.
3535+ The only change vs the normal version is this yields whenever waiting for input
3636+ *)
3737+ val run
3838+ : ?tick_period:float
3939+ -> ?tick:(unit -> unit)
4040+ -> ?term:Term.t
4141+ -> ?renderer:Renderer.t
4242+ -> ?quit:bool Lwd.var
4343+ -> ?quit_on_escape:bool
4444+ -> ?quit_on_ctrl_q:bool
4545+ -> ui Lwd.t
4646+ -> unit
4747+end
+1
jj_tui/bin/dune
···55 signal
66 jj_tui
77 nottui
88+ nottui_picos
89 base
910 stdio
1011 picos_io
+18-30
jj_tui/bin/main.ml
···55open Picos_std_structured
66open Jj_tui.Logging
7788+let await_read_unix fd timeout : [ `Ready | `NotReady ] =
99+ let rec select () =
1010+ match Unix.select [ fd ] [] [ fd ] timeout with
1111+ | [], [], [] ->
1212+ `NotReady
1313+ | _ ->
1414+ `Ready
1515+ | exception Unix.Unix_error (Unix.EINTR, _, _) ->
1616+ select ()
1717+ in
1818+ select ()
1919+;;
2020+821(* let file_logger ~logs_stream=
922 let logs_crs=Picos_std_sync.Stream.tap logs_stream in
1023 let file=Picos_io.Unix.openfile "" in
···2841 `Unhandled)
2942 in
3043 let rec loop () =
3131- let open Picos_std_event in
3244 if not (Lwd.peek quit)
3345 then (
3446 (* let start_time = Unix.gettimeofday() in *)
···3648 let prev_term_width, prev_term_height = Lwd.peek Vars.term_width_height in
3749 if term_width <> prev_term_width || term_height <> prev_term_height
3850 then Lwd.set Vars.term_width_height (term_width, term_height);
3939- let stored_fd=ref (Obj.magic()) in
4040- Nottui.Ui_loop.step
4141- ~await_read:(fun unix_fd timeout ->
4242-4343- let fd =
4444- if (Picos_io_fd.unsafe_get (!stored_fd)) = unix_fd then
4545- !stored_fd
4646- else
4747- let picos_fd=Picos_io_fd.create ~dispose:false unix_fd in
4848- stored_fd:=picos_fd;
4949- picos_fd
5050- in
5151- let res =
5252- let event_ret ret = Event.map (fun _ -> ret) in
5353- Picos_std_event.Event.select
5454- [
5555- Picos_io_select.on fd `R |> event_ret `Ready
5656- ; Picos_io_select.on fd `W |> event_ret `Ready
5757- ; Picos_io_select.timeout ~seconds:timeout |> event_ret `NotReady
5858- ]
5959- in
6060- res)
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))
6155 ~process_event:true
6256 ~timeout:0.01
6357 ~renderer
6458 term
6559 (Lwd.observe @@ root);
6666- (*Sleep for a bit to stop spinning the cpu
6767- TODO: May not be needed, nottui may sleep for a bit anyway
6868- *)
6969- (* let end_time = Unix.gettimeofday () in *)
7070- (* let elapsed = end_time -. start_time in *)
7171- (* let sleep_time = max 0.01 (0.01 -. elapsed) in *)
7272- (* Picos_io.Unix.sleepf sleep_time; *)
7360 loop ())
7461 in
7562 loop ()
···8370 Flock.terminate ()
8471;;
85727373+Picos_io.Unix
8674let start () =
8775 Picos_mux_multififo.run_on ~n_domains:1 (fun _ ->
8876 Flock.join_after @@ fun () ->