[go: up one dir, main page]

File: embed.ml

package info (click to toggle)
advi 1.10.2-3
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 15,008 kB
  • sloc: ml: 12,279; sh: 1,032; ansic: 1,016; makefile: 705; perl: 55
file content (304 lines) | stat: -rw-r--r-- 10,696 bytes parent folder | download | duplicates (6)
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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
(***********************************************************************)
(*                                                                     *)
(*                             Active-DVI                              *)
(*                                                                     *)
(*                   Projet Cristal, INRIA Rocquencourt                *)
(*                                                                     *)
(*  Copyright 2002 Institut National de Recherche en Informatique et   *)
(*  en Automatique.  All rights reserved.  This file is distributed    *)
(*  under the terms of the GNU Lesser General Public License.          *)
(*                                                                     *)
(*  Jun Furuse, Didier Rmy and Pierre Weis.                           *)
(*  Contributions by Roberto Di Cosmo, Didier Le Botlan,               *)
(*  Xavier Leroy, and Alan Schmitt.                                    *)
(*                                                                     *)
(*  Based on Mldvi by Alexandre Miquel.                                *)
(***********************************************************************)

(* $Id: embed.ml,v 1.1 2007/01/18 14:14:36 rousse Exp $ *)

(* Embedding applications (in particular tcl/tk) applications. *)

(* In hash table t, returns all elements (vals) that verify predicate p. *)
let hashtbl_find_all t p =
  let res = ref [] in
  Hashtbl.iter (fun k x -> if p x then res := x :: !res) t;
  !res;;

type app_mode = | Fake | Raw | Sticky | Persistent | Ephemeral;;

type signal = int;;

type app = {
  app_name : Launch.app_name;
  app_mode : app_mode;
  app_pid : int;
  app_wid : GraphicsY11.window_id;
};;

let app_table = Hashtbl.create 17;;

let add_app app_name app_mode pid wid =
 Hashtbl.add app_table app_name {
   app_name = app_name;
   app_mode = app_mode;
   app_pid = pid;
   app_wid = wid;
  };;

let pid_in_app_table pid =
 hashtbl_find_all app_table (fun app -> app.app_pid == pid) <> [];;

(* Register an application with its mode, name, (sub)window id,
   and a fake process id (actually max_int).
   This function does not actually launch the application, it just
   allocates the ressources to launch it afterwards. *)
let fake_embed_app command app_mode app_name width height x gry =
 let wid = GraphicsY11.open_subwindow ~x ~y:gry ~width ~height in
 add_app app_name app_mode max_int wid;;

(* The function that launches all embedded applications.

   When encountering an embedded application, a call to raw_embed_app
   is stored in the list of applications to be launched at the next
   pause (in the [embeds] list reference).

   This function allocates a (sub)window for the application and tries
   to launch the application into this window. *)
let raw_embed_app command app_mode app_name width height x gry =
 (*Misc.debug (Printf.sprintf "Launching command %s" command);*)
 if Launch.can_execute_command command then begin

  let wid = GraphicsY11.open_subwindow ~x ~y:gry ~width ~height in

  (***
    The following ``@'' macros are recognized in embedded commands:

    @p : designates the embedding target window id (an X window identifier)
         in this case the geometry x and y specification should be 0.

      If @p is not specified, the applications will be treated by the WM.
      (If they are X apps, of course...)

    @g : designates the geometry like 100x100+20+30
    @w : designates the width of the target window in pixel
    @h : designates the height of the target window in pixel
    @x : designates the abscissa of the application against the root
    @y : designates the ordiante of the application against the root

    Why using "@" ?
    Just because '\' is for TeX. "%" is for TeX. "$" is for TeX...

    For some time, we also accept ``!'' as a synonymous for ``@''.
  ***)

  (* If there is no @p, the application geometry will be treated
     by the WM. In such cases, we try to fix the geometry
     so that it is against the root window. *)

  let against_wid =
     Misc.contains_string command "@p" ||
     Misc.contains_string command "!p" in

  let geom_x, geom_y =
    if against_wid then "0", "0" else
    (* fix the geometry *)
    let (ww, wh, wx, wy) = GraphicsY11.get_geometry () in
    string_of_int (wx + x),
    string_of_int (wy + (wh - gry) - height) in

  let geom_w = string_of_int width
  and geom_h = string_of_int height in

  let geom = Printf.sprintf "%sx%s+%s+%s" geom_w geom_h geom_x geom_y in

  let env = function
    | 'p' -> wid
    | 'g' -> geom
    | 'w' -> geom_w
    | 'h' -> geom_h
    | 'x' -> geom_x
    | 'y' -> geom_y
    | _ -> raise Not_found in

  let command = Misc.string_substitute_var env command in
  let pid = Launch.fork_process command in
  if pid_in_app_table pid
  then failwith (Printf.sprintf "pid %d is already in the app_table!" pid)
  else add_app app_name app_mode pid wid
 end;;

let find_embedded_app app_name = Hashtbl.find app_table app_name;;

let find_all_embedded_app app_name =
  hashtbl_find_all app_table (fun app -> app.app_name = app_name);;

let map_embed app =
  GraphicsY11.map_subwindow app.app_wid;;

let map_embedded_app app_name =
  try
    map_embed (find_embedded_app app_name)
  with Not_found -> ();;

let map_all_embedded_app app_name =
  List.iter map_embed (find_all_embedded_app app_name);;

let unmap_embed app = GraphicsY11.unmap_subwindow app.app_wid;;

let unmap_embedded_app app_name =
  try
    unmap_embed (find_embedded_app app_name)
  with Not_found -> ();;

let unmap_all_embedded_app app_name =
  List.iter unmap_embed (find_all_embedded_app app_name);;

