Update SDL driver: Enlarge char, attr to 32 bits, 64 bits per terminal cell. Colors and attributes are complete, only blink does not work.
authorRadek Brich <radek.brich@devl.cz>
Sat, 05 Jan 2013 23:00:41 +0100
changeset 52 50a1857557da
parent 51 dce7325109c1
child 53 c4263588b716
Update SDL driver: Enlarge char, attr to 32 bits, 64 bits per terminal cell. Colors and attributes are complete, only blink does not work.
.hgignore
demo_colors.py
sdlterm/demo.cc
sdlterm/src/sdlterm.cc
sdlterm/src/sdlterm.h
tuikit/application.py
--- a/.hgignore	Sat Jan 05 18:56:45 2013 +0100
+++ b/.hgignore	Sat Jan 05 23:00:41 2013 +0100
@@ -7,8 +7,8 @@
 ^sdlterm/font
 ^sdlterm/cython/sdlterm.cpp$
 ^sdlterm/demo$
-^\.project
-^\.pydevproject
+^(.*/)?\.c?project$
+^(.*/)?\.pydevproject$
 ^\.settings
 .*\.appstats
 __pycache__
--- a/demo_colors.py	Sat Jan 05 18:56:45 2013 +0100
+++ b/demo_colors.py	Sat Jan 05 23:00:41 2013 +0100
@@ -12,13 +12,20 @@
         Application.__init__(self)
         self.top.add_handler('keypress', self.on_top_keypress)
 
-        for attr in ['blink', 'bold', 'dim', 'standout', 'underline']:
+        for attr in ['blink', 'bold', 'standout', 'underline']:
             label = Label(attr)
             label.color = 'test-' + attr
             self.top.add(label)
 
         self.top.layout = VerticalLayout()
 
+    def applytheme(self):
+        Application.applytheme(self)
+        self.driver.setcolor('test-blink',              'cyan on blue, blink')
+        self.driver.setcolor('test-bold',               'cyan on blue, bold')
+        self.driver.setcolor('test-standout',           'cyan on blue, standout')
+        self.driver.setcolor('test-underline',          'cyan on blue, underline')
+
     def on_top_keypress(self, ev):
         if ev.keyname == 'escape':
             self.terminate()
--- a/sdlterm/demo.cc	Sat Jan 05 18:56:45 2013 +0100
+++ b/sdlterm/demo.cc	Sat Jan 05 23:00:41 2013 +0100
@@ -19,12 +19,18 @@
     term.resize(800, 600);
     term.select_font("font/DejaVuSansMono.ttf", "font/DejaVuSansMono-Bold.ttf", 12);
     term.erase();
-    term.set_attr( term.prepare_attr(14, 1, 1) );
 
     char hello[] = "Hello World!";
-    for (char *c = hello; *c; c++)
+    for (int bg = 0; bg < 16; bg++)
     {
-        term.putch(5 + (c - hello), 5, *c);
+        for (int fg = 0; fg < 16; fg++)
+        {
+            for (char *c = hello; *c; c++)
+            {
+                term.set_attr( term.prepare_attr(fg, bg, 1) );
+                term.putch(5 + 6 * bg + (c - hello), 5 + fg, *c);
+            }
+        }
     }
     term.commit();
 }
--- a/sdlterm/src/sdlterm.cc	Sat Jan 05 18:56:45 2013 +0100
+++ b/sdlterm/src/sdlterm.cc	Sat Jan 05 23:00:41 2013 +0100
@@ -12,9 +12,9 @@
 }
 
 
-SDL_Surface *GlyphCache::lookup_glyph(Uint16 ch)
+SDL_Surface *GlyphCache::lookup_glyph(Uint64 id)
 {
-    auto iter = _glyph_map.find(ch);
+    auto iter = _glyph_map.find(id);
     if (iter == _glyph_map.end())
     {
         return NULL;
@@ -23,9 +23,9 @@
 }
 
 
-void GlyphCache::put_glyph(Uint16 ch, SDL_Surface *srf)
+void GlyphCache::put_glyph(Uint64 id, SDL_Surface *srf)
 {
-    _glyph_map[ch] = srf;
+    _glyph_map[id] = srf;
 }
 
 
@@ -119,24 +119,29 @@
 }
 
 
