# HG changeset patch # User Radek Brich # Date 1357596917 -3600 # Node ID 282a07d2068001e678b3133515abd7a0f8400e39 # Parent 1ab0edd5d7846437111f31f5c0e0dad634f24fe0 Drop PyGame driver, replaced by compiled SDL driver. diff -r 1ab0edd5d784 -r 282a07d20680 extra/pygame_font_render_glyph.patch --- 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 -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" diff -r 1ab0edd5d784 -r 282a07d20680 tests/pygame_font.py --- 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)) - diff -r 1ab0edd5d784 -r 282a07d20680 tuikit/driver_pygame.py --- 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 -