class MultiTkIp

methods for construction

get target IP

get info

instance methods to treat tables

class methods to treat tables

for callback operation

pseudo-toplevel operation support

Constants

BASE_DIR
RUN_EVENTLOOP_ON_MAIN_THREAD

Ruby 1.9 !!!!!!!!!!!!!!!!!!!!!!!!!!

WITH_ENCODING
WITH_RUBY_VM

Attributes

cmd[R]
ip[R]
ret[RW]
target[RW]

Public Class Methods

_DEFAULT_MASTER() click to toggle source
# File lib/remote-tk.rb, line 21
def self._DEFAULT_MASTER
  # work only once
  if @flag
    @flag = nil
    @@DEFAULT_MASTER
  else
    nil
  end
end
_IP_TABLE() click to toggle source
# File lib/remote-tk.rb, line 17
def self._IP_TABLE; @@IP_TABLE; end
_TK_TABLE_LIST() click to toggle source
# File lib/remote-tk.rb, line 18
def self._TK_TABLE_LIST; @@TK_TABLE_LIST; end
__getip() click to toggle source
# File lib/multi-tk.rb, line 1459
def self.__getip
  current = Thread.current
  if current.kind_of?(@@CALLBACK_SUBTHREAD)
    return current[:callback_ip]
  end
  if TclTkLib.mainloop_thread? != false && current[:callback_ip]
    return current[:callback_ip]
  end
  if current.group == ThreadGroup::Default
    @@DEFAULT_MASTER
  else
    ip = @@IP_TABLE[current.group]
    unless ip
      fail SecurityError,
        "cannot call Tk methods on #{Thread.current.inspect}"
    end
    ip
  end
end
__init_ip_env__(table, script) click to toggle source
# File lib/multi-tk.rb, line 943
def self.__init_ip_env__(table, script)
  ret = []
  mtx  = (Thread.current[:MultiTk_ip_Mutex] ||= Mutex.new)
  cond = (Thread.current[:MultiTk_ip_CondVar] ||= ConditionVariable.new)
  mtx.synchronize{
    @init_ip_env_queue.enq([mtx, cond, ret, table, script])
    cond.wait(mtx)
  }
  if ret[0].kind_of?(Exception)
    raise ret[0]
  else
    ret[0]
  end
end
__pseudo_toplevel() click to toggle source
# File lib/multi-tk.rb, line 833
def self.__pseudo_toplevel
  Thread.current.group == ThreadGroup::Default &&
    MultiTkIp.__getip == @@DEFAULT_MASTER &&
    self.__pseudo_toplevel_evaluable? && @pseudo_toplevel[1]
end
__pseudo_toplevel=(m) click to toggle source
# File lib/multi-tk.rb, line 839
def self.__pseudo_toplevel=(m)
  unless (Thread.current.group == ThreadGroup::Default &&
            MultiTkIp.__getip == @@DEFAULT_MASTER)
    fail SecurityError, "no permission to manipulate"
  end

  # if m.kind_of?(Module) && m.respond_to?(:pseudo_toplevel_evaluable?)
  if m.respond_to?(:pseudo_toplevel_evaluable?)
    @pseudo_toplevel[0] = true
    @pseudo_toplevel[1] = m
  else
    fail ArgumentError, 'fail to set pseudo-toplevel'
  end
  self
end
__pseudo_toplevel_evaluable=(mode) click to toggle source
# File lib/multi-tk.rb, line 863
def self.__pseudo_toplevel_evaluable=(mode)
  unless (Thread.current.group == ThreadGroup::Default &&
            MultiTkIp.__getip == @@DEFAULT_MASTER)
    fail SecurityError, "no permission to manipulate"
  end

  @pseudo_toplevel[0] = (mode)? true: false
end
__pseudo_toplevel_evaluable?() click to toggle source
# File lib/multi-tk.rb, line 855
def self.__pseudo_toplevel_evaluable?
  begin
    @pseudo_toplevel[0] && @pseudo_toplevel[1].pseudo_toplevel_evaluable?
  rescue Exception
    false
  end
end
_ip_id_() click to toggle source
# File lib/multi-tk.rb, line 1451
def self._ip_id_
  __getip._ip_id_
end
add_tk_procs(name, args=nil, body=nil) click to toggle source
# File lib/multi-tk.rb, line 1800
def self.add_tk_procs(name, args=nil, body=nil)
  if name.kind_of?(Array) # => an array of [name, args, body]
    name.each{|param| self.add_tk_procs(*param)}
  else
    name = name.to_s
    @@ADD_TK_PROCS << [name, args, body]
    @@IP_TABLE.each{|tg, ip|
      ip._add_tk_procs(name, args, body)
    }
  end
end
alive?() click to toggle source
# File lib/multi-tk.rb, line 1612
def self.alive?
  __getip.alive?
end
assign_receiver_and_watchdog(target) click to toggle source
# File lib/multi-tk.rb, line 911
def self.assign_receiver_and_watchdog(target)
  ret = [nil]
  @assign_thread.raise(@assign_request.new(target, ret))
  while ret[0] == nil
    unless @assign_thread.alive?
      raise RuntimeError, 'lost the thread to assign a receiver and a watchdog thread'
    end
  end
  if ret[0].kind_of?(Exception)
    raise ret[0]
  else
    ret[0]
  end
end
cb_entry_class() click to toggle source
# File lib/multi-tk.rb, line 1831
def self.cb_entry_class
  @@CB_ENTRY_CLASS