-SDL_Surface *GlyphRenderer::render_cell(Uint16 ch, Uint16 attr)
+SDL_Surface *GlyphRenderer::render_cell(Uint32 ch, Uint32 attr)
 {
     SDL_Surface *cell_surface;
     TTF_Font *font;
     SDL_Color fgcolor, bgcolor;
 
     // try cache
-    cell_surface = _cache.lookup_glyph(ch);
+    Uint64 id = (Uint64)ch | (Uint64)attr << 32;
+    cell_surface = _cache.lookup_glyph(id);
     if (cell_surface)
     {
         return cell_surface;
     }
 
     // load attributes
-    _colormap.index_to_rgb(attr & 0x000F, fgcolor);
-    _colormap.index_to_rgb((attr & 0x00F0) >> 4, bgcolor);
-    Style style = (Style) ((attr & 0xFF00) >> 8);
-    font = (style == Style::BOLD) ? _font_bold : _font_regular;
+    _colormap.index_to_rgb((attr & 0x000000FF), fgcolor);
+    _colormap.index_to_rgb((attr & 0x0000FF00) >> 8, bgcolor);
+    int style = (attr & 0xFF000000) >> 24;
+    font = (style & Style::BOLD) ? _font_bold : _font_regular;
+    if (style & Style::STANDOUT)
+    {
+        std::swap(fgcolor, bgcolor);
+    }
 
     // create surface for whole cell and fill it with bg color
     cell_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
@@ -152,13 +157,30 @@
     // render glyph, blit it onto cell surface
     if (ch)
     {
-        SDL_Surface *glyph_surface = TTF_RenderGlyph_Shaded(font, ch, fgcolor, 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);
+        // when glyph is not provided by BOLD font but is provided by REGULAR font, use that (better than nothing)
+        if ((style & Style::BOLD) && !TTF_GlyphIsProvided(font, ch) && TTF_GlyphIsProvided(_font_regular, ch))
+        {
+            // use bold style of regular font instead of bold font
+            TTF_SetFontStyle(_font_regular, TTF_STYLE_BOLD);
+            _render_glyph(cell_surface, _font_regular, ch, fgcolor, bgcolor);
+            TTF_SetFontStyle(_font_regular, TTF_STYLE_NORMAL);
+        }
+        else
+        {
+            // normal case
+            _render_glyph(cell_surface, font, ch, fgcolor, bgcolor);
+        }
+        if (style & Style::UNDERLINE)
+        {
+            // draw underline
+            SDL_LockSurface(cell_surface);
+            int y = 1 + TTF_FontAscent(font);
+            Uint32 fgcolor_mapped = SDL_MapRGB(cell_surface->format, fgcolor.r, fgcolor.g, fgcolor.b);
+            Uint32 *p = (Uint32 *)(cell_surface->pixels + y * cell_surface->pitch);
+            for (int x = 0; x < _cell_width; x++)
+                *p++ = fgcolor_mapped;
+            SDL_UnlockSurface(cell_surface);
+        }
     }
 
     // convert to display format
@@ -167,12 +189,26 @@
     SDL_FreeSurface(tmp_surface);
 
     // put to cache
-    _cache.put_glyph(ch, cell_surface);
+    _cache.put_glyph(id, cell_surface);
 
     return cell_surface;
 }
 
 
+void GlyphRenderer::_render_glyph(SDL_Surface *cell_surface, TTF_Font *font, Uint32 ch,
+        SDL_Color fgcolor, SDL_Color bgcolor)
+{
+    int minx, maxy;
+    SDL_Rect dst_rect;
+    SDL_Surface *glyph_surface = TTF_RenderGlyph_Shaded(font, ch, fgcolor, bgcolor);
+    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);
+}
+
+
 void TerminalScreen::select_font(const char *fname_regular, const char *fname_bold, int ptsize)
 {
     _render.open_font(fname_regular, fname_bold, ptsize);
@@ -204,7 +240,7 @@
 }
 
 
-void TerminalScreen::putch(int x, int y, Uint16 ch, Uint16 attr)
+void TerminalScreen::putch(int x, int y, Uint32 ch, Uint32 attr)
 {
     TerminalCell &cell = _cells_front[y * _width + x];
     cell.ch = ch;
--- a/sdlterm/src/sdlterm.h	Sat Jan 05 18:56:45 2013 +0100
+++ b/sdlterm/src/sdlterm.h	Sat Jan 05 23:00:41 2013 +0100
@@ -5,12 +5,12 @@
 #include <vector>
 
 
-enum class Style: Uint16
+namespace Style
 {
-    BOLD      = 1 << 0,  // bold font
-    UNDERLINE = 1 << 1,  // underline text
-    STANDOUT  = 1 << 2,  // inverse bg/fg
-    BLINK     = 1 << 3,  // blinking
+    const int BOLD      = 1 << 0;  // bold font
+    const int UNDERLINE = 1 << 1;  // underline text
+    const int STANDOUT  = 1 << 2;  // inverse bg/fg
+    const int BLINK     = 1 << 3;  // blinking
 };
 
 
@@ -44,12 +44,13 @@
 class GlyphCache
 {
 public:
-    SDL_Surface *lookup_glyph(Uint16 ch);
-    void put_glyph(Uint16 ch, SDL_Surface *srf);
+    // lookup glyph in cache, id is combined char and attr
+    SDL_Surface *lookup_glyph(Uint64 id);
+    void put_glyph(Uint64 id, SDL_Surface *srf);
     void flush();
 
 private:
-    std::map<Uint16, SDL_Surface*> _glyph_map;
+    std::map<Uint64, SDL_Surface*> _glyph_map;
 };
 
 
@@ -63,7 +64,7 @@
     void close_font();
 
     // do not free surface returned!
-    SDL_Surface *render_cell(Uint16 ch, Uint16 attr);
+    SDL_Surface *render_cell(Uint32 ch, Uint32 attr);
 
     int get_cell_width() { return _cell_width; };
     int get_cell_height() { return _cell_height; };
@@ -75,13 +76,30 @@
     int _cell_height;
     GlyphCache _cache;
     ColorMap _colormap;
+
+    void _render_glyph(SDL_Surface *cell_surface, TTF_Font *font, Uint32 ch,
+            SDL_Color fgcolor, SDL_Color bgcolor);
 };
 
 
