# HG changeset patch # User Radek Brich # Date 1357386032 -3600 # Node ID 1f00e90fd72a5f94f8fd964fbf4a7f2e6f2be7cc # Parent 537d7c6b48a2b38ce39235e8588ed4b307298aa3 Add SDL driver prototype. Update sdlterm: Handle keyboard, mouse events. Add glyph cache. diff -r 537d7c6b48a2 -r 1f00e90fd72a sdlterm/cython/sdlterm.pyx --- a/sdlterm/cython/sdlterm.pyx Sat Jan 05 00:40:27 2013 +0100 +++ b/sdlterm/cython/sdlterm.pyx Sat Jan 05 12:40:32 2013 +0100 @@ -10,11 +10,27 @@ cdef extern from "sdlterm.h": - enum EventType: - pass + cdef struct Event_key: + char *keyname + Py_UNICODE unicode + + cdef struct Event_mouse: + int x, y + int button - union Event: - EventType type + cdef struct Event: + # enum + int QUIT + int RESIZE + int KEYPRESS + int MOUSEDOWN + int MOUSEUP + int MOUSEMOVE + int MOUSEWHEEL + + int type + Event_key key + Event_mouse mouse cdef cppclass Terminal: Terminal() except + @@ -34,6 +50,9 @@ void get_next_event(Event event) + int get_width() + int get_height() + cdef class SDLTerminal: cdef Terminal *thisptr # hold a C++ instance which we're wrapping @@ -60,5 +79,28 @@ def get_next_event(self): cdef Event event self.thisptr.get_next_event(event) - return dict() + if event.type == event.MOUSEMOVE: + return ('mousemove', event.mouse.x, event.mouse.y) + if event.type == event.MOUSEDOWN: + return ('mousedown', event.mouse.x, event.mouse.y, event.mouse.button) + if event.type == event.MOUSEUP: + return ('mouseup', event.mouse.x, event.mouse.y, event.mouse.button) + if event.type == event.KEYPRESS: + keyname = event.key.keyname + if keyname: + keyname = keyname.decode() + else: + keyname = None + char = event.key.unicode + if char == '\x00': + char = None + return ('keypress', keyname, char) + if event.type == event.QUIT: + return ('quit',) + return ('unknown',) + property width: + def __get__(self): return self.thisptr.get_width() + property height: + def __get__(self): return self.thisptr.get_height() + diff -r 537d7c6b48a2 -r 1f00e90fd72a sdlterm/demo.cc --- a/sdlterm/demo.cc Sat Jan 05 00:40:27 2013 +0100 +++ b/sdlterm/demo.cc Sat Jan 05 12:40:32 2013 +0100 @@ -31,7 +31,7 @@ switch (event.type) { - case EventType::quit: + case Event::QUIT: done = true; break; diff -r 537d7c6b48a2 -r 1f00e90fd72a sdlterm/demo.py --- a/sdlterm/demo.py Sat Jan 05 00:40:27 2013 +0100 +++ b/sdlterm/demo.py Sat Jan 05 12:40:32 2013 +0100 @@ -16,5 +16,7 @@ while True: event = term.get_next_event() - break + print(event) + if event[0] == 'keypress' and event[1] == 'escape': + break diff -r 537d7c6b48a2 -r 1f00e90fd72a sdlterm/src/sdlterm.cc --- a/sdlterm/src/sdlterm.cc Sat Jan 05 00:40:27 2013 +0100 +++ b/sdlterm/src/sdlterm.cc Sat Jan 05 12:40:32 2013 +0100 @@ -3,6 +3,33 @@ #include +SDL_Surface *GlyphCache::lookup_glyph(Uint16 ch) +{ + auto iter = _glyph_map.find(ch); + if (iter == _glyph_map.end()) + { + return NULL; + } + return iter->second; +} + + +void GlyphCache::put_glyph(Uint16 ch, SDL_Surface *srf) +{ + _glyph_map[ch] = srf; +} + + +void GlyphCache::flush() +{ + for (auto iter = _glyph_map.begin(); iter != _glyph_map.end(); iter++) + { + SDL_FreeSurface(iter->second); + } + _glyph_map.clear(); +} + + GlyphRenderer::GlyphRenderer() : _font_regular(NULL), _font_bold(NULL) { @@ -16,6 +43,7 @@ GlyphRenderer::~GlyphRenderer() { + _cache.flush(); close_font(); TTF_Quit(); } @@ -84,6 +112,15 @@ SDL_Surface *GlyphRenderer::render_glyph(Uint16 ch) { + SDL_Surface *cell_surface; + + // try cache + cell_surface = _cache.lookup_glyph(ch); + if (cell_surface) + { + return cell_surface; + } + TTF_Font *font = _font_regular; SDL_Color color={0xff,0xff,0xff}, bgcolor={0,100,100}; if (ch != 'W') @@ -93,7 +130,7 @@ } // create surface for whole cell and fill it with bg color - SDL_Surface *cell_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, + cell_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, _cell_width, _cell_height, 32, 0, 0, 0, 0); SDL_Rect dst_rect; dst_rect.x = 0; @@ -112,6 +149,7 @@ SDL_BlitSurface(glyph_surface, NULL, cell_surface, &dst_rect); SDL_FreeSurface(glyph_surface); + _cache.put_glyph(ch, cell_surface); return cell_surface; } @@ -133,7 +171,7 @@ void TerminalScreen::resize(int pxwidth, int pxheight) { - _screen_surface = SDL_SetVideoMode(pxwidth, pxheight, 8, SDL_SWSURFACE|SDL_ANYFORMAT|SDL_RESIZABLE); + _screen_surface = SDL_SetVideoMode(pxwidth, pxheight, 32, SDL_HWSURFACE|SDL_RESIZABLE); if (_screen_surface == NULL) { @@ -183,7 +221,6 @@ 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++; } } @@ -218,6 +255,7 @@ fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError()); throw std::exception(); } + SDL_EnableUNICODE(1); } @@ -236,12 +274,28 @@ switch (sdl_event.type) { case SDL_QUIT: - event.type = EventType::quit; + event.type = Event::QUIT; return; case SDL_KEYDOWN: //switch(event.key.keysym.sym) - event.type = EventType::keypress; + event.type = Event::KEYPRESS; + event.key.unicode = sdl_event.key.keysym.unicode; + strncpy(event.key.keyname, _translate_keyname(sdl_event.key.keysym.sym), 10); + return; + + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + event.type = (sdl_event.type == SDL_MOUSEBUTTONDOWN) ? Event::MOUSEDOWN : Event::MOUSEUP; + event.mouse.x = sdl_event.button.x / _screen.get_cell_width(); + event.mouse.y = sdl_event.button.y / _screen.get_cell_height(); + event.mouse.button = sdl_event.button.button; + return; + + case SDL_MOUSEMOTION: + event.type = Event::MOUSEMOVE; + event.mouse.x = sdl_event.motion.x / _screen.get_cell_width(); + event.mouse.y = sdl_event.motion.y / _screen.get_cell_height(); return; default: @@ -249,3 +303,41 @@ } } } + + +const char *Terminal::_translate_keyname(SDLKey sym) +{ + switch (sym) + { + case SDLK_BACKSPACE: return "backspace"; + case SDLK_TAB: return "tab"; + case SDLK_RETURN: return "enter"; + case SDLK_KP_ENTER: return "enter"; + case SDLK_PAUSE: return "pause"; + case SDLK_ESCAPE: return "escape"; + case SDLK_DELETE: return "delete"; + case SDLK_INSERT: return "insert"; + case SDLK_UP: return "up"; + case SDLK_DOWN: return "down"; + case SDLK_LEFT: return "left"; + case SDLK_RIGHT: return "right"; + case SDLK_HOME: return "home"; + case SDLK_END: return "end"; + case SDLK_PAGEUP: return "pageup"; + case SDLK_PAGEDOWN: return "pagedown"; + case SDLK_F1: return "f1"; + case SDLK_F2: return "f2"; + case SDLK_F3: return "f3"; + case SDLK_F4: return "f4"; + case SDLK_F5: return "f5"; + case SDLK_F6: return "f6"; + case SDLK_F7: return "f7"; + case SDLK_F8: return "f8"; + case SDLK_F9: return "f9"; + case SDLK_F10: return "f10"; + case SDLK_F11: return "f11"; + case SDLK_F12: return "f12"; + default: return ""; + } +} + diff -r 537d7c6b48a2 -r 1f00e90fd72a sdlterm/src/sdlterm.h --- a/sdlterm/src/sdlterm.h Sat Jan 05 00:40:27 2013 +0100 +++ b/sdlterm/src/sdlterm.h Sat Jan 05 12:40:32 2013 +0100 @@ -5,6 +5,18 @@ #include +class GlyphCache +{ +public: + SDL_Surface *lookup_glyph(Uint16 ch); + void put_glyph(Uint16 ch, SDL_Surface *srf); + void flush(); + +private: + std::map _glyph_map; +}; + + class GlyphRenderer { public: @@ -14,6 +26,7 @@ void open_font(const char *fname_regular, const char *fname_bold, int ptsize); void close_font(); + // do not free surface returned! SDL_Surface *render_glyph(Uint16 ch); int get_cell_width() { return _cell_width; }; @@ -24,7 +37,7 @@ TTF_Font *_font_bold; int _cell_width; int _cell_height; - std::map _cache; + GlyphCache _cache; }; @@ -48,6 +61,11 @@ void putch(int x, int y, Uint16 ch, Uint16 attr); void commit(); + int get_width() { return _width; }; + int get_height() { return _height; }; + int get_cell_width() { return _cell_width; }; + int get_cell_height() { return _cell_height; }; + private: SDL_Surface *_screen_surface; TerminalCell *_cells; @@ -64,42 +82,24 @@ }; -enum class EventType : Uint8 +struct Event { - quit, - resize, - keypress, - mousedown, - mouseup, - mousemove, - mousewheel -}; - - -struct WindowEvent -{ - EventType type; -}; - + enum { QUIT, RESIZE, KEYPRESS, MOUSEDOWN, MOUSEUP, MOUSEMOVE, MOUSEWHEEL }; + int type; -struct KeyboardEvent -{ - EventType type; -}; - - -struct MouseEvent -{ - EventType type; -}; - - -union Event -{ - EventType type; - WindowEvent window; - KeyboardEvent key; - MouseEvent mouse; + union + { + struct + { + char keyname[10]; + Uint16 unicode; + } key; + struct + { + int x, y; + int button; + } mouse; + }; }; @@ -125,11 +125,16 @@ void get_next_event(Event &event); + int get_width() { return _screen.get_width(); }; + int get_height() { return _screen.get_height(); }; + private: TerminalScreen _screen; Uint16 _attr; int _cursor_x; int _cursor_y; bool _cursor_visible; + + const char *_translate_keyname(SDLKey sym); }; diff -r 537d7c6b48a2 -r 1f00e90fd72a tuikit/driver_dummy.py --- a/tuikit/driver_dummy.py Sat Jan 05 00:40:27 2013 +0100 +++ b/tuikit/driver_dummy.py Sat Jan 05 12:40:32 2013 +0100 @@ -12,40 +12,42 @@ class DriverDummy(Driver): - + '''Dummy driver class''' - + def __init__(self): '''Initialize instance attributes''' Driver.__init__(self) self.log = logging.getLogger('tuikit') self.size.w, self.size.h = 80, 25 - + def start(self, mainfunc): '''Start driver and run mainfunc.''' mainfunc() ## input ## - + def getevents(self, timeout=None): '''Process input, return list of events. - + This dummy implementation just returns 'q' and Escape key presses. - + ''' events = [('keypress', None, 'q'), ('keypress', 'escape', None)] return events ## drawing ## - + def erase(self): '''Clear screen.''' self.log.info('DummyDriver.erase()') def putch(self, x, y, c): '''Output one unicode character to specified coordinates.''' + if not self.clipstack.test(x, y): + return self.log.info('DummyDriver.putch(x=%r, y=%r, c=%r)', x, y, c) def commit(self): @@ -54,20 +56,20 @@ ## 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') - + ''' self.log.info('DummyDriver.setcolor(name=%r, desc=%r)', name, desc) def pushcolor(self, name): '''Add color on top of stack and use this color for following output.''' self.log.info('DummyDriver.pushcolor(name=%r)', name) - + def popcolor(self): '''Remove color from top of stack and use new top color for following output.''' self.log.info('DummyDriver.popcolor()') diff -r 537d7c6b48a2 -r 1f00e90fd72a tuikit/driver_sdl.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tuikit/driver_sdl.py Sat Jan 05 12:40:32 2013 +0100 @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +'''SDL driver. + +Requires C++ extension module. +See sdlterm directory. + +''' + +from tuikit.driver import Driver + +import logging + +import sys +sys.path.insert(0, 'sdlterm/build/lib.linux-x86_64-3.2') +from sdlterm import SDLTerminal + + +class DriverSDL(Driver): + + '''SDL driver class''' + + def __init__(self): + '''Initialize instance attributes''' + Driver.__init__(self) + self.log = logging.getLogger('tuikit') + self.sdlterm = SDLTerminal() + + def start(self, mainfunc): + '''Start driver and run mainfunc.''' + self.sdlterm.resize(800, 600) + self.sdlterm.select_font("sdlterm/font/DejaVuSansMono.ttf", "sdlterm/font/DejaVuSansMono-Bold.ttf", 12) + 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.''' + event = self.sdlterm.get_next_event() + 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') + + ''' + self.log.info('DummyDriver.setcolor(name=%r, desc=%r)', name, desc) + + def pushcolor(self, name): + '''Add color on top of stack and use this color for following output.''' + self.log.info('DummyDriver.pushcolor(name=%r)', name) + + def popcolor(self): + '''Remove color from top of stack and use new top color for following output.''' + self.log.info('DummyDriver.popcolor()') + + + ## cursor ## + + def showcursor(self, x, y): + '''Set cursor to be shown at x, y coordinates.''' + self.log.info('DummyDriver.showcursor(x=%r, y=%r)', x, y) + + def hidecursor(self): + '''Hide cursor.''' + self.log.info('DummyDriver.hidecursor()') + + +driverclass = DriverSDL +