Exemplo n.º 1
0
 def pos(self, yloc=None, xloc=None):
     """
     Returns terminal sequence to move cursor to window-relative position.
     """
     term = getterminal()
     return term.move((yloc if yloc is not None else 0) + self.yloc,
                      (xloc if xloc is not None else 0) + self.xloc)
Exemplo n.º 2
0
    def __init__(self, height, width, yloc, xloc, colors=None, glyphs=None):
        """
        Class constructor for base windowing class.

        :param int width: width of window.
        :param int height: height of window.
        :param int yloc: y-location of window.
        :param int xloc: x-location of window.
        :param dict colors: color theme, only key value of ``highlight`` is used.
        :param dict glyphs: bordering window character glyphs.
        """
        self.height = height
        self.width = width
        self.yloc = yloc
        self.xloc = xloc
        self.init_theme(colors, glyphs)

        self._xpadding = 1
        self._ypadding = 1
        self._alignment = 'left'
        self._moved = False

        if not self.isinview():
            # https://github.com/jquast/x84/issues/161
            warnings.warn(
                'AnsiWindow(height={self.height}, width={self.width}, '
                'yloc={self.yloc}, xloc={self.xloc}) not in viewport '
                'Terminal(height={term.height}, width={term.width})'
                .format(self=self, term=getterminal()), FutureWarning)
Exemplo n.º 3
0
Arquivo: editor.py Projeto: hick/x84
 def eol(self):
     """
     Return True when no more input can be accepted (end of line).
     """
     from x84.bbs.session import getterminal
     term = getterminal()
     return term.length(self.content) >= self.max_length
Exemplo n.º 4
0
 def isinview(self):
     """ Whether this window is in bounds of terminal dimensions. """
     term = getterminal()
     return (self.xloc >= 0
             and self.xloc + self.width <= term.width
             and self.yloc >= 0
             and self.yloc + self.height <= term.height)
Exemplo n.º 5
0
    def __init__(self, *args, **kwargs):
        """
        Class constructor.

        :param int width: width of window.
        :param int yloc: y-location of window.
        :param int xloc: x-location of window.
        :param int max_length: maximum length of input (may be larger than width).
        :param dict colors: color theme, only key value of ``highlight`` is used.
        :param dict glyphs: bordering window character glyphs.
        :param dict keyset: command keys, global ``PC_KEYSET`` is used by default.
        """
        self._term = getterminal()
        self._horiz_shift = 0
        self._horiz_pos = 0
        # self._enable_scrolling = False
        self._horiz_lastshift = 0
        self._scroll_pct = kwargs.pop('scroll_pct', 25.0)
        self._margin_pct = kwargs.pop('margin_pct', 10.0)
        self._carriage_returned = False
        self._max_length = kwargs.pop('max_length', 0)
        self._quit = False
        self._bell = False
        self.content = kwargs.pop('content', u'')
        self._input_length = self._term.length(self.content)
        # there are some flaws about how a 'height' of a window must be
        # '3', even though we only want 1; we must also offset (y, x) by
        # 1 and width by 2: issue #161.
        kwargs['height'] = 3
        self.init_keystrokes(keyset=kwargs.pop('keyset', PC_KEYSET.copy()))
        AnsiWindow.__init__(self, *args, **kwargs)
Exemplo n.º 6
0
def ansiwrap(ucs, width=70, **kwargs):
    """Wrap a single paragraph of Unicode Ansi sequences,
    returning a list of wrapped lines.
    """
    warnings.warn('ansiwrap() deprecated, getterminal() now'
                  'supplies an equivalent .wrap() API')
    return getterminal().wrap(text=ucs, width=width, **kwargs)
Exemplo n.º 7
0
def encode_pipe(ucs):
    """
    encode_pipe(ucs) -> unicode

    Return new unicode terminal sequence, replacing EMCA-48 ANSI
    color sequences with their pipe-equivalent values.
    """
    # TODO: Support all kinds of terminal color sequences,
    # such as kermit or avatar or some such .. something non-emca
    outp = u''
    nxt = 0
    term = getterminal()
    ANSI_COLOR = re.compile(r'\033\[(\d{2,3})m')
    for idx in range(0, len(ucs)):
        if idx == nxt:
            # at sequence, point beyond it,
            match = ANSI_COLOR.match(ucs[idx:])
            if match:
                #nxt = idx + measure_length(ucs[idx:], term)
                nxt = idx + len(match.group(0))
                # http://wiki.mysticbbs.com/mci_codes
                value = int(match.group(1)) - 30
                if value >= 0 and value <= 60:
                    outp += u'|%02d' % (value,)
        if nxt <= idx:
            # append non-sequence to outp,
            outp += ucs[idx]
            # point beyond next sequence, if any,
            # otherwise point to next character
            nxt = idx + 1 #measure_length(ucs[idx:], term) + 1
    return outp
Exemplo n.º 8
0
 def process_keystroke(self, keystroke):
     """
     Process the keystroke received by run method and return terminal
     sequence suitable for refreshing when that keystroke modifies the
     window.
     """
     from x84.bbs.session import getterminal
     term = getterminal()
     self.moved = False
     rstr = u''
     if keystroke in self.keyset['refresh']:
         rstr += self.refresh()
     elif keystroke in self.keyset['up']:
         rstr += self.move_up()
     elif keystroke in self.keyset['down']:
         rstr += self.move_down()
     elif keystroke in self.keyset['home']:
         rstr += self.move_home()
     elif keystroke in self.keyset['end']:
         rstr += self.move_end()
     elif keystroke in self.keyset['pgup']:
         rstr += self.move_pgup()
     elif keystroke in self.keyset['pgdown']:
         rstr += self.move_pgdown()
     elif keystroke in self.keyset['exit']:
         self._quit = True
     else:
         logger = logging.getLogger()
         logger.debug('unhandled, %r', keystroke)
     return rstr
