# # $Id: dispatcher.icn,v 1.4 2004/12/10 19:05:47 rparlett Exp $ # # This file is in the public domain. # # Author: Robert Parlett (parlett@dial.pipex.com) # package gui link graphics import lang $include "guih.icn" # # # This class handles Icon events, dispatching them to # the appropriate dialog. It also controls any active Tickers, # activating them between events as and when appropriate. # class Dispatcher : Object( dialogs, tickers, idle_sleep, idle_sleep_min, idle_sleep_max ) # # # The single instance of the Dispatcher class. # global dispatcher # # Compute the ticker sleep rate. # # @p method compute_idle_sleep() if *tickers = 0 then idle_sleep := idle_sleep_max else { # # Get minimum ticker rate # idle_sleep := &null every n := (!tickers).ticker_rate do (/idle_sleep := n) | (idle_sleep >:= n) # # Divide by number of tickers so that tickers with same tick # rate are still scheduled correctly. # idle_sleep /:= *tickers # # Make between 10 and 50; not too quick to give a busy wait, not # too slow that events are not processed promptly. # idle_sleep <:= idle_sleep_min idle_sleep >:= idle_sleep_max } end # # Time of day # # @p method curr_time_of_day() local t t := gettimeofday() return t[1] * 1000 + t[2] / 1000 end # # Delete a ticker # # @p method stop_ticker(t) if \t.ticker_rate then { delete(tickers, t) t.ticker_rate := &null compute_idle_sleep() } end # # Add a ticker, or reset its time to a new value. # # @p method start_ticker(t, n, d) insert(tickers, t) if /d then t.next_tick_time := 0 else t.next_tick_time := curr_time_of_day() + d t.ticker_rate := n compute_idle_sleep() end # # Change a ticker's tick rate, to take effect after its # next tick. # # @p method retime_ticker(t, n) t.ticker_rate := n compute_idle_sleep() end # # Add a dialog # # @p method add(d) insert(dialogs, d) end # # Delete a dialog # # @p method del(d) delete(dialogs, d) end # # Loop until dialog r is closed processing events and tickers. # # @p method message_loop(r) local w, bag, d while \r.is_open do { do_event() | do_validate() | do_ticker() | delay(idle_sleep) } end method do_event() local d, bag, e bag := [] every d := !dialogs do { if *Pending(d.win) > 0 then { if /d.is_blocked_flag then put(bag, d) else { while *Pending(d.win) > 0 do { # # Discard the event and beep in the window. # e := ::Event(d.win) if not(e === (-12 | &lrelease | &rrelease | &mrelease | &ldrag | &rdrag | &mdrag)) then Alert(d.win) } } } } if d := ?bag then { d.process_event(::Event(d.win)) return } end method do_validate() local d, bag bag := [] every d := !dialogs do { if d.needs_validate() then put(bag, d) } if d := ?bag then { d.invoke_validate() return } end method do_ticker() local curr_time, d, bag curr_time := curr_time_of_day() bag := [] every d := !tickers do { if curr_time >= d.next_tick_time then put(bag, d) } if d := ?bag then { d.tick() # # We have to take into account the fact that d.tick() may # delete itself as a ticker. # d.next_tick_time := \d.ticker_rate + curr_time_of_day() return } end # # Return a list of unblocked dialogs. # # @p method list_unblocked() local d, res res := [] every d := !dialogs do if /d.is_blocked_flag then put(res, d) return res end initially dialogs := set([]) tickers := set([]) idle_sleep_min := 10 idle_sleep_max := 50 compute_idle_sleep() end