Update sdlterm: Optimize commit() - use two cell buffers, redraw only dirty cells, not everything. Fix keypress event, filter mousemove.
authorRadek Brich <radek.brich@devl.cz>
Sat, 05 Jan 2013 16:47:30 +0100
changeset 49 1611c462c3e3
parent 48 1f00e90fd72a
child 50 c5b8b9d2da95
Update sdlterm: Optimize commit() - use two cell buffers, redraw only dirty cells, not everything. Fix keypress event, filter mousemove.
sdlterm/cython/sdlterm.pyx
sdlterm/src/sdlterm.cc
sdlterm/src/sdlterm.h
tuikit/application.py
--- a/sdlterm/cython/sdlterm.pyx	Sat Jan 05 12:40:32 2013 +0100
+++ b/sdlterm/cython/sdlterm.pyx	Sat Jan 05 16:47:30 2013 +0100
@@ -3,7 +3,7 @@
 # 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
+# distutils: extra_compile_args = --std=c++11
 # cython: language_level=3
 
 from libcpp cimport bool
@@ -35,8 +35,8 @@
     cdef cppclass Terminal:
         Terminal() except +
 
-        void select_font(char *fname_regular, char *fname_bold, int ptsize)
-        void resize(int pxwidth, int pxheight)
+        void select_font(char *fname_regular, char *fname_bold, int ptsize) except +
+        void resize(int pxwidth, int pxheight) except +
 
         void erase()
         void putch(int x, int y, Py_UNICODE ch)
@@ -56,6 +56,7 @@
 
 cdef class SDLTerminal:
     cdef Terminal *thisptr      # hold a C++ instance which we're wrapping
+    cdef Event event
 
     def __cinit__(self):
         self.thisptr = new Terminal()
@@ -77,8 +78,8 @@
         self.thisptr.commit()
 
     def get_next_event(self):
-        cdef Event event
-        self.thisptr.get_next_event(event)
+        self.thisptr.get_next_event(self.event)
+        event = self.event
         if event.type == event.MOUSEMOVE:
             return ('mousemove', event.mouse.x, event.mouse.y)
         if event.type == event.MOUSEDOWN:
--- a/sdlterm/src/sdlterm.cc	Sat Jan 05 12:40:32 2013 +0100
+++ b/sdlterm/src/sdlterm.cc	Sat Jan 05 16:47:30 2013 +0100
@@ -1,6 +1,7 @@
 #include "sdlterm.h"
 
-#include <stdexcept>
+#include <exception>
+#include <algorithm>
 
 
 SDL_Surface *GlyphCache::lookup_glyph(Uint16 ch)
@@ -122,12 +123,7 @@
 	}
 
 	TTF_Font *font = _font_regular;
-	SDL_Color color={0xff,0xff,0xff}, bgcolor={0,100,100};
-	if (ch != 'W')
-	{
-		bgcolor.g = 0;
-		bgcolor.b = 0;
-	}
+	SDL_Color color={0xff,0xff,0xff}, bgcolor={0,0,0};
 
 	// create surface for whole cell and fill it with bg color
 	cell_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
@@ -141,28 +137,29 @@
 	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);
+	if (ch)
+	{
+		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);
+	}
 
+	// convert to display format
+	SDL_Surface *tmp_surface = cell_surface;
+	cell_surface = SDL_DisplayFormat(tmp_surface);
+	SDL_FreeSurface(tmp_surface);
+
+	// put to cache
 	_cache.put_glyph(ch, cell_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);
@@ -171,7 +168,7 @@
 
 void TerminalScreen::resize(int pxwidth, int pxheight)
 {
-    _screen_surface = SDL_SetVideoMode(pxwidth, pxheight, 32, SDL_HWSURFACE|SDL_RESIZABLE);
+    _screen_surface = SDL_SetVideoMode(pxwidth, pxheight, 0, SDL_SWSURFACE|SDL_ANYFORMAT|SDL_RESIZABLE);
 
     if (_screen_surface == NULL)
     {
@@ -190,19 +187,13 @@
 
 void TerminalScreen::erase()
 {
-	TerminalCell * cell = _cells;
-	for (int i = 0; i < _width * _height; i++)
-	{
-		cell->ch = ' ';
-		cell->attr = 0;
-		cell++;
-	}
+	std::fill(_cells_front.begin(), _cells_front.end(), TerminalCell());
 }
 
 
 void TerminalScreen::putch(int x, int y, Uint16 ch, Uint16 attr)
 {
-	TerminalCell &cell = _cells[y * _width + x];
+	TerminalCell &cell = _cells_front[y * _width + x];
 	cell.ch = ch;
 	cell.attr = attr;
 }
@@ -210,18 +201,24 @@
 
 void TerminalScreen::commit()
 {
-	TerminalCell * cell = _cells;
+	auto front_iter = _cells_front.begin();
+	auto back_iter = _cells_back.begin();
 	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);
-			cell++;
+			if (*front_iter != *back_iter)
+			{
+				dst_rect.x = x * _cell_width;
+				dst_rect.y = y * _cell_height;
+				glyph_surface = _render.render_glyph(front_iter->ch);
+				SDL_BlitSurface(glyph_surface, NULL, _screen_surface, &dst_rect);
+				*back_iter = *front_iter;
+			}
+			front_iter++;
+			back_iter++;
 		}
 	}
 
