class wxui(base.BaseUI): def __init__(self): base.BaseUI.__init__(self) exported.hook_register("shutdown_hook", self.shutdown) exported.hook_register("to_user_hook", self.write) exported.hook_register("config_change_hook", self.configChangeHandler) exported.hook_register("bell_hook", self.bell) exported.hook_register("prompt_hook", lambda x: self.write( { 'message': message.Message(x["prompt"], message.MUDDATA, x["session"]) } ) ) self.app = wx.App() self.window = window() self.window.input.Bind(wx.EVT_CHAR_HOOK, self.process) def process(self, event): key = event.GetKeyCode() if key == wx.WXK_RETURN: self.input_data() event.Skip() def runui(self): global HELP_TEXT exported.add_help("mudder", HELP_TEXT) self.window.Show() self.app.MainLoop() def wantMainThread(self): return 1 def shutdown(self, args): wx.GetApp().ExitMainLoop() def bell(self, args): """ Handles incoming bell characters.""" sys.stdout.write('\07') def configChangeHandler(self, args): """ Handles config changes (including mudecho).""" name = args["name"] newvalue = args["newvalue"] return def input_data(self, *args, **kwargs): """ This is the poll loop for user input.""" try: data = self.window.input.GetValue() self.window.input.SetValue("") if data != None: self.handleinput(data) except select.error, e: (errno,name) = e if errno == 4: exported.write_message("system exit: select.error.") event.ShutdownEvent().enqueue() return except SystemExit: exported.write_message("system exit: you'll be back...") event.ShutdownEvent().enqueue()
def run(self): """ This is the poll loop for user input.""" try: while not self.shutdownflag: if os.name == 'posix': if self._rline == 1: data = self._posix_readline_input() else: data = self._posix_input() else: data = self._non_posix_input() if data != None: self.handleinput(data) # FIXME - this is just plain icky. the issue is that # we need to know we're ending _before_ we block for # the next input. otherwise Lyntin will shut down except # for this thread which will hang around blocking until # the user hits the enter key. # # any good ideas for dealing with this are more than welcome. if data.find("#end") == 0: break except select.error, e: (errno, name) = e if errno == 4: exported.write_message("system exit: select.error.") event.ShutdownEvent().enqueue() return
def end_cmd(ses, args, input): """ Closes all sessions and quits out of Lyntin. Note: on most muds this will leave your character in a state of linkdeath--it does not sell all your stuff, return you to town, save your character, tell your friends goodbye, or anything of that nature. category: commands """ exported.write_message("end: you'll be back...") event.ShutdownEvent().enqueue()
def input_data(self, *args, **kwargs): """ This is the poll loop for user input.""" try: data = self.window.input.GetValue() self.window.input.SetValue("") if data != None: self.handleinput(data) except select.error, e: (errno,name) = e if errno == 4: exported.write_message("system exit: select.error.") event.ShutdownEvent().enqueue() return
# the user hits the enter key. # # any good ideas for dealing with this are more than welcome. if data.find("#end") == 0: break except select.error, e: (errno, name) = e if errno == 4: exported.write_message("system exit: select.error.") event.ShutdownEvent().enqueue() return except SystemExit: exported.write_message("system exit: you'll be back...") event.ShutdownEvent().enqueue() except: exported.write_traceback() event.ShutdownEvent().enqueue() def write(self, args): """ Handles writing information from the mud and/or Lyntin to the user. """ msg = args["message"] if type(msg) == types.StringType: msg = message.Message(msg, message.LTDATA)
def main(defaultoptions={}): """ This parses the command line arguments and makes sure they're all valid, instantiates a ui, does some setup, spins off an engine thread, and goes into the ui's mainloop. @param defaultoptions: the boot options to use. we update the config.options dict with these options--this is the easiest way to override the ui, moduledir, datadir, et al from a Lyntin run script. @type defaultoptions: dict """ try: import sys, os, traceback, ConfigParser from lyntin import config, event, utils, exported from lyntin.ui import base import locale locale.setlocale(locale.LC_ALL, '') config.options.update(defaultoptions) # read through options and arguments optlist = utils.parse_args(sys.argv[1:]) for mem in optlist: if mem[0] == '--help': print constants.HELPTEXT sys.exit(0) elif mem[0] == '--version': print constants.VERSION sys.exit(0) elif mem[0] in ["--configuration", "-c"]: # ini files OVERRIDE the default options # they can provide multiple ini files, but each new # ini file will OVERRIDE the contents of the previous ini file # where the two files intersect. parser = ConfigParser.ConfigParser() parser.read([mem[1]]) newoptions = {} for s in parser.sections(): for o in parser.options(s): c = parser.get(s, o).split(",") if newoptions.has_key(o): newoptions[o] += c else: newoptions[o] = c config.options.update(newoptions) else: opt = mem[0] while opt.startswith("-"): opt = opt[1:] if len(opt) > 0: if config.options.has_key(opt): if type(config.options[opt]) is list: config.options[opt].append(mem[1]) else: config.options[opt] = mem[1] else: config.options[opt] = [mem[1]] for mem in ["datadir", "ui", "commandchar"]: if config.options.has_key(mem) and type( config.options[mem]) is list: config.options[mem] = config.options[mem][0] # if they haven't set the datadir via the command line, then # we go see if they have a HOME in their environment variables.... if not config.options["datadir"]: if os.environ.has_key("HOME"): config.options["datadir"] = os.environ["HOME"] config.options["datadir"] = utils.fixdir(config.options["datadir"]) def on_shutdown(): """ This gets called by the Python interpreter atexit. The reason we do shutdown stuff here is we're more likely to catch things here than we are to let everything cycle through the ShutdownEvent. This should probably get fixed up at some point in the future. """ sys.stderr.write("goodbye.\n") #exported.hook_spam("shutdown_hook", {}) import atexit atexit.register(on_shutdown) # instantiate the engine Engine.instance = Engine() exported.myengine = Engine.instance Engine.instance._setupConfiguration() # instantiate the ui uiinstance = None try: uiname = str(config.options['ui']) modulename = uiname + "ui" uiinstance = base.get_ui(modulename) if not uiinstance: raise ValueError("No ui instance.") except Exception, e: print "Cannot start '%s': %s" % (uiname, e) traceback.print_exc() sys.exit(0) Engine.instance.setUI(uiinstance) # do some more silly initialization stuff # adds the .lyntinrc file to the readfile list if it exists. if config.options["datadir"]: lyntinrcfile = config.options["datadir"] + ".lyntinrc" if os.path.exists(lyntinrcfile): # we want the .lyntinrc file read in first, so then other # files can overwrite the contents therein config.options['readfile'].insert(0, lyntinrcfile) # import modules listed in modulesinit exported.write_message("Loading Lyntin modules.") try: import modules.__init__ modules.__init__.load_modules() except: exported.write_traceback("Modules did not load correctly.") sys.exit(1) # spam the startup hook exported.hook_spam("startup_hook", {}) commandchar = Engine.instance._managers["config"].get("commandchar") # handle command files for mem in config.options['readfile']: exported.write_message("Reading in file " + mem) # we have to escape windows os separators because \ has a specific # meaning in the argparser mem = mem.replace("\\", "\\\\") exported.lyntin_command("%sread %s" % (commandchar, mem), internal=1) # we're done initialization! exported.write_message(constants.STARTUPTEXT) Engine.instance.writePrompt() engine_thread = Engine.instance.startthread("engine", Engine.instance.runengine) timer_thread = Engine.instance.startthread("timer", Engine.instance.runtimer) try: Engine.instance._ui.runui() finally: sys.stderr.write("Shutting down...") event.ShutdownEvent().enqueue() engine_thread.join(10) timer_thread.join(10)
def runui(self): # # This is the loop for user input polling and for mud output. # global HELP_TEXT exported.add_help("cursesui", HELP_TEXT) try: stdscr = curses.initscr() if curses.has_colors(): curses.start_color() for i in xrange(1, 64): curses.init_pair(i, i % 8, i / 8) curses.raw() curses.noecho() curses.nonl() curses.meta(1) out = None edit = None scrollback = None lines = self.lines_ exported.write_message( "For Cursesui help, type \"#help cursesui\".") exported.write_message( "For some commands help, type \"#help curses\".") dirty_count = 0 timestamp = 0 output_count = 0 hotkey_buffer = '' keyboard_buffer = [] select_timeout = 100 keyboard_fd = sys.stdin.fileno() output_pipe_fd = self.output_[0] select_input_list = [keyboard_fd, output_pipe_fd] while self.running_: # # set output windows: # if not out: stdscr = curses.initscr() (screen_h, screen_w) = stdscr.getmaxyx() win = curses.newwin(1, screen_w, screen_h - 1, 0) if edit: edit.attach(win) else: edit = inputbox(self, win) if not scrollback: out = scroller( curses.newwin(screen_h - 1, screen_w, 0, 0), lines) else: scroll_h = screen_h / 3 * 2 out_h = (screen_h - 2) - scroll_h scrollback = scroller( curses.newwin(scroll_h, screen_w, 0, 0), lines[:]) scrollback.redraw() wborder = curses.newwin(1, screen_w, scroll_h, 0) wborder.bkgd(curses.ACS_HLINE) wborder.erase() wborder.noutrefresh() out = scroller( curses.newwin(out_h, screen_w, scroll_h + 1, 0), lines) out.redraw() edit._align() if keyboard_buffer and not hotkey_buffer: ch = keyboard_buffer.pop() else: ch = win.getch() if ch == curses.ERR: # drop the hotkey buffer when the keyboard goes idle hotkey_buffer = '' # enter idle mode: (i, o, x) = select.select(select_input_list, [], [], select_timeout) if not i: # timeout was hit: out.redraw(self.cfg_maxscrollback_) select_timeout = 100 dirty_count = 0 continue else: if keyboard_fd in i: ch = win.getch() if output_pipe_fd in i: line = os.read(output_pipe_fd, 1024) dirty_count += len(line) if ch == curses.ERR: timestamp_now = time() if ((timestamp_now - timestamp) > 0.2) or (dirty_count > self.cfg_lazy_): out.redraw(self.cfg_maxscrollback_) select_timeout = 100 dirty_count = 0 output_count = 0 else: select_timeout = 0.2 output_count += 1 timestamp = timestamp_now continue keyboard_buffer.insert(0, ch) if ch < 256: keycodename = chr(ch) hotkey_buffer += keycodename if self.cfg_keydebug_: if is_a_char(keycodename): exported.write_message(keycodename) elif ch == 0x1b: exported.write_message("<ESC>") binding = bindings.get(hotkey_buffer) if binding: hotkey_buffer = '' keyboard_buffer = [] self.handleinput(binding[1], internal=1) continue elif not filter(lambda x: x.startswith(hotkey_buffer), bindings.keys()): hotkey_buffer = '' continue else: keycodename = keytext.get(ch) if keycodename: if self.cfg_keydebug_: exported.write_message(keycodename) binding = bindings.get(keycodename) if binding: self.handleinput(binding[1], internal=1) keyboard_buffer.pop() # get it back hotkey_buffer = '' continue if ch == curses.KEY_PPAGE: if not scrollback: scrollback = 1 # create scrollback window at next iteration out = None else: scrollback.redraw(scroll=-(scroll_h / 2 + 1)) continue if ch == curses.KEY_NPAGE: if scrollback: scrollback.redraw(scroll=scroll_h / 2 + 1) continue if ch == curses.ascii.ESC and scrollback: scrollback = None out = None continue ch = edit.do_command(ch) if ch in (curses.ascii.CR, curses.ascii.LF): self.handleinput(edit.get_string()) edit.set("") elif ch in (curses.KEY_RESIZE, curses.ascii.FF): # force screen redraw out = None continue elif ch == curses.ascii.ETX: # Ctrl-C break except SystemExit: event.ShutdownEvent().enqueue() endcurses() except: import traceback endcurses() traceback.print_exc(file=sys.stdout) raw_input("Press enter to exit...") event.ShutdownEvent().enqueue() else: endcurses()