| import os | |
| import sys | |
| import string | |
| from Tkinter import * | |
| from ScrolledText import ScrolledText | |
| from Dialog import Dialog | |
| import signal | |
| BUFSIZE = 512 | |
| class ShellWindow(ScrolledText): | |
| def __init__(self, master=None, shell=None, **cnf): | |
| if not shell: | |
| try: | |
| shell = os.environ['SHELL'] | |
| except KeyError: | |
| shell = '/bin/sh' | |
| shell = shell + ' -i' | |
| args = string.split(shell) | |
| shell = args[0] | |
| apply(ScrolledText.__init__, (self, master), cnf) | |
| self.pos = '1.0' | |
| self.bind('<Return>', self.inputhandler) | |
| self.bind('<Control-c>', self.sigint) | |
| self.bind('<Control-t>', self.sigterm) | |
| self.bind('<Control-k>', self.sigkill) | |
| self.bind('<Control-d>', self.sendeof) | |
| self.pid, self.fromchild, self.tochild = spawn(shell, args) | |
| self.tk.createfilehandler(self.fromchild, READABLE, | |
| self.outputhandler) | |
| def outputhandler(self, file, mask): | |
| data = os.read(file, BUFSIZE) | |
| if not data: | |
| self.tk.deletefilehandler(file) | |
| pid, sts = os.waitpid(self.pid, 0) | |
| print 'pid', pid, 'status', sts | |
| self.pid = None | |
| detail = sts>>8 | |
| cause = sts & 0xff | |
| if cause == 0: | |
| msg = "exit status %d" % detail | |
| else: | |
| msg = "killed by signal %d" % (cause & 0x7f) | |
| if cause & 0x80: | |
| msg = msg + " -- core dumped" | |
| Dialog(self.master, | |
| text=msg, | |
| title="Exit status", | |
| bitmap='warning', | |
| default=0, | |
| strings=('OK',)) | |
| return | |
| self.insert(END, data) | |
| self.pos = self.index("end - 1 char") | |
| self.yview_pickplace(END) | |
| def inputhandler(self, *args): | |
| if not self.pid: | |
| self.no_process() | |
| return "break" | |
| self.insert(END, "\n") | |
| line = self.get(self.pos, "end - 1 char") | |
| self.pos = self.index(END) | |
| os.write(self.tochild, line) | |
| return "break" | |
| def sendeof(self, *args): | |
| if not self.pid: | |
| self.no_process() | |
| return "break" | |
| os.close(self.tochild) | |
| return "break" | |
| def sendsig(self, sig): | |
| if not self.pid: | |
| self.no_process() | |
| return "break" | |
| os.kill(self.pid, sig) | |
| return "break" | |
| def sigint(self, *args): | |
| return self.sendsig(signal.SIGINT) | |
| def sigquit(self, *args): | |
| return self.sendsig(signal.SIGQUIT) | |
| def sigterm(self, *args): | |
| return self.sendsig(signal.SIGTERM) | |
| def sigkill(self, *args): | |
| return self.sendsig(signal.SIGKILL) | |
| def no_process(self): | |
| Dialog(self.master, | |
| text="No active process", | |
| title="No process", | |
| bitmap='error', | |
| default=0, | |
| strings=('OK',)) | |
| MAXFD = 100 # Max number of file descriptors (os.getdtablesize()???) | |
| def spawn(prog, args): | |
| p2cread, p2cwrite = os.pipe() | |
| c2pread, c2pwrite = os.pipe() | |
| pid = os.fork() | |
| if pid == 0: | |
| # Child | |
| for i in 0, 1, 2: | |
| try: | |
| os.close(i) | |
| except os.error: | |
| pass | |
| if os.dup(p2cread) <> 0: | |
| sys.stderr.write('popen2: bad read dup\n') | |
| if os.dup(c2pwrite) <> 1: | |
| sys.stderr.write('popen2: bad write dup\n') | |
| if os.dup(c2pwrite) <> 2: | |
| sys.stderr.write('popen2: bad write dup\n') | |
| os.closerange(3, MAXFD) | |
| try: | |
| os.execvp(prog, args) | |
| finally: | |
| sys.stderr.write('execvp failed\n') | |
| os._exit(1) | |
| os.close(p2cread) | |
| os.close(c2pwrite) | |
| return pid, c2pread, p2cwrite | |
| def test(): | |
| shell = string.join(sys.argv[1:]) | |
| root = Tk() | |
| root.minsize(1, 1) | |
| if shell: | |
| w = ShellWindow(root, shell=shell) | |
| else: | |
| w = ShellWindow(root) | |
| w.pack(expand=1, fill=BOTH) | |
| w.focus_set() | |
| w.tk.mainloop() | |
| if __name__ == '__main__': | |
| test() |