Exemplo n.º 9
0
Arquivo: editor.py Projeto: hick/x84
    def backspace(self):
        """
        Remove character from end of content buffer,
        scroll as necessary.
        """
        if 0 == len(self.content):
            return u''
        from x84.bbs.session import getterminal
        term = getterminal()

        rstr = u''
        # measured backspace erases over double-wide
        len_toss = term.length(self.content[-1])
        len_move = len(self.content[-1])
        self.content = self.content[:-1]
        if (self.is_scrolled and (self._horiz_pos < self.scroll_amt)):
            # shift left,
            self._horiz_shift -= self.scroll_amt
            self._horiz_pos += self.scroll_amt
            rstr += self.refresh()
        else:
            rstr += u''.join((
                self.fixate(0),
                u'\b' * len_toss,
                u' ' * len_move,
                u'\b' * len_move,))
            self._horiz_pos -= 1
        return rstr
Exemplo n.º 10
0
    def __init__(self, cmd='/bin/uname', args=(), env=None, cp437=False):
        """
        Class initializer.

        :param str cmd: full path of command to execute.
        :param tuple args: command arguments as tuple.
        :param bool cp437: When true, forces decoding of external program as
                           codepage 437.  This is the most common encoding used
                           by DOS doors.
        :param dict env: Environment variables to extend to the sub-process.
                         You should more than likely specify values for TERM,
                         PATH, HOME, and LANG.
        """
        self._session, self._term = getsession(), getterminal()
        self.cmd = cmd
        if isinstance(args, tuple):
            self.args = (self.cmd,) + args
        elif isinstance(args, list):
            self.args = [self.cmd, ] + args
        else:
            raise ValueError('args must be tuple or list')

        self.log = logging.getLogger(__name__)
        self.env = (env or {}).copy()
        self.env.update(
            {'LANG': env.get('LANG', 'en_US.UTF-8'),
             'TERM': env.get('TERM', self._term.kind),
             'PATH': env.get('PATH', get_ini('door', 'path')),
             'HOME': env.get('HOME', os.getenv('HOME')),
             'LINES': str(self._term.height),
             'COLUMNS': str(self._term.width),
             })

        self.cp437 = cp437
        self._utf8_decoder = codecs.getincrementaldecoder('utf8')()
Exemplo n.º 11
0
 def title(self, ansi_text):
     """
     Returns sequence that positions and displays unicode sequence
     'ansi_text' at the title location of the window.
     """
     term = getterminal()
     xloc = self.width / 2 - min(term.length(ansi_text) / 2, self.width / 2)
     return self.pos(0, max(0, xloc)) + ansi_text
Exemplo n.º 12
0
 def init_theme(self):
     """
     Initialize color['highlight'].
     """
     from x84.bbs.session import getterminal
     self.colors['highlight'] = getterminal().reverse_green
     self.glyphs['strip'] = u' $'  # indicates content was stripped
     AnsiWindow.init_theme(self)
Exemplo n.º 13
0
Arquivo: selector.py Projeto: hick/x84
 def init_theme(self, colors=None, glyphs=None):
     from x84.bbs.session import getterminal
     term = getterminal()
     colors = colors or {
         'selected': term.reverse_yellow,
         'unselected': term.bold_black,
     }
     AnsiWindow.init_theme(self, colors=colors, glyphs=glyphs)
Exemplo n.º 14
0
Arquivo: lightbar.py Projeto: hick/x84
 def init_theme(self, colors=None, glyphs=None):
     """
     Initialize color['highlight'].
     """
     from x84.bbs.session import getterminal
     colors = colors or {'highlight': getterminal().reverse_yellow}
     glyphs = glyphs or {'strip': u' $'}
     AnsiWindow.init_theme(self, colors, glyphs)
Exemplo n.º 15
0
 def footer(self, ansi_text):
     """
     Returns sequence that positions and displays unicode sequence
     'ansi_text' at the bottom edge of the window.
     """
     term = getterminal()
     xloc = self.width / 2 - min(term.length(ansi_text) / 2, self.width / 2)
     return self.pos(max(0, self.height - 1), max(0, xloc)) + ansi_text
Exemplo n.º 16
0
Arquivo: editor.py Projeto: hick/x84
 def init_theme(self, colors=None, glyphs=None):
     AnsiWindow.init_theme(self, colors, glyphs)
     if 'highlight' not in self.colors:
         from x84.bbs.session import getterminal
         term = getterminal()
         self.colors['highlight'] = term.yellow_reverse
     if 'strip' not in self.glyphs:
         self.glyphs['strip'] = u'$ '
Exemplo n.º 17
0
 def isinview(self):
     """
     Returns True if window is in view of the terminal window.
     """
     term = getterminal()
     return (self.xloc >= 0
             and self.xloc + self.width <= term.width
             and self.yloc >= 0
             and self.yloc + self.height <= term.height)
Exemplo n.º 18
0
 def init_theme(self):
     """
     Initialize colors['selected'] and colors['unselected'].
     """
     from x84.bbs.session import getterminal
     term = getterminal()
     AnsiWindow.init_theme(self)
     self.colors['selected'] = term.reverse
     self.colors['unselected'] = term.normal
Exemplo n.º 19
0
 def init_keystrokes(self, keyset):
     """ Sets keyboard keys for various editing keystrokes. """
     from x84.bbs.session import getterminal
     self.keyset = keyset
     term = getterminal()
     self.keyset['refresh'].append(term.KEY_REFRESH)
     self.keyset['left'].append(term.KEY_LEFT)
     self.keyset['right'].append(term.KEY_RIGHT)
     self.keyset['enter'].append(term.KEY_ENTER)
     self.keyset['exit'].append(term.KEY_ESCAPE)
