Drop PyGame driver, replaced by compiled SDL driver.
authorRadek Brich <radek.brich@devl.cz>
Mon, 07 Jan 2013 23:15:17 +0100
changeset 56 282a07d20680
parent 55 1ab0edd5d784
child 57 911927edbdde
Drop PyGame driver, replaced by compiled SDL driver.
extra/pygame_font_render_glyph.patch
tests/pygame_font.py
tuikit/driver_pygame.py
--- a/extra/pygame_font_render_glyph.patch	Mon Jan 07 00:23:45 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-# Add Font.render_glyph() method to PyGame.
-#
-# This is nicer than full string rendering, as Tuikit renders screen
-# one character at the time and render_glyph is optimized for caching.
-# See documentation for TTF_RenderGlyph_Shaded and TTF_GlyphMetrics.
-#
-# Radek Brich <radek.brich@devl.cz>
-diff -rupd pygame-1.9.1release/src/font.c pygame-1.9.1release-my/src/font.c
---- pygame-1.9.1release/src/font.c	2009-06-17 01:18:34.000000000 +0200
-+++ pygame-1.9.1release-my/src/font.c	2011-10-09 23:19:46.000000000 +0200
-@@ -371,6 +371,78 @@ font_render (PyObject* self, PyObject* a
- }
- 
- static PyObject*
-+font_render_glyph (PyObject* self, PyObject* args)
-+{
-+    TTF_Font* font = PyFont_AsFont (self);
-+    int aa;
-+    PyObject* text, *final;
-+    PyObject* fg_rgba_obj, *bg_rgba_obj = NULL;
-+    Uint8 rgba[4];
-+    SDL_Surface* surf;
-+    SDL_Color foreg, backg;
-+
-+    if (!PyArg_ParseTuple (args, "OiO|O", &text, &aa, &fg_rgba_obj,
-+                           &bg_rgba_obj))
-+        return NULL;
-+
-+    if (!RGBAFromColorObj (fg_rgba_obj, rgba))
-+        return RAISE (PyExc_TypeError, "Invalid foreground RGBA argument");
-+    foreg.r = rgba[0];
-+    foreg.g = rgba[1];
-+    foreg.b = rgba[2];
-+    if (bg_rgba_obj)
-+    {
-+        if (!RGBAFromColorObj (bg_rgba_obj, rgba))
-+            return RAISE (PyExc_TypeError, "Invalid background RGBA argument");
-+        backg.r = rgba[0];
-+        backg.g = rgba[1];
-+        backg.b = rgba[2];
-+        backg.unused = 0;
-+    }
-+    else
-+    {
-+        backg.r = 0;
-+        backg.g = 0;
-+        backg.b = 0;
-+        backg.unused = 0;
-+    }
-+
-+    if (PyUnicode_Check (text) && PyObject_Length (text) == 1)
-+    {
-+        Py_UNICODE *buf = PyUnicode_AsUnicode (text);
-+        Uint16 ch = (Uint16) buf[0];
-+
-+        if (aa)
-+        {
-+            if (!bg_rgba_obj)
-+                surf = TTF_RenderGlyph_Blended (font, ch, foreg);
-+            else
-+                surf = TTF_RenderGlyph_Shaded (font, ch, foreg, backg);
-+        }
-+        else
-+            surf = TTF_RenderGlyph_Solid (font, ch, foreg);
-+    }
-+    else
-+        return RAISE (PyExc_TypeError, "char must be string of exactly one unicode character");
-+
-+    if (!surf)
-+        return RAISE (PyExc_SDLError, TTF_GetError());
-+
-+    if (!aa && bg_rgba_obj) /*turn off transparancy*/
-+    {
-+        SDL_SetColorKey (surf, 0, 0);
-+        surf->format->palette->colors[0].r = backg.r;
-+        surf->format->palette->colors[0].g = backg.g;
-+        surf->format->palette->colors[0].b = backg.b;
-+    }
-+
-+    final = PySurface_New (surf);
-+    if (!final)
-+        SDL_FreeSurface (surf);
-+    return final;
-+}
-+
-+static PyObject*
- font_size (PyObject* self, PyObject* args)
- {
-     TTF_Font* font = PyFont_AsFont (self);
-@@ -509,6 +581,7 @@ static PyMethodDef font_methods[] =
- 
-     { "metrics", font_metrics, METH_VARARGS, DOC_FONTMETRICS },
-     { "render", font_render, METH_VARARGS, DOC_FONTRENDER },
-+    { "render_glyph", font_render_glyph, METH_VARARGS, DOC_FONTRENDERGLYPH },
-     { "size", font_size, METH_VARARGS, DOC_FONTSIZE },
- 
-     { NULL, NULL, 0, NULL }
-diff -rupd pygame-1.9.1release/src/pygamedocs.h pygame-1.9.1release-my/src/pygamedocs.h
---- pygame-1.9.1release/src/pygamedocs.h	2009-06-19 08:20:25.000000000 +0200
-+++ pygame-1.9.1release-my/src/pygamedocs.h	2011-10-09 23:27:34.000000000 +0200
-@@ -299,6 +299,8 @@
- 
- #define DOC_FONTRENDER "Font.render(text, antialias, color, background=None): return Surface\ndraw text on a new Surface"
- 
-+#define DOC_FONTRENDERGLYPH "Font.render_glyph(char, antialias, color, background=None): return Surface\ndraw glyph of char on a new Surface"
-+
- #define DOC_FONTSIZE "Font.size(text): return (width, height)\ndetermine the amount of space needed to render text"
- 
- #define DOC_FONTSETUNDERLINE "Font.set_underline(bool): return None\ncontrol if text is rendered with an underline"
--- a/tests/pygame_font.py	Mon Jan 07 00:23:45 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-
-import pygame
-
-pygame.init()
-
-font = pygame.font.SysFont('dejavusansmono', 14)
-
-print('Size: %s %s' % font.size('Q'))
-print('Linesize: %s' % font.get_linesize())
-print('Height: %s' % font.get_height())
-print('Ascent: %s' % font.get_ascent())
-print('Descent: %s' % font.get_descent())
-
-chars = '┌─┐└─┘│'
-metrics = font.metrics(chars)
-print('metrics=(minx, maxx, miny, maxy, advance)')
-
-for c, m in zip(chars, metrics):
-    s = font.size(c)
-    surface = font.render(c, False, (255,255,255), (0,0,0))
-    ss = surface.get_size()
-    ssg = None
-    if hasattr(font, 'render_glyph'):
-        surface_glyph = font.render_glyph(c, False, (255,255,255), (0,0,0))
-        ssg = surface_glyph.get_size()
-    print('%s metrics=%s size=%s surface_size=%s glyph_size=%s' % (c, m, s, ss, ssg))
-
--- a/tuikit/driver_pygame.py	Mon Jan 07 00:23:45 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,370 +0,0 @@
-# -*- coding: utf-8 -*-
-'''PyGame driver.'''
-
-import pygame
-import logging
-
-from tuikit.driver import Driver
-from tuikit.common import Coords, Size
-
-
-class TerminalScreen:
-
-    '''Provide character-level output to screen SDL surface.
-
-    This is performance bottleneck and should be optimized as much as possible.
-
-    '''
-
-    def __init__(self):
-        fontselect = 'dejavusansmono,liberationmono,freemono'
-        self.font = pygame.font.SysFont(fontselect, 14)
-        self.font_bold = pygame.font.SysFont(fontselect, 14, True)
-        # get advance of some random char (all should be same in monospace font)
-        advance = self.font.metrics('Q')[0][4]
-        height = self.font.get_height()
-        self.charsize = Size(advance, height)
-        self.ascent = self.font.get_ascent()
-
-        # choose self.render() implementation
-        if hasattr(self.font, 'render_glyph'):
-            self.render_char = self.render_glyph
-        else:
-            self.render_char = self.render_noglyph
-
-        self.chars = None
-        self.attrs = None
-        self.default_attr = None
-        self.current_attr = None
-
-    def set_default_attr(self, fg, bg, flags):
-        self.default_attr = (fg, bg, flags)
-
-    def set_attr(self, fg, bg, flags):
-        self.current_attr = (fg, bg, flags)
-
-    def reset_attr(self):
-        self.current_attr = self.default_attr
-
-    def reset(self, w, h):
-        self.w, self.h = w, h
-        numchars = w * h
-        self.chars = [' '] * numchars
-        self.attrs = [self.default_attr] * numchars
-
-    def clear(self):
-        numchars = self.w * self.h
-        for pos in range(numchars):
-            self.chars[pos] = ' '
-            self.attrs[pos] = self.default_attr
-
-    def putch(self, x, y, c):
-        pos = y * self.w + x
-        self.chars[pos] = c
-        self.attrs[pos] = self.current_attr
-
-    def update(self, surface):
-        pos = 0
-        for y in range(self.h):
-            for x in range(self.w):
-                fgcolor, bgcolor, flags = self.attrs[pos]
-                c = self.chars[pos]
-                self.render_char(surface, x, y, c,
-                    fgcolor, bgcolor, flags)
-                pos += 1
-
-    def render_glyph(self, screen, x, y, c, fgcolor, bgcolor, flags):
-        '''Render using render_glyph and metrics.
-
-        This is the correct way, but the output seems same as of render_noglyph
-        and this implementation requires patching PyGame to work.
-
-        This implements render() method. See render_noglyph for other implementation.
-
-        '''
-        # draw background
-        dest = Coords(x * self.charsize.w, y * self.charsize.h)
-        if bgcolor != self.default_attr[1]:
-            screen.fill(bgcolor, pygame.Rect(dest.x, dest.y, self.charsize.w, self.charsize.h))
-
-        if c == ' ':
-            return
-
-        # choose font
-        if flags == 1:
-            font = self.font_bold
-        else:
-            font = self.font
-
-        # render character, get metrics
-        surface = font.render_glyph(c, True, fgcolor, bgcolor)
-        metrics = font.metrics(c)[0]
-        minx, maxx, miny, maxy, advance = metrics
-        height, ascent = self.charsize.h, self.ascent
-
-        # clips origin and area of rendered character according to metrics
-        startx, starty = 0, 0
-        if minx < 0:
-            startx = abs(minx)
-            minx = 0
-        if maxy > ascent:
-            starty = maxy - ascent
-            maxy -= starty
-        if ascent - miny > height:
-            miny = ascent - height
-        if maxx > advance:
-            maxx = advance
-
-        # draw character
-        dest.x += minx
-        dest.y += ascent - maxy
-        area = pygame.Rect(startx, starty, maxx - minx, maxy - miny)
-        screen.blit(surface, tuple(dest), area)
-
-    def render_noglyph(self, screen, x, y, c, fgcolor, bgcolor, attr):
-        '''Render character using normal text rendering.
-
-        This implements render() method. See render_glyph for other implementation.
-
-        '''
-        if attr == 'bold':
-            font = self.font_bold
-        else:
-            font = self.font
-
-        # render character, get metrics
-        surface = font.render(c, True, fgcolor, bgcolor)
-        metrics = font.metrics(c)[0]
-        minx = metrics[0]
-        startx = 0
-        if minx < 0:
-            startx = abs(minx)
-
-        # draw background
-        dest = Coords(x * self.charsize.w, y * self.charsize.h)
-        screen.fill(bgcolor, pygame.Rect(dest.x, dest.y, self.charsize.w, self.charsize.h))
-
-        # draw character
-        area = pygame.Rect(startx, 0, self.charsize.w, self.charsize.h)
-        screen.blit(surface, tuple(dest), area)
-
-
-class DriverPygame(Driver):
-
-    '''PyGame driver class.'''
-
-    keymap = {
-        pygame.K_ESCAPE     : 'escape',
-        pygame.K_TAB        : 'tab',
-        pygame.K_RETURN     : 'enter',
-        pygame.K_BACKSPACE  : 'backspace',
-        pygame.K_F1         : 'f1',
-        pygame.K_F2         : 'f2',
-        pygame.K_F3         : 'f3',
-        pygame.K_F4         : 'f4',
-        pygame.K_F5         : 'f5',
-        pygame.K_F6         : 'f6',
-        pygame.K_F7         : 'f7',
-        pygame.K_F8         : 'f8',
-        pygame.K_F9         : 'f9',
-        pygame.K_F10        : 'f10',
-        pygame.K_F11        : 'f11',
-        pygame.K_F12        : 'f12',
-        pygame.K_INSERT     : 'insert',
-        pygame.K_DELETE     : 'delete',
-        pygame.K_HOME       : 'home',
-        pygame.K_END        : 'end',
-        pygame.K_PAGEUP     : 'pageup',
-        pygame.K_PAGEDOWN   : 'pagedown',
-        pygame.K_UP         : 'up',
-        pygame.K_DOWN       : 'down',
-        pygame.K_LEFT       : 'left',
-        pygame.K_RIGHT      : 'right',
-        pygame.K_PRINT      : 'print',
-        pygame.K_SCROLLOCK  : 'scrollock',
-        pygame.K_PAUSE      : 'pause',
-        }
-
-    colormap = {
-        'black'         : (0,0,0),
-        'blue'          : (23,23,178),
-        'green'         : (23,178,23),
-        'cyan'          : (23,178,178),
-        'red'           : (178,23,23),
-        'magenta'       : (178,23,178),
-        'yellow'        : (178,103,23),
-        'white'         : (178,178,178),
-        'intenseblack'  : (104,104,104),
-        'intenseblue'   : (84,84,255),
-        'intensegreen'  : (84,255,84),
-        'intensecyan'   : (84,255,255),
-        'intensered'    : (255,84,84),
-        'intensemagenta': (255,84,255),
-        'intenseyellow' : (255,255,84),
-        'intensewhite'  : (255,255,255),
-        }
-
-    def __init__(self):
-        '''Initialize instance attributes'''
-        Driver.__init__(self)
-        self.log = logging.getLogger('tuikit')
-        self.screen = None
-        self.size.w, self.size.h = 120, 40  # screen size in characters
-        self.term = None
-        self.charsize = Size(16, 8)  # character size in pixels
-        self.last_keypress = None
-        self.last_key = None
-        self.colors = {}     # maps names to curses attributes
-        self.colorstack = [] # pushcolor/popcolor puts or gets attributes from this
-
-    def start(self, mainfunc):
-        pygame.init()
-        self.term = TerminalScreen()
-        self.charsize = self.term.charsize
-        self.resize_screen()
-        self.term.set_default_attr(self.colormap['white'], self.colormap['black'], 0)
-        self.term.reset_attr()
-        mainfunc()
-
-    def resize_screen(self):
-        mode = self.size.w * self.charsize.w, self.size.h * self.charsize.h
-        self.screen = pygame.display.set_mode(mode, pygame.RESIZABLE)
-        self.term.reset(self.size.w, self.size.h)
-
-    ## input ##
-
-    def getevents(self, timeout=None):
-        '''Process input, return list of events.'''
-        events = []
-        for ev in pygame.event.get():
-            # mouse
-            if ev.type == pygame.MOUSEMOTION:
-                mx = ev.pos[0] // self.charsize.w
-                my = ev.pos[1] // self.charsize.h
-                events.append(('mousemove', mx, my))
-            elif ev.type in (pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP):
-                mx = ev.pos[0] // self.charsize.w
-                my = ev.pos[1] // self.charsize.h
-                evname = {pygame.MOUSEBUTTONDOWN: 'mousedown', pygame.MOUSEBUTTONUP: 'mouseup'}
-                events.append((evname[ev.type], mx, my, ev.button))
-
-            # keyboard
-            elif ev.type == pygame.KEYDOWN:
-                keypress = self.keypress_from_pygame_event(ev)
-                if keypress:
-                    events.append(keypress)
-                    self.last_keypress = keypress
-                    self.last_key = ev.key
-                    pygame.time.set_timer(pygame.USEREVENT, 200)
-            elif ev.type == pygame.USEREVENT: # repeat last key press
-                events.append(self.last_keypress)
-                pygame.time.set_timer(pygame.USEREVENT, 50)
-            elif ev.type == pygame.KEYUP:
-                if ev.key == self.last_key:
-                    pygame.time.set_timer(pygame.USEREVENT, 0)
-
-            # window
-            elif ev.type == pygame.VIDEORESIZE:
-                neww, newh = ev.w // self.charsize.w, ev.h // self.charsize.h
-                if neww != self.size.w or newh != self.size.h:
-                    self.size.w, self.size.h = neww, newh
-                    self.resize_screen()
-                    events.append(('resize',))
-            elif ev.type == pygame.QUIT:
-                events.append(('quit',))
-
-            else:
-                self.log.warning('Unknown PyGame event: %r', ev.type)
-        return events
-
-    def keypress_from_pygame_event(self, ev):
-        if ev.key in self.keymap:
-            keypress = ('keypress', self.keymap[ev.key], None)
-        elif ev.unicode:
-            keypress = ('keypress', None, ev.unicode)
-        else:
-            self.log.debug('Unknown key: key=%r unicode=%r' % (ev.key, ev.unicode))
-            keypress = None
-        return keypress
-
-    ## drawing ##
-
-    def erase(self):
-        '''Clear screen.'''
-        self.term.clear()
-        self.screen.fill(self.term.default_attr[1])
-
-    def putch(self, x, y, c):
-        if not self.clipstack.test(x, y):
-            return
-        self.term.putch(x, y, c)
-
-    def commit(self):
-        '''Commit changes to the screen.'''
-        self.term.update(self.screen)
-        pygame.display.flip()
-
-
-    ## colors ##
-
-    def _parsecolor(self, name, attr=None):
-        name = name.lower().strip()
-        if attr == 'bold':
-            name = 'intense' + name
-        return self.colormap[name]
-
-    def _parseattrs(self, attrs):
-        res = ''
-        for a in attrs:
-            a = a.lower().strip()
-            if a == 'bold':
-                res = 1
-        return res
-
-    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 ')
-        attrs = parts[1:]
-        attr = self._parseattrs(attrs)
-        fg = self._parsecolor(fg, attr)
-        bg = self._parsecolor(bg)
-        self.colors[name] = (fg, bg, attr)
-
-    def pushcolor(self, name):
-        '''Add color on top of stack and use this color for following output.'''
-        # add prefix if such color is available
-        if len(self.colorprefix):
-            prefixname = self.colorprefix[-1] + name
-            if prefixname in self.colors:
-                name = prefixname
-        col = self.colors[name]
-        self.current_color = col
-        self.colorstack.append(col)
-
-    def popcolor(self):
-        '''Remove color from top of stack and use new top color for following output.'''
-        self.colorstack.pop()
-        if len(self.colorstack):
-            col = self.colorstack[-1]
-            self.term.set_attr(col[0], col[1], col[2])
-        else:
-            self.term.reset_attr()
-
-
-    ## cursor ##
-
-    def showcursor(self, x, y):
-        '''Set cursor to be shown at x, y coordinates.'''
-
-    def hidecursor(self):
-        '''Hide cursor.'''
-
-
-driverclass = DriverPygame
-