2 # remote-tk.rb - supports to control remote Tk interpreters
3 # by Hidetoshi NAGAI <nagai@ai.kyutech.ac.jp>
6 fail RuntimeError, "'remote-tk' library must be required before requiring 'multi-tk'"
10 class RemoteTkIp < MultiTkIp; end
13 @@IP_TABLE = {}.taint unless defined?(@@IP_TABLE)
14 @@TK_TABLE_LIST = [].taint unless defined?(@@TK_TABLE_LIST)
15 def self._IP_TABLE; @@IP_TABLE; end
16 def self._TK_TABLE_LIST; @@TK_TABLE_LIST; end
19 def self._DEFAULT_MASTER
30 @@IP_TABLE = MultiTkIp._IP_TABLE unless defined?(@@IP_TABLE)
31 @@TK_TABLE_LIST = MultiTkIp._TK_TABLE_LIST unless defined?(@@TK_TABLE_LIST)
41 if defined?(@@DEFAULT_MASTER)
42 MultiTkIp._DEFAULT_MASTER
44 @@DEFAULT_MASTER = MultiTkIp._DEFAULT_MASTER
49 ###############################
52 undef new_master, new_slave, new_safe_slave
53 undef new_trusted_slave, new_safeTk
63 def initialize(remote_ip, displayof=nil, timeout=5)
65 fail SecurityError, "cannot access another interpreter at level #{$SAFE}"
68 @interp = MultiTkIp.__getip
70 fail SecurityError, "safe-IP cannot create RemoteTkIp"
74 @interp.allow_ruby_exit = false
75 @appname = @interp._invoke('tk', 'appname')
76 @remote = remote_ip.to_s.dup.freeze
77 if displayof.kind_of?(TkWindow)
78 @displayof = displayof.path.dup.freeze
83 fail RuntimeError, "no Tk application named \"#{@remote}\""
91 @force_default_encoding ||= [false].taint
92 @encoding ||= [nil].taint
93 def @encoding.to_s; self.join(nil); end
95 @tk_windows.taint unless @tk_windows.tainted?
96 @tk_table_list.taint unless @tk_table_list.tainted?
97 @slave_ip_tbl.taint unless @slave_ip_tbl.tainted?
98 @slave_ip_top.taint unless @slave_ip_top.tainted?
102 @threadgroup = ThreadGroup.new
104 @safe_level = [$SAFE]
106 @wait_on_mainloop = [true, 0]
108 @cmd_queue = Queue.new
111 @cmd_receiver, @receiver_watchdog = _create_receiver_and_watchdog()
113 @threadgroup.add @cmd_receiver
114 @threadgroup.add @receiver_watchdog
118 @@DEFAULT_MASTER.assign_receiver_and_watchdog(self)
120 @@IP_TABLE[@threadgroup] = self
121 @@TK_TABLE_LIST.size.times{
122 (tbl = {}).tainted? || tbl.taint
123 @tk_table_list << tbl
126 @ret_val = TkVariable.new
127 if timeout > 0 && ! _available_check(timeout)
128 fail RuntimeError, "cannot create connection"
130 @ip_id = _create_connection
136 self.freeze # defend against modification
140 return true if (Thread.current.group == ThreadGroup::Default)
141 MultiTkIp.__getip == @interp && ! @interp.safe?
143 def self.manipulable?
147 def _is_master_of?(tcltkip_obj)
148 tcltkip_obj == @interp
150 protected :_is_master_of?
156 def _available_check(timeout = 5)
157 raise SecurityError, "no permission to manipulate" unless self.manipulable?
159 return nil if timeout < 1
161 @interp._invoke('send', '-async', @remote,
162 'send', '-async', Tk.appname,
163 "set #{@ret_val.id} ready")
165 if @ret_val != 'ready'
166 (1..(timeout*5)).each{
169 break if @ret_val == 'ready'
172 @ret_val.value == 'ready'
174 private :_available_check
176 def _create_connection
177 raise SecurityError, "no permission to manipulate" unless self.manipulable?
179 ip_id = '_' + @interp._invoke('send', @remote, <<-'EOS') + '_'
180 if {[catch {set _rubytk_control_ip_id_} ret] != 0} {
181 set _rubytk_control_ip_id_ 0
183 set _rubytk_control_ip_id_ [expr $ret + 1]
185 return $_rubytk_control_ip_id_
188 @interp._invoke('send', @remote, <<-EOS)
189 proc rb_out#{ip_id} args {
190 send #{@appname} rb_out \$args
196 private :_create_connection
198 def _appsend(enc_mode, async, *cmds)
199 raise SecurityError, "no permission to manipulate" unless self.manipulable?
201 p ['_appsend', [@remote, @displayof], enc_mode, async, cmds] if $DEBUG
203 fail SecurityError, "cannot send commands at level 4"
204 elsif $SAFE >= 1 && cmds.find{|obj| obj.tainted?}
205 fail SecurityError, "cannot send tainted commands at level #{$SAFE}"
208 cmds = @interp._merge_tklist(*TkUtil::_conv_args([], enc_mode, *cmds))
211 @interp.__invoke('send', '-async', '-displayof', @displayof,
212 '--', @remote, *cmds)
214 @interp.__invoke('send', '-displayof', @displayof,
215 '--', @remote, *cmds)
219 @interp.__invoke('send', '-async', '--', @remote, *cmds)
221 @interp.__invoke('send', '--', @remote, *cmds)
227 def ready?(timeout=5)
229 fail ArgumentError, "timeout must be positive number"
231 _available_check(timeout)
235 return false if _appsend(false, false, 'info', 'command', 'ruby') == ""
236 [ _appsend(false, false, 'ruby', 'RUBY_VERSION'),
237 _appsend(false, false, 'set', 'tk_patchLevel') ]
240 def appsend(async, *args)
241 raise SecurityError, "no permission to manipulate" unless self.manipulable?
243 if async != true && async != false && async != nil
248 Tk.appsend_displayof(@remote, @displayof, async, *args)
250 Tk.appsend(@remote, async, *args)
254 def rb_appsend(async, *args)
255 raise SecurityError, "no permission to manipulate" unless self.manipulable?
257 if async != true && async != false && async != nil
262 Tk.rb_appsend_displayof(@remote, @displayof, async, *args)
264 Tk.rb_appsend(@remote, async, *args)
268 def create_slave(name, safe=false)
274 _appsend(false, false, "interp create #{safe_opt} -- #{name}")
278 fail RuntimeError, 'cannot change safe mode of the remote interpreter'
282 _appsend(false, false, 'interp issafe')
293 def allow_ruby_exit= (mode)
294 fail RuntimeError, 'cannot change mode of the remote interpreter'
298 _appsend(false, true, 'exit')
302 raise SecurityError, "no permission to manipulate" unless self.manipulable?
305 lst = @interp._invoke_without_enc('winfo', 'interps',
306 '-displayof', @displayof)
308 lst = @interp._invoke_without_enc('winfo', 'interps')
310 # unless @interp._split_tklist(lst).index(@remote)
311 unless @interp._split_tklist(lst).index(_toUTF8(@remote))
319 raise SecurityError, "no permission to manipulate" unless self.manipulable?
322 inf = @interp._invoke_without_enc('info', 'command', '.')
326 if !inf.kind_of?(String) || inf != '.'
333 def invalid_namespace?
338 fail RuntimeError, 'cannot restart the remote interpreter'
342 _appsend(false, false, str)
345 _appsend(nil, false, str)
347 def _eval_without_enc(str)
348 _appsend(false, false, str)
350 def _eval_with_enc(str)
351 _appsend(true, false, str)
355 _appsend(nil, false, *args)
359 _appsend(false, false, *args)
362 _appsend(nil, false, *args)
364 def _invoke_without_enc(*args)
365 _appsend(false, false, *args)
367 def _invoke_with_enc(*args)
368 _appsend(true, false, *args)
371 def _toUTF8(str, encoding=nil)
372 raise SecurityError, "no permission to manipulate" unless self.manipulable?
373 @interp._toUTF8(str, encoding)
376 def _fromUTF8(str, encoding=nil)
377 raise SecurityError, "no permission to manipulate" unless self.manipulable?
378 @interp._fromUTF8(str, encoding)
381 def _thread_vwait(var_name)
382 _appsend(false, 'thread_vwait', varname)
385 def _thread_tkwait(mode, target)
386 _appsend(false, 'thread_tkwait', mode, target)
390 raise SecurityError, "no permission to manipulate" unless self.manipulable?
391 @interp._return_value
394 def _get_variable(var_name, flag)
396 _appsend(false, 'set', TkComm::_get_eval_string(var_name))
398 def _get_variable2(var_name, index_name, flag)
400 _appsend(false, 'set', "#{TkComm::_get_eval_string(var_name)}(#{TkComm::_get_eval_string(index_name)})")
403 def _set_variable(var_name, value, flag)
405 _appsend(false, 'set', TkComm::_get_eval_string(var_name), TkComm::_get_eval_string(value))
407 def _set_variable2(var_name, index_name, value, flag)
409 _appsend(false, 'set', "#{TkComm::_get_eval_string(var_name)}(#{TkComm::_get_eval_string(index_name)})", TkComm::_get_eval_string(value))
412 def _unset_variable(var_name, flag)
414 _appsend(false, 'unset', TkComm::_get_eval_string(var_name))
416 def _unset_variable2(var_name, index_name, flag)
418 _appsend(false, 'unset', "#{var_name}(#{index_name})")
421 def _get_global_var(var_name)
422 _appsend(false, 'set', TkComm::_get_eval_string(var_name))
424 def _get_global_var2(var_name, index_name)
425 _appsend(false, 'set', "#{TkComm::_get_eval_string(var_name)}(#{TkComm::_get_eval_string(index_name)})")
428 def _set_global_var(var_name, value)
429 _appsend(false, 'set', TkComm::_get_eval_string(var_name), TkComm::_get_eval_string(value))
431 def _set_global_var2(var_name, index_name, value)
432 _appsend(false, 'set', "#{TkComm::_get_eval_string(var_name)}(#{TkComm::_get_eval_string(index_name)})", TkComm::_get_eval_string(value))
435 def _unset_global_var(var_name)
436 _appsend(false, 'unset', TkComm::_get_eval_string(var_name))
438 def _unset_global_var2(var_name, index_name)
439 _appsend(false, 'unset', "#{var_name}(#{index_name})")
442 def _split_tklist(str)
443 raise SecurityError, "no permission to manipulate" unless self.manipulable?
444 @interp._split_tklist(str)
447 def _merge_tklist(*args)
448 raise SecurityError, "no permission to manipulate" unless self.manipulable?
449 @interp._merge_tklist(*args)
452 def _conv_listelement(str)
453 raise SecurityError, "no permission to manipulate" unless self.manipulable?
454 @interp._conv_listelement(str)
458 fail RuntimeError, 'not support "_create_console" on the remote interpreter'
462 fail RuntimeError, 'not support "mainloop" on the remote interpreter'
464 def mainloop_watchdog
465 fail RuntimeError, 'not support "mainloop_watchdog" on the remote interpreter'
467 def do_one_evant(flag = nil)
468 fail RuntimeError, 'not support "do_one_event" on the remote interpreter'
470 def mainloop_abort_on_exception
471 fail RuntimeError, 'not support "mainloop_abort_on_exception" on the remote interpreter'
473 def mainloop_abort_on_exception=(mode)
474 fail RuntimeError, 'not support "mainloop_abort_on_exception=" on the remote interpreter'
476 def set_eventloop_tick(*args)
477 fail RuntimeError, 'not support "set_eventloop_tick" on the remote interpreter'
479 def get_eventloop_tick
480 fail RuntimeError, 'not support "get_eventloop_tick" on the remote interpreter'
482 def set_no_event_wait(*args)
483 fail RuntimeError, 'not support "set_no_event_wait" on the remote interpreter'
485 def get_no_event_wait
486 fail RuntimeError, 'not support "get_no_event_wait" on the remote interpreter'
488 def set_eventloop_weight(*args)
489 fail RuntimeError, 'not support "set_eventloop_weight" on the remote interpreter'
491 def get_eventloop_weight
492 fail RuntimeError, 'not support "get_eventloop_weight" on the remote interpreter'
498 fail RuntimeError, 'not support "mainloop" on the remote interpreter'
500 def mainloop_watchdog(*args)
501 fail RuntimeError, 'not support "mainloop_watchdog" on the remote interpreter'
503 def do_one_evant(flag = nil)
504 fail RuntimeError, 'not support "do_one_event" on the remote interpreter'
506 def mainloop_abort_on_exception
507 fail RuntimeError, 'not support "mainloop_abort_on_exception" on the remote interpreter'
509 def mainloop_abort_on_exception=(mode)
510 fail RuntimeError, 'not support "mainloop_abort_on_exception=" on the remote interpreter'
512 def set_eventloop_tick(*args)
513 fail RuntimeError, 'not support "set_eventloop_tick" on the remote interpreter'
515 def get_eventloop_tick
516 fail RuntimeError, 'not support "get_eventloop_tick" on the remote interpreter'
518 def set_no_event_wait(*args)
519 fail RuntimeError, 'not support "set_no_event_wait" on the remote interpreter'
521 def get_no_event_wait
522 fail RuntimeError, 'not support "get_no_event_wait" on the remote interpreter'
524 def set_eventloop_weight(*args)
525 fail RuntimeError, 'not support "set_eventloop_weight" on the remote interpreter'
527 def get_eventloop_weight
528 fail RuntimeError, 'not support "get_eventloop_weight" on the remote interpreter'