Exemplo n.º 20
0
Arquivo: pager.py Projeto: jquast/x84
 def _content_wrap(self, ucs):
     """ Return word-wrapped text ``ucs`` that contains newlines. """
     term = getterminal()
     lines = []
     for line in ucs.splitlines():
         if line.strip():
             lines.extend(term.wrap(line, self.visible_width - 1))
         else:
             lines.append(u'')
     return lines
Exemplo n.º 21
0
    def refresh(self):
        """
        Returns unicode byts suitable for drawing line.
        No movement or positional sequences are returned.
        """
        from x84.bbs.session import getterminal

        lightbar = u"".join((self.colors.get("highlight", u""), " " * self.width, "\b" * self.width))
        content = self.hidden * len(Ansi(self.content)) if self.hidden else self.content
        return u"".join((lightbar, content, getterminal().cursor_visible))
Exemplo n.º 22
0
    def fixate(self, x_adjust=0):
        """
        Return terminal sequence suitable for placing cursor at current
        position in window. Set x_adjust to -1 to position cursor 'on'
        the last character, or 0 for 'after' (default).
        """
        from x84.bbs.session import getterminal

        xpos = self._xpadding + self._horiz_pos + x_adjust
        return self.pos(1, xpos) + getterminal().cursor_visible
Exemplo n.º 23
0
    def refresh_row(self, row):
        """
        Return unicode byte sequence suitable for moving to location ypos of
        window-relative row, and displaying any valid entry there, or using
        glyphs['erase'] if out of bounds. Strings are ansi color safe, and
        will be trimmed using glyphs['strip'] if their displayed width is
        wider than window.
        """
        from x84.bbs.session import getterminal
        from x84.bbs.output import decode_pipe
        term = getterminal()

        pos = self.pos(self.ypadding + row, self.xpadding)
        entry = self.vitem_shift + row
        if entry >= len(self.content):
            # out-of-bounds;
            disp_erase = self.glyphs.get('erase', u' ') * self.visible_width
            return u''.join((pos, disp_erase,))

        def fit_row(ucs):
            """ Strip a unicode row to fit window boundry, if necessary """
            column = self.visible_width + 1
            wrapped = term.wrap(ucs, column)
            if len(wrapped) > 1:
                marker = self.glyphs.get('strip', u' $')
                marker_column = self.visible_width - len(marker)
                wrapped = term.wrap(ucs, marker_column)
                ucs = term.ljust(wrapped[0].rstrip(), marker_column) + marker
                return ucs
            return term.ljust(ucs, column)

        # allow ucs data with '\r\n', to accomidate soft and hardbreaks; just
        # don't display them, really wrecks up cusor positioning.
        ucs = self.content[entry][1].strip(u'\r\n')

        # highlighted entry; strip of ansi sequences, use color 'highlight'
        # trim and append '$ ' if it cannot fit,
        if entry == self.index:
            ucs = term.strip_seqs(ucs)
            if term.length(ucs) > self.visible_width:
                ucs = fit_row(ucs)
            return u''.join((pos,
                             term.normal,
                             self.colors.get('highlight', u''),
                             self.align(ucs),
                             term.normal,))
        # unselected entry; retain ansi sequences, decode any pipe characters,
        # trim and append '$ ' if it cannot fit
        ucs = decode_pipe(ucs)
        if term.length(ucs) > self.visible_width:
            ucs = fit_row(ucs)
        return u''.join((pos,
                         self.colors.get('lowlight', u''),
                         self.align(ucs),
                         term.normal,))
Exemplo n.º 24
0
Arquivo: pager.py Projeto: jquast/x84
 def init_keystrokes(self, keyset):
     """ Sets keyboard keys for various editing keystrokes. """
     term = getterminal()
     self.keyset = keyset
     self.keyset['home'].append(term.KEY_HOME)
     self.keyset['end'].append(term.KEY_END)
     self.keyset['pgup'].append(term.KEY_PGUP)
     self.keyset['pgdown'].append(term.KEY_PGDOWN)
     self.keyset['up'].append(term.KEY_UP)
     self.keyset['down'].append(term.KEY_DOWN)
     self.keyset['down'].append(term.KEY_ENTER)
     self.keyset['exit'].append(term.KEY_ESCAPE)
Exemplo n.º 25
0
    def align(self, text, width=None):
        """
        Return ``text`` alignmnd to ``width`` using self.alignment.

        When None (default), the visible width of this window is used.
        """
        term = getterminal()
        width = width if width is not None else (self.visible_width)
        return (term.rjust(text, width) if self.alignment == 'right' else
                term.ljust(text, width) if self.alignment == 'left' else
                term.center(text, width)
                )
Exemplo n.º 26
0
Arquivo: pager.py Projeto: gofore/x84
 def init_keystrokes(self, keyset):
     """ Sets keyboard keys for various editing keystrokes. """
     term = getterminal()
     self.keyset = keyset
     self.keyset["home"].append(term.KEY_HOME)
     self.keyset["end"].append(term.KEY_END)
     self.keyset["pgup"].append(term.KEY_PGUP)
     self.keyset["pgdown"].append(term.KEY_PGDOWN)
     self.keyset["up"].append(term.KEY_UP)
     self.keyset["down"].append(term.KEY_DOWN)
     self.keyset["down"].append(term.KEY_ENTER)
     self.keyset["exit"].append(term.KEY_ESCAPE)
