textui.py :  » Development » Lyntin » lyntin-4.2 » lyntin » ui » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Development » Lyntin 
Lyntin » lyntin 4.2 » lyntin » ui » textui.py
#######################################################################
# This file is part of Lyntin.
# copyright (c) Free Software Foundation 2001-2007
#
# Lyntin is distributed under the GNU General Public License license.  See the
# file LICENSE for distribution details.
# $Id: textui.py,v 1.10 2007/07/24 00:39:03 willhelm Exp $
#######################################################################
"""
Holds the text ui class.
"""
import re, sys, os, select, types
from lyntin import ansi,engine,event,utils,exported,config
from lyntin.ui import base,message


HELP_TEXT = """
The textui is the most basic ui you can get.  It works great over
telnet/ssh, but terrible in the Win32 command window.  At the same 
time, because it's so basic, it tends to be a good testing ui.

To enable GNU readline support (if it's available) use:

   --readline on

at the command line.  It'll look for a "readlinerc" file in your
datadir and set readline options from that.

Also, depending on what terminal you're running Lyntin from, it might 
help to set the TERMTYPE at the commandline.  Lyntin returns "lyntin" 
when asked, but doing something like:

   --term xterm

will kick Lyntin into returning "xterm" as the TERMTYPE.
"""

try:
  import termios
except ImportError:
  pass

myui = None

DEFAULT_COLOR = list(ansi.DEFAULT_COLOR)
DEFAULT_ANSI = chr(27) + "[0m"

def get_ui_instance():
  global myui
  if myui == None:
    myui = Textui()
  return myui

