sdlterm/src/sdlterm.cc
author Radek Brich <radek.brich@devl.cz>
Sat, 05 Jan 2013 00:40:27 +0100
changeset 47 537d7c6b48a2
child 48 1f00e90fd72a
permissions -rw-r--r--
Add sdlterm prototype: extension module for SDL driver.

#include "sdlterm.h"

#include <stdexcept>


GlyphRenderer::GlyphRenderer()
 : _font_regular(NULL), _font_bold(NULL)
{
	if (TTF_Init() == -1)
	{
	    printf("TTF_Init: %s\n", TTF_GetError());
	    throw std::exception();
	}
}


GlyphRenderer::~GlyphRenderer()
{
	close_font();
	TTF_Quit();
}


void GlyphRenderer::open_font(const char *fname_regular, const char *fname_bold, int ptsize)
{
	close_font();

	// open regular font
	_font_regular = TTF_OpenFont(fname_regular, ptsize);
	if (!_font_regular)
	{
		printf("TTF_OpenFont: %s\n", TTF_GetError());
		throw std::exception();
	}

	// open bold font
	_font_bold = TTF_OpenFont(fname_bold, ptsize);
	if (!_font_bold)
	{
		printf("TTF_OpenFont: %s\n", TTF_GetError());
		throw std::exception();
	}

	// update metrics for regular font
	int advance;
	if (TTF_GlyphMetrics(_font_regular, 'M', NULL, NULL, NULL, NULL, &advance) == -1)
	{
	    printf("TTF_GlyphMetrics: %s\n", TTF_GetError());
	}
	_cell_width = advance;
	_cell_height = TTF_FontHeight(_font_regular);

	// read metrics for bold font
	if (TTF_GlyphMetrics(_font_bold, 'M', NULL, NULL, NULL, NULL, &advance) == -1)
	{
		printf("TTF_GlyphMetrics: %s\n", TTF_GetError());
	}
	if (advance > _cell_width)
	{
		_cell_width = advance;
	}
	int height = TTF_FontHeight(_font_bold);
	if (height > _cell_height)
	{
		_cell_height = height;
	}
}


void GlyphRenderer::close_font()
{
	if (_font_regular)
	{
		TTF_CloseFont(_font_regular);
		_font_regular = NULL;
	}
	if (_font_bold)
	{
		TTF_CloseFont(_font_bold);
		_font_bold = NULL;
	}
}


SDL_Surface *GlyphRenderer::render_glyph(Uint16 ch)
{
	TTF_Font *font = _font_regular;
	SDL_Color color={0xff,0xff,0xff}, bgcolor={0,100,100};
	if (ch != 'W')
	{
		bgcolor.g = 0;
		bgcolor.b = 0;
	}

	// create surface for whole cell and fill it with bg color
	SDL_Surface *cell_surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
			_cell_width, _cell_height, 32, 0, 0, 0, 0);
	SDL_Rect dst_rect;
	dst_rect.x = 0;
	dst_rect.y = 0;
	dst_rect.w = _cell_width;
	dst_rect.h = _cell_height;
	Uint32 bgcolor_mapped = SDL_MapRGB(cell_surface->format, bgcolor.r, bgcolor.g, bgcolor.b);
	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);

	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);
	_reset_cells();
}

void TerminalScreen::resize(int pxwidth, int pxheight)
{
    _screen_surface = SDL_SetVideoMode(pxwidth, pxheight, 8, SDL_SWSURFACE|SDL_ANYFORMAT|SDL_RESIZABLE);

    if (_screen_surface == NULL)
    {
		fprintf(stderr, "Unable to set video: %s\n", SDL_GetError());
		throw std::exception();
    }

    SDL_WM_SetCaption("terminal", NULL);

    _pixel_width = pxwidth;
    _pixel_height = pxheight;

    _reset_cells();
}


void TerminalScreen::erase()
{
	TerminalCell * cell = _cells;
	for (int i = 0; i < _width * _height; i++)
	{
		cell->ch = ' ';
		cell->attr = 0;
		cell++;
	}
}


void TerminalScreen::putch(int x, int y, Uint16 ch, Uint16 attr)
{
	TerminalCell &cell = _cells[y * _width + x];
	cell.ch = ch;
	cell.attr = attr;
}


void TerminalScreen::commit()
{
	TerminalCell * cell = _cells;
	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);
			SDL_FreeSurface(glyph_surface);
			cell++;
		}
	}

	SDL_UpdateRect(_screen_surface, 0, 0, 0, 0);
}


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];
}


Terminal::Terminal()
 : _screen()
{
    if (SDL_Init(SDL_INIT_VIDEO) == -1)
    {
		fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
		throw std::exception();
    }
}


Terminal::~Terminal()
{
	SDL_Quit();
}


void Terminal::get_next_event(Event &event)
{
	SDL_Event sdl_event;

	while (SDL_WaitEvent(&sdl_event))
	{
		switch (sdl_event.type)
		{
			case SDL_QUIT:
				event.type = EventType::quit;
				return;

			case SDL_KEYDOWN:
				//switch(event.key.keysym.sym)
				event.type = EventType::keypress;
				return;

			default:
				break; // continue loop
		}
	}
}