Exemplo n.º 27
0
Arquivo: editor.py Projeto: hick/x84
 def init_keystrokes(self, keyset):
     """
     This initializer sets keyboard keys for backspace/exit.
     """
     from x84.bbs.session import getterminal
     term = getterminal()
     self.keyset = keyset
     self.keyset['refresh'].append(term.KEY_REFRESH)
     self.keyset['backspace'].append(term.KEY_BACKSPACE)
     self.keyset['backspace'].append(term.KEY_DELETE)
     self.keyset['enter'].append(term.KEY_ENTER)
     self.keyset['exit'].append(term.KEY_ESCAPE)
Exemplo n.º 28
0
 def init_keystrokes(self):
     """
     Merge curses-detected application keys into a VI_KEYSET-formatted
     keyset, for keys 'refresh', 'left', 'right', 'enter', and 'exit'.
     """
     from x84.bbs.session import getterminal
     term = getterminal()
     self.keyset['refresh'].append(term.KEY_REFRESH)
     self.keyset['left'].append(term.KEY_LEFT)
     self.keyset['right'].append(term.KEY_RIGHT)
     self.keyset['enter'].append(term.KEY_ENTER)
     self.keyset['exit'].append(term.KEY_ESCAPE)
Exemplo n.º 29
0
 def read(self):
     """ Reads input until the ENTER or ESCAPE key is pressed (Blocking). """
     from x84.bbs import getterminal
     from x84.bbs.output import echo
     self._selected = False
     self._quit = False
     echo(self.refresh())
     term = getterminal()
     while not (self.selected or self.quit):
         echo(self.process_keystroke(term.inkey()) or u'')
     if self.quit:
         return None
     return self.selection
Exemplo n.º 30
0
Arquivo: pager.py Projeto: gofore/x84
    def refresh_row(self, row):
        """
        Return unicode string suitable for refreshing pager row.

        :param int row: target row by visible index.
        :rtype: str
        """
        term = getterminal()
        ucs = u""
        if row < len(self.visible_content):
            ucs = self.visible_content[row]
        disp_position = self.pos(row + self.ypadding, self.xpadding)
        return u"".join((term.normal, disp_position, self.align(ucs), term.normal))
Exemplo n.º 31
0
 def init_keystrokes(self, keyset):
     """
     This initializer sets keyboard keys for various editing keystrokes.
     """
     from x84.bbs.session import getterminal
     term = getterminal()
     self.keyset = keyset
     self.keyset['home'].append(term.KEY_HOME)
     self.keyset['end'].append(term.KEY_END)
     self.keyset['pgup'].append(term.KEY_PGUP)
     self.keyset['pgdown'].append(term.KEY_PGDOWN)
     self.keyset['up'].append(term.KEY_UP)
     self.keyset['down'].append(term.KEY_DOWN)
     self.keyset['enter'].append(term.KEY_ENTER)
     self.keyset['exit'].append(term.KEY_ESCAPE)
Exemplo n.º 32
0
Arquivo: editor.py Projeto: hick/x84
    def refresh(self):
        """
        Returns unicode suitable for drawing edit line.

        No movement or positional sequences are returned.
        """
        from x84.bbs.session import getterminal
        term = getterminal()
        disp_lightbar = u''.join(
            (term.normal, self.colors.get('highlight', u''), ' ' * self.width,
             '\b' * self.width))
        content = self.content
        if self.hidden:
            content = self.hidden * term.length(self.content)
        return u''.join((disp_lightbar, content, term.cursor_visible))
Exemplo n.º 33
0
    def read(self):
        """
        Reads input until the ENTER or ESCAPE key is pressed (Blocking).

        Returns selection content, or None when canceled.
        """
        self._selected = False
        self._quit = False
        echo(self.refresh())
        term = getterminal()
        while not (self.selected or self.quit):
            echo(self.process_keystroke(term.inkey()))
        if self.quit:
            return None
        return self.selection[0]
Exemplo n.º 34
0
 def init_theme(self):
     """
     This initializer sets glyphs and colors appropriate for a "theme",
     override this method to create a common color and graphic set.
     """
     session = getsession()
     term = getterminal()
     self.colors['normal'] = term.normal
     if term.number_of_colors != 0:
         self.colors['border'] = term.cyan
     # start with default 'ascii'
     self.glyphs = GLYPHSETS['ascii'].copy()
     # PC-DOS 'thin' on smart terminals
     if session.env.get('TERM') != 'unknown':
         self.glyphs = GLYPHSETS['thin'].copy()
Exemplo n.º 35
0
    def read(self):
        """
        Reads input until the ENTER or ESCAPE key is pressed (Blocking).

        Allows backspacing. Returns unicode text, or None when canceled.
        """
        echo(self.refresh())
        self._quit = False
        self._carriage_returned = False
        term = getterminal()
        while not (self.quit or self.carriage_returned):
            inp = term.inkey()
            echo(self.process_keystroke(inp))
        if not self.quit:
            return self.content
        return None
Exemplo n.º 36
0
Arquivo: pager.py Projeto: splaice/x84
    def refresh(self, start_row=0):
        """
        Return unicode string suitable for refreshing pager window.

        :param int start_row: refresh from only visible row 'start_row'
                              and downward. This can be useful if only
                              the last line is modified; or in an
                              'insert' operation: only the last line
                              need be refreshed.
        :rtype: str
        """
        term = getterminal()
        return u''.join([term.normal] + [
            self.refresh_row(row)
            for row in range(start_row, len(self.visible_content))
        ] + [term.normal])