@@ -231,24 +228,19 @@
 
 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];
+	_cells_front.resize(num_cells);
+	_cells_back.resize(num_cells);
 }
 
 
 Terminal::Terminal()
- : _screen()
+ : _screen(), _mousemove_last_x(-1)
 {
     if (SDL_Init(SDL_INIT_VIDEO) == -1)
     {
@@ -267,7 +259,7 @@
 
 void Terminal::get_next_event(Event &event)
 {
-	SDL_Event sdl_event;
+	static SDL_Event sdl_event;
 
 	while (SDL_WaitEvent(&sdl_event))
 	{
@@ -278,11 +270,25 @@
 				return;
 
 			case SDL_KEYDOWN:
+			{
 				//switch(event.key.keysym.sym)
 				event.type = Event::KEYPRESS;
-				event.key.unicode = sdl_event.key.keysym.unicode;
-				strncpy(event.key.keyname, _translate_keyname(sdl_event.key.keysym.sym), 10);
+				const char *keyname = _translate_keyname(sdl_event.key.keysym.sym);
+				// return only keyname or unicode, never both
+				if (keyname)
+				{
+					strncpy(event.key.keyname, keyname, 10);
+					event.key.unicode = 0;
+				}
+				else
+				{
+					event.key.keyname[0] = 0;
+					event.key.unicode = sdl_event.key.keysym.unicode;
+					if (!event.key.unicode)
+						break; // continue loop (unknown key)
+				}
 				return;
+			}
 
 			case SDL_MOUSEBUTTONDOWN:
 			case SDL_MOUSEBUTTONUP:
@@ -290,13 +296,21 @@
 				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;
+				_mousemove_last_x = -1;
 				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;
+				if (_mousemove_last_x != event.mouse.x ||
+		            _mousemove_last_y != event.mouse.y)
+				{
+					_mousemove_last_x = event.mouse.x;
+					_mousemove_last_y = event.mouse.y;
+					return;
+				}
+				break; // continue loop when mouse position did not change
 
 			default:
 				break; // continue loop
@@ -337,7 +351,7 @@
 		case SDLK_F10:			return "f10";
 		case SDLK_F11:			return "f11";
 		case SDLK_F12:			return "f12";
-		default: return "";
+		default: return NULL;
 	}
 }
 
--- a/sdlterm/src/sdlterm.h	Sat Jan 05 12:40:32 2013 +0100
+++ b/sdlterm/src/sdlterm.h	Sat Jan 05 16:47:30 2013 +0100
@@ -45,14 +45,15 @@
 {
 	Uint16 ch;
 	Uint16 attr;
+	bool operator !=(const TerminalCell &rhs) const { return ch != rhs.ch || attr != rhs.attr; };
 };
 
 
 class TerminalScreen
 {
 public:
-	TerminalScreen(): _screen_surface(NULL), _cells(NULL), _render() {};
-	~TerminalScreen();
+	TerminalScreen(): _screen_surface(NULL), _render() {};
+	~TerminalScreen() {};
 
 	void select_font(const char *fname_regular, const char *fname_bold, int ptsize);
 	void resize(int pxwidth, int pxheight);
@@ -68,7 +69,8 @@
 
 private:
 	SDL_Surface *_screen_surface;
-	TerminalCell *_cells;
+	std::vector<TerminalCell> _cells_front;
+	std::vector<TerminalCell> _cells_back;
 	GlyphRenderer _render;
 
 	int _pixel_width;  // terminal window width in pixels
@@ -135,6 +137,9 @@
 	int _cursor_y;
 	bool _cursor_visible;
 
+	int _mousemove_last_x;
+	int _mousemove_last_y;
+
 	const char *_translate_keyname(SDLKey sym);
 };
 
--- a/tuikit/application.py	Sat Jan 05 12:40:32 2013 +0100
+++ b/tuikit/application.py	Sat Jan 05 16:47:30 2013 +0100
@@ -67,7 +67,7 @@
 
     '''Application class. Defines main loop.'''
 
-    def __init__(self, driver = 'curses'):
+    def __init__(self, driver = 'sdl'):
         '''Create application.'''
 
         self.top = TopWindow()