--- 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/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
-