let move_or_resize_persistent_app
    command app_mode app_name width height x gry =
  try
    let app = find_embedded_app app_name in
    let wid = app.app_wid in
    GraphicsY11.resize_subwindow wid width height;
    let gry = gry + height - width in
    GraphicsY11.move_subwindow wid x gry
  with Not_found -> ();;

(* In hash table t, verifies that at least one element verifies p. *)
let hashtbl_exists t f =
  try Hashtbl.iter (fun _ x -> if f x then raise Exit) t; false
  with Exit -> true;;

(* embedded apps must be displayed when synced. *)
let embed_app command app_mode app_name width height x gry =
  let already_launched app_name = Hashtbl.mem app_table app_name in
  match app_mode with
  | Fake ->
     Launch.add_embed
      (fun () ->
        (* prerr_endline ("Launching fake app " ^ app_name); *)
        fake_embed_app command app_mode app_name width height x gry)
  | Raw ->
     Launch.add_embed
      (fun () ->
        (* prerr_endline ("Launching raw app " ^ app_name); *)
        raw_embed_app command app_mode app_name width height x gry)
  | Sticky ->
     if not (already_launched app_name) then
     Launch.add_embed
      (fun () ->
        (* prerr_endline ("Launching sticky app " ^ app_name); *)
        raw_embed_app command app_mode app_name width height x gry) else
     Launch.add_persist
      (fun () ->
        (* prerr_endline ("Moving " ^ app_name); *)
        move_or_resize_persistent_app command app_mode app_name
          width height x gry)
  | Persistent ->
     if not (already_launched app_name) then
     Launch.add_embed
      (fun () ->
        (* prerr_endline ("Launching persistent app " ^ app_name); *)
        raw_embed_app command app_mode app_name width height x gry);
     Launch.add_persist
      (fun () ->
        (* prerr_endline ("Mapping " ^ app_name); *)
        map_embedded_app app_name);
     Launch.add_unmap_embed
      (fun () ->
        (* prerr_endline ("Unmapping " ^ app_name); *)
        unmap_embedded_app app_name)
  | Ephemeral ->
     Launch.add_embed
      (fun () ->
        (* prerr_endline ("Launching ephemeral app " ^ app_name); *)
        raw_embed_app command app_mode app_name width height x gry);;

(* Kill the process and close the associated window. *)
let unembed_app app =
  (* prerr_endline (Printf.sprintf "kill_app (pid=%d, window=%s)" pid wid); *)
  begin
    try Hashtbl.remove app_table app.app_name with
    | _ ->
       Misc.warning
         (Printf.sprintf "kill_app failed to remove application %s..."
            app.app_name)
  end;
  (* Fake apps cannot be killed! *)
  if app.app_mode <> Fake then begin
    begin try Unix.kill app.app_pid Sys.sigquit with _ -> 
      (* prerr_endline
         (Printf.sprintf
            "kill_app (pid=%d,window=%s): process already dead" pid wid); *)
      ()
    end;
    while
      try
        let pid', _ = Unix.waitpid [Unix.WNOHANG] 0 in
        pid' <> 0
      with
      | Unix.Unix_error(Unix.ECHILD, _, _) -> false
    do () done;
    (* prerr_endline (Printf.sprintf "kill_app (pid=%d, window=%s)" pid wid); *)
  end;
  (* if this is the forked process, do not close the window!!! *)
  if Unix.getpid () = Launch.advi_process
  then GraphicsY11.close_subwindow app.app_wid;;

let unembed_apps_with_mode mode =
  (* begin match mode with
  | Fake -> prerr_endline "Killing fake apps"
  | Raw -> prerr_endline "Killing raw apps"
  | Persistent -> prerr_endline "Killing persistent apps"
  | Sticky -> prerr_endline "Killing sticky apps"
  | Ephemeral -> prerr_endline "Killing ephemeral apps"
  end; *)
  let to_be_removed =
    hashtbl_find_all app_table (fun app -> app.app_mode = mode) in
  List.iter unembed_app to_be_removed;;

let signal_app signal app =
  (* prerr_endline
    (Printf.sprintf
      "signal_app (pid=%d, window=%s) signal=%i killing=%b kill is %i"
      app.app_pid app.app_wid sig_val (sig_val = Sys.sigquit) Sys.sigquit); *)
  if signal = Sys.sigquit then unembed_app app else
  try Unix.kill app.app_pid signal
  with _ ->
    (* prerr_endline
        (Printf.sprintf
          "signal_app (pid=%d, window=%s) signal=%i: cannot signal process"
          pid wid signal); *)
    ();;

let kill_embedded_app signal app_name =
  (* prerr_endline
   (Printf.sprintf
     "kill_embedded_app (signal=%i app_name=%s)"
     signal app_name); *)
  try
    let app = find_embedded_app app_name in
    signal_app signal app with
  | Not_found ->
      Misc.warning (Printf.sprintf "application %s is not running" app_name);;

let kill_all_embedded_app signal app_name =
  (* prerr_endline
   (Printf.sprintf
     "kill_all_embedded_app (signal=%i app_name=%s)"
     signal app_name); *)
  let apps = find_all_embedded_app app_name in
  List.iter (signal_app signal) apps;;

let kill_ephemeral_apps () =
  unembed_apps_with_mode Ephemeral;;

let kill_persistent_apps () =
  Launch.unmap_persistent_apps ();
  unembed_apps_with_mode Sticky;
  unembed_apps_with_mode Persistent;;
  unembed_apps_with_mode Raw;;

let kill_all_embedded_apps () =
  kill_ephemeral_apps ();
  kill_persistent_apps ();;