DriverSDL: add support for key modifiers.
#include "SDL.h"
#include "SDL_ttf.h"
#include <map>
#include <string>
#include <vector>
#include <exception>
namespace Style
{
enum {
BOLD = 1 << 24, // bold font
UNDERLINE = 1 << 25, // underline text
STANDOUT = 1 << 26, // inverse bg/fg
BLINK = 1 << 27, // blinking
};
};
namespace KeyMod
{
enum {
SHIFT = 1<<0,
ALT = 1<<1,
CTRL = 1<<2,
META = 1<<3,
};
};
class ColorMap
{
private:
Uint8 _map[16][3] = {
{0,0,0}, // 0 - black
{23,23,178}, // 1 - blue
{23,178,23}, // 2 - green
{23,178,178}, // 3 - cyan
{178,23,23}, // 4 - red
{178,23,178}, // 5 - magenta
{178,103,23}, // 6 - brown
{178,178,178}, // 7 - light gray
{104,104,104}, // 8 - gray
{84,84,255}, // 9 - light blue
{84,255,84}, // 10 - light green
{84,255,255}, // 11 - light cyan
{255,84,84}, // 12 - light red
{255,84,255}, // 13 - light magenta
{255,255,84}, // 14 - yellow
{255,255,255}, // 15 - white
};
public:
void index_to_rgb(int index, SDL_Color &color) const;
};
class GlyphCache
{
public:
// 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<Uint64, SDL_Surface*> _glyph_map;
};
/* TTF font glyph renderer
*
* Renders uniformly sized cells with single character.
* Wraps SDL_ttf functions, see its documentation here:
*
* http://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html
*
*/
class GlyphRenderer
{
public:
GlyphRenderer();
~GlyphRenderer();
void open_font(const char *fname_regular, const char *fname_bold, int ptsize);
void close_font();
// do not free surface returned!
SDL_Surface *render_cell(Uint32 ch, Uint32 attr, bool blink_state);
int get_cell_width() const { return _cell_width; };
int get_cell_height() const { return _cell_height; };
private:
TTF_Font *_font_regular;
TTF_Font *_font_bold;
int _cell_width;
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
{
Uint32 ch;
Uint32 attr;
TerminalCell() : ch(0), attr(7) {};
bool operator !=(const TerminalCell &rhs) const { return ch != rhs.ch || attr != rhs.attr; };
};
class TerminalScreen
{
public:
TerminalScreen():
_screen_surface(NULL), _cells_front(0), _cells_back(0), _render(),
_pixel_width(0), _pixel_height(0), _width(0), _height(0),
_cell_width(0), _cell_height(0), _blink_state(1) {};
~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, Uint32 ch, Uint32 attr);
void toggle_cursor(int x, int y);
void toggle_blink() { _blink_state = !_blink_state; _draw_blink(); };
void commit();
// force full redraw on next commit()
void redraw();
int get_width() const { return _width; };
int get_height() const { return _height; };
int get_cell_width() const { return _cell_width; };
int get_cell_height() const { return _cell_height; };
private:
SDL_Surface *_screen_surface;
std::vector<TerminalCell> _cells_front;
std::vector<TerminalCell> _cells_back;
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;
int _blink_state; // 0 - blink chars hidden, 1 - visible
void _reset_cells();
void _draw_blink();
};
struct Event
{
enum { QUIT, RESIZE, KEYPRESS, MOUSEDOWN, MOUSEUP, MOUSEMOVE, MOUSEWHEEL };
int type;
union
{
struct
{
char keyname[10];
Uint32 unicode;
int mod;
} key;
struct
{
int x, y;
int button;
} 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, Uint32 ch) { _screen.putch(x, y, ch, _attr); };
void commit();
Uint32 prepare_attr(Uint8 fg, Uint8 bg, Uint32 style) { return (Uint32)fg | (Uint32)bg << 8 | (style & 0xFF000000); };
void set_attr(Uint32 value) { _attr = value; };
void show_cursor(int x, int y) { _cursor_x = x; _cursor_y = y; _cursor_visible = true; };
void hide_cursor() { _cursor_visible = false; };
/* Wait for an event.
*
* Timeout is in miliseconds, zero means wait indefinitely.
* Returns false on timeout, true on event.
*/
bool wait_event(Event &event, Uint32 timeout);
int get_width() const { return _screen.get_width(); };
int get_height() const { return _screen.get_height(); };
private:
TerminalScreen _screen;
Uint32 _attr;
int _cursor_x;
int _cursor_y;
bool _cursor_visible;
int _mousemove_last_x;
int _mousemove_last_y;
const char *_translate_keyname(SDLKey sym);
int _translate_mod(SDLMod mod);
static Uint32 _wait_event_callback(Uint32 interval, void *param);
static Uint32 _blink_toggle_callback(Uint32 interval, void *param);
};
class SDLTermError: public std::exception
{
public:
SDLTermError(const std::string &a_msg)
: std::exception(), _msg(a_msg) {};
virtual ~SDLTermError() throw() {};
virtual const char *what() const throw() { return _msg.c_str(); };
private:
std::string _msg;
};