Exemplo n.º 37
0
Arquivo: ansiwin.py Projeto: hick/x84
 def init_theme(self, colors=None, glyphs=None):
     """
     This initializer sets glyphs and colors appropriate for a "theme".
     """
     # set defaults,
     term = getterminal()
     self.colors = {
         'normal': term.normal,
         'border': term.number_of_colors and term.cyan or term.normal,
     }
     self.glyphs = GLYPHSETS['thin'].copy()
     # allow user override
     if colors is not None:
         self.colors.update(colors)
     if glyphs is not None:
         self.glyphs.update(glyphs)
Exemplo n.º 38
0
    def refresh_row(self, row):
        """
        Return unicode string suitable for refreshing pager row.

        :param int row: target row by visible index.
        :rtype: str
        """
        term = getterminal()
        ucs = u''
        if row < len(self.visible_content):
            ucs = self.visible_content[row]
        disp_position = self.pos(row + self.ypadding, self.xpadding)
        return u''.join((term.normal,
                         disp_position,
                         self.align(ucs),
                         term.normal))
Exemplo n.º 39
0
def decode_pipe(ucs):
    """
    Return ucs containing 'pipe codes' with terminal color sequences.

    These are sometimes known as LORD codes, as they were used in the DOS Door
    game of the same name. Compliments :func:`encode_pipe`.

    :param str ucs: string containing 'pipe codes'.
    :rtype: str
    """
    # simple optimization, no '|' ? exit early!
    if u'|' not in ucs:
        return ucs
    term = getterminal()
    outp = u''
    ptr = 0
    match = None
    ANSI_PIPE = re.compile(r'\|(\d{2,3}|\|)')

    for match in ANSI_PIPE.finditer(ucs):
        val = match.group(1)
        # allow escaping using a second pipe
        if val == u'|':
            outp += ucs[ptr:match.start() + 1]
            ptr = match.end()
            continue
        # 0..7 -> 7
        while val.startswith('0'):
            val = val[1:]
        int_value = 0 if 0 == len(val) else int(val, 10)
        assert int_value >= 0 and int_value <= 256
        # colors 0-7 and 16-256 are as-is term.color()
        # special accommodations for 8-15, some termcaps are ok
        # with term.color(11), whereas others have trouble, help
        # out by using dim color and bold attribute instead.
        attr = u''
        if int_value == 7:
            attr = term.normal
        elif int_value < 7 or int_value >= 16:
            attr = term.normal + term.color(int_value)
        elif int_value <= 15:
            attr = term.normal + term.color(int_value - 8) + term.bold
        outp += ucs[ptr:match.start()] + attr
        ptr = match.end()

    outp = ucs if match is None else u''.join((outp, ucs[match.end():]))
    return u''.join((outp, term.normal))
Exemplo n.º 40
0
 def refresh(self):
     """
     Returns unicode byts suitable for drawing line.
     No movement or positional sequences are returned.
     """
     from x84.bbs.session import getterminal
     term = getterminal()
     lightbar = u''.join((
         term.normal,
         self.colors.get('highlight', u''),
         ' ' * self.width,
         '\b' * self.width))
     content = (self.hidden * len(Ansi(self.content))
                if self.hidden else self.content)
     return u''.join((
         lightbar,
         content,
         term.cursor_visible))
Exemplo n.º 41
0
    def __init__(self,
                 cmd='/bin/uname',
                 args=(),
                 env=None,
                 cp437=False,
                 raw=False):
        """
        Class initializer.

        :param str cmd: full path of command to execute.
        :param tuple args: command arguments as tuple.
        :param bool cp437: When true, forces decoding of external program as
                           codepage 437.  This is the most common encoding used
                           by DOS doors.
        :param dict env: Environment variables to extend to the sub-process.
                         You should more than likely specify values for TERM,
                         PATH, HOME, and LANG.
        :param bool raw: Whether or not to use raw output
        """
        self._session, self._term = getsession(), getterminal()
        self.cmd = cmd
        if isinstance(args, tuple):
            self.args = (self.cmd, ) + args
        elif isinstance(args, list):
            self.args = [
                self.cmd,
            ] + args
        else:
            raise ValueError('args must be tuple or list')

        self.log = logging.getLogger(__name__)
        self.env = (env or {}).copy()
        self.env.update({
            'LANG': env.get('LANG', 'en_US.UTF-8'),
            'TERM': env.get('TERM', self._term.kind),
            'PATH': env.get('PATH', get_ini('door', 'path')),
            'HOME': env.get('HOME', os.getenv('HOME')),
            'LINES': str(self._term.height),
            'COLUMNS': str(self._term.width),
        })

        self.cp437 = cp437
        self._utf8_decoder = codecs.getincrementaldecoder('utf8')()
        self.raw = raw
Exemplo n.º 42
0
def decode_pipe(ucs):
    """
    decode_pipe(ucs) -> unicode

    Return new terminal sequence, replacing 'pipe codes', such as u'|03'
    with this terminals equivalent attribute sequence.
    """
    # simple optimization, no '|' ? exit early!
    if u'|' not in ucs:
        return ucs
    term = getterminal()
    outp = u''
    ptr = 0
    match = None
    ANSI_PIPE = re.compile(r'\|(\d{2,3}|\|)')

    for match in ANSI_PIPE.finditer(ucs):
        val = match.group(1)
        # allow escaping using a second pipe
        if val == u'|':
            outp += ucs[ptr:match.start() + 1]
            ptr = match.end()
            continue
        # 07 -> 7
        while val.startswith('0'):
            val = val[1:]
        int_value = 0 if 0 == len(val) else int(val, 10)
        assert int_value >= 0 and int_value <= 256
        # colors 0-7 and 16-256 are as-is term.color()
        # special accommodations for 8-15, some termcaps are ok
        # with term.color(11), whereas others have trouble, help
        # out by using dim color and bold attribute instead.
        attr = u''
        if int_value == 7:
            attr = term.normal
        elif int_value < 7 or int_value >= 16:
            attr = term.normal + term.color(int_value)
        elif int_value <= 15:
            attr = term.normal + term.color(int_value - 8) + term.bold
        outp += ucs[ptr:match.start()] + attr
        ptr = match.end()

    outp = ucs if match is None else u''.join((outp, ucs[match.end():]))
    return u''.join((outp, term.normal))