+/* One cell of terminal window
+ *
+ * Each cell contains one character, its color and attributes.
+ * Character is encoded in 32bit unicode.
+ * Other 32 bits are attributes:
+ *   0-7   (8b) - foreground color index
+ *   8-15  (8b) - background color index
+ *   16-23 (8b) - RESERVED for alpha channel
+ *   24    (1b) - bold font
+ *   25    (1b) - underline
+ *   26    (1b) - standout (swap fg/bg color)
+ *   27    (1b) - blink
+ *   28-31 (4b) - RESERVED
+ */
 struct TerminalCell
 {
-    Uint16 ch;
-    Uint16 attr;
+    Uint32 ch;
+    Uint32 attr;
     bool operator !=(const TerminalCell &rhs) const { return ch != rhs.ch || attr != rhs.attr; };
 };
 
@@ -99,7 +117,7 @@
     void resize(int pxwidth, int pxheight);
 
     void erase();
-    void putch(int x, int y, Uint16 ch, Uint16 attr);
+    void putch(int x, int y, Uint32 ch, Uint32 attr);
     void commit();
 
     int get_width() { return _width; };
@@ -134,7 +152,7 @@
         struct
         {
             char keyname[10];
-            Uint16 unicode;
+            Uint32 unicode;
         } key;
         struct
         {
@@ -156,11 +174,11 @@
     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 putch(int x, int y, Uint32 ch) { _screen.putch(x, y, ch, _attr); };
     void commit() { _screen.commit(); };
 
-    Uint16 prepare_attr(Uint8 fg, Uint8 bg, Uint8 style) { return fg | bg << 4 | style << 8; };
-    void set_attr(Uint16 value) { _attr = value; };
+    Uint32 prepare_attr(Uint8 fg, Uint8 bg, Uint8 style) { return (Uint32)fg | (Uint32)bg << 8 | (Uint32)style << 24; };
+    void set_attr(Uint32 value) { _attr = value; };
 
     void set_cursor(int x, int y) { _cursor_x = x; _cursor_y = y; };
     void show_cursor(bool visible) { _cursor_visible = visible; };
@@ -172,7 +190,7 @@
 
 private:
     TerminalScreen _screen;
-    Uint16 _attr;
+    Uint32 _attr;
     int _cursor_x;
     int _cursor_y;
     bool _cursor_visible;
--- a/tuikit/application.py	Sat Jan 05 18:56:45 2013 +0100
+++ b/tuikit/application.py	Sat Jan 05 23:00:41 2013 +0100
@@ -124,6 +124,9 @@
 
 
     def applytheme(self):
+        #TODO: allow custom colors:
+        #    e.g. "blue (#2020FF) on black (#101010), underline"
+        #    color in brackets is used when driver supports custom colors
         driver = self.driver
         driver.setcolor('normal',                  'white on black')
         driver.setcolor('strong',                  'white on black, bold')
@@ -137,12 +140,6 @@
         driver.setcolor('menu-active',             'white on cyan, bold')
         driver.setcolor('combo:normal',            'black on green')
 
-        driver.setcolor('test-blink',              'cyan on blue, blink')
-        driver.setcolor('test-bold',               'cyan on blue, bold')
-        driver.setcolor('test-dim',                'cyan on blue, dim')
-        driver.setcolor('test-standout',           'cyan on blue, standout')
-        driver.setcolor('test-underline',          'cyan on blue, underline')
-
 
     def get_driver_instance(self, name):
         module = __import__('tuikit.driver_' + name, fromlist=['driverclass'])