README: mention SDL2 backend
[rofl0r-concol.git] / sdlconsole.c
blob7981e64a156339dcee301b634d74c8f738dd8cc6
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #if USE_SDL2 +0 == 0
5 #include <SDL/SDL.h>
6 #else
7 #include <SDL2/SDL.h>
8 #endif
9 #include "console.h"
10 #include "rect.h"
11 #include <stdarg.h>
13 #ifndef CONSOLE_FONT
14 #include "fonts/testfont.h"
15 #endif
17 #define SDL_mutex_lock(X) SDL_mutexP(X)
18 #define SDL_mutex_unlock(X) SDL_mutexV(X)
20 #pragma RcB2 DEP "sdlconsole_chartab.c"
22 void console_resize(Console *c, int w, int h);
24 static SDL_mutex *screens_lock;
26 void console_init(struct Console *self) {
27 memset(self, 0, sizeof(struct Console));
28 self->backendtype = cb_sdl;
30 void console_settitle(Console *self, const char *title) {
31 #if SDL_MAJOR_VERSION == 2
32 struct SDLConsole *c = &self->backend.sdl;
33 SDL_SetWindowTitle(c->window, title);
34 #else
35 (void) self;
36 SDL_WM_SetCaption(title, NULL);
37 #endif
40 #if SDL_MAJOR_VERSION == 1
41 #define update_rect(C, X, Y, W, H) SDL_UpdateRect((C)->surface, X, Y, W, H)
42 #else
43 static void update_rect(struct SDLConsole *c, int x, int y, int w, int h) {
44 SDL_Surface *s = c->surface;
45 const SDL_Rect* area = w ? &(SDL_Rect){.x = x, .y = y, .w = w, .h = h} : 0;
46 SDL_UpdateTexture(c->texture, area, s->pixels, s->pitch);
47 SDL_RenderClear(c->renderer);
48 SDL_RenderCopy(c->renderer, c->texture, NULL, NULL);
49 SDL_RenderPresent(c->renderer);
51 #endif
53 void console_init_graphics(Console* self, point resolution, font* fnt) {
54 struct SDLConsole *c = &self->backend.sdl;
55 c->paintmode = 0;
56 c->cursorblink = 1;
57 c->fullscreen = 0;
58 self->automove = 0;
59 self->isblinking = 0;
60 c->surface = NULL;
62 #ifndef CONSOLE_FONT
63 if(!fnt) fnt = &testfont;
64 #endif
65 if (SDL_Init(SDL_INIT_VIDEO) < 0) {
66 printf("Could not initialize SDL: %s\n", SDL_GetError());
67 exit(1);
69 screens_lock = SDL_CreateMutex();
71 c->fnt = fnt;
72 #if SDL_MAJOR_VERSION == 2
73 c->window = SDL_CreateWindow("sdl-console",
74 SDL_WINDOWPOS_UNDEFINED,
75 SDL_WINDOWPOS_UNDEFINED,
76 resolution.x, resolution.y,
77 0 | SDL_WINDOW_RESIZABLE /* | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL*/);
78 c->renderer = SDL_CreateRenderer(c->window, -1, 0);
79 SDL_EnableScreenSaver();
80 SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "5");
81 #endif
82 console_resize(self, resolution.x, resolution.y);
83 //c->fnt = bitfont_to_font(&int10_font_16);
84 #if SDL_MAJOR_VERSION == 1
85 console_settitle(self, "sdl-console");
86 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
87 #endif
90 void console_cleanup(Console* self) {
91 struct SDLConsole *c = &self->backend.sdl;
92 if(c->fullscreen) console_toggle_fullscreen(self);
93 SDL_DestroyMutex(screens_lock);
94 SDL_Quit();
97 int console_getcolorcount(Console *self) { (void) self; return 256; }
99 void console_initoutput(Console* self) {(void) self;}
101 void console_clear(Console* self) {
102 rect fs = rect_zero;
103 fs.bottomright.x = self->dim.x -1;
104 fs.bottomright.y = self->dim.y -1;
105 console_fill(self, &fs, ' ');
107 #undef _POSIX_C_SOURCE
108 #define _POSIX_C_SOURCE 200809L
109 #include <time.h>
110 #include <errno.h>
112 static int msleep(long millisecs) {
113 struct timespec req, rem;
114 req.tv_sec = millisecs / 1000;
115 req.tv_nsec = (millisecs % 1000) * 1000 * 1000;
116 int ret;
117 while((ret = nanosleep(&req, &rem)) == -1 && errno == EINTR) req = rem;
118 return ret;
121 void console_sleep(struct Console* self, int ms) {
122 (void) self;
123 msleep(ms);
126 #define INCLUDED_FROM_SDLCONSOLE
127 #include "sdlconsole_keyboard.c"
129 static void handle_event_action(Console *c, SDL_Event *event, int action) {
130 switch(action) {
131 case ea_resized:
132 #if SDL_MAJOR_VERSION == 1
133 console_resize(c, event->resize.w, event->resize.h);
134 #else
135 console_resize(c, event->window.data1, event->window.data2);
136 #endif
137 break;
138 case ea_redraw:
139 console_refresh(c);
140 break;
141 case ea_toggle_fullscreen:
142 console_toggle_fullscreen(c);
143 break;
147 int console_getkey_nb(Console* c) {
148 SDL_Event event;
149 /* Loop through waiting messages and process them */
150 while (SDL_PollEvent(&event)) {
151 int k, action;
152 k = sdlconsole_translate_event(c, &event, &action);
153 handle_event_action(c, &event, action);
154 return k;
156 return CK_UNDEF;
159 int console_getkey(Console* c) {
160 SDL_Event event;
161 /* Loop through waiting messages and process them */
162 int ret, action;
163 while(1) {
164 if (SDL_WaitEvent(&event)) {
165 ret = sdlconsole_translate_event(c, &event, &action);
166 handle_event_action(c, &event, action);
167 if(ret != CK_UNDEF) return ret;
168 } else { // error happened, prevent 100% cpu loop
169 console_sleep(c, 10);
170 break;
173 return CK_UNDEF;
177 /* prints a char and NOT advances cursor */
178 void console_addchar(struct Console* self, int c, unsigned int attributes) {
179 (void) attributes;
180 int save = self->automove;
181 self->automove = 0;
182 console_putchar(self, c, 0);
183 self->automove = save;
185 /* prints a char and advances cursor */
186 void console_printchar(struct Console* self, int c, unsigned int attributes) {
187 (void) attributes;
188 int save = self->automove;
189 self->automove = 1;
190 console_putchar(self, c, 0);
191 self->automove = save;
194 void console_goto(Console* c, int x, int y) {
195 c->cursor.x = x;
196 c->cursor.y = y;
200 void console_lock(void) {
201 while(SDL_mutex_lock(screens_lock) == -1) {
202 printf("warning: couldn't aquire screens_lock\n");
203 SDL_Delay(1);
207 void console_unlock(void) {
208 SDL_mutex_unlock(screens_lock);
211 static void alloc_cache(SDLConsole *c) {
212 if(c->cache) free(c->cache);
213 c->cache=calloc(c->res.x*c->res.y, sizeof(sdl_rgb_tuple));
216 void console_resize(Console *self, int w, int h) {
217 SDLConsole *c = &self->backend.sdl;
218 console_lock();
219 c->res.x = w;
220 c->res.y = h;
221 self->dim.x = w / c->fnt->dim.x;
222 self->dim.y = h / c->fnt->dim.y;
223 if(c->surface)
224 SDL_FreeSurface(c->surface);
225 #if SDL_MAJOR_VERSION == 2
226 c->surface = SDL_CreateRGBSurface(0, w, h, 32,
227 0x00FF0000,
228 0x0000FF00,
229 0x000000FF,
230 0xFF000000);
232 if(c->texture) SDL_DestroyTexture(c->texture);
233 c->texture = SDL_CreateTexture(c->renderer,
234 SDL_PIXELFORMAT_ARGB8888,
235 SDL_TEXTUREACCESS_STREAMING,
236 w, h);
237 #else
238 c->surface = SDL_SetVideoMode(w, h, 32, SDL_RESIZABLE | SDL_HWPALETTE);
239 #endif
240 if(!c->surface) {
241 printf("Couldn't set screen mode to %d x %d : %s\n", w, h, SDL_GetError());
242 exit(1);
244 alloc_cache(c);
245 console_unlock();
248 void console_toggle_fullscreen(Console* self) {
249 SDLConsole *c = &self->backend.sdl;
250 console_lock();
251 #if SDL_MAJOR_VERSION == 2
252 SDL_SetWindowFullscreen(c->window, c->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN);
253 #elif SDL_MAJOR_VERSION == 1
254 SDL_WM_ToggleFullScreen(c->surface);
255 #endif
256 c->fullscreen = ~ c->fullscreen;
257 console_unlock();
260 void console_getbounds(Console* c, int* x, int* y) {
261 *x = c->dim.x;
262 *y = c->dim.y;
265 static inline sdl_rgb_t rgb_to_srgb(rgb_t col) {
266 sdl_rgb_t ret;
267 ret.colors.a = col.a;
268 ret.colors.r = col.r;
269 ret.colors.g = col.g;
270 ret.colors.b = col.b;
271 return ret;
274 static inline rgb_t srgb_to_rgb(sdl_rgb_t col) {
275 rgb_t ret;
276 ret.a = col.colors.a;
277 ret.r = col.colors.r;
278 ret.g = col.colors.g;
279 ret.b = col.colors.b;
280 return ret;
283 rgb_tuple console_getcolors(Console* self) {
284 struct SDLConsole *c = &self->backend.sdl;
285 rgb_tuple ret;
286 ret.fgcolor = srgb_to_rgb(c->color.fgcolor);
287 ret.bgcolor = srgb_to_rgb(c->color.bgcolor);
288 return ret;
291 int console_setcolor(struct Console* self, int is_fg, rgb_t color) {
292 struct SDLConsole *c = &self->backend.sdl;
293 sdl_rgb_t* dest = is_fg ? &c->color.fgcolor : &c->color.bgcolor;
294 *dest = rgb_to_srgb(color);
295 return 1;
298 void console_refresh(Console* self) {
299 struct SDLConsole *c = &self->backend.sdl;
300 console_lock();
301 update_rect(c, 0, 0, 0, 0);
302 console_unlock();
305 void console_blink_cursor(Console* self) {
306 struct SDLConsole *c = &self->backend.sdl;
307 sdl_rgb_t *ptr = (sdl_rgb_t *) ((SDL_Surface*) c->surface)->pixels;
308 int x, y, rx, ry, lineoffset;
309 console_lock();
310 if(c->cursorblink || self->isblinking) {
311 for (y = 0, ry = self->cursor.y * c->fnt->dim.y; y < c->fnt->dim.y; y++, ry += 1) {
312 for (x = 0, rx = self->cursor.x * c->fnt->dim.x; x < c->fnt->dim.x; x++, rx++) {
313 lineoffset = ry * (((SDL_Surface*) c->surface)->pitch / 4);
314 ptr[lineoffset + rx].val = ~ptr[lineoffset + rx].val;
317 update_rect(c, self->cursor.x * c->fnt->dim.x ,self->cursor.y * c->fnt->dim.y, c->fnt->dim.x, c->fnt->dim.y);
318 self->isblinking = ~self->isblinking;
320 console_unlock();
323 #include <assert.h>
324 #include <limits.h>
325 #define BA_TARGET_BYTE(X, Y) ((X) + ((Y) / CHAR_BIT))
326 #define BA_BIT_DISTANCE(X, Y) ((Y) % CHAR_BIT)
327 #define BA_GET(X, Y) (!!( *BA_TARGET_BYTE(X, Y) & (1 << BA_BIT_DISTANCE(X, Y)) ))
328 static char* bitfont_get_char(font* f, unsigned int ch) {
329 static unsigned int lastchr = (unsigned) -1;
330 static unsigned char char_data[16*16];
331 if(ch != lastchr) {
332 unsigned char* p = char_data;
333 size_t i, start = f->pointsperchar * ch;
334 assert(f->pointsperchar <= sizeof(char_data));
335 for(i = 0; i < f->pointsperchar; i++)
336 *(p++) = BA_GET(f->characters, start + i);
337 lastchr = ch;
339 return (char*) char_data;
342 void console_putchar(Console* self, int ch, int doupdate) {
343 struct SDLConsole *c = &self->backend.sdl;
344 console_unblink(self);
345 console_lock();
346 if(self->cursor.y >= self->dim.y || self->cursor.x >= self->dim.x)
347 goto skip;
348 sdl_rgb_tuple cache_test = c->color;
349 cache_test.bgcolor.colors.a = (unsigned char) ch;
350 if(!memcmp(&c->cache[self->cursor.y * self->dim.x + self->cursor.x], &cache_test, 8)) goto skip;
351 else c->cache[self->cursor.y * self->dim.x + self->cursor.x] = cache_test;
352 unsigned char* font = (void*)bitfont_get_char(c->fnt, ch & 0xff);
353 int pitch_div_4 = (((SDL_Surface*) c->surface)->pitch / 4);
354 sdl_rgb_t *ptr = (sdl_rgb_t *) ((SDL_Surface*) c->surface)->pixels;
355 ptr += self->cursor.y * c->fnt->dim.y * pitch_div_4;
356 ptr += self->cursor.x * c->fnt->dim.x;
357 size_t advance = (pitch_div_4 - c->fnt->dim.x);
358 sdl_rgb_t color[2] = {[0] = c->color.bgcolor, [1] = c->color.fgcolor};
359 int x, y;
360 for (y = 0; y < c->fnt->dim.y; y++, ptr += advance) {
361 for (x = 0; x < c->fnt->dim.x; x++, font++, ptr++) {
362 *ptr = color[*font];
365 if(doupdate) update_rect(c, self->cursor.x * c->fnt->dim.x ,self->cursor.y * c->fnt->dim.y, c->fnt->dim.x, c->fnt->dim.y);
366 skip:
367 console_unlock();
368 if(self->automove) console_advance_cursor(self, 1);