Exemplo n.º 43
0
Arquivo: editor.py Projeto: hick/x84
 def read(self):
     """
     Reads input until the ENTER or ESCAPE key is pressed (Blocking).
     Allows backspacing. Returns unicode text, or None when canceled.
     """
     from x84.bbs import getch
     from x84.bbs.output import echo
     from x84.bbs.session import getterminal
     term = getterminal()
     self._carriage_returned = False
     self._quit = False
     echo(self.refresh())
     while not (self.quit or self.carriage_returned):
         inp = getch()
         echo(self.process_keystroke(inp))
     echo(term.normal)
     if not self.quit:
         return self.content
     return None
Exemplo n.º 44
0
Arquivo: ansiwin.py Projeto: hick/x84
    def __init__(self, height, width, yloc, xloc, colors=None, glyphs=None):
        """
        Constructor class for a simple Window.
        """
        self.height = height
        self.width = width
        self.yloc = yloc
        self.xloc = xloc
        self.init_theme(colors, glyphs)

        self._xpadding = 1
        self._ypadding = 1
        self._alignment = 'left'
        self._moved = False

        assert self.isinview(), (
            'AnsiWindow(height={self.height}, width={self.width}, '
            'yloc={self.yloc}, xloc={self.xloc}) not in viewport '
            'Terminal(height={term.height}, width={term.width})'
            .format(self=self, term=getterminal()))
Exemplo n.º 45
0
 def __init__(self, height, width, yloc, xloc):
     """
     Construct an ansi window. Its base purpose is to provide
     window-relativie positions using the pos() method.
     """
     self.height = height
     self.width = width
     self.yloc = yloc
     self.xloc = xloc
     self._xpadding = 1
     self._ypadding = 1
     self._alignment = 'left'
     self._moved = False
     self.glyphs = dict()
     self.colors = dict()
     self.init_theme()
     term = getterminal()
     assert self.isinview(), (
         'AnsiWindow(height=%s, width=%s, yloc=%s, xloc=%s)'
         ' not in viewport Terminal(height=%s, width=%s)' %
         (height, width, yloc, xloc, term.height, term.width))
Exemplo n.º 46
0
Arquivo: editor.py Projeto: hick/x84
 def process_keystroke(self, keystroke):
     """
     Process the keystroke received by read method and take action.
     """
     from x84.bbs.session import getterminal
     term = getterminal()
     self._quit = False
     if keystroke in self.keyset['refresh']:
         return u'\b' * term.length(self.content) + self.refresh()
     elif keystroke in self.keyset['backspace']:
         if len(self.content) != 0:
             len_toss = term.length(self.content[-1])
             self.content = self.content[:-1]
             return u''.join((
                 u'\b' * len_toss,
                 u' ' * len_toss,
                 u'\b',
             ))
     elif keystroke in self.keyset['backword']:
         if len(self.content) != 0:
             ridx = self.content.rstrip().rfind(' ') + 1
             toss = term.length(self.content[ridx:])
             move = len(self.content[ridx:])
             self.content = self.content[:ridx]
             return u''.join((
                 u'\b' * toss,
                 u' ' * move,
                 u'\b' * move,
             ))
     elif keystroke in self.keyset['enter']:
         self._carriage_returned = True
     elif keystroke in self.keyset['exit']:
         self._quit = True
     elif type(keystroke) is int:
         return u''
     elif (ord(keystroke) >= ord(' ') and
           (term.length(self.content) < self.width or self.width is None)):
         self.content += keystroke
         return keystroke if not self.hidden else self.hidden
     return u''
Exemplo n.º 47
0
    def decode_pipe(self):
        """
        S.decode_pipe() -> unicode

        Return new terminal sequence, replacing 'pipe codes', such as u'|03'
        with this terminals equivalent attribute sequence.
        """
        term = getterminal()
        ucs = u''
        ptr = 0
        match = None
        for match in ANSI_PIPE.finditer(self):
            ucs_value = match.group(1)
            # allow escaping using a second pipe
            if match.start() and self[match.start() - 1] == '|':
                continue
            # 07 -> 7
            while ucs_value.startswith('0'):
                ucs_value = ucs_value[1:]
            int_value = 0 if 0 == len(ucs_value) else int(ucs_value, 10)
            assert int_value >= 0 and int_value <= 256
            # colors 0-7 and 16-256 are as-is term.color()
            # special accomidations for 8-15, some termcaps are ok
            # with term.color(11), whereas others have trouble, help
            # out by using dim color and bold attribute instead.
            attr = u''
            if int_value <= 7 or int_value >= 16:
                attr = term.normal + term.color(int_value)
            elif int_value <= 15:
                attr = term.normal + term.color(int_value - 8) + term.bold
            ucs += self[ptr:match.start()] + attr
            ptr = match.end()
        if match is None:
            ucs = self
        else:
            ucs += self[match.end():]
        ptr = 0
        return ''.join((ucs, term.normal))
