From 6175ca03be6d0d51359f9017123708987d0f5eb7 Mon Sep 17 00:00:00 2001 From: shyouhei Date: Wed, 15 Aug 2007 23:23:39 +0000 Subject: add tag v1_8_5_91 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/tags/v1_8_5_91@13046 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ruby_1_8_5/ext/tk/lib/tk/timer.rb | 634 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 634 insertions(+) create mode 100644 ruby_1_8_5/ext/tk/lib/tk/timer.rb (limited to 'ruby_1_8_5/ext/tk/lib/tk/timer.rb') diff --git a/ruby_1_8_5/ext/tk/lib/tk/timer.rb b/ruby_1_8_5/ext/tk/lib/tk/timer.rb new file mode 100644 index 0000000000..49b7f1b06a --- /dev/null +++ b/ruby_1_8_5/ext/tk/lib/tk/timer.rb @@ -0,0 +1,634 @@ +# +# tk/timer.rb : methods for Tcl/Tk after command +# +# $Id: timer.rb,v 1.1.2.15 2005/05/08 14:20:53 nagai Exp $ +# +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 {eval {ruby_cmd TkTimer callback} $id} ret]] != 0} { + return -code $st $ret + } { + return $ret + } + EOL + + DEFAULT_IGNORE_EXCEPTIONS = [ NameError, RuntimeError ].freeze + + ############################### + # class methods + ############################### + def self.start(*args, &b) + self.new(*args, &b).start + end + + def self.callback(obj_id) + ex_obj = Tk_CBTBL[obj_id] + return "" if ex_obj == nil; # canceled + ex_obj.cb_call + end + + def self.info(obj = nil) + if obj + if obj.kind_of?(TkTimer) + if obj.after_id + inf = tk_split_list(tk_call_without_enc('after','info',obj.after_id)) + [Tk_CBTBL[inf[0][1]], inf[1]] + else + nil + end + else + fail ArgumentError, "TkTimer object is expected" + end + else + tk_call_without_enc('after', 'info').split(' ').collect!{|id| + ret = Tk_CBTBL.find{|key,val| val.after_id == id} + (ret == nil)? id: ret[1] + } + end + end + + + ############################### + # instance methods + ############################### + def do_callback + @in_callback = true + @after_id = nil + begin + @return_value = @current_proc.call(self) + rescue SystemExit + exit(0) + rescue Interrupt + exit!(1) + rescue Exception => e + if @cancel_on_exception && + @cancel_on_exception.find{|exc| e.kind_of?(exc)} + cancel + @return_value = e + @in_callback = false + return e + else + fail e + 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) + if TkCore::INTERP.deleted? + self.cancel + return self + end + @after_script = "rb_after #{@id}" + @after_id = tk_call_without_enc('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) + if TkComm._callback_entry?(@sleep_time) + 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, &b) + # @id = Tk_CBID.join('') + @id = Tk_CBID.join(TkCore::INTERP._ip_id_) + Tk_CBID[1].succ! + + @wait_var = TkVariable.new(0) + + @cb_cmd = TkCore::INTERP.get_cb_entry(self.method(:do_callback)) + + @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 = DEFAULT_IGNORE_EXCEPTIONS + # Unless @cancel_on_exception, Ruby/Tk shows an error dialog box when + # an excepsion is raised on TkTimer callback procedure. + # If @cancel_on_exception is an array of exception classes and the raised + # exception is included in the array, Ruby/Tk cancels executing TkTimer + # callback procedures silently (TkTimer#cancel is called and no dialog is + # shown). + + if b + case args.size + when 0 + add_procs(b) + when 1 + args << -1 << b + else + args << b + end + end + + 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) + if mode.kind_of?(Array) + @cancel_on_exception = mode + elsif mode + @cancel_on_exception = DEFAULT_IGNORE_EXCEPTIONS + else + @cancel_on_exception = false + end + #self + end + + def running? + @running + end + + def loop_rest + @do_loop + end + + def loop_rest=(rest) + @do_loop = rest + #self + end + + def set_interval(interval) + #if interval != 'idle' && interval != :idle \ + # && !interval.kind_of?(Integer) && !interval.kind_of?(Proc) + if interval != 'idle' && interval != :idle \ + && !interval.kind_of?(Integer) && !TkComm._callback_entry?(interval) + fail ArgumentError, "expect Integer or Proc" + end + @sleep_time = interval + end + + def set_procs(interval, loop_exec, *procs) + #if interval != 'idle' && interval != :idle \ + # && !interval.kind_of?(Integer) && !interval.kind_of?(Proc) + if interval != 'idle' && interval != :idle \ + && !interval.kind_of?(Integer) && !TkComm._callback_entry?(interval) + fail ArgumentError, "expect Integer or Proc for 1st argument" + end + @sleep_time = interval + + @loop_proc = [] + procs.each{|e| + # if e.kind_of?(Proc) + if TkComm._callback_entry?(e) + @loop_proc.push([e]) + else + @loop_proc.push(e) + end + } + @proc_max = @loop_proc.size + @current_pos = 0 + + if loop_exec.kind_of?(Integer) && loop_exec < 0 + @loop_exec = -1 + elsif loop_exec == true + @loop_exec = -1 + elsif loop_exec == nil || loop_exec == false || loop_exec == 0 + @loop_exec = 0 + else + if not loop_exec.kind_of?(Integer) + fail ArgumentError, "expect Integer for 2nd argument" + end + @loop_exec = loop_exec + end + @do_loop = @loop_exec + + self + end + + def add_procs(*procs) + procs.each{|e| + # if e.kind_of?(Proc) + if TkComm._callback_entry?(e) + @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) + if TkComm._callback_entry?(e) + @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=nil, init_proc=nil, *init_args, &b) + # set parameters for 'restart' + sleep = @init_sleep unless sleep + + if sleep != 'idle' && sleep != :idle && !sleep.kind_of?(Integer) + fail ArgumentError, "expect Integer or 'idle' for 1st argument" + end + + @init_sleep = sleep + @init_proc = init_proc + @init_args = init_args + + @init_proc = b if !@init_proc && b + @init_proc = proc{|*args| } if @init_sleep > 0 && !@init_proc + + self + end + + def start(*init_args, &b) + return nil if @running + + Tk_CBTBL[@id] = self + @do_loop = @loop_exec + @current_pos = 0 + @return_value = nil + @after_id = nil + + @init_sleep = 0 + @init_proc = nil + @init_args = nil + + argc = init_args.size + if argc > 0 + sleep = init_args.shift + if sleep != 'idle' && sleep != :idle && !sleep.kind_of?(Integer) + fail ArgumentError, "expect Integer or 'idle' for 1st argument" + end + @init_sleep = sleep + end + @init_proc = init_args.shift if argc > 1 + @init_args = init_args if argc > 2 + + @init_proc = b if !@init_proc && b + @init_proc = proc{|*args| } if @init_sleep > 0 && !@init_proc + + @current_sleep = @init_sleep + @running = true + if @init_proc + # if not @init_proc.kind_of?(Proc) + if !TkComm._callback_entry?(@init_proc) + fail ArgumentError, "Argument '#{@init_proc}' need to be Proc" + end + @current_proc = @init_proc + set_callback(@init_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 + @current_script = [] + + @set_next = false if @in_callback + + self + end + + def restart(*restart_args, &b) + cancel if @running + if restart_args == [] && !b + start(@init_sleep, @init_proc, *@init_args) + else + start(*restart_args, &b) + 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 + return restart() if @current_script.empty? + sleep, cmd = @current_script + fail RuntimeError, "no procedure to continue" unless cmd + if wait + unless wait.kind_of?(Integer) + fail ArgumentError, "expect Integer for 1st argument" + end + sleep = wait + end + Tk_CBTBL[@id] = self + @running = true + @after_id = tk_call_without_enc('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_without_enc('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 + + unless @running + if @return_value.kind_of?(Exception) + fail @return_value + else + return @return_value + end + end + + @wait_var.wait(on_thread, check_root) + if @return_value.kind_of?(Exception) + fail @return_value + else + @return_value + end + 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 + + +class TkRTTimer < TkTimer + DEFAULT_OFFSET_LIST_SIZE = 5 + + def initialize(*args, &b) + super(*args, &b) + + @offset_list = Array.new(DEFAULT_OFFSET_LIST_SIZE){ [0, 0] } + @offset_s = 0 + @offset_u = 0 + @est_time = nil + end + + def start(*args, &b) + return nil if @running + @est_time = nil + @cb_start_time = Time.now + super(*args, &b) + end + + def cancel + super() + @est_time = nil + @cb_start_time = Time.now + self + end + alias stop cancel + + def continue(wait=nil) + fail RuntimeError, "is already running" if @running + @cb_start_time = Time.now + super(wait) + end + + def set_interval(interval) + super(interval) + @est_time = nil + end + + def _offset_ave + size = 0 + d_sec = 0; d_usec = 0 + @offset_list.each_with_index{|offset, idx| + # weight = 1 + weight = idx + 1 + size += weight + d_sec += offset[0] * weight + d_usec += offset[1] * weight + } + offset_s, mod = d_sec.divmod(size) + offset_u = ((mod * 1000000 + d_usec) / size.to_f).round + [offset_s, offset_u] + end + private :_offset_ave + + 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 + + cmd, *cmd_args = @loop_proc[@current_pos] + @current_pos += 1 + @current_proc = cmd + + @offset_s, @offset_u = _offset_ave + + if TkComm._callback_entry?(@sleep_time) + sleep = @sleep_time.call(self) + else + sleep = @sleep_time + end + + if @est_time + @est_time = Time.at(@est_time.to_i, @est_time.usec + sleep*1000) + else + @est_time = Time.at(@cb_start_time.to_i, + @cb_start_time.usec + sleep*1000) + end + + now = Time.now + real_sleep = ((@est_time.to_i - now.to_i + @offset_s)*1000.0 + + (@est_time.usec - now.usec + @offset_u)/1000.0).round + if real_sleep <= 0 + real_sleep = 0 + @offset_s = now.to_i + @offset_u = now.usec + end + @current_sleep = real_sleep + + set_callback(real_sleep, cmd_args) + end + + def cb_call + if @est_time + @offset_list.shift + + @cb_start_time = Time.now + + if @current_sleep == 0 + @offset_list.push([ + @offset_s - @cb_start_time.to_i, + @offset_u - @cb_start_time.usec + ]) + else + @offset_list.push([ + @offset_s + (@est_time.to_i - @cb_start_time.to_i), + @offset_u + (@est_time.usec - @cb_start_time.usec) + ]) + end + end + + @cb_cmd.call + end +end -- cgit v1.2.3