blob: 6b574cf2a4c9a76d42cae2edef356d7b2773907d [file] [log] [blame]
Guido van Rossum7045dd01991-08-16 13:28:28 +00001# Standard main loop for *all* STDWIN applications.
2# This requires that applications:
3# - register their windows on creation and unregister them when closed
4# - have a 'dispatch' function as a window member
5
6
Guido van Rossumfea2af11993-01-04 09:16:51 +00007# XXX This is UNIX specific! For the Mac we need to use a simpler version!
8
9
Guido van Rossum7045dd01991-08-16 13:28:28 +000010import stdwin, stdwinq
11from stdwinevents import *
12
13
14# List of windows known to the main loop.
15#
16windows = []
17
18
Guido van Rossum18fc5691992-11-26 09:17:19 +000019# Last window that ever received an event
20#
21last_window = None
22
23
Guido van Rossum7045dd01991-08-16 13:28:28 +000024# Function to register a window.
25#
26def register(win):
27 # First test the dispatch function by passing it a null event --
28 # this catches registration of unconforming windows.
Guido van Rossum89a78691992-12-14 12:57:56 +000029 win.dispatch((WE_NULL, win, None))
Guido van Rossum7045dd01991-08-16 13:28:28 +000030 if win not in windows:
31 windows.append(win)
32
33
34# Function to unregister a window.
35# It is not an error to unregister an already unregistered window
36# (this is useful for cleanup actions).
37#
38def unregister(win):
Guido van Rossum18fc5691992-11-26 09:17:19 +000039 global last_window
40 if win == last_window:
41 last_window = None
Guido van Rossum7045dd01991-08-16 13:28:28 +000042 if win in windows:
43 windows.remove(win) # Not in 0.9.1
Guido van Rossum3f4f9171991-11-19 20:41:07 +000044 # 0.9.1 solution:
45 #for i in range(len(windows)):
46 # if windows[i] = win:
47 # del windows[i]
48 # break
Guido van Rossum7045dd01991-08-16 13:28:28 +000049
50
51# Interfaces used by WindowSched.
52#
53def countwindows():
54 return len(windows)
55#
56def anywindow():
57 if windows:
58 return windows[0]
59 else:
60 return None
61
62
Guido van Rossum18fc5691992-11-26 09:17:19 +000063# NEW: register any number of file descriptors
64#
65fdlist = []
66select_args = None
67select_handlers = None
68#
69def registerfd(fd, mode, handler):
70 if mode not in ('r', 'w', 'x'):
71 raise ValueError, 'mode must be r, w or x'
72 if type(fd) <> type(0):
73 fd = fd.fileno() # If this fails it's not a proper select arg
74 for i in range(len(fdlist)):
75 if fdlist[i][:2] == (fd, mode):
76 raise ValueError, \
77 '(fd, mode) combination already registered'
78 fdlist.append((fd, mode, handler))
79 make_select_args()
80#
81def unregisterfd(fd, *args):
82 if type(fd) <> type(0):
83 fd = fd.fileno() # If this fails it's not a proper select arg
84 args = (fd,) + args
85 n = len(args)
86 for i in range(len(fdlist)):
87 if fdlist[i][:n] == args:
88 del fdlist[i]
89 make_select_args()
90#
91def make_select_args():
92 global select_args, select_handlers
93 rlist, wlist, xlist = [], [], []
94 rhandlers, whandlers, xhandlers = {}, {}, {}
95 for fd, mode, handler in fdlist:
96 if mode == 'r':
97 rlist.append(fd)
98 rhandlers[`fd`] = handler
99 if mode == 'w':
100 wlist.append(fd)
101 whandlers[`fd`] = handler
102 if mode == 'x':
103 xlist.append(fd)
104 xhandlers[`fd`] = handler
105 if rlist or wlist or xlist:
106 select_args = rlist, wlist, xlist
107 select_handlers = rhandlers, whandlers, xhandlers
108 else:
109 select_args = None
110 select_handlers = None
111#
112def do_select():
113 import select
114 reply = apply(select.select, select_args)
115 for mode in 0, 1, 2:
116 list = reply[mode]
117 for fd in list:
118 handler = select_handlers[mode][`fd`]
119 handler(fd, 'rwx'[mode])
120
121
Guido van Rossum7045dd01991-08-16 13:28:28 +0000122# Event processing main loop.
123# Return when there are no windows left, or when an unhandled
124# exception occurs. (It is safe to restart the main loop after
125# an unsuccessful exit.)
126# Python's stdwin.getevent() turns WE_COMMAND/WC_CANCEL events
127# into KeyboardInterrupt exceptions; these are turned back in events.
128#
Guido van Rossumfea2af11993-01-04 09:16:51 +0000129recursion_level = 0 # Hack to make it reentrant
Guido van Rossum7045dd01991-08-16 13:28:28 +0000130def mainloop():
Guido van Rossumfea2af11993-01-04 09:16:51 +0000131 global recursion_level
132 recursion_level = recursion_level + 1
133 try:
134 stdwin_select_handler() # Process events already in queue
135 fd = stdwin.fileno()
136 while 1:
137 if windows:
138 if recursion_level == 1:
139 registerfd(fd, 'r', stdwin_select_handler)
140 try:
141 while windows:
142 do_select()
143 stdwin_select_handler()
144 finally:
145 if recursion_level == 1:
146 unregisterfd(fd)
147 elif fdlist:
148 while fdlist and not windows:
Guido van Rossum18fc5691992-11-26 09:17:19 +0000149 do_select()
Guido van Rossumfea2af11993-01-04 09:16:51 +0000150 else:
151 break
152 finally:
153 recursion_level = recursion_level - 1
154
155
156# Check for events without ever blocking
157#
158def check():
159 stdwin_select_handler()
160 # XXX Should check for socket stuff as well
Guido van Rossum18fc5691992-11-26 09:17:19 +0000161
162
163# Handle stdwin events until none are left
164#
165def stdwin_select_handler(*args):
166 while 1:
Guido van Rossum7045dd01991-08-16 13:28:28 +0000167 try:
Guido van Rossum18fc5691992-11-26 09:17:19 +0000168 event = stdwinq.pollevent()
Guido van Rossum7045dd01991-08-16 13:28:28 +0000169 except KeyboardInterrupt:
Guido van Rossum18fc5691992-11-26 09:17:19 +0000170 event = (WE_COMMAND, None, WC_CANCEL)
171 if event is None:
172 break
173 dispatch(event)
174
175
176# Run a modal dialog loop for a window. The dialog window must have
177# been registered first. This prohibits most events (except size/draw
178# events) to other windows. The modal dialog loop ends when the
179# dialog window unregisters itself.
180#
181passthrough = WE_SIZE, WE_DRAW
182beeping = WE_MOUSE_DOWN, WE_COMMAND, WE_CHAR, WE_KEY, WE_CLOSE, WE_MENU
183#
184def modaldialog(window):
185 if window not in windows:
186 raise ValueError, 'modaldialog window not registered'
187 while window in windows:
188 try:
189 event = stdwinq.getevent()
190 except KeyboardInterrupt:
191 event = WE_COMMAND, None, WC_CANCEL
192 etype, ewindow, edetail = event
193 if etype not in passthrough and ewindow <> window:
194 if etype in beeping:
195 stdwin.fleep()
196 continue
197 dispatch(event)
Guido van Rossum7045dd01991-08-16 13:28:28 +0000198
199
200# Dispatch a single event.
Guido van Rossum18fc5691992-11-26 09:17:19 +0000201# Events for the no window in particular are sent to the active window
202# or to the last window that received an event (these hacks are for the
203# WE_LOST_SEL event, which is directed to no particular window).
Guido van Rossum7045dd01991-08-16 13:28:28 +0000204# Windows not in the windows list don't get their events:
205# events for such windows are silently ignored.
206#
207def dispatch(event):
Guido van Rossum18fc5691992-11-26 09:17:19 +0000208 global last_window
209 if event[1] == None:
210 active = stdwin.getactive()
211 if active: last_window = active
212 else:
213 last_window = event[1]
214 if last_window in windows:
215 last_window.dispatch(event)
216
217
218# Dialog base class
219#
220class Dialog:
221 #
222 def init(self, title):
223 self.window = stdwin.open(title)
224 self.window.dispatch = self.dispatch
225 register(self.window)
226 return self
227 #
228 def close(self):
229 unregister(self.window)
230 del self.window.dispatch
231 self.window.close()
232 #
233 def dispatch(self, event):
234 etype, ewindow, edetail = event
235 if etype == WE_CLOSE:
236 self.close()
237
238
239# Standard modal dialogs
240# XXX implemented using stdwin dialogs for now
241#
242def askstr(prompt, default):
243 return stdwin.askstr(prompt, default)
244#
245def askync(prompt, yesorno):
246 return stdwin.askync(prompt, yesorno)
247#
248def askfile(prompt, default, new):
249 return stdwin.askfile(prompt, default, new)
250#
251def message(msg):
252 stdwin.message(msg)