258 lines
6.1 KiB
Python
Executable File
258 lines
6.1 KiB
Python
Executable File
# Standard main loop for *all* STDWIN applications.
|
|
# This requires that applications:
|
|
# - register their windows on creation and unregister them when closed
|
|
# - have a 'dispatch' function as a window member
|
|
|
|
|
|
import stdwin, stdwinq
|
|
from stdwinevents import *
|
|
|
|
|
|
# List of windows known to the main loop.
|
|
#
|
|
windows = []
|
|
|
|
|
|
# Last window that ever received an event
|
|
#
|
|
last_window = None
|
|
|
|
|
|
# Function to register a window.
|
|
#
|
|
def register(win):
|
|
# First test the dispatch function by passing it a null event --
|
|
# this catches registration of unconforming windows.
|
|
win.dispatch((WE_NULL, win, None))
|
|
if win not in windows:
|
|
windows.append(win)
|
|
|
|
|
|
# Function to unregister a window.
|
|
# It is not an error to unregister an already unregistered window
|
|
# (this is useful for cleanup actions).
|
|
#
|
|
def unregister(win):
|
|
global last_window
|
|
if win == last_window:
|
|
last_window = None
|
|
if win in windows:
|
|
windows.remove(win) # Not in 0.9.1
|
|
# 0.9.1 solution:
|
|
#for i in range(len(windows)):
|
|
# if windows[i] = win:
|
|
# del windows[i]
|
|
# break
|
|
|
|
|
|
# Interfaces used by WindowSched.
|
|
#
|
|
def countwindows():
|
|
return len(windows)
|
|
#
|
|
def anywindow():
|
|
if windows:
|
|
return windows[0]
|
|
else:
|
|
return None
|
|
|
|
|
|
# NEW: register any number of file descriptors
|
|
#
|
|
fdlist = []
|
|
select_args = None
|
|
select_handlers = None
|
|
#
|
|
def registerfd(fd, mode, handler):
|
|
if mode not in ('r', 'w', 'x'):
|
|
raise ValueError, 'mode must be r, w or x'
|
|
if type(fd) <> type(0):
|
|
fd = fd.fileno() # If this fails it's not a proper select arg
|
|
for i in range(len(fdlist)):
|
|
if fdlist[i][:2] == (fd, mode):
|
|
raise ValueError, \
|
|
'(fd, mode) combination already registered'
|
|
fdlist.append((fd, mode, handler))
|
|
make_select_args()
|
|
#
|
|
def unregisterfd(fd, *args):
|
|
if type(fd) <> type(0):
|
|
fd = fd.fileno() # If this fails it's not a proper select arg
|
|
args = (fd,) + args
|
|
n = len(args)
|
|
for i in range(len(fdlist)):
|
|
if fdlist[i][:n] == args:
|
|
del fdlist[i]
|
|
make_select_args()
|
|
#
|
|
def make_select_args():
|
|
global select_args, select_handlers
|
|
rlist, wlist, xlist = [], [], []
|
|
rhandlers, whandlers, xhandlers = {}, {}, {}
|
|
for fd, mode, handler in fdlist:
|
|
if mode == 'r':
|
|
rlist.append(fd)
|
|
rhandlers[`fd`] = handler
|
|
if mode == 'w':
|
|
wlist.append(fd)
|
|
whandlers[`fd`] = handler
|
|
if mode == 'x':
|
|
xlist.append(fd)
|
|
xhandlers[`fd`] = handler
|
|
if rlist or wlist or xlist:
|
|
select_args = rlist, wlist, xlist
|
|
select_handlers = rhandlers, whandlers, xhandlers
|
|
else:
|
|
select_args = None
|
|
select_handlers = None
|
|
#
|
|
def do_select():
|
|
import select
|
|
reply = apply(select.select, select_args)
|
|
for mode in 0, 1, 2:
|
|
list = reply[mode]
|
|
for fd in list:
|
|
handler = select_handlers[mode][`fd`]
|
|
handler(fd, 'rwx'[mode])
|
|
|
|
|
|
# Event processing main loop.
|
|
# Return when there are no windows left, or when an unhandled
|
|
# exception occurs. (It is safe to restart the main loop after
|
|
# an unsuccessful exit.)
|
|
# Python's stdwin.getevent() turns WE_COMMAND/WC_CANCEL events
|
|
# into KeyboardInterrupt exceptions; these are turned back in events.
|
|
#
|
|
recursion_level = 0 # Hack to make it reentrant
|
|
def mainloop():
|
|
global recursion_level
|
|
recursion_level = recursion_level + 1
|
|
try:
|
|
stdwin_select_handler() # Process events already in queue
|
|
while 1:
|
|
if windows and not fdlist:
|
|
while windows and not fdlist:
|
|
try:
|
|
event = stdwinq.getevent()
|
|
except KeyboardInterrupt:
|
|
event = (WE_COMMAND, \
|
|
None, WC_CANCEL)
|
|
dispatch(event)
|
|
elif windows and fdlist:
|
|
fd = stdwin.fileno()
|
|
if recursion_level == 1:
|
|
registerfd(fd, 'r', stdwin_select_handler)
|
|
try:
|
|
while windows:
|
|
do_select()
|
|
stdwin_select_handler()
|
|
finally:
|
|
if recursion_level == 1:
|
|
unregisterfd(fd)
|
|
elif fdlist:
|
|
while fdlist and not windows:
|
|
do_select()
|
|
else:
|
|
break
|
|
finally:
|
|
recursion_level = recursion_level - 1
|
|
|
|
|
|
# Check for events without ever blocking
|
|
#
|
|
def check():
|
|
stdwin_select_handler()
|
|
# XXX Should check for socket stuff as well
|
|
|
|
|
|
# Handle stdwin events until none are left
|
|
#
|
|
def stdwin_select_handler(*args):
|
|
while 1:
|
|
try:
|
|
event = stdwinq.pollevent()
|
|
except KeyboardInterrupt:
|
|
event = (WE_COMMAND, None, WC_CANCEL)
|
|
if event is None:
|
|
break
|
|
dispatch(event)
|
|
|
|
|
|
# Run a modal dialog loop for a window. The dialog window must have
|
|
# been registered first. This prohibits most events (except size/draw
|
|
# events) to other windows. The modal dialog loop ends when the
|
|
# dialog window unregisters itself.
|
|
#
|
|
passthrough = WE_SIZE, WE_DRAW
|
|
beeping = WE_MOUSE_DOWN, WE_COMMAND, WE_CHAR, WE_KEY, WE_CLOSE, WE_MENU
|
|
#
|
|
def modaldialog(window):
|
|
if window not in windows:
|
|
raise ValueError, 'modaldialog window not registered'
|
|
while window in windows:
|
|
try:
|
|
event = stdwinq.getevent()
|
|
except KeyboardInterrupt:
|
|
event = WE_COMMAND, None, WC_CANCEL
|
|
etype, ewindow, edetail = event
|
|
if etype not in passthrough and ewindow <> window:
|
|
if etype in beeping:
|
|
stdwin.fleep()
|
|
continue
|
|
dispatch(event)
|
|
|
|
|
|
# Dispatch a single event.
|
|
# Events for the no window in particular are sent to the active window
|
|
# or to the last window that received an event (these hacks are for the
|
|
# WE_LOST_SEL event, which is directed to no particular window).
|
|
# Windows not in the windows list don't get their events:
|
|
# events for such windows are silently ignored.
|
|
#
|
|
def dispatch(event):
|
|
global last_window
|
|
if event[1] == None:
|
|
active = stdwin.getactive()
|
|
if active: last_window = active
|
|
else:
|
|
last_window = event[1]
|
|
if last_window in windows:
|
|
last_window.dispatch(event)
|
|
|
|
|
|
# Dialog base class
|
|
#
|
|
class Dialog:
|
|
#
|
|
def init(self, title):
|
|
self.window = stdwin.open(title)
|
|
self.window.dispatch = self.dispatch
|
|
register(self.window)
|
|
return self
|
|
#
|
|
def close(self):
|
|
unregister(self.window)
|
|
del self.window.dispatch
|
|
self.window.close()
|
|
#
|
|
def dispatch(self, event):
|
|
etype, ewindow, edetail = event
|
|
if etype == WE_CLOSE:
|
|
self.close()
|
|
|
|
|
|
# Standard modal dialogs
|
|
# XXX implemented using stdwin dialogs for now
|
|
#
|
|
def askstr(prompt, default):
|
|
return stdwin.askstr(prompt, default)
|
|
#
|
|
def askync(prompt, yesorno):
|
|
return stdwin.askync(prompt, yesorno)
|
|
#
|
|
def askfile(prompt, default, new):
|
|
return stdwin.askfile(prompt, default, new)
|
|
#
|
|
def message(msg):
|
|
stdwin.message(msg)
|