# GL STDWIN # # See stdwingl for a convenient hack to use this instead of built-in stdwin # without modifying your application, except for one line in the main file. # # Intrinsic differences with built-in stdwin (hard or impossible to fix): # - Need to call w.close() to close a window !!! # - Need to call m.close() to remove a menu !!! # - Doesn't enforce the existence of at most one drawing object # - No textedit package # - No X11 selections # # Not yet implemented: # - shade drawing # - elliptical arc drawing (need to play with transformation) # - more than one mouse button # - scroll bars (need to redo viewport handling to get this) # - partial redraws # - dialog boxes # - timer events # - cursors # # Extra features: # - color (for now, you need to know the colormap index) import gl import fm from GL import * from DEVICE import * from stdwinevents import * # Customizable constants # DEF_FONT = 'Times-Roman' # Default font DEF_SIZE = 12 # Default font size (points) MASK = 20 # Viewport minus scrmask # A structure to hold global variables # class Struct: pass G = Struct() # G.queue = [] # Pending STDWIN events G.drawqueue = [] # Windows that need WE_REDRAW G.windowmap = {} # Map window id to window object G.windowmap['0'] = None # For convenience G.focus = None # Input focus G.fg = BLACK # Foreground color G.bg = WHITE # Background color G.def_size = 0, 0 # Default window size G.def_pos = 0, 0 # Default window position # G.size = DEF_SIZE G.font = fm.findfont(DEF_FONT).scalefont(G.size) # Initialize GL # gl.foreground() gl.noport() dummygid = gl.winopen('') # Ask for all sorts of events # # Both REDRAW (= resize and/or redraw!) and INPUTCHANGE are implicitly queued #qdevice(REDRAW) #qdevice(INPUTCHANGE) # # Keyboard gl.qdevice(KEYBD) gl.qdevice(LEFTARROWKEY) gl.qdevice(RIGHTARROWKEY) gl.qdevice(UPARROWKEY) gl.qdevice(DOWNARROWKEY) gl.qdevice(LEFTALTKEY) gl.qdevice(RIGHTALTKEY) # # Mouse gl.qdevice(LEFTMOUSE) #gl.qdevice(MIDDLEMOUSE) gl.qdevice(RIGHTMOUSE) # Menu button # NB MOUSEX, MOUSEY events are queued on button down # # Window close requests gl.qdevice(WINQUIT) gl.qdevice(WINSHUT) # # These aren't needed #gl.qdevice(TIMER0) #gl.qdevice(WINFREEZE) #gl.qdevice(WINTHAW) #gl.qdevice(REDRAWICONIC) # STDWIN: create a new window # def open(title): h, v = G.def_pos width, height = G.def_size if h > 0 or v > 0: # Choose arbitrary defaults if h < 0: h = 10 if v < 0: v = 30 if width <= 0: width = 400 if height <= 0: height = 300 gl.prefposition(h, h+width, 1024-v, 1024-v-height) elif width > 0 or height > 0: if width <= 0: width = 400 if height <= 0: height = 300 gl.prefsize(width, height) from glstdwwin import WindowObject win = WindowObject()._init(title) G.windowmap[`win._gid`] = win return win # STDWIN: set default initial window position (0 means use default) # def setdefwinpos(h, v): G.def_pos = h, v # STDWIN: set default window size (0 means use default) # def setdefwinsize(width, height): G.def_size = width, height # STDWIN: beep or ring the bell # def fleep(): gl.ringbell() # STDWIN: set default foreground color # def setfgcolor(color): G.fg = color # STDWIN: set default background color # def setbgcolor(color): G.bg = color # STDWIN: get default foreground color # def getfgcolor(): return G.fgcolor # STDWIN: get default background color # def getbgcolor(): return G.bgcolor # Table mapping characters to key codes # key2code = key = {} key['A'] = AKEY key['B'] = BKEY key['C'] = CKEY key['D'] = DKEY key['E'] = EKEY key['F'] = FKEY key['G'] = GKEY key['H'] = HKEY key['I'] = IKEY key['J'] = JKEY key['K'] = KKEY key['L'] = LKEY key['M'] = MKEY key['N'] = NKEY key['O'] = OKEY key['P'] = PKEY key['Q'] = QKEY key['R'] = RKEY key['S'] = SKEY key['T'] = TKEY key['U'] = UKEY key['V'] = VKEY key['W'] = WKEY key['X'] = XKEY key['Y'] = YKEY key['Z'] = ZKEY key['0'] = ZEROKEY key['1'] = ONEKEY key['2'] = TWOKEY key['3'] = THREEKEY key['4'] = FOURKEY key['5'] = FIVEKEY key['6'] = SIXKEY key['7'] = SEVENKEY key['8'] = EIGHTKEY key['9'] = NINEKEY del key # code2key = {} codelist = [] for key in key2code.keys(): code = key2code[key] code2key[`code`] = key codelist.append(code) del key # STDWIN: wait for the next event # commands = {} commands['\r'] = WC_RETURN commands['\b'] = WC_BACKSPACE commands['\t'] = WC_TAB # def getevent(): while 1: # # Get next event from the processed queue, if any # if G.queue: event = G.queue[0] del G.queue[0] #print 'getevent from queue -->', event return event # # Get next event from the draw queue, if any, # but only if there is nothing in the system queue. # if G.drawqueue and not gl.qtest(): win = G.drawqueue[0] del G.drawqueue[0] gl.winset(win._gid) gl.color(win._bg) gl.clear() event = WE_DRAW, win, win._area #print 'getevent from drawqueue -->', event return event # # Get next event from system queue, blocking if necessary # until one is available. # Some cases immediately return the event, others do nothing # or append one or more events to the processed queue. # dev, val = gl.qread() # if dev == REDRAW: win = G.windowmap[`val`] old_area = win._area win._fixviewport() win._needredraw() if old_area <> win._area: #print 'getevent --> WE_SIZE' return WE_SIZE, win, None elif dev == KEYBD: if val == 3: raise KeyboardInterrupt # Control-C in window character = chr(val) if commands.has_key(character): return WE_COMMAND, G.focus, commands[character] return WE_CHAR, G.focus, character elif dev == LEFTARROWKEY: if val: return WE_COMMAND, G.focus, WC_LEFT elif dev == RIGHTARROWKEY: if val: return WE_COMMAND, G.focus, WC_RIGHT elif dev == UPARROWKEY: if val: return WE_COMMAND, G.focus, WC_UP elif dev == DOWNARROWKEY: if val: return WE_COMMAND, G.focus, WC_DOWN elif dev in (LEFTALTKEY, RIGHTALTKEY): if val: for code in codelist: gl.qdevice(code) else: for code in codelist: gl.unqdevice(code) elif dev in codelist: if val: event = G.focus._doshortcut(code2key[`dev`]) if event: return event elif dev == LEFTMOUSE: G.mousex = gl.getvaluator(MOUSEX) G.mousey = gl.getvaluator(MOUSEY) if val: type = WE_MOUSE_DOWN gl.qdevice(MOUSEX) gl.qdevice(MOUSEY) else: type = WE_MOUSE_UP gl.unqdevice(MOUSEX) gl.unqdevice(MOUSEY) return _mouseevent(type) elif dev == MOUSEX: G.mousex = val return _mouseevent(WE_MOUSE_MOVE) elif dev == MOUSEY: G.mousey = val return _mouseevent(WE_MOUSE_MOVE) elif dev == RIGHTMOUSE: # Menu button press/release if val: # Press event = G.focus._domenu() if event: return event elif dev == INPUTCHANGE: if G.focus: G.queue.append(WE_DEACTIVATE, G.focus, None) G.focus = G.windowmap[`val`] if G.focus: G.queue.append(WE_ACTIVATE, G.focus, None) elif dev in (WINSHUT, WINQUIT): return WE_CLOSE, G.windowmap[`val`], None else: print '*** qread() --> dev:', dev, 'val:', val # Helper routine to construct a mouse (up, move or down) event # def _mouseevent(type): gl.winset(G.focus._gid) orgx, orgy = gl.getorigin() sizex, sizey = gl.getsize() x = G.mousex - orgx y = G.mousey - orgy return type, G.focus, ((x, sizey-y), 1, 0, 0) # STDWIN: text measuring functions def baseline(): (printermatched, fixed_width, xorig, yorig, xsize, ysize, \ height, nglyphs) = G.font.getfontinfo() return height - yorig def lineheight(): (printermatched, fixed_width, xorig, yorig, xsize, ysize, \ height, nglyphs) = G.font.getfontinfo() return height def textbreak(string, width): # XXX Slooooow! n = len(string) nwidth = textwidth(string[:n]) while nwidth > width: n = n-1 nwidth = textwidth(string[:n]) return n def textwidth(string): return G.font.getstrwidth(string) # STDWIN: set default font and size def setfont(fontname): G.font = fm.findfont(fontname).scalefont(G.size) def setsize(size): ratio = float(size) / float(G.size) G.size = size G.font = G.font.scalefont(ratio) # Utility functions # Exclusive-or of two BYTES # def xor(x, y): a = bits(x) b = bits(y) c = [0, 0, 0, 0, 0, 0, 0, 0] for i in range(8): c[i] = (a[i] + b[i]) % 2 return stib(c) # Return the bits of a byte as a list of 8 integers # def bits(x): b = [0, 0, 0, 0, 0, 0, 0, 0] for i in range(8): x, b[i] = divmod(x, 2) return b # Convert a list of 8 integers (0|1) to a byte # def stib(b): x = 0 shift = 1 for i in range(8): x = x + b[i]*shift shift = shift*2 return x