mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
382b4ae9a3
* replace Tcl/Tk's vwait and tkwait to switch on threads smoothly and avoid seg-fault. * add TclTkIp._thread_vwait and _thread_tkwait for waiting on a thread. ( Because Tcl/Tk's vwait and tkwait command wait on a eventloop. ) ext/tk/lib/multi-tk.rb: * support TclTkIp._thread_vwait and _thread_tkwait ext/tk/lib/tk.rb: * now, TkVariable#wait has 2 arguments. If 1st argument is true, waits on a thread. If false, waits on an eventloop. If 2nd argument is true, checks existence of rootwidgets. If false, doesn't. Default is wait(true, false). * add TkVariable#tkwait(arg) which is equal to TkVariable#wait(arg, true) * wait_visibility and wait_destroy have an argument for waiting on a thread or an eventloop. * improve of accessing Tcl/Tk's special variables ext/tk/lib/tkafter.rb: * support 'wait on a thread' and 'wait on an eventloop' git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@4762 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
412 lines
8 KiB
Ruby
412 lines
8 KiB
Ruby
#
|
|
# tkafter.rb : methods for Tcl/Tk after command
|
|
#
|
|
# $Id$
|
|
#
|
|
require 'tk'
|
|
|
|
class TkTimer
|
|
include TkCore
|
|
extend TkCore
|
|
|
|
TkCommandNames = ['after'.freeze].freeze
|
|
|
|
Tk_CBID = ['a'.freeze, '00000'.taint].freeze
|
|
Tk_CBTBL = {}.taint
|
|
|
|
TkCore::INTERP.add_tk_procs('rb_after', 'id', <<-'EOL')
|
|
if {[set st [catch {ruby [format "TkTimer.callback %%Q!%s!" $id]} ret]] != 0} {
|
|
return -code $st $ret
|
|
} {
|
|
return $ret
|
|
}
|
|
EOL
|
|
|
|
|
|
###############################
|
|
# class methods
|
|
###############################
|
|
def self.callback(obj_id)
|
|
@after_id = nil
|
|
ex_obj = Tk_CBTBL[obj_id]
|
|
return "" if ex_obj == nil; # canceled
|
|
#_get_eval_string(ex_obj.do_callback)
|
|
begin
|
|
ex_obj.cb_call
|
|
rescue Exception
|
|
ex_obj.cancel
|
|
""
|
|
end
|
|
end
|
|
|
|
def self.info
|
|
tk_call('after', 'info').split(' ').collect!{|id|
|
|
ret = Tk_CBTBL.find{|key,val| val.after_id == id}
|
|
(ret == nil)? id: ret[1]
|
|
}
|
|
end
|
|
|
|
###############################
|
|
# instance methods
|
|
###############################
|
|
def do_callback
|
|
@in_callback = true
|
|
begin
|
|
@return_value = @current_proc.call(self)
|
|
rescue Exception
|
|
if @cancel_on_exception
|
|
cancel
|
|
return nil
|
|
else
|
|
fail $!
|
|
end
|
|
end
|
|
if @set_next
|
|
set_next_callback(@current_args)
|
|
else
|
|
@set_next = true
|
|
end
|
|
@in_callback = false
|
|
@return_value
|
|
end
|
|
|
|
def set_callback(sleep, args=nil)
|
|
@after_script = "rb_after #{@id}"
|
|
@after_id = tk_call('after', sleep, @after_script)
|
|
@current_args = args
|
|
@current_script = [sleep, @after_script]
|
|
self
|
|
end
|
|
|
|
def set_next_callback(args)
|
|
if @running == false || @proc_max == 0 || @do_loop == 0
|
|
Tk_CBTBL.delete(@id) ;# for GC
|
|
@running = false
|
|
@wait_var.value = 0
|
|
return
|
|
end
|
|
if @current_pos >= @proc_max
|
|
if @do_loop < 0 || (@do_loop -= 1) > 0
|
|
@current_pos = 0
|
|
else
|
|
Tk_CBTBL.delete(@id) ;# for GC
|
|
@running = false
|
|
@wait_var.value = 0
|
|
return
|
|
end
|
|
end
|
|
|
|
@current_args = args
|
|
|
|
if @sleep_time.kind_of? Proc
|
|
sleep = @sleep_time.call(self)
|
|
else
|
|
sleep = @sleep_time
|
|
end
|
|
@current_sleep = sleep
|
|
|
|
cmd, *cmd_args = @loop_proc[@current_pos]
|
|
@current_pos += 1
|
|
@current_proc = cmd
|
|
|
|
set_callback(sleep, cmd_args)
|
|
end
|
|
|
|
def initialize(*args)
|
|
@id = Tk_CBID.join
|
|
Tk_CBID[1].succ!
|
|
|
|
@wait_var = TkVariable.new(0)
|
|
|
|
# @cb_cmd = TkCore::INTERP.get_cb_entry(self.method(:do_callback))
|
|
@cb_cmd = TkCore::INTERP.get_cb_entry(proc{
|
|
begin
|
|
self.do_callback
|
|
rescue
|
|
self.cancel
|
|
end
|
|
})
|
|
|
|
@set_next = true
|
|
|
|
@init_sleep = 0
|
|
@init_proc = nil
|
|
@init_args = []
|
|
|
|
@current_script = []
|
|
@current_proc = nil
|
|
@current_args = nil
|
|
@return_value = nil
|
|
|
|
@sleep_time = 0
|
|
@current_sleep = 0
|
|
@loop_exec = 0
|
|
@do_loop = 0
|
|
@loop_proc = []
|
|
@proc_max = 0
|
|
@current_pos = 0
|
|
|
|
@after_id = nil
|
|
@after_script = nil
|
|
|
|
@cancel_on_exception = true
|
|
|
|
set_procs(*args) if args != []
|
|
|
|
@running = false
|
|
@in_callback = false
|
|
end
|
|
|
|
attr :after_id
|
|
attr :after_script
|
|
attr :current_proc
|
|
attr :current_args
|
|
attr :current_sleep
|
|
alias :current_interval :current_sleep
|
|
attr :return_value
|
|
|
|
attr_accessor :loop_exec
|
|
|
|
def cb_call
|
|
@cb_cmd.call
|
|
end
|
|
|
|
def get_procs
|
|
[@init_sleep, @init_proc, @init_args, @sleep_time, @loop_exec, @loop_proc]
|
|
end
|
|
|
|
def current_status
|
|
[@running, @current_sleep, @current_proc, @current_args,
|
|
@do_loop, @cancel_on_exception]
|
|
end
|
|
|
|
def cancel_on_exception?
|
|
@cancel_on_exception
|
|
end
|
|
|
|
def cancel_on_exception=(mode)
|
|
@cancel_on_exception = mode
|
|
end
|
|
|
|
def running?
|
|
@running
|
|
end
|
|
|
|
def loop_rest
|
|
@do_loop
|
|
end
|
|
|
|
def loop_rest=(rest)
|
|
@do_loop = rest
|
|
end
|
|
|
|
def set_procs(interval, loop_exec, *procs)
|
|
if !interval == 'idle' \
|
|
&& !interval.kind_of?(Integer) && !interval.kind_of?(Proc)
|
|
fail format("%s need to be Integer or Proc", interval.inspect)
|
|
end
|
|
@sleep_time = interval
|
|
|
|
@loop_proc = []
|
|
procs.each{|e|
|
|
if e.kind_of? Proc
|
|
@loop_proc.push([e])
|
|
else
|
|
@loop_proc.push(e)
|
|
end
|
|
}
|
|
@proc_max = @loop_proc.size
|
|
@current_pos = 0
|
|
|
|
@do_loop = 0
|
|
if loop_exec
|
|
if loop_exec.kind_of?(Integer) && loop_exec < 0
|
|
@loop_exec = -1
|
|
elsif loop_exec == nil || loop_exec == false || loop_exec == 0
|
|
@loop_exec = 1
|
|
else
|
|
if not loop_exec.kind_of?(Integer)
|
|
fail format("%s need to be Integer", loop_exec.inspect)
|
|
end
|
|
@loop_exec = loop_exec
|
|
end
|
|
@do_loop = @loop_exec
|
|
end
|
|
|
|
self
|
|
end
|
|
|
|
def add_procs(*procs)
|
|
procs.each{|e|
|
|
if e.kind_of? Proc
|
|
@loop_proc.push([e])
|
|
else
|
|
@loop_proc.push(e)
|
|
end
|
|
}
|
|
@proc_max = @loop_proc.size
|
|
|
|
self
|
|
end
|
|
|
|
def delete_procs(*procs)
|
|
procs.each{|e|
|
|
if e.kind_of? Proc
|
|
@loop_proc.delete([e])
|
|
else
|
|
@loop_proc.delete(e)
|
|
end
|
|
}
|
|
@proc_max = @loop_proc.size
|
|
|
|
cancel if @proc_max == 0
|
|
|
|
self
|
|
end
|
|
|
|
def delete_at(n)
|
|
@loop_proc.delete_at(n)
|
|
@proc_max = @loop_proc.size
|
|
cancel if @proc_max == 0
|
|
self
|
|
end
|
|
|
|
def set_start_proc(sleep, init_proc, *init_args)
|
|
if !sleep == 'idle' && !sleep.kind_of?(Integer)
|
|
fail format("%s need to be Integer", sleep.inspect)
|
|
end
|
|
@init_sleep = sleep
|
|
@init_proc = init_proc
|
|
@init_args = init_args
|
|
self
|
|
end
|
|
|
|
def start(*init_args)
|
|
return nil if @running
|
|
|
|
Tk_CBTBL[@id] = self
|
|
@do_loop = @loop_exec
|
|
@current_pos = 0
|
|
|
|
argc = init_args.size
|
|
if argc > 0
|
|
sleep = init_args.shift
|
|
if !sleep == 'idle' && !sleep.kind_of?(Integer)
|
|
fail format("%s need to be Integer", sleep.inspect)
|
|
end
|
|
@init_sleep = sleep
|
|
end
|
|
@init_proc = init_args.shift if argc > 1
|
|
@init_args = init_args if argc > 0
|
|
|
|
@current_sleep = @init_sleep
|
|
@running = true
|
|
if @init_proc
|
|
if not @init_proc.kind_of? Proc
|
|
fail format("%s need to be Proc", @init_proc.inspect)
|
|
end
|
|
@current_proc = @init_proc
|
|
set_callback(sleep, @init_args)
|
|
@set_next = false if @in_callback
|
|
else
|
|
set_next_callback(@init_args)
|
|
end
|
|
|
|
self
|
|
end
|
|
|
|
def reset(*reset_args)
|
|
restart() if @running
|
|
|
|
if @init_proc
|
|
@return_value = @init_proc.call(self)
|
|
else
|
|
@return_value = nil
|
|
end
|
|
|
|
@current_pos = 0
|
|
@current_args = @init_args
|
|
@set_next = false if @in_callback
|
|
|
|
self
|
|
end
|
|
|
|
def restart(*restart_args)
|
|
cancel if @running
|
|
if restart_args == []
|
|
start(@init_sleep, @init_proc, *@init_args)
|
|
else
|
|
start(*restart_args)
|
|
end
|
|
end
|
|
|
|
def cancel
|
|
@running = false
|
|
@wait_var.value = 0
|
|
tk_call 'after', 'cancel', @after_id if @after_id
|
|
@after_id = nil
|
|
Tk_CBTBL.delete(@id) ;# for GC
|
|
self
|
|
end
|
|
alias stop cancel
|
|
|
|
def continue(wait=nil)
|
|
fail RuntimeError, "is already running" if @running
|
|
sleep, cmd = @current_script
|
|
fail RuntimeError, "no procedure to continue" unless cmd
|
|
if wait
|
|
if not wait.kind_of? Integer
|
|
fail RuntimeError, format("%s need to be Integer", wait.inspect)
|
|
end
|
|
sleep = wait
|
|
end
|
|
Tk_CBTBL[@id] = self
|
|
@running = true
|
|
@after_id = tk_call('after', sleep, cmd)
|
|
self
|
|
end
|
|
|
|
def skip
|
|
fail RuntimeError, "is not running now" unless @running
|
|
cancel
|
|
Tk_CBTBL[@id] = self
|
|
@running = true
|
|
set_next_callback(@current_args)
|
|
self
|
|
end
|
|
|
|
def info
|
|
if @after_id
|
|
inf = tk_split_list(tk_call('after', 'info', @after_id))
|
|
[Tk_CBTBL[inf[0][1]], inf[1]]
|
|
else
|
|
nil
|
|
end
|
|
end
|
|
|
|
def wait(on_thread = true, check_root = false)
|
|
if $SAFE >= 4
|
|
fail SecurityError, "can't wait timer at $SAFE >= 4"
|
|
end
|
|
return self unless @running
|
|
@wait_var.wait(on_thread, check_root)
|
|
self
|
|
end
|
|
def eventloop_wait(check_root = false)
|
|
wait(false, check_root)
|
|
end
|
|
def thread_wait(check_root = false)
|
|
wait(true, check_root)
|
|
end
|
|
def tkwait(on_thread = true)
|
|
wait(on_thread, true)
|
|
end
|
|
def eventloop_tkwait
|
|
wait(false, true)
|
|
end
|
|
def thread_tkwait
|
|
wait(true, true)
|
|
end
|
|
end
|
|
|
|
TkAfter = TkTimer
|