class Textui(base.BaseUI):
  """
  This is the text ui.  It's super basic and should run almost
  anywhere.  It lacks several useful functions that the Tkui
  and the Curses ui (eventually) will have.
  """
  def __init__(self):
    """ Initialize the textui."""
    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)
    self._currcolors = {}
    self._unfinishedcolor = {}

    self._tio = 0
    self._rline = 0
    self._echo = 1

    # grab the stdin stream at once,
    # enabling it's further redirection by another module
    self._stdin = sys.stdin

  def runui(self):
    global HELP_TEXT
    exported.add_help("textui", HELP_TEXT)
    exported.write_message("For textui help, type \"#help textui\".")

    # termios is the module that allows us to change echo for a terminal
    # but only if the module is present
    try:
      import termios
    except ImportError:
      self._tio = 0
    else:
      self._tio = 1
      echonew = termios.tcgetattr(self._stdin.fileno())
      self._onecho_attr = echonew[3]
      self._offecho_attr = echonew[3] & ~termios.ECHO

    if config.options.has_key("readline"):
      try:
        import readline
      except ImportError:
        self._rline = 0
        exported.write_error("Readline not available for your system.")
      else:
        self._rline = 1

        # we do some stuff to grab the readlinerc file if they have one
        # so the user can set some readline oriented things which makes 
        # things a little nicer for the user.
        d = exported.get_config("datadir")

        try:
          readline.read_init_file(d + "readlinerc")
        except:
          exported.write_error("Note: No readlinerc file available in %s." % d)
        
        exported.write_message("Readline enabled.")

    if self._tio == 0 or self._rline == 1:
      exported.write_error("Warming: echo off is unavailable.  " +
                           "Your password will be visible.")

    # go into the main loop
    self.run()
      
  def shutdown(self, args):
    """ Shuts down the textui and makes sure that we're echoing!"""
    if self._echo == 0:
      self.turnonecho()

  def turnonecho(self):
    """ Turns on echo if termios module is present."""
    if self._tio == 0 or self._rline == 1:
      return
    fd = self._stdin.fileno()
    new = termios.tcgetattr(fd)
    new[3] = self._onecho_attr
    try:
      termios.tcsetattr(fd, termios.TCSADRAIN, new)
      self._echo = 1
    except Exception, e:
      exported.write_error("textui: unable to turn on echo: %s" % e)

  def turnoffecho(self):
    """ Turns off echo if termios module is present."""
    if self._tio == 0 or self._rline == 1:
      return

    fd = self._stdin.fileno()
    new = termios.tcgetattr(fd)
    new[3] = self._offecho_attr
    try:
      termios.tcsetattr(fd, termios.TCSADRAIN, new)
      self._echo = 0
    except Exception, e:
      exported.write_error("textui: unable to turn off echo: %s" % e)


  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"]

    if name == "mudecho":
      if newvalue == 0:
        # echo off
        self.turnoffecho()
      else:
        # echo on
        self.turnonecho()

  def _posix_readline_input(self):
    """
    If the os is posix and the readline module is present, then we 
    use raw_input to grab user input.
    """
    try:
      return raw_input()
    except EOFError:
      pass

  def _posix_input(self):
    """
    If the os is posix and there is no readline module, then we
    use sys.stdin.readline().
    """
    readers,w,e = select.select([self._stdin], [], [])
    if readers:
      for mem in readers:
        try:
          return mem.readline()
        except IOError:
          pass

  def _non_posix_input(self):
    return self._stdin.readline()

  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

    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)

    line = msg.data
    ses = msg.session

    if line == '' or self.showTextForSession(ses) == 0:
      return

    # we prepend the session name to the text if this is not the 
    # current session sending text.
    pretext = ""
    if ses != None and ses != exported.get_current_session():
      pretext = "[" + ses.getName() + "] "

    if msg.type == message.ERROR or msg.type == message.LTDATA:
      if msg.type == message.ERROR:
        pretext = "error: " + pretext
      else:
        pretext = "lyntin: " + pretext

      line = pretext + utils.chomp(line).replace("\n", "\n" + pretext)
      if exported.get_config("ansicolor") == 1:
        line = DEFAULT_ANSI + line
      sys.stdout.write(line + "\n")
      return

    elif msg.type == message.USERDATA:
      # we don't print user data in the textui
      return

    if exported.get_config("ansicolor") == 0:
      if pretext:
        if line.endswith("\n"):
          line = (pretext + line[:-1].replace("\n", "\n" + pretext) + "\n")
        else:
          line = pretext + line.replace("\n", "\n" + pretext)
      sys.stdout.write(line)
      sys.stdout.flush()
      return

    # each session has a saved current color for mud data.  we grab
    # that current color--or user our default if we don't have one
    # for the session yet.
    if self._currcolors.has_key(ses):
      color = self._currcolors[ses]
    else:
      # need a copy of the list and not a reference to the list itself.
      color = list(DEFAULT_COLOR)


    # some sessions have an unfinished color as well--in case we
    # got a part of an ansi color code in a mud message, and the other
    # part is in another message.
    if self._unfinishedcolor.has_key(ses):
      leftover = self._unfinishedcolor[ses]
    else:
      leftover = ""

    lines = line.splitlines(1)
    if lines:
      for i in range(0, len(lines)):
        mem = lines[i]
        acolor = ansi.convert_tuple_to_ansi(color)

        color, leftover = ansi.figure_color(mem, color, leftover)

        if pretext:
          lines[i] = DEFAULT_ANSI + pretext + acolor + mem
        else:
          lines[i] = DEFAULT_ANSI + acolor + mem

      sys.stdout.write("".join(lines) + DEFAULT_ANSI)
      sys.stdout.flush()

    self._currcolors[ses] = color
    self._unfinishedcolor[ses] = leftover


  def prompt(self):
    """ Prints a prompt to the user."""
    sys.stdout.write("> ")
    sys.stdout.flush()

  def flush(self):
    """ Flushes the stdout.  Not sure we really need this
    but it's here."""
    sys.stdout.flush()

# Local variables:
# mode:python
# py-indent-offset:2
# tab-width:2
# End:
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.