# HG changeset patch # User Radek Brich # Date 1357342827 -3600 # Node ID 537d7c6b48a2b38ce39235e8588ed4b307298aa3 # Parent 2b43a7f38c3453f0eab051a36d8f2583275a9413 Add sdlterm prototype: extension module for SDL driver. diff -r 2b43a7f38c34 -r 537d7c6b48a2 .hgignore --- a/.hgignore Sat Jan 05 00:37:11 2013 +0100 +++ b/.hgignore Sat Jan 05 00:40:27 2013 +0100 @@ -1,9 +1,14 @@ -.*~ -^tuikit/.*\.pyc +.*~$ +^tuikit/.*\.pyc$ ^docs/_build ^tuikit\.log ^build -\.project -\.pydevproject -\.settings +^sdlterm/build +^sdlterm/font +^sdlterm/cython/sdlterm.cpp$ +^sdlterm/demo$ +^\.project +^\.pydevproject +^\.settings .*\.appstats +__pycache__ diff -r 2b43a7f38c34 -r 537d7c6b48a2 sdlterm/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdlterm/Makefile Sat Jan 05 00:40:27 2013 +0100 @@ -0,0 +1,8 @@ +CXXFLAGS=`sdl-config --cflags` -g -Wall -std=c++11 -Isrc +LDFLAGS=`sdl-config --libs` -lSDL_ttf +VPATH=src + +demo: demo.cc sdlterm.cc sdlterm.h + +clean: + rm -f demo diff -r 2b43a7f38c34 -r 537d7c6b48a2 sdlterm/cython/sdlterm.pyx --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdlterm/cython/sdlterm.pyx Sat Jan 05 00:40:27 2013 +0100 @@ -0,0 +1,64 @@ +# distutils: language = c++ +# distutils: sources = src/sdlterm.cc +# distutils: include_dirs = /usr/include/SDL src +# distutils: libraries = SDL SDL_ttf +# distutils: define_macros = _GNU_SOURCE=1 _REENTRANT +# distutils: extra_compile_args = --std=c++0x +# cython: language_level=3 + +from libcpp cimport bool + + +cdef extern from "sdlterm.h": + enum EventType: + pass + + union Event: + EventType type + + cdef cppclass Terminal: + Terminal() except + + + void select_font(char *fname_regular, char *fname_bold, int ptsize) + void resize(int pxwidth, int pxheight) + + void erase() + void putch(int x, int y, Py_UNICODE ch) + void commit() + + int prepare_attr(int fg, int bg, int style) + void set_attr(int value) + + void set_cursor(int x, int y) + void show_cursor(bool visible) + + void get_next_event(Event event) + + +cdef class SDLTerminal: + cdef Terminal *thisptr # hold a C++ instance which we're wrapping + + def __cinit__(self): + self.thisptr = new Terminal() + def __dealloc__(self): + del self.thisptr + + def select_font(self, fname_regular, fname_bold, ptsize): + fname_regular = fname_regular.encode('utf8') + fname_bold = fname_bold.encode('utf8') + self.thisptr.select_font(fname_regular, fname_bold, ptsize) + def resize(self, width, height): + self.thisptr.resize(width, height) + + def erase(self): + self.thisptr.erase() + def putch(self, x, y, ch): + self.thisptr.putch(x, y, ch) + def commit(self): + self.thisptr.commit() + + def get_next_event(self): + cdef Event event + self.thisptr.get_next_event(event) + return dict() + diff -r 2b43a7f38c34 -r 537d7c6b48a2 sdlterm/demo.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdlterm/demo.cc Sat Jan 05 00:40:27 2013 +0100 @@ -0,0 +1,56 @@ +#include "sdlterm.h" + + +class Application +{ +public: + Terminal term; + bool done; + + Application() : term(), done(false) {}; + + void init(); + void wait_and_process_event(); +}; + + +void Application::init() +{ + term.resize(800, 600); + term.select_font("font/DejaVuSansMono.ttf", "font/DejaVuSansMono-Bold.ttf", 12); + term.erase(); + term.putch(5, 5, 'W'); + term.commit(); +} + + +void Application::wait_and_process_event() +{ + Event event; + term.get_next_event(event); + + switch (event.type) + { + case EventType::quit: + done = true; + break; + + default: + break; + } +} + + +int main(int argc, char *argv[]) +{ + Application app; + app.init(); + + while (!app.done) + { + app.wait_and_process_event(); + } + + return 0; +} + diff -r 2b43a7f38c34 -r 537d7c6b48a2 sdlterm/demo.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdlterm/demo.py Sat Jan 05 00:40:27 2013 +0100 @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import sys +sys.path.insert(0, 'build/lib.linux-x86_64-3.2') + +from sdlterm import SDLTerminal + +if __name__ == '__main__': + term = SDLTerminal() + term.resize(800, 600) + term.select_font("font/DejaVuSansMono.ttf", "font/DejaVuSansMono-Bold.ttf", 12) + term.erase() + term.putch(5, 5, 'W') + term.commit() + + while True: + event = term.get_next_event() + break + diff -r 2b43a7f38c34 -r 537d7c6b48a2 sdlterm/setup.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdlterm/setup.py Sat Jan 05 00:40:27 2013 +0100 @@ -0,0 +1,7 @@ +from distutils.core import setup +from Cython.Build import cythonize + +setup( + ext_modules = cythonize("cython/sdlterm.pyx") +) + diff -r 2b43a7f38c34 -r 537d7c6b48a2 sdlterm/src/sdlterm.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdlterm/src/sdlterm.cc Sat Jan 05 00:40:27 2013 +0100 @@ -0,0 +1,251 @@ +#include "sdlterm.h" + +#include + + +GlyphRenderer::GlyphRenderer() + : _font_regular(NULL), _font_bold(NULL) +{ + if (TTF_Init() == -1) + { + printf("TTF_Init: %s\n", TTF_GetError()); + throw std::exception(); + } +} + + +GlyphRenderer::~GlyphRenderer() +{ + close_font(); + TTF_Quit(); +} + + +void GlyphRenderer::open_font(const char *fname_regular, const char *fname_bold, int ptsize) +{ + close_font(); + + // open regular font + _font_regular = TTF_OpenFont(fname_regular, ptsize); + if (!_font_regular) + { + printf("TTF_OpenFont: %s\n", TTF_GetError()); + throw std::exception(); + } + + // open bold font + _font_bold = TTF_OpenFont(fname_bold, ptsize); + if (!_font_bold) + { + printf("TTF_OpenFont: %s\n", TTF_GetError()); + throw std::exception(); + } + + // update metrics for regular font + int advance; + if (TTF_GlyphMetrics(_font_regular, 'M', NULL, NULL, NULL, NULL, &advance) == -1) + { + printf("TTF_GlyphMetrics: %s\n", TTF_GetError()); + } + _cell_width = advance; + _cell_height = TTF_FontHeight(_font_regular); + + // read metrics for bold font + if (TTF_GlyphMetrics(_font_bold, 'M', NULL, NULL, NULL, NULL, &advance) == -1) + { + printf("TTF_GlyphMetrics: %s\n", TTF_GetError()); + } + if (advance > _cell_width) + { + _cell_width = advance; + } + int height = TTF_FontHeight(_font_bold); + if (height > _cell_height) + { + _cell_height = height; + } +} + + +void GlyphRenderer::close_font() +{ + if (_font_regular) + { + TTF_CloseFont(_font_regular); + _font_regular = NULL; + } + if (_font_bold) + { + TTF_CloseFont(_font_bold); + _font_bold = NULL; + } +} + + +SDL_Surface *GlyphRenderer::render_glyph(Uint16 ch) +{ + TTF_Font *font = _font_regular; + SDL_Color color={0xff,0xff,0xff}, bgcolor={0,100,100}; + if (ch != 'W') + { + bgcolor.g = 0; + bgcolor.b = 0; + } + + // create surface for whole cell and fill it with bg color + SDL_Surface *cell_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, + _cell_width, _cell_height, 32, 0, 0, 0, 0); + SDL_Rect dst_rect; + dst_rect.x = 0; + dst_rect.y = 0; + dst_rect.w = _cell_width; + dst_rect.h = _cell_height; + Uint32 bgcolor_mapped = SDL_MapRGB(cell_surface->format, bgcolor.r, bgcolor.g, bgcolor.b); + SDL_FillRect(cell_surface, &dst_rect, bgcolor_mapped); + + // render glyph, blit it onto cell surface + SDL_Surface *glyph_surface = TTF_RenderGlyph_Shaded(font, ch, color, bgcolor); + int minx, maxy; + TTF_GlyphMetrics(font, ch, &minx, NULL, NULL, &maxy, NULL); + dst_rect.x = minx; + dst_rect.y = TTF_FontAscent(font) - maxy; + SDL_BlitSurface(glyph_surface, NULL, cell_surface, &dst_rect); + SDL_FreeSurface(glyph_surface); + + return cell_surface; +} + + +TerminalScreen::~TerminalScreen() +{ + if (_cells) + { + delete[] _cells; + } +} + + +void TerminalScreen::select_font(const char *fname_regular, const char *fname_bold, int ptsize) +{ + _render.open_font(fname_regular, fname_bold, ptsize); + _reset_cells(); +} + +void TerminalScreen::resize(int pxwidth, int pxheight) +{ + _screen_surface = SDL_SetVideoMode(pxwidth, pxheight, 8, SDL_SWSURFACE|SDL_ANYFORMAT|SDL_RESIZABLE); + + if (_screen_surface == NULL) + { + fprintf(stderr, "Unable to set video: %s\n", SDL_GetError()); + throw std::exception(); + } + + SDL_WM_SetCaption("terminal", NULL); + + _pixel_width = pxwidth; + _pixel_height = pxheight; + + _reset_cells(); +} + + +void TerminalScreen::erase() +{ + TerminalCell * cell = _cells; + for (int i = 0; i < _width * _height; i++) + { + cell->ch = ' '; + cell->attr = 0; + cell++; + } +} + + +void TerminalScreen::putch(int x, int y, Uint16 ch, Uint16 attr) +{ + TerminalCell &cell = _cells[y * _width + x]; + cell.ch = ch; + cell.attr = attr; +} + + +void TerminalScreen::commit() +{ + TerminalCell * cell = _cells; + SDL_Surface *glyph_surface; + SDL_Rect dst_rect; + for (int y = 0; y < _height; y++) + { + for (int x = 0; x < _width; x++) + { + dst_rect.x = x * _cell_width; + dst_rect.y = y * _cell_height; + glyph_surface = _render.render_glyph(cell->ch); + SDL_BlitSurface(glyph_surface, NULL, _screen_surface, &dst_rect); + SDL_FreeSurface(glyph_surface); + cell++; + } + } + + SDL_UpdateRect(_screen_surface, 0, 0, 0, 0); +} + + +void TerminalScreen::_reset_cells() +{ + if (_cells) + { + delete[] _cells; + _cells = NULL; + } + + _cell_width = _render.get_cell_width(); + _cell_height = _render.get_cell_height(); + _width = _pixel_width / _cell_width; + _height = _pixel_height / _cell_height; + + int num_cells = _width * _height; + _cells = new TerminalCell[num_cells]; +} + + +Terminal::Terminal() + : _screen() +{ + if (SDL_Init(SDL_INIT_VIDEO) == -1) + { + fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError()); + throw std::exception(); + } +} + + +Terminal::~Terminal() +{ + SDL_Quit(); +} + + +void Terminal::get_next_event(Event &event) +{ + SDL_Event sdl_event; + + while (SDL_WaitEvent(&sdl_event)) + { + switch (sdl_event.type) + { + case SDL_QUIT: + event.type = EventType::quit; + return; + + case SDL_KEYDOWN: + //switch(event.key.keysym.sym) + event.type = EventType::keypress; + return; + + default: + break; // continue loop + } + } +} diff -r 2b43a7f38c34 -r 537d7c6b48a2 sdlterm/src/sdlterm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sdlterm/src/sdlterm.h Sat Jan 05 00:40:27 2013 +0100 @@ -0,0 +1,135 @@ +#include "SDL.h" +#include "SDL_ttf.h" + +#include +#include + + +class GlyphRenderer +{ +public: + GlyphRenderer(); + ~GlyphRenderer(); + + void open_font(const char *fname_regular, const char *fname_bold, int ptsize); + void close_font(); + + SDL_Surface *render_glyph(Uint16 ch); + + int get_cell_width() { return _cell_width; }; + int get_cell_height() { return _cell_height; }; + +private: + TTF_Font *_font_regular; + TTF_Font *_font_bold; + int _cell_width; + int _cell_height; + std::map _cache; +}; + + +struct TerminalCell +{ + Uint16 ch; + Uint16 attr; +}; + + +class TerminalScreen +{ +public: + TerminalScreen(): _screen_surface(NULL), _cells(NULL), _render() {}; + ~TerminalScreen(); + + void select_font(const char *fname_regular, const char *fname_bold, int ptsize); + void resize(int pxwidth, int pxheight); + + void erase(); + void putch(int x, int y, Uint16 ch, Uint16 attr); + void commit(); + +private: + SDL_Surface *_screen_surface; + TerminalCell *_cells; + GlyphRenderer _render; + + int _pixel_width; // terminal window width in pixels + int _pixel_height; + int _width; // width in characters + int _height; // height in characters + int _cell_width; // character cell width in pixels + int _cell_height; + + void _reset_cells(); +}; + + +enum class EventType : Uint8 +{ + quit, + resize, + keypress, + mousedown, + mouseup, + mousemove, + mousewheel +}; + + +struct WindowEvent +{ + EventType type; +}; + + +struct KeyboardEvent +{ + EventType type; +}; + + +struct MouseEvent +{ + EventType type; +}; + + +union Event +{ + EventType type; + WindowEvent window; + KeyboardEvent key; + MouseEvent mouse; +}; + + +class Terminal +{ +public: + Terminal(); + ~Terminal(); + + void select_font(const char *fname_regular, const char *fname_bold, int ptsize) + { _screen.select_font(fname_regular, fname_bold, ptsize); }; + void resize(int pxwidth, int pxheight) { _screen.resize(pxwidth, pxheight); }; + + void erase() { _screen.erase(); }; + void putch(int x, int y, Uint16 ch) { _screen.putch(x, y, ch, _attr); }; + void commit() { _screen.commit(); }; + + Uint16 prepare_attr(Uint8 fg, Uint8 bg, Uint8 style) { return fg | bg << 8 | style << 16; }; + void set_attr(Uint16 value) { _attr = value; }; + + void set_cursor(int x, int y) { _cursor_x = x; _cursor_y = y; }; + void show_cursor(bool visible) { _cursor_visible = visible; }; + + void get_next_event(Event &event); + +private: + TerminalScreen _screen; + Uint16 _attr; + int _cursor_x; + int _cursor_y; + bool _cursor_visible; +}; +