tuikit/driver_sdl.py
author Radek Brich <radek.brich@devl.cz>
Tue, 08 Jan 2013 01:12:07 +0100
changeset 57 911927edbdde
parent 54 86b84535726e
child 61 15088f62c4ac
permissions -rw-r--r--
DriverSDL: Add support for timeouts.

# -*- coding: utf-8 -*-
'''SDL driver.

Requires C++ extension module.
See sdlterm directory.

'''

from tuikit.driver import Driver

import logging
import subprocess

import sys
sys.path.insert(0, 'sdlterm/build/lib.linux-x86_64-3.2')
from sdlterm import SDLTerminal


class DriverSDL(Driver):

    '''SDL driver class'''

    color_map = {
        'black'         : 0,
        'blue'          : 1,
        'green'         : 2,
        'cyan'          : 3,
        'red'           : 4,
        'magenta'       : 5,
        'brown'         : 6,
        'lightgray'     : 7,
        'gray'          : 8,
        'lightblue'     : 9,
        'lightgreen'    : 10,
        'lightcyan'     : 11,
        'lightred'      : 12,
        'lightmagenta'  : 13,
        'yellow'        : 14,
        'white'         : 15,
    }

    style_map = {
        'bold'      : 1 << 0,  # bold font
        'underline' : 1 << 1,  # underline text
        'standout'  : 1 << 2,  # inverse bg/fg
        'blink'     : 1 << 3,  # blinking text
        'dim'       : 0,
    }

    def __init__(self):
        '''Initialize instance attributes'''
        Driver.__init__(self)
        self.log = logging.getLogger('tuikit')
        self.sdlterm = SDLTerminal()
        self.colors = {}     # maps names to attributes
        self.colorstack = [] # pushcolor/popcolor puts or gets attributes from this

    def start(self, mainfunc):
        '''Start driver and run mainfunc.'''
        self.select_system_font()
        self.sdlterm.resize(800, 600)
        self.size.w, self.size.h = self.sdlterm.width, self.sdlterm.height
        mainfunc()


    ## input ##

    def getevents(self, timeout=None):
        '''Process input, return list of events.

        timeout -- float, in seconds

        '''
        if timeout is not None:
            timeout = int(timeout*1000)
        event = self.sdlterm.wait_event(timeout)
        if not event:
            return []
        if event[0] == 'resize':
            self.size.w, self.size.h = self.sdlterm.width, self.sdlterm.height
        return [event]


    ## drawing ##

    def erase(self):
        '''Clear screen.'''
        self.sdlterm.erase()

    def putch(self, x, y, c):
        '''Output one unicode character to specified coordinates.'''
        if not self.clipstack.test(x, y):
            return
        self.sdlterm.putch(x, y, c)

    def commit(self):
        '''Commit changes to the screen.'''
        self.sdlterm.commit()


    ## colors ##

    def setcolor(self, name, desc):
        '''Define color name.

        name - name of color (e.g. 'normal', 'active')
        desc - color description - foreground, background, attributes (e.g. 'black on white, bold')

        '''
        parts = desc.split(',')
        fg, bg = parts[0].split(' on ')
        style = parts[1:]
        fg = self._color_by_name(fg)
        bg = self._color_by_name(bg)
        style = self._style_by_names(style)
        self.colors[name] = self.sdlterm.prepare_attr(fg, bg, style);

    def pushcolor(self, name):
        '''Add color on top of stack and use this color for following output.'''
        if len(self.colorprefix):
            prefixname = self.colorprefix[-1] + name
            if prefixname in self.colors:
                name = prefixname
        attr = self.colors[name]
        self.sdlterm.set_attr(attr)
        self.colorstack.append(attr)

    def popcolor(self):
        '''Remove color from top of stack and use new top color for following output.'''
        self.colorstack.pop()
        if len(self.colorstack):
            attr = self.colorstack[-1]
        else:
            attr = 0
        self.sdlterm.set_attr(attr)

    def _color_by_name(self, name):
        name = name.lower().strip()
        return self.color_map[name]

    def _style_by_names(self, names):
        style = 0
        for name in names:
            name = name.lower().strip()
            style |= self.style_map[name]
        return style


    ## cursor ##

    def showcursor(self, x, y):
        '''Set cursor to be shown at x, y coordinates.'''
        if not self.clipstack.test(x, y):
            return
        self.sdlterm.show_cursor(x, y)

    def hidecursor(self):
        '''Hide cursor.'''
        self.sdlterm.hide_cursor()

    ## private utility ##

    def _lookup_system_font(self, family, style):
        """Find system font by family name and style.

        style -- font style: 'normal', 'bold'

        Returns font file name.

        Uses fontconfig, it must be installed in system.

        """
        return subprocess.check_output([
            'fc-match',
            '%s:style=%s:fontformat=truetype:spacing=mono' % (family, style),
            '-f%{file}'])

    def select_system_font(self, family='monospace'):
        """Search for system fonts by family name and use them.

        Example family names:
            'monospace' - default monospace system font
            'DejaVu Sans Mono'
            'Liberation Mono'
        """
        fname_regular = self._lookup_system_font(family, 'normal')
        fname_bold = self._lookup_system_font(family, 'bold')
        self.log.info('DriverSDL: using fonts:\n%s\n%s', fname_regular.decode(), fname_bold.decode())
        self.sdlterm.select_font(fname_regular, fname_bold, 12)


driverclass = DriverSDL