Exemplo n.º 48
0
Arquivo: editor.py Projeto: hick/x84
    def refresh(self):
        """
        Return unicode sequence suitable for refreshing the entire
        line and placing the cursor.

        A strange by-product; if scrolling was not previously enabled,
        it is if wrapping must occur; this can happen if a
        non-scrolling editor was provided a very large .content
        buffer, then later .refresh()'d. -- essentially enabling
        infinate scrolling
        """
        # reset position and detect new position
        from x84.bbs import getterminal
        term = getterminal()
        self._horiz_lastshift = self._horiz_shift
        self._horiz_shift = 0
        self._horiz_pos = 0
        for _count in range(term.length(self.content)):
            if (self._horiz_pos > (self.visible_width - self.scroll_amt)):
                self._horiz_shift += self.scroll_amt
                self._horiz_pos -= self.scroll_amt
                self.enable_scrolling = True
            self._horiz_pos += 1
        if self._horiz_shift > 0:
            self._horiz_shift += len(self.glyphs['strip'])
            prnt = u''.join((
                self.glyphs['strip'],
                self.content[self._horiz_shift:],
            ))
        else:
            prnt = self.content
        return u''.join((
            self.pos(self.ypadding, self.xpadding),
            term.normal,
            self.colors.get('highlight', u''),
            self.align(prnt),
            self.fixate(),
        ))
Exemplo n.º 49
0
Arquivo: editor.py Projeto: hick/x84
 def add(self, u_chr):
     """
     Returns output sequence necessary to add a character to
     content buffer.  An empty content buffer is returned if
     no data could be inserted. Sequences for re-displaying
     the full input line are returned when the character
     addition caused the window to scroll horizontally.
     Otherwise, the input is simply returned to be displayed
     ('local echo').
     """
     from x84.bbs import getterminal
     term = getterminal()
     if self.eol:
         # cannot input, at end of line!
         return u''
     # append to input
     self.content += u_chr
     # return character appended as output, ensure .fixate() is used first!
     self._horiz_pos += 1
     if self._horiz_pos >= (self.visible_width - self.margin_amt):
         # scrolling is required,
         return self.refresh()
     return term.normal + self.colors['highlight'] + u_chr
Exemplo n.º 50
0
    def __init__(self, width=None, content=u'', hidden=False,
                 colors=None, glyphs=None, keyset=None):
        """
        Class initializer.

        :param int width: the maximum input length.
        :param str content: given default content.
        :param str hidden: When non-False, a single 'mask' character for output.
        :param dict colors: optional dictionary containing key 'highlight'.
        :param dict glyphs: optional dictionary of window border characters.
        :param dict keyset: optional dictionary of line editing values.
        """
        self._term = getterminal()
        self.content = content or u''
        self.hidden = hidden
        self._width = width
        self._input_length = self._term.length(content)

        self._quit = False
        self._carriage_returned = False

        self.init_keystrokes(keyset=keyset or PC_KEYSET.copy())
        self.init_theme(colors=colors, glyphs=glyphs)
Exemplo n.º 51
0
    def __init__(self, height, width, yloc, xloc, colors=None, glyphs=None):
        """
        Class initializer for base windowing class.

        :param int width: width of window.
        :param int height: height of window.
        :param int yloc: y-location of window.
        :param int xloc: x-location of window.
        :param dict colors: color theme.
        :param dict glyphs: bordering window character glyphs.
        """
        from x84.bbs.session import getterminal
        self._term = getterminal()

        self.height = height
        self.width = width
        self.yloc = yloc
        self.xloc = xloc
        self.init_theme(colors, glyphs)

        self._xpadding = 1
        self._ypadding = 1
        self._alignment = 'left'
        self._moved = False
Exemplo n.º 52
0
    def refresh_row(self, row):
        """ Return string sequence suitable for refreshing current selection.

        Return unicode byte sequence suitable for moving to location ypos of
        window-relative row, and displaying any valid entry there, or using
        glyphs['erase'] if out of bounds. Strings are ansi color safe, and
        will be trimmed using glyphs['strip'] if their displayed width is
        wider than window.
        """
        term = getterminal()

        pos = self.pos(self.ypadding + row, self.xpadding)
        entry = self.vitem_shift + row
        if entry >= len(self.content):
            # out-of-bounds;
            disp_erase = self.glyphs.get('erase', u' ') * self.visible_width
            return u''.join((
                pos,
                disp_erase,
            ))

        def fit_row(ucs):
            """ Strip a unicode row to fit window boundry, if necessary """
            column = self.visible_width - 1
            wrapped = term.wrap(ucs, column)
            if len(wrapped) > 1:
                marker = self.glyphs.get('strip', u' $')
                marker_column = self.visible_width - len(marker)
                wrapped = term.wrap(ucs, marker_column)
                ucs = term.ljust(wrapped[0].rstrip(), marker_column) + marker
                return ucs
            return term.ljust(ucs, column)

        # allow ucs data with '\r\n', to accomidate soft and hardbreaks; just
        # don't display them, really wrecks up cusor positioning.
        ucs = self.content[entry][1].strip(u'\r\n')

        # highlighted entry; strip of ansi sequences, use color 'highlight'
        # trim and append '$ ' if it cannot fit,
        if entry == self.index:
            ucs = term.strip_seqs(ucs)
            if term.length(ucs) > self.visible_width:
                ucs = fit_row(ucs)
            return u''.join((
                pos,
                term.normal,
                self.colors.get('highlight', u''),
                self.align(ucs),
                term.normal,
            ))
        # unselected entry; retain ansi sequences, decode any pipe characters,
        # trim and append '$ ' if it cannot fit
        ucs = decode_pipe(ucs)
        if term.length(ucs) > self.visible_width:
            ucs = fit_row(ucs)
        return u''.join((
            pos,
            self.colors.get('lowlight', u''),
            self.align(ucs),
            term.normal,
        ))
Exemplo n.º 53
0
 def init_theme(self, colors=None, glyphs=None):
     """ Set color and bordering glyphs theme. """
     colors = colors or {'highlight': getterminal().reverse_yellow}
     glyphs = glyphs or {'strip': u' $'}
     AnsiWindow.init_theme(self, colors, glyphs)