end
create_table() click to toggle source
# File lib/multi-tk.rb, line 1742
def self.create_table
  if __getip.slave?
    begin
      raise SecurityError, "slave-IP has no permission creating a new table"
    rescue SecurityError => e
      #p e.backtrace
      # Is called on a Ruby/Tk library?
      caller_info = e.backtrace[1]
      if caller_info =~ %r{^#{MultiTkIp::BASE_DIR}/(tk|tkextlib)/[^:]+\.rb:}
        # Probably, caller is a Ruby/Tk library  -->  allow creating
      else
        raise e
      end
    end
  end

  id = @@TK_TABLE_LIST.size
  obj = Object.new
  @@TK_TABLE_LIST << obj
  obj.instance_variable_set(:@id, id)
  obj.instance_variable_set(:@mutex, Mutex.new)
  obj.instance_eval{
    def self.mutex
      @mutex
    end
    def self.method_missing(m, *args)
      MultiTkIp.tk_object_table(@id).__send__(m, *args)
    end
  }
  obj.freeze
  @@IP_TABLE.each{|tg, ip| ip._add_new_tables }
  return obj
end
get_cb_entry(cmd) click to toggle source
# File lib/multi-tk.rb, line 1834
def self.get_cb_entry(cmd)
  @@CB_ENTRY_CLASS.new(__getip, cmd).freeze
end
inherited(subclass) click to toggle source
# File lib/multi-tk.rb, line 969
def self.inherited(subclass)
  # trust if on ThreadGroup::Default or @@DEFAULT_MASTER's ThreadGroup
  if @@IP_TABLE[Thread.current.group] == @@DEFAULT_MASTER
    begin
      class << subclass
        self.methods.each{|m|
          name = m.to_s
          begin
            unless name == '__id__' || name == '__send__' || name == 'freeze'
              undef_method(m)
            end
          rescue Exception
            # ignore all exceptions
          end
        }
      end
    ensure
      subclass.freeze
      fail SecurityError,
        "cannot create subclass of MultiTkIp on a untrusted ThreadGroup"
    end
  end
end
init_ip_env(script = nil, &block) click to toggle source
# File lib/multi-tk.rb, line 1776
def self.init_ip_env(script = nil, &block)
  script ||= block
  @@INIT_IP_ENV << script
  if __getip.slave?
    begin
      raise SecurityError, "slave-IP has no permission initializing IP env"
    rescue SecurityError => e
      #p e.backtrace
      # Is called on a Ruby/Tk library?
      caller_info = e.backtrace[1]
      if caller_info =~ %r{^#{MultiTkIp::BASE_DIR}/(tk|tkextlib)/[^:]+\.rb:}
        # Probably, caller is a Ruby/Tk library  -->  allow creating
      else
        raise e
      end
    end
  end

  # @@IP_TABLE.each{|tg, ip|
  #   ip._init_ip_env(script)
  # }
  @@DEFAULT_MASTER.__init_ip_env__(@@IP_TABLE, script)
end
init_ip_internal() click to toggle source
# File lib/multi-tk.rb, line 1824
def self.init_ip_internal
  __getip._init_ip_internal(@@INIT_IP_ENV, @@ADD_TK_PROCS)
end
ip_name() click to toggle source
# File lib/multi-tk.rb, line 1625
def self.ip_name
  __getip.ip_name
end
mainloop(check_root = true) click to toggle source
# File lib/multi-tk.rb, line 787
def self.mainloop(check_root = true)
  begin
    TclTkLib.set_eventloop_window_mode(true)
    @interp_thread.value
  ensure
    TclTkLib.set_eventloop_window_mode(false)
  end
end
manipulable?() click to toggle source
# File lib/multi-tk.rb, line 1656
def self.manipulable?
  true
end
master?() click to toggle source
# File lib/multi-tk.rb, line 1590
def self.master?
  __getip.master?
end
method_missing(m, *args) click to toggle source
# File lib/multi-tk.rb, line 1767
def self.method_missing(m, *args)
  MultiTkIp.tk_object_table(@id).__send__(m, *args)
end
mutex() click to toggle source
# File lib/multi-tk.rb, line 1764
def self.mutex
  @mutex
end
new(ip, cmd) click to toggle source
# File lib/multi-tk.rb, line 186
def initialize(ip, cmd)
  @ip = ip
  @safe = safe = 0
  # @cmd = cmd
  cmd = MultiTkIp._proc_on_safelevel(&cmd)
  @cmd = proc{|*args| cmd.call(safe, *args)}
  self.freeze
end
Also aliased as: __new
new(target, ret) click to toggle source
Calls superclass method
# File lib/multi-tk.rb, line 875
def self.new(target, ret)
  obj = super()
  obj.target = target
  obj.ret = ret
  obj
end
new_master(_safe=nil, keys={}, &blk) click to toggle source
# File lib/multi-tk.rb, line 1486
def new_master(_safe=nil, keys={}, &blk)
  if MultiTkIp::WITH_RUBY_VM
    #### TODO !!!!!!
    fail RuntimeError,
         'sorry, still not support multiple master-interpreters on RubyVM'
  end

  if safe.kind_of?(Hash)
    keys = safe
  elsif safe.kind_of?(Integer)
    raise ArgumentError, "unexpected argument(s)" unless keys.kind_of?(Hash)
    if !keys.key?(:safe) && !keys.key?('safe')
      keys[:safe] = safe
    end
  elsif safe == nil
    # do nothing
  else
    raise ArgumentError, "unexpected argument(s)"
  end

  ip = __new(__getip, nil, keys)
  if block_given?
    ip._proc_on_safelevel(&blk).call(0)
  end
  ip
end
Also aliased as: new
new_safeTk(safe=1, keys={}, &blk)
Alias for: new_safe_slave
new_safe_slave(safe=1, keys={}, &blk) click to toggle source
# File lib/multi-tk.rb, line 1537
def new_safe_slave(safe=1, keys={}, &blk)
  if safe.kind_of?(Hash)
    keys = safe
  elsif safe.kind_of?(Integer)
    raise ArgumentError, "unexpected argument(s)" unless keys.kind_of?(Hash)
    if !keys.key?(:safe) && !keys.key?('safe')
      keys[:safe] = safe
    end
  else
    raise ArgumentError, "unexpected argument(s)"
  end

  ip = __new(__getip, true, keys)
  if block_given?
    ip._proc_on_safelevel(&blk).call(0)
  end
  ip
end
Also aliased as: new_safeTk
new_slave(safe=nil, keys={}, &blk) click to toggle source
# File lib/multi-tk.rb, line 1515
def new_slave(safe=nil, keys={}, &blk)
  if safe.kind_of?(Hash)
    keys = safe
  elsif safe.kind_of?(Integer)
    raise ArgumentError, "unexpected argument(s)" unless keys.kind_of?(Hash)
    if !keys.key?(:safe) && !keys.key?('safe')
      keys[:safe] = safe
    end
  elsif safe == nil
    # do nothing
  else
    raise ArgumentError, "unexpected argument(s)"
  end

  ip = __new(__getip, false, keys)
  if block_given?
    ip._proc_on_safelevel(&blk).call(0)
  end
  ip
end
Also aliased as: new_trusted_slave
new_trusted_slave(safe=nil, keys={}, &blk)
Alias for: new_slave
path() click to toggle source
# File lib/multi-tk.rb, line 1619
def self.path
  __getip.path
end
remove_tk_procs(*names) click to toggle source
# File lib/multi-tk.rb, line 1812
def self.remove_tk_procs(*names)
  names.each{|name|
    name = name.to_s
    @@ADD_TK_PROCS.delete_if{|elem|
      elem.kind_of?(Array) && elem[0].to_s == name
    }
  }
  @@IP_TABLE.each{|tg, ip|
    ip._remove_tk_procs(*names)
  }
end
safe_level() click to toggle source
# File lib/multi-tk.rb, line 330
def self.safe_level
  0
end
safe_level=(safe) click to toggle source
# File lib/multi-tk.rb, line 324
def self.safe_level=(safe)
  0
end
set_safe_level(safe) click to toggle source
# File lib/multi-tk.rb, line 321
def self.set_safe_level(safe)
  0
end
slave?() click to toggle source
# File lib/multi-tk.rb, line 1597
def self.slave?
  not self.master?
end
slaves(all = false) click to toggle source
# File lib/multi-tk.rb, line 1647
def self.slaves(all = false)
  __getip.slaves(all)
end
table() click to toggle source
# File lib/multi-tk.rb, line 1446
def self.table
  @table
end
tk_cmd_tbl() click to toggle source
# File lib/multi-tk.rb, line 1733
def self.tk_cmd_tbl
  @@TK_CMD_TBL
end
tk_object_table(id) click to toggle source
# File lib/multi-tk.rb, line 1739
def self.tk_object_table(id)
  __getip._tk_table_list[id]
end
tk_windows() click to toggle source
# File lib/multi-tk.rb, line 1736
def self.tk_windows
  __getip._tk_windows
end
to_eval() click to toggle source
# File lib/multi-tk.rb, line 1631
def self.to_eval
  __getip.to_eval
end

Private Class Methods

__new(ip, cmd)
Alias for: new

Public Instance Methods

__create_safe_slave_obj(safe_opts, app_name, tk_opts) click to toggle source
# File lib/multi-tk.rb, line 1176
def __create_safe_slave_obj(safe_opts, app_name, tk_opts)
  raise SecurityError, "no permission to manipulate" unless self.manipulable?

  # safe interpreter
  ip_name = _create_slave_ip_name
  slave_ip = @interp.create_slave(ip_name, true)
  slave_ip.instance_eval{
    @force_default_encoding ||= [false]
    @encoding ||= [nil]
    def @encoding.to_s; self.join(nil); end
  }
  @slave_ip_tbl[ip_name] = slave_ip
  def slave_ip.safe_base?
    true
  end

  @interp._eval("::safe::interpInit #{ip_name}")

  slave_ip._invoke('set', 'argv0', app_name) if app_name.kind_of?(String)

  if tk_opts
    tk_opts = __check_safetk_optkeys(tk_opts)
    if tk_opts.key?('use')
      @slave_ip_top[ip_name] = ''
    else
      tk_opts, top_path = __create_safetk_frame(slave_ip, ip_name, app_name,
                                                tk_opts)
      @slave_ip_top[ip_name] = top_path
    end
    @interp._eval("::safe::loadTk #{ip_name} #{_keys2opts(tk_opts)}")
    @interp._invoke('__replace_slave_tk_commands__', ip_name)
  else
    @slave_ip_top[ip_name] = nil
  end

  if safe_opts.key?('deleteHook') || safe_opts.key?(:deleteHook)
    @interp._eval("::safe::interpConfigure #{ip_name} " +
                  _keys2opts(safe_opts))
  else
    @interp._eval("::safe::interpConfigure #{ip_name} " +
                  _keys2opts(safe_opts) + '-deleteHook {' +
                  TkComm._get_eval_string(proc{|slave|
                                            self._default_delete_hook(slave)
                                          }) + '}')
  end

  [slave_ip, ip_name]
end
__create_trusted_slave_obj(name, keys) click to toggle source
# File lib/multi-tk.rb, line 1225
def __create_trusted_slave_obj(name, keys)
  raise SecurityError, "no permission to manipulate" unless self.manipulable?

  ip_name = _create_slave_ip_name
  slave_ip = @interp.create_slave(ip_name, false)
  slave_ip.instance_eval{
    @force_default_encoding ||= [false]
    @encoding ||= [nil]
    def @encoding.to_s; self.join(nil); end
  }
  slave_ip._invoke('set', 'argv0', name) if name.kind_of?(String)
  slave_ip._invoke('set', 'argv', _keys2opts(keys))
  @interp._invoke('load', '', 'Tk', ip_name)
  @interp._invoke('__replace_slave_tk_commands__', ip_name)
  @slave_ip_tbl[ip_name] = slave_ip
  [slave_ip, ip_name]
end
__pseudo_toplevel() click to toggle source

instance method

# File lib/multi-tk.rb, line 1849
def __pseudo_toplevel
  ip = MultiTkIp.__getip
  (ip == @@DEFAULT_MASTER || ip == self) &&
    self.__pseudo_toplevel_evaluable? && @pseudo_toplevel[1]
end
__pseudo_toplevel=(m) click to toggle source
# File lib/multi-tk.rb, line 1855
def __pseudo_toplevel=(m)
  unless (Thread.current.group == ThreadGroup::Default &&
            MultiTkIp.__getip == @@DEFAULT_MASTER)
    fail SecurityError, "no permission to manipulate"
  end

  # if m.kind_of?(Module) && m.respond_to?(:pseudo_toplevel_evaluable?)
  if m.respond_to?(:pseudo_toplevel_evaluable?)
    @pseudo_toplevel[0] = true
    @pseudo_toplevel[1] = m
  else
    fail ArgumentError, 'fail to set pseudo-toplevel'
  end
  self
end
__pseudo_toplevel_evaluable=(mode) click to toggle source
# File lib/multi-tk.rb, line 1879
def __pseudo_toplevel_evaluable=(mode)
  unless (Thread.current.group == ThreadGroup::Default &&
            MultiTkIp.__getip == @@DEFAULT_MASTER)
    fail SecurityError, "no permission to manipulate"
  end

  @pseudo_toplevel[0] = (mode)? true: false
end
__pseudo_toplevel_evaluable?() click to toggle source
# File lib/multi-tk.rb, line 1871
def __pseudo_toplevel_evaluable?
  begin
    @pseudo_toplevel[0] && @pseudo_toplevel[1].pseudo_toplevel_evaluable?
  rescue Exception
    false
  end
end
_add_new_tables() click to toggle source
# File lib/multi-tk.rb, line 1683
def _add_new_tables
  (@@TK_TABLE_LIST.size - @tk_table_list.size).times{
    @tk_table_list << {}
  }
end
_add_tk_procs(name, args, body) click to toggle source
# File lib/multi-tk.rb, line 1693
def _add_tk_procs(name, args, body)
  return if slave?
  @interp._invoke('proc', name, args, body) if args && body
  @interp._invoke('interp', 'slaves').split.each{|slave|
    @interp._invoke('interp', 'alias', slave, name, '', name)
  }
end
_create_slave_object(keys={}) click to toggle source
# File lib/multi-tk.rb, line 1245
def _create_slave_object(keys={})
  raise SecurityError, "no permission to manipulate" unless self.manipulable?

  ip = MultiTkIp.new_slave(self, keys={})
  @slave_ip_tbl[ip.name] = ip
end
_default_delete_hook(slave) click to toggle source
# File lib/multi-tk.rb, line 1408
def _default_delete_hook(slave)
  raise SecurityError, "no permission to manipulate" unless self.manipulable?
  @slave_ip_tbl.delete(slave)
  top = @slave_ip_top.delete(slave)
  if top.kind_of?(String)
    # call default hook of safetk.tcl (ignore exceptions)
    if top == ''
      begin
        @interp._eval("::safe::disallowTk #{slave}")
      rescue
        warn("Warning: fail to call '::safe::disallowTk'") if $DEBUG
      end
    else # toplevel path
      begin
        @interp._eval("::safe::tkDelete {} #{top} #{slave}")
      rescue
        warn("Warning: fail to call '::safe::tkDelete'") if $DEBUG
        begin
          @interp._eval("destroy #{top}")
        rescue
          warn("Warning: fail to destroy toplevel") if $DEBUG
        end
      end
    end
  end
end
_destroy_slaves_of_slaveIP(ip) click to toggle source
# File lib/multi-tk.rb, line 345
  def _destroy_slaves_of_slaveIP(ip)
    unless ip.deleted?
      # ip._split_tklist(ip._invoke('interp', 'slaves')).each{|name|
      ip._split_tklist(ip._invoke_without_enc('interp', 'slaves')).each{|name|
        name = _fromUTF8(name)
        begin
          # ip._eval_without_enc("#{name} eval {foreach i [after info] {after cancel $i}}")
          after_ids = ip._eval_without_enc("#{name} eval {after info}")
          ip._eval_without_enc("#{name} eval {foreach i {#{after_ids}} {after cancel $i}}")
        rescue Exception
        end
        begin
          # ip._invoke('interp', 'eval', name, 'destroy', '.')
          ip._invoke(name, 'eval', 'destroy', '.')
        rescue Exception
        end

        # safe_base?
        if ip._eval_without_enc("catch {::safe::interpConfigure #{name}}") == '0'
          begin
            ip._eval_without_enc("::safe::interpDelete #{name}")
          rescue Exception
          end
        end
=begin
        if ip._invoke('interp', 'exists', name) == '1'
          begin
            ip._invoke(name, 'eval', 'exit')
          rescue Exception
          end
        end
=end
        unless ip.deleted?
          if ip._invoke('interp', 'exists', name) == '1'
            begin
              ip._invoke('interp', 'delete', name)
            rescue Exception
            end
          end
        end
      }
    end
  end
_init_ip_env(script) click to toggle source
# File lib/multi-tk.rb, line 1689
def _init_ip_env(script)
  self.eval_proc{script.call(self)}
end
_init_ip_internal(init_ip_env, add_tk_procs) click to toggle source
# File lib/multi-tk.rb, line 1717
def _init_ip_internal(init_ip_env, add_tk_procs)
  #init_ip_env.each{|script| self.eval_proc{script.call(self)}}
  init_ip_env.each{|script| self._init_ip_env(script)}
  add_tk_procs.each{|name, args, body|
    if master?
      @interp._invoke('proc', name, args, body) if args && body
    else
      @set_alias_proc.call(name)
    end
  }
end
_ip_id_() click to toggle source
# File lib/multi-tk.rb, line 1454
def _ip_id_
  # for RemoteTkIp
  ''
end
_receiver_mainloop(check_root) click to toggle source
# File lib/multi-tk.rb, line 593
def _receiver_mainloop(check_root)
  if @evloop_thread[0] && @evloop_thread[0].alive?
    @evloop_thread[0]
  else
    @evloop_thread[0] = Thread.new{
      while !@interp.deleted?
        #if check_root
        #  inf = @interp._invoke_without_enc('info', 'command', '.')
        #  break if !inf.kind_of?(String) || inf != '.'
        #end
        break if check_root && !@interp.has_mainwindow?
        sleep 0.5
      end
    }
    @evloop_thread[0]
  end
end
_remove_tk_procs(*names) click to toggle source
# File lib/multi-tk.rb, line 1701
def _remove_tk_procs(*names)
  return if slave?
  names.each{|name|
    name = name.to_s

    return if @interp.deleted?
    @interp._invoke('rename', name, '')

    return if @interp.deleted?
    @interp._invoke('interp', 'slaves').split.each{|slave|
      return if @interp.deleted?
      @interp._invoke('interp', 'alias', slave, name, '') rescue nil
    }
  }
end
_tk_cmd_tbl() click to toggle source
# File lib/multi-tk.rb, line 1669
def _tk_cmd_tbl
  tbl = {}
  MultiTkIp.tk_cmd_tbl.each{|id, ent| tbl[id] = ent if ent.ip == self }
  tbl
end
_tk_table_list() click to toggle source
# File lib/multi-tk.rb, line 1679
def _tk_table_list
  @tk_table_list
end
_tk_windows() click to toggle source
# File lib/multi-tk.rb, line 1675
def _tk_windows
  @tk_windows
end
alive?() click to toggle source
# File lib/multi-tk.rb, line 1601
def alive?
  raise SecurityError, "no permission to manipulate" unless self.manipulable?
  begin
    return false unless @cmd_receiver.alive?
    return false if @interp.deleted?
    return false if @interp._invoke('interp', 'exists', '') == '0'
  rescue Exception
    return false
  end
  true
end
call(*args) click to toggle source
# File lib/multi-tk.rb, line 198
def call(*args)
  unless @ip.deleted?
    current = Thread.current
    backup_ip = current[:callback_ip]
    current[:callback_ip] = @ip
    begin
      ret = @ip.cb_eval(@cmd, *args)
      fail ret if ret.kind_of?(Exception)
      ret
    rescue TkCallbackBreak, TkCallbackContinue => e
      fail e
    rescue SecurityError => e
      # in 'exit', 'exit!', and 'abort' : security error --> delete IP
      if e.backtrace[0] =~ /^(.+?):(\d+):in `(exit|exit!|abort)'/
        @ip.delete
      elsif @ip.safe?
        if @ip.respond_to?(:cb_error)
          @ip.cb_error(e)
        else
          nil # ignore
        end
      else
        fail e
      end
    rescue Exception => e
      fail e if e.message =~ /^TkCallback/

      if @ip.safe?
        if @ip.respond_to?(:cb_error)
          @ip.cb_error(e)
        else
          nil # ignore
        end
      else
        fail e
      end
    ensure
      current[:callback_ip] = backup_ip
    end
  end
end
cb_error(e) click to toggle source
# File lib/multi-tk.rb, line 307
def cb_error(e)
  if @cb_error_proc[0].respond_to?(:call)
    @cb_error_proc[0].call(e)
  end
end
cb_eval(cmd, *args) click to toggle source
# File lib/multi-tk.rb, line 1838
def cb_eval(cmd, *args)
  self.eval_callback(*args,
                     &_proc_on_safelevel{|*params|
                       TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *params))
                     })
end
inspect() click to toggle source
# File lib/multi-tk.rb, line 195
def inspect
  cmd.inspect
end
ip_name() click to toggle source
# File lib/multi-tk.rb, line 1622
def ip_name
  @ip_name || ''
end
manipulable?() click to toggle source
# File lib/multi-tk.rb, line 1651
def manipulable?
  return true if (Thread.current.group == ThreadGroup::Default)
  ip = MultiTkIp.__getip
  (ip == self) || ip._is_master_of?(@interp)
end
master?() click to toggle source
# File lib/multi-tk.rb, line 1583
def master?
  if @ip_name
    false
  else
    true
  end
end
mutex() click to toggle source
# File lib/multi-tk.rb, line 92
def mutex; @mutex; end
path() click to toggle source
# File lib/multi-tk.rb, line 1616
def path
  @ip_name || ''
end
running_mainloop?() click to toggle source
# File lib/multi-tk.rb, line 341
def running_mainloop?
  @wait_on_mainloop[1] > 0
end
safe_level() click to toggle source
# File lib/multi-tk.rb, line 327
def safe_level
  0
end
safe_level=(safe) click to toggle source
# File lib/multi-tk.rb, line 318
def safe_level=(safe)
  0
end
set_cb_error(cmd = nil, &block) click to toggle source
# File lib/multi-tk.rb, line 303
def set_cb_error(cmd = nil, &block)
  @cb_error_proc[0] = cmd || block
end
set_safe_level(safe) click to toggle source
# File lib/multi-tk.rb, line 315
def set_safe_level(safe)
  0
end
slave?() click to toggle source
# File lib/multi-tk.rb, line 1594
def slave?
  not master?
end
slaves(all = false) click to toggle source
# File lib/multi-tk.rb, line 1635
def slaves(all = false)
  raise SecurityError, "no permission to manipulate" unless self.manipulable?
  @interp._invoke('interp','slaves').split.map!{|name|
    if @slave_ip_tbl.key?(name)
      @slave_ip_tbl[name]
    elsif all
      name
    else
      nil
    end
  }.compact!
end
to_eval() click to toggle source
# File lib/multi-tk.rb, line 1628
def to_eval
  @ip_name || ''
end
wait_on_mainloop=(bool) click to toggle source
# File lib/multi-tk.rb, line 337
def wait_on_mainloop=(bool)
  @wait_on_mainloop[0] = bool
end
wait_on_mainloop?() click to toggle source
# File lib/multi-tk.rb, line 334
def wait_on_mainloop?
  @wait_on_mainloop[0]
end

Protected Instance Methods

_is_master_of?(tcltkip_obj) click to toggle source
# File lib/multi-tk.rb, line 1660
def _is_master_of?(tcltkip_obj)
  tcltkip_obj.slave_of?(@interp)
end

Private Instance Methods

__check_safetk_optkeys(optkeys) click to toggle source
# File lib/multi-tk.rb, line 1040
def __check_safetk_optkeys(optkeys)
  # based on 'safetk.tcl'
  new_keys = {}
  optkeys.each{|k,v| new_keys[k.to_s] = v}

  # check 'display'
  if !new_keys.key?('display')
    begin
      #new_keys['display'] = @interp._invoke('winfo screen .')
      new_keys['display'] = @interp._invoke('winfo', 'screen', '.')
    rescue
      if ENV[DISPLAY]
        new_keys['display'] = ENV[DISPLAY]
      elsif !new_keys.key?('use')
        warn "Warning: no screen info or ENV[DISPLAY], so use ':0.0'"
        new_keys['display'] = ':0.0'
      end
    end
  end

  # check 'use'
  if new_keys.key?('use')
    # given 'use'
    case new_keys['use']
    when TkWindow
      new_keys['use'] = TkWinfo.id(new_keys['use'])
      #assoc_display = @interp._eval('winfo screen .')
      assoc_display = @interp._invoke('winfo', 'screen', '.')
    when /^\..*/
      new_keys['use'] = @interp._invoke('winfo', 'id', new_keys['use'])
      assoc_display = @interp._invoke('winfo', 'screen', new_keys['use'])
    else
      begin
        pathname = @interp._invoke('winfo', 'pathname', new_keys['use'])
        assoc_display = @interp._invoke('winfo', 'screen', pathname)
      rescue
        assoc_display = new_keys['display']
      end
    end

    # match display?
    if assoc_display != new_keys['display']
      if optkeys.key?(:display) || optkeys.key?('display')
        fail RuntimeError,
          "conflicting 'display'=>#{new_keys['display']} " +
          "and display '#{assoc_display}' on 'use'=>#{new_keys['use']}"
      else
        new_keys['display'] = assoc_display
      end
    end
  end

  # return
  new_keys
end
__create_safetk_frame(slave_ip, slave_name, app_name, keys) click to toggle source
# File lib/multi-tk.rb, line 1097
def __create_safetk_frame(slave_ip, slave_name, app_name, keys)
  # display option is used by ::safe::loadTk
  loadTk_keys = {}
  loadTk_keys['display'] = keys['display']
  dup_keys = keys.dup

  # keys for toplevel : allow followings
  toplevel_keys = {}
  ['height', 'width', 'background', 'menu'].each{|k|
    toplevel_keys[k] = dup_keys.delete(k) if dup_keys.key?(k)
  }
  toplevel_keys['classname'] = 'SafeTk'
  toplevel_keys['screen'] = dup_keys.delete('display')

  # other keys used by pack option of container frame

  # create toplevel widget
  begin
    top = TkToplevel.new(toplevel_keys)
  rescue NameError => e
    fail e unless @interp.safe?
    fail SecurityError, "unable create toplevel on the safe interpreter"
  end
  msg = "Untrusted Ruby/Tk applet (#{slave_name})"
  if app_name.kind_of?(String)
    top.title "#{app_name} (#{slave_name})"
  else
    top.title msg
  end

  # procedure to delete slave interpreter
  slave_delete_proc = proc{
    unless slave_ip.deleted?
      #if slave_ip._invoke('info', 'command', '.') != ""
      #  slave_ip._invoke('destroy', '.')
      #end
      #slave_ip.delete
      slave_ip._eval_without_enc('exit')
    end
    begin
      top.destroy if top.winfo_exist?
    rescue
      # ignore
    end
  }
  tag = TkBindTag.new.bind('Destroy', slave_delete_proc)

  top.bindtags = top.bindtags.unshift(tag)

  # create control frame
  TkFrame.new(top, :bg=>'red', :borderwidth=>3, :relief=>'ridge') {|fc|
    fc.bindtags = fc.bindtags.unshift(tag)

    TkFrame.new(fc, :bd=>0){|f|
      TkButton.new(f,
                   :text=>'Delete', :bd=>1, :padx=>2, :pady=>0,
                   :highlightthickness=>0, :command=>slave_delete_proc
                   ).pack(:side=>:right, :fill=>:both)
      f.pack(:side=>:right, :fill=>:both, :expand=>true)
    }

    TkLabel.new(fc, :text=>msg, :padx=>2, :pady=>0,
                :anchor=>:w).pack(:side=>:left, :fill=>:both, :expand=>true)

    fc.pack(:side=>:bottom, :fill=>:x)
  }

  # container frame for slave interpreter
  dup_keys['fill'] = :both  unless dup_keys.key?('fill')
  dup_keys['expand'] = true unless dup_keys.key?('expand')
  c = TkFrame.new(top, :container=>true).pack(dup_keys)
  c.bind('Destroy', proc{top.destroy})

  # return keys
  loadTk_keys['use'] = TkWinfo.id(c)
  [loadTk_keys, top.path]
end
_check_and_return(thread, exception, wait=0) click to toggle source
# File lib/multi-tk.rb, line 251
def _check_and_return(thread, exception, wait=0)
  unless thread
    unless exception.kind_of?(MultiTkIp_OK)
      msg = "#{exception.class}: #{exception.message}"

      if @interp.deleted?
        warn("Warning (#{self}): " + msg)
        return nil
      end

      if safe?
        warn("Warning (#{self}): " + msg) if $DEBUG
        return nil
      end

      begin
        @interp._eval_without_enc(@interp._merge_tklist('bgerror', msg))
      rescue Exception
        warn("Warning (#{self}): " + msg)
      end
    end
    return nil
  end

  if wait == 0
    # no wait
    Thread.pass
    if thread.stop?
      thread.raise exception
    end
    return thread
  end

  # wait to stop the caller thread
  wait.times{
    if thread.stop?
      # ready to send exception
      thread.raise exception
      return thread
    end

    # wait
    Thread.pass
  }

  # unexpected error
  thread.raise RuntimeError, "the thread may not wait for the return value"
  return thread
end
_create_receiver_and_watchdog(lvl = 0) click to toggle source
# File lib/multi-tk.rb, line 611
def _create_receiver_and_watchdog(lvl = 0)
  lvl = 0

  # command-procedures receiver
  receiver = Thread.new(lvl){|_safe_level|
    last_thread = {}

    loop do
      break if @interp.deleted?
      thread, cmd, *args = @cmd_queue.deq
      if thread == @system
        # control command
        case cmd
        when 'call_mainloop'
          thread = args.shift
          _check_and_return(thread,
                            MultiTkIp_OK.new(_receiver_mainloop(*args)))
        else
          # ignore
        end

      else
        # procedure
        last_thread[thread] = _receiver_eval_proc(last_thread[thread],
                                                  0, thread,
                                                  cmd, *args)
      end
    end
  }

  # watchdog of receiver
  watchdog = Thread.new{
    begin
      loop do
        sleep 1
        if @interp.deleted?
          receiver.kill
          @cmd_queue.close
        end
        break unless receiver.alive?
      end
    rescue Exception
      # ignore all kind of Exception
    end

    # receiver is dead
    retry_count = 3
    loop do
      Thread.pass
      begin
        thread, _, *_ = @cmd_queue.deq(true) # non-block
      rescue ThreadError
        # queue is empty
        retry_count -= 1
        break if retry_count <= 0
        sleep 0.5
        retry
      end
      next unless thread
      if thread.alive?
        if @interp.deleted?
          thread.raise RuntimeError, 'the interpreter is already deleted'
        else
          thread.raise RuntimeError,
            'the interpreter no longer receives command procedures'
        end
      end
    end
  }

  # return threads
  [receiver, watchdog]
end
_create_slave_ip_name() click to toggle source
# File lib/multi-tk.rb, line 1029
def _create_slave_ip_name
  @@SLAVE_IP_ID.mutex.synchronize{
    name = @@SLAVE_IP_ID.join('')
    @@SLAVE_IP_ID[1].succ!
    name.freeze
  }
end
_keys2opts(src_keys) click to toggle source
# File lib/multi-tk.rb, line 243
def _keys2opts(src_keys)
  return nil if src_keys == nil
  keys = {}; src_keys.each{|k, v| keys[k.to_s] = v}
  #keys.collect{|k,v| "-#{k} #{v}"}.join(' ')
  keys.collect{|k,v| "-#{k} #{TclTkLib._conv_listelement(TkComm::_get_eval_string(v))}"}.join(' ')
end
_parse_slaveopts(keys) click to toggle source
# File lib/multi-tk.rb, line 1002
def _parse_slaveopts(keys)
  name = nil
  safe = false
  safe_opts = {}
  tk_opts   = {}

  keys.each{|k,v|
    k_str = k.to_s
    if k_str == 'name'
      name = v
    elsif k_str == 'safe'
      safe = v
    elsif @@SAFE_OPT_LIST.member?(k_str)
      safe_opts[k_str] = v
    else
      tk_opts[k_str] = v
    end
  }

  if keys['without_tk'] || keys[:without_tk]
    [name, safe, safe_opts, nil]
  else
    [name, safe, safe_opts, tk_opts]
  end
end
_receiver_eval_proc(last_thread, _safe_level, thread, cmd, *args) click to toggle source
# File lib/multi-tk.rb, line 573
def _receiver_eval_proc(last_thread, _safe_level, thread, cmd, *args)
  if thread
    Thread.new{
      last_thread.join if last_thread
      unless @interp.deleted?
        _receiver_eval_proc_core(0, thread, cmd, *args)
      end
    }
  else
    Thread.new{
      unless  @interp.deleted?
        _receiver_eval_proc_core(0, thread, cmd, *args)
      end
    }
    last_thread
  end
end
_receiver_eval_proc_core(_safe_level, thread, cmd, *args) click to toggle source
# File lib/multi-tk.rb, line 389
  def _receiver_eval_proc_core(_safe_level, thread, cmd, *args)
    begin
      normal_ret = false
      ret = catch(:IRB_EXIT) do  # IRB hack
        retval = cmd.call(0, *args)
        normal_ret = true
        retval
      end
      unless normal_ret
        # catch IRB_EXIT
        exit(ret)
      end
      ret
    rescue SystemExit => e
      # delete IP
      unless @interp.deleted?
        @slave_ip_tbl.each{|name, subip|
          _destroy_slaves_of_slaveIP(subip)
          begin
            # subip._eval_without_enc("foreach i [after info] {after cancel $i}")
            after_ids = subip._eval_without_enc("after info")
            subip._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}")
          rescue Exception
          end
=begin
          begin
            subip._invoke('destroy', '.') unless subip.deleted?
          rescue Exception
          end
=end
          # safe_base?
          if @interp._eval_without_enc("catch {::safe::interpConfigure #{name}}") == '0'
            begin
              @interp._eval_without_enc("::safe::interpDelete #{name}")
            rescue Exception
            else
              next if subip.deleted?
            end
          end
          if subip.respond_to?(:safe_base?) && subip.safe_base? &&
              !subip.deleted?
            # do 'exit' to call the delete_hook procedure
            begin
              subip._eval_without_enc('exit')
            rescue Exception
            end
          else
            begin
              subip.delete unless subip.deleted?
            rescue Exception
            end
          end
        }

        begin
          # @interp._eval_without_enc("foreach i [after info] {after cancel $i}")
          after_ids = @interp._eval_without_enc("after info")
          @interp._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}")
        rescue Exception
        end
        begin
          @interp._invoke('destroy', '.') unless @interp.deleted?
        rescue Exception
        end
        if @safe_base && !@interp.deleted?
          # do 'exit' to call the delete_hook procedure
          @interp._eval_without_enc('exit')
        else
          @interp.delete unless @interp.deleted?
        end
      end

      if e.backtrace[0] =~ /^(.+?):(\d+):in `(exit|exit!|abort)'/
        _check_and_return(thread, MultiTkIp_OK.new($3 == 'exit'))
      else
        _check_and_return(thread, MultiTkIp_OK.new(nil))
      end

      # if master? && !safe? && allow_ruby_exit?
      if !@interp.deleted? && master? && !safe? && allow_ruby_exit?
=begin
        ObjectSpace.each_object(TclTkIp){|obj|
          obj.delete unless obj.deleted?
        }
=end
        #exit(e.status)
        fail e
      end
      # break

    rescue SecurityError => e
      # in 'exit', 'exit!', and 'abort' : security error --> delete IP
      if e.backtrace[0] =~ /^(.+?):(\d+):in `(exit|exit!|abort)'/
        ret = ($3 == 'exit')
        unless @interp.deleted?
          @slave_ip_tbl.each{|name, subip|
            _destroy_slaves_of_slaveIP(subip)
            begin
              # subip._eval_without_enc("foreach i [after info] {after cancel $i}")
              after_ids = subip._eval_without_enc("after info")
              subip._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}")
            rescue Exception
            end
=begin
            begin
              subip._invoke('destroy', '.') unless subip.deleted?
            rescue Exception
            end
=end
            # safe_base?
            if @interp._eval_without_enc("catch {::safe::interpConfigure #{name}}") == '0'
              begin
                @interp._eval_without_enc("::safe::interpDelete #{name}")
              rescue Exception
              else
                next if subip.deleted?
              end
            end
            if subip.respond_to?(:safe_base?) && subip.safe_base? &&
                !subip.deleted?
              # do 'exit' to call the delete_hook procedure
              begin
                subip._eval_without_enc('exit')
              rescue Exception
              end
            else
              begin
                subip.delete unless subip.deleted?
              rescue Exception
              end
            end
          }

          begin
            # @interp._eval_without_enc("foreach i [after info] {after cancel $i}")
            after_ids = @interp._eval_without_enc("after info")
            @interp._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}")
          rescue Exception
          end
=begin
          begin
            @interp._invoke('destroy', '.') unless @interp.deleted?
          rescue Exception
          end
=end
          if @safe_base && !@interp.deleted?
            # do 'exit' to call the delete_hook procedure
            @interp._eval_without_enc('exit')
          else
            @interp.delete unless @interp.deleted?
          end
        end
        _check_and_return(thread, MultiTkIp_OK.new(ret))
        # break

      else
        # raise security error
        _check_and_return(thread, e)
      end

    rescue Exception => e
      # raise exception
      begin
        bt = _toUTF8(e.backtrace.join("\n"))
        if MultiTkIp::WITH_ENCODING
          bt.force_encoding('utf-8')
        else
          bt.instance_variable_set(:@encoding, 'utf-8')
        end
      rescue Exception
        bt = e.backtrace.join("\n")
      end
      begin
        @interp._set_global_var('errorInfo', bt)
      rescue Exception
      end
      _check_and_return(thread, e)

    else
      # no exception
      _check_and_return(thread, MultiTkIp_OK.new(ret))
    end
  end