summaryrefslogtreecommitdiffstats
path: root/Lib/lib-stdwin/mainloop.py
blob: aa40c34b4ec508fa48a25b3ada1f7b9d4f20c8e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# 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)
	#
	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)