Exemplo n.º 54
0
 def __new__(cls, object):
     warnings.warn('Ansi() deprecated, getterminal() now provides '
                   '.length(), .rjust(), .wrap(), etc.')
     new = Sequence.__new__(cls, object, getterminal())
     return new
Exemplo n.º 55
0
def showart(filepattern,
            encoding=None,
            auto_mode=True,
            center=False,
            poll_cancel=False,
            msg_cancel=None,
            force=False):
    """
    Yield unicode sequences for any given ANSI Art (of art_encoding).

    Effort is made to parse SAUCE data, translate input to unicode, and trim
    artwork too large to display.  If ``poll_cancel`` is not ``False``,
    represents time as float for each line to block for keypress -- if any is
    received, then iteration ends and ``msg_cancel`` is displayed as last line
    of art.

    If you provide no ``encoding``, the piece encoding will be based on either
    the encoding in the SAUCE record, the configured default or the default
    fallback ``CP437`` encoding.

    Alternate codecs are available if you provide the ``encoding`` argument.
    For example, if you want to show an Amiga style ASCII art file::

        >>> from x84.bbs import echo, showart
        >>> for line in showart('test.asc', 'topaz'):
        ...     echo(line)

    The ``auto_mode`` flag will, if set, only respect the selected encoding if
    the active session is UTF-8 capable.

    If ``center`` is set to ``True``, the piece will be centered respecting the
    current terminal's width.

    If ``force`` is set to true then the artwork will be displayed even if it's
    wider than the screen.

    """
    # pylint: disable=R0913,R0914
    #         Too many arguments
    #         Too many local variables
    term = getterminal()

    # When the given artfile pattern's folder is not absolute, nor relative to
    # our cwd, build a relative position of the folder by the calling module's
    # containing folder.  This only works for subdirectories (like 'art/').
    _folder = os.path.dirname(filepattern)
    if not (_folder.startswith(os.path.sep) or os.path.isdir(_folder)):
        # On occasion, after a general exception in a script, re-calling the
        # same script may cause yet another exception, HERE.  The 2nd call is
        # fine though; this only would effect a developer.
        #
        # Just try again.
        caller_module = inspect.stack()[1][1]
        rel_folder = os.path.dirname(caller_module)
        if _folder:
            rel_folder = os.path.join(rel_folder, _folder)
        if os.path.isdir(rel_folder):
            filepattern = os.path.join(rel_folder,
                                       os.path.basename(filepattern))

    # Open the piece
    try:
        filename = os.path.relpath(random.choice(glob.glob(filepattern)))
    except IndexError:
        filename = None

    if filename is None:
        yield u''.join((
            term.bold_red(u'-- '),
            u'no files matching {0}'.format(filepattern),
            term.bold_red(u' --'),
        ))
        return

    file_basename = os.path.basename(filename)

    # Parse the piece
    parsed = SAUCE(filename)

    # If no explicit encoding is given, we go through a couple of steps to
    # resolve the possible file encoding:
    if encoding is None:
        # 1. See if the SAUCE record has a font we know about, it's in the
        #    filler
        if parsed.record and parsed.filler_str in SAUCE_FONT_MAP:
            encoding = SAUCE_FONT_MAP[parsed.filler_str]

        # 2. Get the system default art encoding,
        #    or fall-back to cp437
        else:
            encoding = get_ini('system', 'art_utf8_codec') or 'cp437'

    # If auto_mode is enabled, we'll only use the input encoding on UTF-8
    # capable terminals, because our codecs do not know how to "transcode"
    # between the various encodings.
    if auto_mode:

        def _decode(what):
            # pylint: disable=C0111
            #         Missing function docstring (col 8)
            session = getsession()
            if session.encoding == 'utf8':
                return what.decode(encoding)
            elif session.encoding == 'cp437':
                return what.decode('cp437')
            else:
                return what

    # If auto_mode is disabled, we'll just respect whatever input encoding was
    # selected before
    else:
        _decode = lambda what: what.decode(encoding)

    # For wide terminals, center piece on screen using cursor movement
    # when center=True.
    padding = u''
    if center and term.width > 81:
        padding = term.move_x((term.width / 2) - 40)
    lines = _decode(parsed.data).splitlines()
    for idx, line in enumerate(lines):

        if poll_cancel is not False and term.inkey(poll_cancel):
            # Allow slow terminals to cancel by pressing a keystroke
            msg_cancel = msg_cancel or u''.join((
                term.normal,
                term.bold_black(u'-- '),
                u'canceled {0} by input'.format(os.path.basename(filename)),
                term.bold_black(u' --'),
            ))
            yield u'\r\n' + term.center(msg_cancel).rstrip() + u'\r\n'
            return

        line_length = term.length(line.rstrip())

        if force is False and not padding and term.width < line_length:
            # if the artwork is too wide and force=False, simply stop displaying it.
            msg_too_wide = u''.join((
                term.normal,
                term.bold_black(u'-- '),
                (u'canceled {0}, too wide:: {1}'.format(
                    file_basename, line_length)),
                term.bold_black(u' --'),
            ))
            yield (u'\r\n' + term.center(msg_too_wide).rstrip() + u'\r\n')
            return
        if idx == len(lines) - 1:
            # strip DOS end of file (^Z)
            line = line.rstrip('\x1a')
            if not line.strip():
                break
        yield padding + line + u'\r\n'
    yield term.normal
Exemplo n.º 56
0
Arquivo: door.py Projeto: splaice/x84
 def pageheight(self):
     """ Terminal height. """
     return getterminal().height