Source file lTerm_running_impl.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
open Lwt
open LTerm_geom
type t = LTerm_widget_base_impl.t
class toplevel = LTerm_toplevel_impl.toplevel
let rec find_focusable widget =
if widget#can_focus then
Some widget
else
find_focusable_in_list widget#children
and find_focusable_in_list = function
| [] ->
None
| child :: rest ->
match find_focusable child with
| Some _ as some -> some
| None -> find_focusable_in_list rest
let rec pick coord widget =
if not (LTerm_geom.in_rect widget#allocation coord) then None
else
let f () = if widget#can_focus then Some(widget, coord) else None in
let w =
List.fold_left
(function None -> pick coord
| Some(w, c) -> (fun _ -> Some(w, c)))
None widget#children
in
if w = None then f() else w
type 'a event =
| Value of 'a
| Event of LTerm_event.t
let lambda_termrc =
Filename.concat LTerm_resources.home ".lambda-termrc"
let file_exists file =
Lwt.catch
(fun () ->
Lwt_unix.access file [Unix.R_OK] >>= fun () ->
return true)
(function
| Unix.Unix_error _ ->
return false
| exn -> Lwt.fail exn)
let apply_resources ?cache load_resources resources_file widget =
if load_resources then
file_exists resources_file >>= fun has_resources ->
match has_resources with
| true ->
LTerm_resources.load resources_file >>= fun resources ->
widget#set_resources resources;
begin
match cache with
| None -> ()
| Some c -> c := resources
end;
return ()
| false ->
return ()
else
return ()
let ref_focus widget =
ref (match find_focusable widget with
| Some w -> w
| None -> widget)
let run_modal term ?save_state ?(load_resources = true) ?(resources_file = lambda_termrc) push_event pop_event widget waiter =
let widget = (widget :> t) in
let resources_cache = ref LTerm_resources.empty in
apply_resources ~cache:resources_cache load_resources resources_file widget >>= fun () ->
let focused = ref_focus widget in
let toplevel = new toplevel focused widget in
let draw_toplevel = ref (fun () -> ()) in
let size_ref = ref { row1 = 0; col1 = 0; row2 = 0; col2 = 0 } in
let layers = ref [toplevel] in
let focuses = ref [focused] in
let push_layer w =
let new_focus = ref_focus w in
let new_top = new toplevel new_focus w in
new_top#set_queue_draw !draw_toplevel;
new_top#set_allocation !size_ref;
focuses := new_focus :: !focuses;
layers := new_top :: !layers;
new_top#set_resources !resources_cache;
new_top#queue_draw
in
let pop_layer () =
match !layers with
| [_] ->
failwith "Trying to destroy the only existing layer."
| _ :: tl ->
layers := tl;
focuses := List.tl !focuses;
(List.hd !layers)#queue_draw
| [] ->
failwith "Internal error: no idea how it happened."
in
toplevel#arm_layer_handlers push_event push_layer pop_event pop_layer;
let draw ui matrix =
let ctx = LTerm_draw.context matrix (LTerm_ui.size ui) in
LTerm_draw.clear ctx;
let layers_rev = List.rev !layers in
let focuses_rev = List.rev !focuses in
List.iter2 (fun top focus -> top#draw ctx !focus) layers_rev focuses_rev;
let current_focus = List.hd !focuses in
match !current_focus#cursor_position with
| Some coord ->
let rect = !current_focus#allocation in
LTerm_ui.set_cursor_visible ui true;
LTerm_ui.set_cursor_position ui { row = rect.row1 + coord.row;
col = rect.col1 + coord.col }
| None ->
LTerm_ui.set_cursor_visible ui false
in
LTerm_ui.create term ?save_state draw >>= fun ui ->
draw_toplevel := (fun () -> LTerm_ui.draw ui);
toplevel#set_queue_draw !draw_toplevel;
let size = LTerm_ui.size ui in
size_ref := { !size_ref with row2 = size.rows; col2 = size.cols};
toplevel#set_allocation !size_ref;
let waiter = waiter >|= fun x -> Value x in
let rec loop () =
let thread = LTerm_ui.wait ui >|= fun x -> Event x in
choose [thread; waiter] >>= function
| Event (LTerm_event.Resize size) ->
size_ref := { !size_ref with row2 = size.rows; col2 = size.cols};
List.iter (fun top -> top#set_allocation !size_ref) !layers;
loop ()
| Event ((LTerm_event.Mouse m) as ev) when LTerm_mouse.(m.button=Button1) -> begin
let current_layer = List.hd !layers in
let picked = pick LTerm_mouse.(coord m) (current_layer :> t) in
match picked with
| Some _ ->
current_layer#move_focus_to picked;
!(List.hd !focuses)#send_event ev;
loop ()
| None ->
loop ()
end
| Event ev ->
!(List.hd !focuses)#send_event ev;
loop ()
| Value value ->
cancel thread;
return value
in
Lwt.finalize loop (fun () -> LTerm_ui.quit ui)
let run term ?save_state ?load_resources ?resources_file widget waiter =
run_modal term ?save_state ?load_resources ?resources_file Lwt_react.E.never Lwt_react.E.never widget waiter
let prepare_simple_run () =
let waiter, wakener = wait () in
let push_ev, push_ev_send = Lwt_react.E.create () in
let pop_ev, pop_ev_send = Lwt_react.E.create () in
let exit = wakeup wakener in
let push_layer w = fun () -> push_ev_send (w :> t) in
let pop_layer = pop_ev_send in
let do_run w =
Lazy.force LTerm.stdout >>= fun term ->
run_modal term push_ev pop_ev w waiter
in
(do_run, push_layer, pop_layer, exit)