import fcntl, os, select, pty, threading, time from buffer import Buffer, ACT_NORM, ACT_NONE from term import XTerm from point import Point # evil evil evil evil evil class XTermBuffer(Buffer, XTerm): btype = 'term' modename = 'pipe' termtype = 'xterm' delay = 0.1 bufsize = 8192 def __init__(self, app, cmd, args, name=None, modename=None): if modename: self.modename = modename XTerm.__init__(self) Buffer.__init__(self) self.application = app self._name = name or '*XTerm*' self._pid, self._pty = pty.fork() if self._pid == 0: # child process env = { 'TERM': self.termtype, 'PATH': os.getenv('PATH'), 'USER': os.getenv('USER'), 'HOME': os.getenv('HOME'), } os.execvpe(cmd, [cmd] + args, env) self._lock = threading.Lock() self._towrite = '' self._done = False self._set_nonblock(self._pty) self._thread = threading.Thread(target=self.pipe_read) self._thread.setDaemon(True) self._thread.start() def _w(self): return self.windows[0] def _get_height(self): return self._w().height def _get_width(self): return self._w().width # TERM STUFF def _term_insert(self, s): w = self._w() p = w.logical_cursor() if p.x == len(self.lines[p.y]): self.insert_string(p, s, act=ACT_NONE, force=True) else: self.overwrite_char(p, s, act=ACT_NONE, force=True) def term_do_newline(self): XTerm.term_do_newline(self) #p = w.logical_cursor() #self.insert_string(p, "XYZ", act=ACT_NONE, force=True) def term_do_style(self): pass def term_do_clear(self): self.set_lines([''], force=True) def term_do_home(self): w = self._w() w.cursor = w.first def term_do_clear_bol(self): w = self._w() p2 = w.logical_cursor() p1 = Point(0, p2.y) self.delete(p1, p2, force=True) def term_do_clear_eol(self): w = self._w() p1 = w.logical_cursor() p2 = Point(len(self.lines[p1.y]), p1.y) self.delete(p1, p2, force=True) def term_do_clear_eos(self): w = self._w() p1 = w.logical_cursor() p2 = self.get_buffer_end() self.delete(p1, p2, force=True) def term_do_backspace(self): self._w().backward() def term_do_tab(self): self._term_insert(' ') def term_do_newline(self): w = self._w() p = w.logical_cursor() if p.y < len(self.lines) - 1: w.start_of_line() w.next_line() else: w.end_of_line() self.insert_string(w.logical_cursor(), "\n", act=ACT_NONE, force=True) def term_do_creturn(self): self._w().start_of_line() def term_do_delete(self): self._w().delete_right() def term_handle_print(self, c): self._term_insert(c) def term_handle_ctl(self, c): n = ord(c) if n == 7: #bell pass elif n == 8: self.term_do_backspace() elif n == 9: self.term_do_tab() elif n == 10: self.term_do_newline() elif n == 13: self.term_do_creturn() elif n == 27: self.term_do_esc(c) elif n == 127: self.term_do_delete() else: self._term_insert('%03o' % n) def term_receive(self, s): for c in s: self.term_handle(c) # BUFFER STUFF def _set_nonblock(self, fd): flags = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NDELAY) def pipe_read(self): fd = self._pty pid, status = os.waitpid(self._pid, os.WNOHANG) if pid: return # wait until we are hooked up and ready to go while not self.windows: time.sleep(0.1) self.application.need_draw = True # ok, so start reading stuff! try: while not self._done: pid, status = os.waitpid(self._pid, os.WNOHANG) if pid: break if self._towrite: ifd, ofd, efd = select.select([fd], [fd], [fd], self.delay) else: ifd, ofd, efd = select.select([fd], [], [fd], self.delay) if ifd: data = os.read(ifd[0], self.bufsize) self.term_receive(data) self.application.need_draw = True if ofd: self._lock.acquire() n = os.write(ofd[0], self._towrite) self._towrite = self._towrite[n:] self._lock.release() if efd: self.term_receive(repr(efd)) #raise Exception, "exception is ready: %s" % repr(efd) pass except OSError: pass except TypeError: raise except AttributeError: raise if not pid: pid, status = os.waitpid(self._pid, os.WNOHANG) s = '\nprocess %d exited (%d)\n' % (pid, status >> 8) self._term_insert(s) self.application.need_draw = True os.close(fd) def pipe_write(self, s): self._lock.acquire() self._towrite += s self._lock.release() def name(self): return self._name def changed(self): return False def readonly(self): return True def undo(self, move, act): raise Exception("invalid") def redo(self, move, act): raise Exception("invalid") def reload(self): raise Exception("invalid") def close(self): self._done = True self._thread.join()