Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 1 | # 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 Rossum | fea2af1 | 1993-01-04 09:16:51 +0000 | [diff] [blame] | 7 | # XXX This is UNIX specific! For the Mac we need to use a simpler version! |
| 8 | |
| 9 | |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 10 | import stdwin, stdwinq |
| 11 | from stdwinevents import * |
| 12 | |
| 13 | |
| 14 | # List of windows known to the main loop. |
| 15 | # |
| 16 | windows = [] |
| 17 | |
| 18 | |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 19 | # Last window that ever received an event |
| 20 | # |
| 21 | last_window = None |
| 22 | |
| 23 | |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 24 | # Function to register a window. |
| 25 | # |
| 26 | def register(win): |
| 27 | # First test the dispatch function by passing it a null event -- |
| 28 | # this catches registration of unconforming windows. |
Guido van Rossum | 89a7869 | 1992-12-14 12:57:56 +0000 | [diff] [blame] | 29 | win.dispatch((WE_NULL, win, None)) |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 30 | 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 | # |
| 38 | def unregister(win): |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 39 | global last_window |
| 40 | if win == last_window: |
| 41 | last_window = None |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 42 | if win in windows: |
| 43 | windows.remove(win) # Not in 0.9.1 |
Guido van Rossum | 3f4f917 | 1991-11-19 20:41:07 +0000 | [diff] [blame] | 44 | # 0.9.1 solution: |
| 45 | #for i in range(len(windows)): |
| 46 | # if windows[i] = win: |
| 47 | # del windows[i] |
| 48 | # break |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 49 | |
| 50 | |
| 51 | # Interfaces used by WindowSched. |
| 52 | # |
| 53 | def countwindows(): |
| 54 | return len(windows) |
| 55 | # |
| 56 | def anywindow(): |
| 57 | if windows: |
| 58 | return windows[0] |
| 59 | else: |
| 60 | return None |
| 61 | |
| 62 | |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 63 | # NEW: register any number of file descriptors |
| 64 | # |
| 65 | fdlist = [] |
| 66 | select_args = None |
| 67 | select_handlers = None |
| 68 | # |
| 69 | def 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 | # |
| 81 | def 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 | # |
| 91 | def 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 | # |
| 112 | def 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 Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 122 | # 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 Rossum | fea2af1 | 1993-01-04 09:16:51 +0000 | [diff] [blame] | 129 | recursion_level = 0 # Hack to make it reentrant |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 130 | def mainloop(): |
Guido van Rossum | fea2af1 | 1993-01-04 09:16:51 +0000 | [diff] [blame] | 131 | 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 Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 149 | do_select() |
Guido van Rossum | fea2af1 | 1993-01-04 09:16:51 +0000 | [diff] [blame] | 150 | else: |
| 151 | break |
| 152 | finally: |
| 153 | recursion_level = recursion_level - 1 |
| 154 | |
| 155 | |
| 156 | # Check for events without ever blocking |
| 157 | # |
| 158 | def check(): |
| 159 | stdwin_select_handler() |
| 160 | # XXX Should check for socket stuff as well |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 161 | |
| 162 | |
| 163 | # Handle stdwin events until none are left |
| 164 | # |
| 165 | def stdwin_select_handler(*args): |
| 166 | while 1: |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 167 | try: |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 168 | event = stdwinq.pollevent() |
Guido van Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 169 | except KeyboardInterrupt: |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 170 | 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 | # |
| 181 | passthrough = WE_SIZE, WE_DRAW |
| 182 | beeping = WE_MOUSE_DOWN, WE_COMMAND, WE_CHAR, WE_KEY, WE_CLOSE, WE_MENU |
| 183 | # |
| 184 | def 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 Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 198 | |
| 199 | |
| 200 | # Dispatch a single event. |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 201 | # 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 Rossum | 7045dd0 | 1991-08-16 13:28:28 +0000 | [diff] [blame] | 204 | # Windows not in the windows list don't get their events: |
| 205 | # events for such windows are silently ignored. |
| 206 | # |
| 207 | def dispatch(event): |
Guido van Rossum | 18fc569 | 1992-11-26 09:17:19 +0000 | [diff] [blame] | 208 | 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 | # |
| 220 | class 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 | # |
| 242 | def askstr(prompt, default): |
| 243 | return stdwin.askstr(prompt, default) |
| 244 | # |
| 245 | def askync(prompt, yesorno): |
| 246 | return stdwin.askync(prompt, yesorno) |
| 247 | # |
| 248 | def askfile(prompt, default, new): |
| 249 | return stdwin.askfile(prompt, default, new) |
| 250 | # |
| 251 | def message(msg): |
| 252 | stdwin.message(msg) |