Add SDL driver prototype. Update sdlterm: Handle keyboard, mouse events. Add glyph cache.
--- 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()
+
--- 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;
--- 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
--- 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 <stdexcept>
+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 "";
+ }
+}
+
--- 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 <vector>
+class GlyphCache
+{
+public:
+ SDL_Surface *lookup_glyph(Uint16 ch);
+ void put_glyph(Uint16 ch, SDL_Surface *srf);
+ void flush();
+
+private:
+ std::map<Uint16, SDL_Surface*> _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<Uint16, SDL_Surface*> _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);
};
--- 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()')
--- /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
+