From 56761e29f655407aa7bbbf27072f9ff6bf055757 Mon Sep 17 00:00:00 2001 From: nagai Date: Fri, 21 May 2004 20:45:27 +0000 Subject: [PATCH] * ext/tk/lib/tk.rb: add Tk.appsend_deny and improve Tk.rb_appsend * ext/tk/lib/tk.rb, ext/tk/lib/tk/*.rb : replace obj.send() -> obj.__send__() * ext/tk/lib/remote-tk.rb: add a new library which create an object to control a Tk interpreter on the other process git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@6384 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 5 + ext/tk/README.fork | 7 +- ext/tk/lib/multi-tk.rb | 39 ++- ext/tk/lib/remote-tk.rb | 397 ++++++++++++++++++++++++++++++ ext/tk/lib/tk.rb | 81 ++++-- ext/tk/lib/tk/bindtag.rb | 3 +- ext/tk/lib/tk/canvastag.rb | 6 +- ext/tk/lib/tk/font.rb | 3 +- ext/tk/lib/tk/image.rb | 3 +- ext/tk/lib/tk/namespace.rb | 3 +- ext/tk/lib/tk/root.rb | 4 +- ext/tk/lib/tk/textmark.rb | 3 +- ext/tk/lib/tk/texttag.rb | 3 +- ext/tk/lib/tk/timer.rb | 3 +- ext/tk/lib/tk/toplevel.rb | 4 +- ext/tk/lib/tk/variable.rb | 3 +- ext/tk/lib/tk/virtevent.rb | 5 +- ext/tk/sample/remote-ip_sample.rb | 33 +++ 18 files changed, 562 insertions(+), 43 deletions(-) create mode 100644 ext/tk/lib/remote-tk.rb create mode 100644 ext/tk/sample/remote-ip_sample.rb diff --git a/ChangeLog b/ChangeLog index d0bb259616..2007656273 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Sat May 22 05:37:11 2004 Hidetoshi NAGAI + + * ext/tk/lib/remote-tk.rb: (NEW library) controll Tk interpreters + on the other processes by Tcl/Tk's 'send' command + Fri May 21 09:22:05 2004 Dave Thomas * lib/rdoc/parsers/parse_rb.rb (RDoc::RubyParser::parse_method_parameters): diff --git a/ext/tk/README.fork b/ext/tk/README.fork index cda89b003c..707d78fc12 100644 --- a/ext/tk/README.fork +++ b/ext/tk/README.fork @@ -4,6 +4,11 @@ process). In the library 'tk.rb', a Tk interpreter is initialized. Therefore, if you want running Tk under a child process, please call "require 'tk'" in the child process. +# If do fork and exec() on the child process, you can +# control Ruby/Tk interpreter on the child process by 'send' command +# of Tcl/Tk. About this, please see Tk.appsend and Tk.rb_appsend, or +# 'remote-tk.rb' and the sample 'sample/remote-ip_sample.rb'. + For example, the following sample1 will NOT work, and sample2 will work properly. @@ -26,4 +31,4 @@ TkButton.new(:text=>'QUIT', :command=>proc{exit}).pack Tk.mainloop ------------------------------------------------------------- - 2004/04/20 Hidetoshi NAGAI + 2004/05/22 Hidetoshi NAGAI diff --git a/ext/tk/lib/multi-tk.rb b/ext/tk/lib/multi-tk.rb index a3cd3857a0..ba1dc733ca 100644 --- a/ext/tk/lib/multi-tk.rb +++ b/ext/tk/lib/multi-tk.rb @@ -6,6 +6,10 @@ require 'tcltklib' require 'tkutil' require 'thread' +if defined? Tk + fail RuntimeError, "'multi-tk' library must be required before requiring 'tk'" +end + ################################################ # ignore exception on the mainloop? @@ -37,14 +41,14 @@ MultiTkIp_OK.freeze class MultiTkIp SLAVE_IP_ID = ['slave'.freeze, '0'.taint].freeze - @@IP_TABLE = {}.taint + @@IP_TABLE = {}.taint unless defined?(@@IP_TABLE) - @@INIT_IP_ENV = [].taint # table of Procs - @@ADD_TK_PROCS = [].taint # table of [name, args, body] + @@INIT_IP_ENV = [].taint unless defined?(@@INIT_IP_ENV) # table of Procs + @@ADD_TK_PROCS = [].taint unless defined?(@@ADD_TK_PROCS) # table of [name, args, body] - @@TK_TABLE_LIST = [].taint + @@TK_TABLE_LIST = [].taint unless defined?(@@TK_TABLE_LIST) - @@TK_CMD_TBL = {}.taint + @@TK_CMD_TBL = {}.taint unless defined?(@@TK_CMD_TBL) ###################################### @@ -603,6 +607,13 @@ end # get target IP class MultiTkIp + def self._ip_id_ + __getip._ip_id_ + end + def _ip_id_ + '' + end + def self.__getip if Thread.current.group == ThreadGroup::Default @@DEFAULT_MASTER @@ -636,7 +647,7 @@ class << MultiTkIp ip.eval_proc(&b) if b ip end - alias new_trusted_slave new_master + alias new_trusted_slave new_slave def new_safe_slave(keys={},&b) ip = __new(__getip, true, keys) @@ -798,7 +809,7 @@ class MultiTkIp @@TK_TABLE_LIST << obj obj.instance_eval <<-EOD def self.method_missing(m, *args) - MultiTkIp.tk_object_table(#{id}).send(m, *args) + MultiTkIp.tk_object_table(#{id}).__send__(m, *args) end EOD obj.freeze @@ -972,7 +983,7 @@ end # class methods to delegate to TclTkIp class << MultiTkIp def method_missing(id, *args) - __getip.send(id, *args) + __getip.__send__(id, *args) end def make_safe @@ -1184,6 +1195,14 @@ class MultiTkIp @interp.restart end + def __eval(str) + @interp.__eval(str) + end + + def __invoke(*args) + @interp.__invoke(*args) + end + def _eval(str) @interp._eval(str) end @@ -1555,7 +1574,7 @@ class MultiTkIp conf = _lst2ary(ip._eval("::safe::interpConfigure " + @ip_name + " -#{slot}")) if conf[0] == '-deleteHook' - if conf[1] =~ /^rb_out (c\d+)/ + if conf[1] =~ /^rb_out\S* (c(_\d+_)?\d+)/ ret[conf[0][1..-1]] = MultiTkIp._tk_cmd_tbl[$1] else ret[conf[0][1..-1]] = conf[1] @@ -1567,7 +1586,7 @@ class MultiTkIp Hash[*_lst2ary(ip._eval("::safe::interpConfigure " + @ip_name))].each{|k, v| if k == '-deleteHook' - if v =~ /^rb_out (c\d+)/ + if v =~ /^rb_out\S* (c(_\d+_)?\d+)/ ret[k[1..-1]] = MultiTkIp._tk_cmd_tbl[$1] else ret[k[1..-1]] = v diff --git a/ext/tk/lib/remote-tk.rb b/ext/tk/lib/remote-tk.rb new file mode 100644 index 0000000000..79fb20380c --- /dev/null +++ b/ext/tk/lib/remote-tk.rb @@ -0,0 +1,397 @@ +# +# remote-tk.rb - supports to control remote Tk interpreters +# by Hidetoshi NAGAI + +if defined? MultiTkIp + fail RuntimeError, "'remote-tk' library must be required before requiring 'multi-tk'" +end + +class MultiTkIp; end +class RemoteTkIp < MultiTkIp; end + +class MultiTkIp + @@IP_TABLE = {}.taint unless defined?(@@IP_TABLE) + @@TK_TABLE_LIST = {}.taint unless defined?(@@TK_TABLE_LIST) + def self._IP_TABLE; @@IP_TABLE; end + def self._TK_TABLE_LIST; @@TK_TABLE_LIST; end +end +class RemoteTkIp < MultiTkIp + @@IP_TABLE = MultiTkIp._IP_TABLE unless defined?(@@IP_TABLE) + @@TK_TABLE_LIST = MultiTkIp._TK_TABLE_LIST unless defined?(@@TK_TABLE_LIST) +end +class << MultiTkIp + undef _IP_TABLE + undef _TK_TABLE_LIST +end + +require 'multi-tk' + +############################### + +class RemoteTkIp < MultiTkIp + include TkUtil + + def initialize(remote_ip, displayof=nil) + if $SAFE >= 4 + fail SecurityError, "cannot access another interpreter at level #{$SAFE}" + end + + @interp = MultiTkIp.__getip + @appname = @interp._invoke('tk', 'appname') + @remote = remote_ip.dup.freeze + if displayof.kind_of?(TkWindow) + @displayof = displayof.path.dup.freeze + else + @displayof = nil + end + if self.deleted? + fail RuntimeError, "no Tk application named \"#{@remote}\"" + end + + @tk_windows = {} + @tk_table_list = [] + @slave_ip_tbl = {} + @slave_ip_top = {} + + @tk_windows.taint unless @tk_windows.tainted? + @tk_table_list.taint unless @tk_table_list.tainted? + @slave_ip_tbl.taint unless @slave_ip_tbl.tainted? + @slave_ip_top.taint unless @slave_ip_top.tainted? + + @system = Object.new + + @threadgroup = ThreadGroup.new + + @cmd_queue = Queue.new + + @cmd_receiver, @receiver_watchdog = _create_receiver_and_watchdog() + + @threadgroup.add @cmd_receiver + @threadgroup.add @receiver_watchdog + + @threadgroup.enclose + + @@IP_TABLE[@threadgroup] = self + @@TK_TABLE_LIST.size.times{ + (tbl = {}).tainted? || tbl.taint + @tk_table_list << tbl + } + + @ip_id = _create_connection + + self.freeze # defend against modification + end + + def _ip_id_ + @ip_id + end + + def _create_connection + ip_id = '_' + @interp._invoke('send', @remote, <<-'EOS') + '_' + if {[catch {set _rubytk_control_ip_id_} ret] != 0} { + set _rubytk_control_ip_id_ 0 + } else { + set _rubytk_control_ip_id_ [expr $ret + 1] + } + return $_rubytk_control_ip_id_ + EOS + + @interp._invoke('send', @remote, <<-EOS) + proc rb_out#{ip_id} args { + send #{@appname} rb_out \$args + } + EOS + + ip_id + end + private :_create_connection + + def _appsend(enc_mode, async, *cmds) + p ['_appsend', [@remote, @displayof], enc_mode, async, cmds] if $DEBUG + if $SAFE >= 4 + fail SecurityError, "cannot send commands at level 4" + elsif $SAFE >= 1 && cmds.find{|obj| obj.tainted?} + fail SecurityError, "cannot send tainted commands at level #{$SAFE}" + end + + cmds = @interp._merge_tklist(*_conv_args([], enc_mode, *cmds)) + if @displayof + if async + @interp.__invoke('send', '-async', '-displayof', @displayof, + '--', @remote, *cmds) + else + @interp.__invoke('send', '-displayof', @displayof, + '--', @remote, *cmds) + end + else + if async + @interp.__invoke('send', '-async', '--', @remote, *cmds) + else + @interp.__invoke('send', '--', @remote, *cmds) + end + end + end + private :_appsend + + def is_rubytk? + if _appsend(false, false, 'info', 'command', 'ruby') == "" + false + else + true + end + end + + def appsend(async, *args) + if async != true && async != false && async != nil + args.unshift(async) + async = false + end + if @displayof + Tk.appsend_displayof(@remote, @displayof, async, *args) + else + Tk.appsend(@remote, async, *args) + end + end + + def rb_appsend(async, *args) + if async != true && async != false && async != nil + args.unshift(async) + async = false + end + if @displayof + Tk.rb_appsend_displayof(@remote, @displayof, async, *args) + else + Tk.rb_appsend(@remote, async, *args) + end + end + + def create_slave(name, safe=false) + if safe + safe_opt = '' + else + safe_opt = '-safe' + end + _appsend(false, false, "interp create #{safe_opt} -- #{name}") + end + + def make_safe + fail RuntimeError, 'cannot change safe mode of the remote interpreter' + end + + def safe? + _appsend(false, false, 'interp issafe') + end + + def delete + _appsend(false, true, 'exit') + end + + def deleted? + if @displayof + lst = @interp._invoke_without_enc('winfo', 'interps', + '-displayof', @displayof) + else + lst = @interp._invoke_without_enc('winfo', 'interps') + end + unless @interp._split_tklist(lst).index(@remote) + true + else + false + end + end + + def restart + fail RuntimeError, 'cannot restart the remote interpreter' + end + + def __eval(str) + _appsend(false, false, str) + end + def _eval(str) + _appsend(nil, false, str) + end + def _eval_without_enc(str) + _appsend(false, false, str) + end + def _eval_with_enc(str) + _appsend(true, false, str) + end + + def _invoke(*args) + _appsend(nil, false, *args) + end + + def __invoke(*args) + _appsend(false, false, *args) + end + def _invoke(*args) + _appsend(nil, false, *args) + end + def _invoke_without_enc(*args) + _appsend(false, false, *args) + end + def _invoke_with_enc(*args) + _appsend(true, false, *args) + end + + def _toUTF8(str, encoding) + @interp._toUTF8(str, encoding) + end + + def _fromUTF8(str, encoding) + @interp._fromUTF8(str, encoding) + end + + def _thread_vwait(var_name) + _appsend(false, 'thread_vwait', varname) + end + + def _thread_tkwait(mode, target) + _appsend(false, 'thread_tkwait', mode, target) + end + + def _return_value + @interp._return_value + end + + def _get_variable(var_name, flag) + # ignore flag + _appsend(false, 'set', _get_eval_string(var_name)) + end + def _get_variable2(var_name, index_name, flag) + # ignore flag + _appsend(false, 'set', "#{_get_eval_string(var_name)}(#{_get_eval_string(index_name)})") + end + + def _set_variable(var_name, value, flag) + # ignore flag + _appsend(false, 'set', _get_eval_string(var_name), _get_eval_string(value)) + end + def _set_variable2(var_name, index_name, value, flag) + # ignore flag + _appsend(false, 'set', "#{_get_eval_string(var_name)}(#{_get_eval_string(index_name)})", _get_eval_string(value)) + end + + def _unset_variable(var_name, flag) + # ignore flag + _appsend(false, 'unset', _get_eval_string(var_name)) + end + def _unset_variable2(var_name, index_name, flag) + # ignore flag + _appsend(false, 'unset', "#{var_name}(#{index_name})") + end + + def _get_global_var(var_name) + _appsend(false, 'set', _get_eval_string(var_name)) + end + def _get_global_var2(var_name, index_name) + _appsend(false, 'set', "#{_get_eval_string(var_name)}(#{_get_eval_string(index_name)})") + end + + def _set_global_var(var_name, value) + _appsend(false, 'set', _get_eval_string(var_name), _get_eval_string(value)) + end + def _set_global_var2(var_name, index_name, value) + _appsend(false, 'set', "#{_get_eval_string(var_name)}(#{_get_eval_string(index_name)})", _get_eval_string(value)) + end + + def _unset_global_var(var_name) + _appsend(false, 'unset', _get_eval_string(var_name)) + end + def _unset_global_var2(var_name, index_name) + _appsend(false, 'unset', "#{var_name}(#{index_name})") + end + + def _split_tklist(str) + @interp._split_tklist(str) + end + + def _merge_tklist(*args) + @interp._merge_tklist(*args) + end + + def _conv_listelement(str) + @interp._conv_listelement(str) + end + + def mainloop + fail RuntimeError, 'not support "mainloop" on the remote interpreter' + end + def mainloop_watchdog + fail RuntimeError, 'not support "mainloop_watchdog" on the remote interpreter' + end + def do_one_evant(flag = nil) + fail RuntimeError, 'not support "do_one_event" on the remote interpreter' + end + def set_eventloop_tick(*args) + fail RuntimeError, 'not support "set_eventloop_tick" on the remote interpreter' + end + def get_eventloop_tick + fail RuntimeError, 'not support "get_eventloop_tick" on the remote interpreter' + end + def set_no_event_wait(*args) + fail RuntimeError, 'not support "set_no_event_wait" on the remote interpreter' + end + def get_no_event_wait + fail RuntimeError, 'not support "get_no_event_wait" on the remote interpreter' + end + def set_eventloop_weight(*args) + fail RuntimeError, 'not support "set_eventloop_weight" on the remote interpreter' + end + def get_eventloop_weight + fail RuntimeError, 'not support "get_eventloop_weight" on the remote interpreter' + end + def mainloop_abort_on_exception + fail RuntimeError, 'not support "mainloop_abort_on_exception" on the remote interpreter' + end + def mainloop_abort_on_exception=(*args) + fail RuntimeError, 'not support "mainloop_abort_on_exception=" on the remote interpreter' + end +end + +class << RemoteTkIp + undef new_master, new_slave, new_safe_slave + undef new_trusted_slave, new_safeTk + + def new(ip_name, displayof=nil, &b) + ip = __new(ip_name, displayof) + ip.eval_proc(&b) if b + ip + end +end + +class << RemoteTkIp + def mainloop + fail RuntimeError, 'not support "mainloop" on the remote interpreter' + end + def mainloop_watchdog + fail RuntimeError, 'not support "mainloop_watchdog" on the remote interpreter' + end + def do_one_evant(flag = nil) + fail RuntimeError, 'not support "do_one_event" on the remote interpreter' + end + def set_eventloop_tick(*args) + fail RuntimeError, 'not support "set_eventloop_tick" on the remote interpreter' + end + def get_eventloop_tick + fail RuntimeError, 'not support "get_eventloop_tick" on the remote interpreter' + end + def set_no_event_wait(*args) + fail RuntimeError, 'not support "set_no_event_wait" on the remote interpreter' + end + def get_no_event_wait + fail RuntimeError, 'not support "get_no_event_wait" on the remote interpreter' + end + def set_eventloop_weight(*args) + fail RuntimeError, 'not support "set_eventloop_weight" on the remote interpreter' + end + def get_eventloop_weight + fail RuntimeError, 'not support "get_eventloop_weight" on the remote interpreter' + end + def mainloop_abort_on_exception + fail RuntimeError, 'not support "mainloop_abort_on_exception" on the remote interpreter' + end + def mainloop_abort_on_exception=(*args) + fail RuntimeError, 'not support "mainloop_abort_on_exception=" on the remote interpreter' + end +end diff --git a/ext/tk/lib/tk.rb b/ext/tk/lib/tk.rb index f337bbb8b6..12d60245e7 100644 --- a/ext/tk/lib/tk.rb +++ b/ext/tk/lib/tk.rb @@ -14,6 +14,10 @@ class TclTkIp # backup original (without encoding) _eval and _invoke alias _eval_without_enc _eval alias _invoke_without_enc _invoke + + def _ip_id_ + '' + end end # define TkComm module (step 1: basic functions) @@ -36,12 +40,12 @@ module TkComm # for backward compatibility Tk_CMDTBL = Object.new def Tk_CMDTBL.method_missing(id, *args) - TkCore::INTERP.tk_cmd_tbl.send(id, *args) + TkCore::INTERP.tk_cmd_tbl.__send__(id, *args) end Tk_CMDTBL.freeze Tk_WINDOWS = Object.new def Tk_WINDOWS.method_missing(id, *args) - TkCore::INTERP.tk_windows.send(id, *args) + TkCore::INTERP.tk_windows.__send__(id, *args) end Tk_WINDOWS.freeze @@ -111,7 +115,7 @@ module TkComm module_function :_genobj_for_tkwidget def tk_tcl2ruby(val, enc_mode = nil) - if val =~ /^rb_out (c\d+)/ + if val =~ /^rb_out\S* (c(_\d+_)?\d+)/ #return Tk_CMDTBL[$1] return TkCore::INTERP.tk_cmd_tbl[$1] #cmd_obj = TkCore::INTERP.tk_cmd_tbl[$1] @@ -133,7 +137,7 @@ module TkComm #Tk_WINDOWS[val] ? Tk_WINDOWS[val] : _genobj_for_tkwidget(val) TkCore::INTERP.tk_windows[val]? TkCore::INTERP.tk_windows[val] : _genobj_for_tkwidget(val) - when /^i\d+$/ + when /^i(_\d+_)?\d+$/ TkImage::Tk_IMGTBL[val]? TkImage::Tk_IMGTBL[val] : val when /^-?\d+\.?\d*(e[-+]?\d+)?$/ val.to_f @@ -437,14 +441,14 @@ end end end def image_obj(val) - if val =~ /^i\d+$/ + if val =~ /^i(_\d+_)?\d+$/ TkImage::Tk_IMGTBL[val]? TkImage::Tk_IMGTBL[val] : val else val end end def procedure(val) - if val =~ /^rb_out (c\d+)/ + if val =~ /^rb_out\S* (c(_\d+_)?\d+)/ #Tk_CMDTBL[$1] #TkCore::INTERP.tk_cmd_tbl[$1] TkCore::INTERP.tk_cmd_tbl[$1].cmd @@ -591,7 +595,7 @@ end def _curr_cmd_id #id = format("c%.4d", Tk_IDs[0]) - id = "c" + TkComm::Tk_IDs[0] + id = "c" + TkCore::INTERP._ip_id_ + TkComm::Tk_IDs[0] end def _next_cmd_id id = _curr_cmd_id @@ -615,10 +619,10 @@ end @cmdtbl.taint unless @cmdtbl.tainted? @cmdtbl.push id #return Kernel.format("rb_out %s", id); - return 'rb_out ' + id + return 'rb_out' + TkCore::INTERP._ip_id_ + ' ' + id end def uninstall_cmd(id) - id = $1 if /rb_out (c\d+)/ =~ id + id = $1 if /rb_out\S* (c(_\d+_)?\d+)/ =~ id #Tk_CMDTBL.delete(id) TkCore::INTERP.tk_cmd_tbl.delete(id) end @@ -655,7 +659,7 @@ end return TkCore::INTERP.tk_windows[@path] = self end else - name = "w" + Tk_IDs[1] + name = "w" + TkCore::INTERP._ip_id_ + Tk_IDs[1] Tk_IDs[1].succ! end if !ppath or ppath == '.' @@ -736,7 +740,7 @@ module TkComm def _bindinfo(what, context=nil) if context tk_call_without_enc(*what+["<#{tk_event_sequence(context)}>"]) .collect {|cmdline| - if cmdline =~ /^rb_out (c\d+)\s+(.*)$/ + if cmdline =~ /^rb_out\S* (c(_\d+_)?\d+)\s+(.*)$/ #[Tk_CMDTBL[$1], $2] [TkCore::INTERP.tk_cmd_tbl[$1], $2] else @@ -876,7 +880,7 @@ module TkCore @id = id end def method_missing(m, *args, &b) - TkCore::INTERP.tk_object_table(@id).send(m, *args, &b) + TkCore::INTERP.tk_object_table(@id).__send__(m, *args, &b) end end @@ -1098,7 +1102,20 @@ module TkCore tk_call('tk', 'appname', name) end + def appsend_deny + tk_call('rename', 'send', '') + end + def appsend(interp, async, *args) + if $SAFE >= 4 + fail SecurityError, "cannot send Tk commands at level 4" + elsif $SAFE >= 1 && args.find{|obj| obj.tainted?} + fail SecurityError, "cannot send tainted Tk commands at level #{$SAFE}" + end + if async != true && async != false && async != nil + args.unshift(async) + async = false + end if async tk_call('send', '-async', '--', interp, *args) else @@ -1107,14 +1124,34 @@ module TkCore end def rb_appsend(interp, async, *args) + if $SAFE >= 4 + fail SecurityError, "cannot send Ruby commands at level 4" + elsif $SAFE >= 1 && args.find{|obj| obj.tainted?} + fail SecurityError, "cannot send tainted Ruby commands at level #{$SAFE}" + end + if async != true && async != false && async != nil + args.unshift(async) + async = false + end #args = args.collect!{|c| _get_eval_string(c).gsub(/[\[\]$"]/, '\\\\\&')} args = args.collect!{|c| _get_eval_string(c).gsub(/[\[\]$"\\]/, '\\\\\&')} - args.push(').to_s"') - appsend(interp, async, 'ruby "(', *args) + # args.push(').to_s"') + # appsend(interp, async, 'ruby "(', *args) + args.push('}.call)"') + appsend(interp, async, 'ruby "TkComm._get_eval_string(proc{', *args) end def appsend_displayof(interp, win, async, *args) + if $SAFE >= 4 + fail SecurityError, "cannot send Tk commands at level 4" + elsif $SAFE >= 1 && args.find{|obj| obj.tainted?} + fail SecurityError, "cannot send tainted Tk commands at level #{$SAFE}" + end win = '.' if win == nil + if async != true && async != false && async != nil + args.unshift(async) + async = false + end if async tk_call('send', '-async', '-displayof', win, '--', interp, *args) else @@ -1123,10 +1160,22 @@ module TkCore end def rb_appsend_displayof(interp, win, async, *args) + if $SAFE >= 4 + fail SecurityError, "cannot send Ruby commands at level 4" + elsif $SAFE >= 1 && args.find{|obj| obj.tainted?} + fail SecurityError, "cannot send tainted Ruby commands at level #{$SAFE}" + end + win = '.' if win == nil + if async != true && async != false && async != nil + args.unshift(async) + async = false + end #args = args.collect!{|c| _get_eval_string(c).gsub(/[\[\]$"]/, '\\\\\&')} args = args.collect!{|c| _get_eval_string(c).gsub(/[\[\]$"\\]/, '\\\\\&')} - args.push(').to_s"') - appsend_displayof(interp, win, async, 'ruby "(', *args) + # args.push(').to_s"') + # appsend_displayof(interp, win, async, 'ruby "(', *args) + args.push('}.call)"') + appsend(interp, win, async, 'ruby "TkComm._get_eval_string(proc{', *args) end def info(*args) diff --git a/ext/tk/lib/tk/bindtag.rb b/ext/tk/lib/tk/bindtag.rb index d309ea6423..adf96b9e3d 100644 --- a/ext/tk/lib/tk/bindtag.rb +++ b/ext/tk/lib/tk/bindtag.rb @@ -26,7 +26,8 @@ class TkBindTag end def initialize(*args, &b) - @id = Tk_BINDTAG_ID.join('') + # @id = Tk_BINDTAG_ID.join('') + @id = Tk_BINDTAG_ID.join(TkCore::INTERP._ip_id_) Tk_BINDTAG_ID[1].succ! BTagID_TBL[@id] = self bind(*args, &b) if args != [] diff --git a/ext/tk/lib/tk/canvastag.rb b/ext/tk/lib/tk/canvastag.rb index 962e08b6e0..5a98127f6b 100644 --- a/ext/tk/lib/tk/canvastag.rb +++ b/ext/tk/lib/tk/canvastag.rb @@ -191,7 +191,8 @@ class TkcTag".freeze].freeze + TkVirtualEventID = ["VirtEvent".freeze, "00000".taint].freeze TkVirtualEventTBL = TkCore::INTERP.create_table TkCore::INTERP.init_ip_env{ TkVirtualEventTBL.clear } @@ -41,7 +41,8 @@ class TkVirtualEvent' + @path = @id = '<' + TkVirtualEventID.join(TkCore::INTERP._ip_id_) + '>' TkVirtualEventID[1].succ! add(*sequences) end diff --git a/ext/tk/sample/remote-ip_sample.rb b/ext/tk/sample/remote-ip_sample.rb new file mode 100644 index 0000000000..3a2dbe4852 --- /dev/null +++ b/ext/tk/sample/remote-ip_sample.rb @@ -0,0 +1,33 @@ +#!/usr/bin/env ruby +require 'remote-tk' + +puts <proc{puts 'This procesure is on the controller-ip (Rubh/Tk)'}, :text=>'print on Ruby/Tk (controller-ip)').pack(:fill=>:x)} +ip.eval_proc{TkButton.new(:command=>'puts {This procesure is on the remote-ip (wish)}', :text=>'print on wish (remote-ip)').pack(:fill=>:x)} + +# If your remote-ip is Ruby/Tk, you can control the remote Ruby by +# 'ruby' or 'ruby_eval' or 'ruby_cmd' on the Tk interpreter. +if ip.is_rubytk? + ip.eval_proc{TkButton.new(:command=>'ruby {p 111; p Array.new(3,"ruby")}', :text=>'ruby cmd on the remote-ip').pack(:fill=>:x)} +end + +ip.eval_proc{TkButton.new(:command=>'exit', :text=>'QUIT').pack(:fill=>:x)} + +TkButton.new(:command=>proc{exit}, :text=>'QUIT', + :padx=>10, :pady=>7).pack(:padx=>10, :pady=>7) + +Tk.mainloop