1 (***********************************************************************)
3 (* MLTk, Tcl/Tk interface of Objective Caml *)
5 (* Francois Rouaix, Francois Pessaux, Jun Furuse and Pierre Weis *)
6 (* projet Cristal, INRIA Rocquencourt *)
7 (* Jacques Garrigue, Kyoto University RIMS *)
9 (* Copyright 2002 Institut National de Recherche en Informatique et *)
10 (* en Automatique and Kyoto University. All rights reserved. *)
11 (* This file is distributed under the terms of the GNU Library *)
12 (* General Public License, with the special exception on linking *)
13 (* described in file LICENSE found in the Objective Caml source tree. *)
15 (***********************************************************************)
17 (* $Id: protocol.ml 7283 2005-12-21 05:29:08Z garrigue $ *)
22 type callback_buffer = string list
23 (* Buffer for reading callback arguments *)
27 | TkTokenList of tkArgs list (* to be expanded *)
28 | TkQuote of tkArgs (* mapped to Tcl list *)
32 external opentk_low : string list -> unit
34 external tcl_eval : string -> string
36 external tk_mainloop : unit -> unit
37 = "camltk_tk_mainloop"
38 external tcl_direct_eval : tkArgs array -> string
39 = "camltk_tcl_direct_eval"
40 external splitlist : string -> string list
42 external tkreturn : string -> unit
44 external callback_init : unit -> unit
46 external finalizeTk : unit -> unit
48 (* Finalize tcl/tk before exiting. This function will be automatically
49 called when you call [Pervasives.exit ()] (This is installed at
50 [install_cleanup ()] *)
52 let tcl_command s = ignore (tcl_eval s);;
55 DONT_WAIT | X_EVENTS | FILE_EVENTS | TIMER_EVENTS | IDLE_EVENTS | ALL_EVENTS
56 external do_one_event : event_flag list -> bool = "camltk_dooneevent"
58 let do_pending () = while do_one_event [DONT_WAIT] do () done
60 exception TkError of string
61 (* Raised by the communication functions *)
62 let () = Callback.register_exception "tkerror" (TkError "")
64 let cltclinterp = ref Nativeint.zero
65 (* For use in other extensions *)
66 let () = Callback.register "cltclinterp" cltclinterp
68 (* Debugging support *)
70 ref (try ignore (Sys.getenv "CAMLTKDEBUG"); true
71 with Not_found -> false)
73 (* This is approximative, since we don't quote what needs to be quoted *)
75 let rec print_arg = function
76 TkToken s -> prerr_string s; prerr_string " "
77 | TkTokenList l -> List.iter print_arg l
78 | TkQuote a -> prerr_string "{"; print_arg a; prerr_string "} "
80 Array.iter print_arg args;
85 * debugging support should not affect performances...
89 if !debug then dump_args args;
90 let res = tcl_direct_eval args in
97 let tkCommand args = ignore (tkEval args)
104 let cCAMLtoTKwidget w =
105 (* Widget.check_class w table; (* with subtyping, it is redundant *) *)
106 TkToken (Widget.name w)
108 let cTKtoCAMLwidget = function
109 "" -> raise (Invalid_argument "cTKtoCAMLwidget")
110 | s -> Widget.get_atom s
112 let callback_naming_table =
113 (Hashtbl.create 401 : (int, callback_buffer -> unit) Hashtbl.t)
115 let callback_memo_table =
116 (Hashtbl.create 401 : (any widget, int) Hashtbl.t)
118 let new_function_id =
119 let counter = ref 0 in
120 function () -> incr counter; !counter
122 let string_of_cbid = string_of_int
124 (* Add a new callback, associated to widget w *)
125 (* The callback should be cleared when w is destroyed *)
126 let register_callback w ~callback:f =
127 let id = new_function_id () in
128 Hashtbl.add callback_naming_table id f;
129 if (forget_type w) <> (forget_type Widget.dummy) then
130 Hashtbl.add callback_memo_table (forget_type w) id;
133 let clear_callback id =
134 Hashtbl.remove callback_naming_table id
136 (* Clear callbacks associated to a given widget *)
137 let remove_callbacks w =
138 let w = forget_type w in
139 let cb_ids = Hashtbl.find_all callback_memo_table w in
140 List.iter clear_callback cb_ids;
141 for i = 1 to List.length cb_ids do
142 Hashtbl.remove callback_memo_table w
145 (* Hand-coded callback for destroyed widgets
146 * This may be extended by the application, or by other layers of Camltk.
147 * Could use bind + of Tk, but I'd rather give an alternate mechanism so
148 * that hooks can be set up at load time (i.e. before openTk)
150 let destroy_hooks = ref []
151 let add_destroy_hook f =
152 destroy_hooks := f :: !destroy_hooks
155 add_destroy_hook (fun w -> remove_callbacks w; Widget.remove w)
157 let install_cleanup () =
158 let call_destroy_hooks = function
160 let w = cTKtoCAMLwidget wname in
161 List.iter (fun f -> f w) !destroy_hooks
162 | _ -> raise (TkError "bad cleanup callback") in
163 let fid = new_function_id () in
164 Hashtbl.add callback_naming_table fid call_destroy_hooks;
165 (* setup general destroy callback *)
166 tcl_command ("bind all <Destroy> {camlcb " ^ (string_of_cbid fid) ^" %W}");
170 prerr_string "camlcb "; prerr_int id
172 (* The callback dispatch function *)
173 let dispatch_callback id args =
176 List.iter (fun x -> prerr_string " "; prerr_string x) args;
179 (Hashtbl.find callback_naming_table id) args;
180 if !debug then prerr_endline "<<-"
182 let protected_dispatch id args =
184 dispatch_callback id args
186 Printf.eprintf "Uncaught exception: %s\n" (Printexc.to_string e);
189 let _ = Callback.register "camlcb" protected_dispatch
191 (* Make sure the C variables are initialised *)
192 let _ = callback_init ()
194 (* Different version of initialisation functions *)
195 let default_display_name = ref ""
196 let default_display () = !default_display_name
198 let camltk_argv = ref []
200 (* options for Arg.parse *)
202 "-display", Arg.String (fun s ->
203 camltk_argv := "-display" :: s :: !camltk_argv),
204 "<disp> : X server to contact (CamlTk)";
205 "-colormap", Arg.String (fun s ->
206 camltk_argv := "-colormap" :: s :: !camltk_argv),
207 "<colormap> : colormap to use (CamlTk)";
208 "-geometry", Arg.String (fun s ->
209 camltk_argv := "-geometry" :: s :: !camltk_argv),
210 "<geom> : size and position (CamlTk)";
211 "-name", Arg.String (fun s ->
212 camltk_argv := "-name" :: s :: !camltk_argv),
213 "<name> : application class (CamlTk)";
214 "-sync", Arg.Unit (fun () ->
215 camltk_argv := "-sync" :: !camltk_argv),
216 ": sync mode (CamlTk)";
217 "-use", Arg.String (fun s ->
218 camltk_argv := "-use" :: s :: !camltk_argv),
219 "<id> : parent window id (CamlTk)";
220 "-window", Arg.String (fun s ->
221 camltk_argv := "-use" :: s :: !camltk_argv),
222 "<id> : parent window id (CamlTk)";
223 "-visual", Arg.String (fun s ->
224 camltk_argv := "-visual" :: s :: !camltk_argv),
225 "<visual> : visual to use (CamlTk)" ]
227 let opentk_with_args argv (* = [argv1;..;argvn] *) =
228 (* argv must be command line for wish *)
229 let argv0 = Sys.argv.(0) in
230 let rec find_display = function
231 | "-display" :: s :: xs -> s
232 | "-colormap" :: s :: xs -> find_display xs
233 | "-geometry" :: s :: xs -> find_display xs
234 | "-name" :: s :: xs -> find_display xs
235 | "-sync" :: xs -> find_display xs
236 | "-use" :: s :: xs -> find_display xs
237 | "-window" :: s :: xs -> find_display xs
238 | "-visual" :: s :: xs -> find_display xs
240 | _ :: xs -> find_display xs
243 default_display_name := find_display argv;
244 opentk_low (argv0 :: argv);
246 Widget.default_toplevel
248 let opentk () = opentk_with_args !camltk_argv;;
250 let openTkClass s = opentk_with_args ["-name"; s]
251 let openTkDisplayClass disp cl = opentk_with_args ["-display"; disp; "-name"; cl]
253 (*JPF CAMLTK/LABLTK? *)
254 let openTk ?(display = "") ?(clas = "LablTk") () =
258 | _ -> ["-display"; display]
260 opentk_with_args (dispopt @ ["-name"; clas])
262 (* Destroy all widgets, thus cleaning up table and exiting the loop *)
264 tcl_command "destroy ."
270 (* [register tclname f] makes [f] available from Tcl with
272 let register tclname ~callback =
273 let s = register_callback Widget.default_toplevel ~callback in
274 tcl_command (Printf.sprintf "proc %s {args} {eval {camlcb %s} $args}"