import xshm, os, time, select, codeop #import cmds BGCOLOR = 0xFFFFFF FGCOLOR = 0x000000 #FGCOLOR = 0xC0C0C0 #BGCOLOR = 0x000000 #WIDTH = 1000 #HEIGHT = 720 WIDTH = 640 HEIGHT = 480 DPY = xshm.Display(WIDTH, HEIGHT, 0) DPY.keyevents() FONT = DPY.font('fixed') KeyPressed = 2 KeyReleased = 3 class TextZone: font = FONT fgcolor = FGCOLOR bgcolor = BGCOLOR def __init__(self, text=''): self.text = text def getheight(self, width): htot = 0 for line in self.getlines(): w, h = self.font.size(line) htot += h return htot def draw(self, y): self.font.select(self.fgcolor, self.bgcolor) for line in self.getlines(): DPY.write(0, y+self.font.ascent, line) w, h = self.font.size(line) y += h return y def getlines(self): return self.text.split('\n') def charpos(self, index): htot = 0 for line, realline in zip(self.getlines(), self.text.split('\n')): index += len(line) - len(realline) if index <= len(line): w, h = self.font.size(line[:index]) return w, htot w, h = self.font.size(line) htot += h index -= (len(line)+1) def drawcursor(self, y, index=None): if index is None: index = len(self.text) try: chr = self.text[index] except IndexError: chr = ' ' else: if chr == '\n': chr = ' ' self.font.select(self.bgcolor, self.fgcolor) x1, y1 = self.charpos(index) DPY.write(x1, y+y1+self.font.ascent, chr) class TextInputZone(TextZone): fgcolor = 0x600000 def __init__(self, ps1='>>> ', ps2='... '): TextZone.__init__(self) self.cursor = len(self.text) self.history = [''] self.historypos = 0 self.ps1 = ps1 self.ps2 = ps2 def getlines(self): lines = self.text.split('\n') lines[0] = self.ps1 + lines[0] for i in range(1, len(lines)): lines[i] = self.ps2 + lines[i] return lines def clear(self): self.historypos = len(self.history) self.history.insert(self.historypos-1, self.getcommand()) self.setcommand('') def getcommand(self): return self.text def setcommand(self, command): self.text = command self.cursor = len(self.text) def draw(self, y): y2 = TextZone.draw(self, y) self.drawcursor(y, self.cursor) return y2 def insert(self, text): self.text = (self.text[:self.cursor] + text + self.text[self.cursor:]) self.cursor += len(text) def newline(self): linestart = self.text[:self.cursor].rfind('\n') + 1 linelength = self.text[linestart:].find('\n') if linelength < 0: linelength = len(self.text) - linestart #else: # orig = linestart+linelength+1 # nextlinelength = self.text[orig:].find('\n') # if nextlinelength < 0: # nextlinelength = len(self.text) - orig # if self.text[orig:orig+nextlinelength] == ' '*nextlinelength: # self.text = self.text[:orig] + self.text[orig+nextlinelength+1:] indent = 0 while indent < linelength and self.text[linestart+indent] == ' ': indent += 1 if linelength > 0 and self.text[linestart+linelength-1] == ':': indent += 4 self.insert('\n' + ' '*indent) def handle(self, (keysym, event, keyname), callback): if event == KeyPressed: if keyname == '\x08': # backspace if self.cursor > 0: self.text = (self.text[:self.cursor-1] + self.text[self.cursor:]) self.cursor -= 1 elif keyname == '\x7f': # del if self.cursor < len(self.text): self.text = (self.text[:self.cursor] + self.text[self.cursor+1:]) elif keyname in ('\r', '\n'): # enter i = self.text.rfind('\n') if i >= 0 and (self.cursor <= i or self.text[i+1:].strip()): self.newline() elif not callback.entertext(self): self.newline() elif keyname == '\x04': # ctrl-D raise SystemExit elif keyname: self.insert(keyname) elif keysym == 65361: # left if self.cursor > 0: self.cursor -= 1 elif keysym == 65363: # right if self.cursor < len(self.text): self.cursor += 1 elif keysym == 65362: # up if self.history[self.historypos] != self.getcommand(): self.historypos = len(self.history)-1 self.history[-1] = self.getcommand() if self.historypos > 0: self.historypos -= 1 self.setcommand(self.history[self.historypos]) elif keysym == 65364: # down if self.history[self.historypos] != self.getcommand(): self.historypos = len(self.history)-1 self.history[-1] = self.getcommand() if self.historypos < len(self.history)-1: self.historypos += 1 self.setcommand(self.history[self.historypos]) elif keysym == 65365: # pgup callback.scroll(-callback.getpagesize()/2) elif keysym == 65366: # pgdn callback.scroll(callback.getpagesize()/2) elif keysym == 65360: # home self.cursor = 0 elif keysym == 65367: # end self.cursor = len(self.text) else: pass #print keysym, `keyname` class ImageZone: def __init__(self): self.w = self.h = self.curh = 0 def getheight(self, width): self.flush() return self.curh def draw(self, y): for deltay, pixmap in self.pixmaps(): DPY.putppm(0, y + deltay, pixmap) return y + self.h class ImageNodeZone(ImageZone): # DISABLED for now CACHE = [] def __init__(self, imagenodes): ImageZone.__init__(self) self.imagenodes = imagenodes def flush(self): cache = self.CACHE for i in range(len(cache)-1, -1, -1): cacheline = cache[i] if cacheline[0] == self.imagenodes: del cache[i] break else: if len(cache) > 24: del cache[0] print '* loading', self.imagenodes try: planes = [cmds.ppmbreak(cmds.load(n)) for n in self.imagenodes] except: w = h = 16 data = '\x00\x00\xFF' * (w * h) else: w = planes[0][0] h = planes[0][1] data = cmds.combine(*[data[2] for data in planes]) del planes pixmap = DPY.pixmap(w, h, data) cacheline = self.imagenodes, pixmap, w, h cache.append(cacheline) imagenodess, pixmap, self.w, self.h = cacheline self.curh = self.h return pixmap def pixmaps(self): return [(0, self.flush())] class ScrollZone: width = WIDTH height = HEIGHT def __init__(self): self.lines = [] self.startline = 0, 0 self.trackbottom = True def bottomline(self): h0 = self.height for i in range(len(self.lines)-1, -1, -1): line = self.lines[i] h = line.getheight(self.width) h0 -= h if h0 <= 0: return i, -h0 return 0, 0 def draw(self, y): startindex, startofs = self.startline y -= startofs for line in self.lines[startindex:]: if y >= self.height: break y = line.draw(y) return y def refresh(self): if self.trackbottom: self.startline = self.bottomline() FONT.select(BGCOLOR, FGCOLOR) DPY.clear() self.draw(0) DPY.flip() def run(self): input = TextInputZone() self.lines.append(input) while True: events = DPY.keyevents() if events: for event in events: input.handle(event, self) else: self.refresh() select.select([DPY.fd()], [], []) def entertext(self, input): command = input.getcommand() zones = do_cmd(command) if zones is None: # more input needed return False else: zone = TextZone('\n'.join(input.getlines())) zone.fgcolor = 0x000000 self.lines.insert(len(self.lines)-1, zone) input.clear() for zone in zones: self.lines.insert(len(self.lines)-1, zone) return True def getpagesize(self): return self.height def scroll(self, dy): if dy < 0: self.trackbottom = False startindex, startofs = self.startline dy += startofs while dy < 0 and startindex > 0: startindex -= 1 dy += self.lines[startindex].getheight(self.width) while startindex < len(self.lines)-1: h = self.lines[startindex].getheight(self.width) if dy < h: break startindex += 1 dy -= h bottomline = self.bottomline() self.startline = startindex, max(dy, 0) if self.startline >= bottomline: self.startline = bottomline self.trackbottom = True class img: def __init__(self, *ns): self.ns = ns def __repr__(self): return 'img(%s)' % (', '.join(map(str, self.ns))) def __mul__(self, other): if isinstance(other, int): other = (other,) elif not isinstance(other, tuple): other = other.ns return img(*(self.ns + other)) GLOBALS = {} # cmds.__dict__ LOCALS = {'img': img} def do_cmd(command, compiler=codeop.CommandCompiler()): import sys, cStringIO, __builtin__ oldstdout = sys.stdout oldstderr = sys.stderr olddisplayhook = sys.displayhook try: result = [] f = cStringIO.StringIO() def flush(): s = f.getvalue() f.seek(0) f.truncate() if s.endswith('\n'): s = s[:-1] if s: result.append(TextZone(s)) def nshow(*ns): flush() result.append(ImageNodeZone(ns)) def mydisplayhook(object): olddisplayhook(object) #if isinstance(object, img): # nshow(*object.ns) #elif isinstance(object, int) and cmds.jpegData.apply(object): # nshow(object) #elif isinstance(object, list): # for item in object: # if isinstance(item, img): # nshow(*item.ns) sys.stdout = sys.stderr = f sys.displayhook = mydisplayhook try: command = command.strip() if command: if '\n' in command: command += '\n' co = compiler(command, '', 'single') if co is None: return None # more input needed exec co in GLOBALS, LOCALS except: import traceback traceback.print_exc() finally: sys.stdout = oldstdout sys.stderr = oldstderr sys.displayhook = olddisplayhook flush() return result main = ScrollZone() main.run()