summaryrefslogtreecommitdiff
path: root/textscreen/txt_sdl.c
diff options
context:
space:
mode:
Diffstat (limited to 'textscreen/txt_sdl.c')
-rw-r--r--textscreen/txt_sdl.c548
1 files changed, 548 insertions, 0 deletions
diff --git a/textscreen/txt_sdl.c b/textscreen/txt_sdl.c
new file mode 100644
index 00000000..a7e08e1d
--- /dev/null
+++ b/textscreen/txt_sdl.c
@@ -0,0 +1,548 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005,2006 Simon Howard
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+//-----------------------------------------------------------------------------
+//
+// Text mode emulation in SDL
+//
+//-----------------------------------------------------------------------------
+
+#include "SDL.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "doomkeys.h"
+
+#include "txt_main.h"
+#include "txt_sdl.h"
+#include "txt_font.h"
+
+#define CHAR_W 8
+#define CHAR_H 16
+
+// Time between character blinks in ms
+
+#define BLINK_PERIOD 250
+
+static SDL_Surface *screen;
+static unsigned char *screendata;
+static int key_mapping = 1;
+
+static TxtSDLEventCallbackFunc event_callback;
+static void *event_callback_data;
+
+//#define TANGO
+
+#ifndef TANGO
+
+static SDL_Color ega_colors[] =
+{
+ {0x00, 0x00, 0x00, 0x00}, // 0: Black
+ {0x00, 0x00, 0xa8, 0x00}, // 1: Blue
+ {0x00, 0xa8, 0x00, 0x00}, // 2: Green
+ {0x00, 0xa8, 0xa8, 0x00}, // 3: Cyan
+ {0xa8, 0x00, 0x00, 0x00}, // 4: Red
+ {0xa8, 0x00, 0xa8, 0x00}, // 5: Magenta
+ {0xa8, 0x54, 0x00, 0x00}, // 6: Brown
+ {0xa8, 0xa8, 0xa8, 0x00}, // 7: Grey
+ {0x54, 0x54, 0x54, 0x00}, // 8: Dark grey
+ {0x54, 0x54, 0xfe, 0x00}, // 9: Bright blue
+ {0x54, 0xfe, 0x54, 0x00}, // 10: Bright green
+ {0x54, 0xfe, 0xfe, 0x00}, // 11: Bright cyan
+ {0xfe, 0x54, 0x54, 0x00}, // 12: Bright red
+ {0xfe, 0x54, 0xfe, 0x00}, // 13: Bright magenta
+ {0xfe, 0xfe, 0x54, 0x00}, // 14: Yellow
+ {0xfe, 0xfe, 0xfe, 0x00}, // 15: Bright white
+};
+
+#else
+
+// Colors that fit the Tango desktop guidelines: see
+// http://tango.freedesktop.org/ also
+// http://uwstopia.nl/blog/2006/07/tango-terminal
+
+static SDL_Color ega_colors[] =
+{
+ {0x2e, 0x34, 0x36, 0x00}, // 0: Black
+ {0x34, 0x65, 0xa4, 0x00}, // 1: Blue
+ {0x4e, 0x9a, 0x06, 0x00}, // 2: Green
+ {0x06, 0x98, 0x9a, 0x00}, // 3: Cyan
+ {0xcc, 0x00, 0x00, 0x00}, // 4: Red
+ {0x75, 0x50, 0x7b, 0x00}, // 5: Magenta
+ {0xc4, 0xa0, 0x00, 0x00}, // 6: Brown
+ {0xd3, 0xd7, 0xcf, 0x00}, // 7: Grey
+ {0x55, 0x57, 0x53, 0x00}, // 8: Dark grey
+ {0x72, 0x9f, 0xcf, 0x00}, // 9: Bright blue
+ {0x8a, 0xe2, 0x34, 0x00}, // 10: Bright green
+ {0x34, 0xe2, 0xe2, 0x00}, // 11: Bright cyan
+ {0xef, 0x29, 0x29, 0x00}, // 12: Bright red
+ {0x34, 0xe2, 0xe2, 0x00}, // 13: Bright magenta
+ {0xfc, 0xe9, 0x4f, 0x00}, // 14: Yellow
+ {0xee, 0xee, 0xec, 0x00}, // 15: Bright white
+};
+
+#endif
+
+//
+// Initialise text mode screen
+//
+// Returns 1 if successful, 0 if an error occurred
+//
+
+int TXT_Init(void)
+{
+ SDL_InitSubSystem(SDL_INIT_VIDEO);
+
+ screen = SDL_SetVideoMode(TXT_SCREEN_W * CHAR_W, TXT_SCREEN_H * CHAR_H, 8, 0);
+
+ if (screen == NULL)
+ return 0;
+
+ SDL_SetColors(screen, ega_colors, 0, 16);
+ SDL_EnableUNICODE(1);
+
+ screendata = malloc(TXT_SCREEN_W * TXT_SCREEN_H * 2);
+ memset(screendata, 0, TXT_SCREEN_W * TXT_SCREEN_H * 2);
+
+ // Ignore all mouse motion events
+
+ SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
+
+ // Repeat key presses so we can hold down arrows to scroll down the
+ // menu, for example. This is what setup.exe does.
+
+ SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
+
+ return 1;
+}
+
+void TXT_Shutdown(void)
+{
+ free(screendata);
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+}
+
+unsigned char *TXT_GetScreenData(void)
+{
+ return screendata;
+}
+
+static inline void UpdateCharacter(int x, int y)
+{
+ unsigned char character;
+ unsigned char *p;
+ unsigned char *s, *s1;
+ int bg, fg;
+ int x1, y1;
+
+ p = &screendata[(y * TXT_SCREEN_W + x) * 2];
+ character = p[0];
+
+ fg = p[1] & 0xf;
+ bg = (p[1] >> 4) & 0xf;
+
+ if (bg & 0x8)
+ {
+ // blinking
+
+ bg &= ~0x8;
+
+ if (((SDL_GetTicks() / BLINK_PERIOD) % 2) == 0)
+ {
+ fg = bg;
+ }
+ }
+
+ p = &int10_font_16[character * CHAR_H];
+
+ s = ((unsigned char *) screen->pixels)
+ + (y * CHAR_H * screen->pitch) + (x * CHAR_W);
+
+ for (y1=0; y1<CHAR_H; ++y1)
+ {
+ s1 = s;
+
+ for (x1=0; x1<CHAR_W; ++x1)
+ {
+ if (*p & (1 << (7-x1)))
+ {
+ *s1++ = fg;
+ }
+ else
+ {
+ *s1++ = bg;
+ }
+ }
+
+ ++p;
+ s += screen->pitch;
+ }
+}
+
+void TXT_UpdateScreenArea(int x, int y, int w, int h)
+{
+ int x1, y1;
+
+ for (y1=y; y1<y+h; ++y1)
+ {
+ for (x1=x; x1<x+w; ++x1)
+ {
+ UpdateCharacter(x1, y1);
+ }
+ }
+
+ SDL_UpdateRect(screen, x * CHAR_W, y * CHAR_H, w * CHAR_W, h * CHAR_H);
+}
+
+void TXT_UpdateScreen(void)
+{
+ TXT_UpdateScreenArea(0, 0, TXT_SCREEN_W, TXT_SCREEN_H);
+}
+
+void TXT_GetMousePosition(int *x, int *y)
+{
+ SDL_GetMouseState(x, y);
+
+ *x /= CHAR_W;
+ *y /= CHAR_H;
+}
+
+//
+// Translates the SDL key
+//
+
+static int TranslateKey(SDL_keysym *sym)
+{
+ switch(sym->sym)
+ {
+ case SDLK_LEFT: return KEY_LEFTARROW;
+ case SDLK_RIGHT: return KEY_RIGHTARROW;
+ case SDLK_DOWN: return KEY_DOWNARROW;
+ case SDLK_UP: return KEY_UPARROW;
+ case SDLK_ESCAPE: return KEY_ESCAPE;
+ case SDLK_RETURN: return KEY_ENTER;
+ case SDLK_TAB: return KEY_TAB;
+ case SDLK_F1: return KEY_F1;
+ case SDLK_F2: return KEY_F2;
+ case SDLK_F3: return KEY_F3;
+ case SDLK_F4: return KEY_F4;
+ case SDLK_F5: return KEY_F5;
+ case SDLK_F6: return KEY_F6;
+ case SDLK_F7: return KEY_F7;
+ case SDLK_F8: return KEY_F8;
+ case SDLK_F9: return KEY_F9;
+ case SDLK_F10: return KEY_F10;
+ case SDLK_F11: return KEY_F11;
+ case SDLK_F12: return KEY_F12;
+
+ case SDLK_BACKSPACE: return KEY_BACKSPACE;
+ case SDLK_DELETE: return KEY_DEL;
+
+ case SDLK_PAUSE: return KEY_PAUSE;
+
+ case SDLK_KP_EQUALS: return KEY_EQUALS;
+
+ case SDLK_LSHIFT:
+ case SDLK_RSHIFT:
+ return KEY_RSHIFT;
+
+ case SDLK_LCTRL:
+ case SDLK_RCTRL:
+ return KEY_RCTRL;
+
+ case SDLK_LALT:
+ case SDLK_LMETA:
+ case SDLK_RALT:
+ case SDLK_RMETA:
+ return KEY_RALT;
+
+ case SDLK_CAPSLOCK: return KEY_CAPSLOCK;
+ case SDLK_SCROLLOCK: return KEY_SCRLCK;
+
+ case SDLK_KP0: return KEYP_0;
+ case SDLK_KP1: return KEYP_1;
+ case SDLK_KP2: return KEYP_2;
+ case SDLK_KP3: return KEYP_3;
+ case SDLK_KP4: return KEYP_4;
+ case SDLK_KP5: return KEYP_5;
+ case SDLK_KP6: return KEYP_6;
+ case SDLK_KP7: return KEYP_7;
+ case SDLK_KP8: return KEYP_8;
+ case SDLK_KP9: return KEYP_9;
+
+ case SDLK_HOME: return KEY_HOME;
+ case SDLK_INSERT: return KEY_INS;
+ case SDLK_END: return KEY_END;
+ case SDLK_PAGEUP: return KEY_PGUP;
+ case SDLK_PAGEDOWN: return KEY_PGDN;
+ case SDLK_KP_MULTIPLY: return KEYP_MULTIPLY;
+ case SDLK_KP_PLUS: return KEYP_PLUS;
+ case SDLK_KP_MINUS: return KEYP_MINUS;
+ case SDLK_KP_DIVIDE: return KEYP_DIVIDE;
+
+ default: break;
+ }
+
+ // Returned value is different, depending on whether key mapping is
+ // enabled. Key mapping is preferable most of the time, for typing
+ // in text, etc. However, when we want to read raw keyboard codes
+ // for the setup keyboard configuration dialog, we want the raw
+ // key code.
+
+ if (key_mapping)
+ {
+ return sym->unicode;
+ }
+ else
+ {
+ return tolower(sym->sym);
+ }
+}
+
+
+signed int TXT_GetChar(void)
+{
+ SDL_Event ev;
+
+ while (SDL_PollEvent(&ev))
+ {
+ // If there is an event callback, allow it to intercept this
+ // event.
+
+ if (event_callback != NULL)
+ {
+ if (event_callback(&ev, event_callback_data))
+ {
+ continue;
+ }
+ }
+
+ // Process the event.
+
+ switch (ev.type)
+ {
+ case SDL_MOUSEBUTTONDOWN:
+ if (ev.button.button == SDL_BUTTON_LEFT)
+ return TXT_MOUSE_LEFT;
+ else if (ev.button.button == SDL_BUTTON_RIGHT)
+ return TXT_MOUSE_RIGHT;
+ else if (ev.button.button == SDL_BUTTON_MIDDLE)
+ return TXT_MOUSE_MIDDLE;
+ break;
+
+ case SDL_KEYDOWN:
+ return TranslateKey(&ev.key.keysym);
+
+ case SDL_QUIT:
+ // Quit = escape
+ return 27;
+
+ default:
+ break;
+ }
+ }
+
+ return -1;
+}
+
+static char *SpecialKeyName(int key)
+{
+ switch (key)
+ {
+ case ' ': return "SPACE";
+ case KEY_RIGHTARROW: return "RIGHT";
+ case KEY_LEFTARROW: return "LEFT";
+ case KEY_UPARROW: return "UP";
+ case KEY_DOWNARROW: return "DOWN";
+ case KEY_ESCAPE: return "ESC";
+ case KEY_ENTER: return "ENTER";
+ case KEY_TAB: return "TAB";
+ case KEY_F1: return "F1";
+ case KEY_F2: return "F2";
+ case KEY_F3: return "F3";
+ case KEY_F4: return "F4";
+ case KEY_F5: return "F5";
+ case KEY_F6: return "F6";
+ case KEY_F7: return "F7";
+ case KEY_F8: return "F8";
+ case KEY_F9: return "F9";
+ case KEY_F10: return "F10";
+ case KEY_F11: return "F11";
+ case KEY_F12: return "F12";
+ case KEY_BACKSPACE: return "BKSP";
+ case KEY_PAUSE: return "PAUSE";
+ case KEY_EQUALS: return "EQUALS";
+ case KEY_MINUS: return "MINUS";
+ case KEY_RSHIFT: return "SHIFT";
+ case KEY_RCTRL: return "CTRL";
+ case KEY_RALT: return "ALT";
+ case KEY_CAPSLOCK: return "CAPS";
+ case KEY_SCRLCK: return "SCRLCK";
+ case KEY_HOME: return "HOME";
+ case KEY_END: return "END";
+ case KEY_PGUP: return "PGUP";
+ case KEY_PGDN: return "PGDN";
+ case KEY_INS: return "INS";
+ case KEY_DEL: return "DEL";
+ /*
+ case KEYP_0: return "PAD0";
+ case KEYP_1: return "PAD1";
+ case KEYP_2: return "PAD2";
+ case KEYP_3: return "PAD3";
+ case KEYP_4: return "PAD4";
+ case KEYP_5: return "PAD5";
+ case KEYP_6: return "PAD6";
+ case KEYP_7: return "PAD7";
+ case KEYP_8: return "PAD8";
+ case KEYP_9: return "PAD9";
+ case KEYP_UPARROW: return "PAD_U";
+ case KEYP_DOWNARROW: return "PAD_D";
+ case KEYP_LEFTARROW: return "PAD_L";
+ case KEYP_RIGHTARROW: return "PAD_R";
+ case KEYP_MULTIPLY: return "PAD*";
+ case KEYP_PLUS: return "PAD+";
+ case KEYP_MINUS: return "PAD-";
+ case KEYP_DIVIDE: return "PAD/";
+ */
+ default: return NULL;
+ }
+}
+
+void TXT_GetKeyDescription(int key, char *buf)
+{
+ char *keyname;
+
+ keyname = SpecialKeyName(key);
+
+ if (keyname != NULL)
+ {
+ strcpy(buf, keyname);
+ }
+ else if (isprint(key))
+ {
+ sprintf(buf, "%c", toupper(key));
+ }
+ else
+ {
+ sprintf(buf, "??%i", key);
+ }
+}
+
+// Searches the desktop screen buffer to determine whether there are any
+// blinking characters.
+
+int TXT_ScreenHasBlinkingChars(void)
+{
+ int x, y;
+ unsigned char *p;
+
+ // Check all characters in screen buffer
+
+ for (y=0; y<TXT_SCREEN_H; ++y)
+ {
+ for (x=0; x<TXT_SCREEN_W; ++x)
+ {
+ p = &screendata[(y * TXT_SCREEN_W + x) * 2];
+
+ if (p[1] & 0x80)
+ {
+ // This character is blinking
+
+ return 1;
+ }
+ }
+ }
+
+ // None found
+
+ return 0;
+}
+
+// Sleeps until an event is received, the screen needs to be redrawn,
+// or until timeout expires (if timeout != 0)
+
+void TXT_Sleep(int timeout)
+{
+ unsigned int start_time;
+
+ if (TXT_ScreenHasBlinkingChars())
+ {
+ int time_to_next_blink;
+
+ time_to_next_blink = BLINK_PERIOD - (SDL_GetTicks() % BLINK_PERIOD);
+
+ // There are blinking characters on the screen, so we
+ // must time out after a while
+
+ if (timeout == 0 || timeout > time_to_next_blink)
+ {
+ // Add one so it is always positive
+
+ timeout = time_to_next_blink + 1;
+ }
+ }
+
+ if (timeout == 0)
+ {
+ // We can just wait forever until an event occurs
+
+ SDL_WaitEvent(NULL);
+ }
+ else
+ {
+ // Sit in a busy loop until the timeout expires or we have to
+ // redraw the blinking screen
+
+ start_time = SDL_GetTicks();
+
+ while (SDL_GetTicks() < start_time + timeout)
+ {
+ if (SDL_PollEvent(NULL) != 0)
+ {
+ // Received an event, so stop waiting
+
+ break;
+ }
+
+ // Don't hog the CPU
+
+ SDL_Delay(1);
+ }
+ }
+}
+
+void TXT_EnableKeyMapping(int enable)
+{
+ key_mapping = enable;
+}
+
+void TXT_SetWindowTitle(char *title)
+{
+ SDL_WM_SetCaption(title, NULL);
+}
+
+void TXT_SDL_SetEventCallback(TxtSDLEventCallbackFunc callback, void *user_data)
+{
+ event_callback = callback;
+ event_callback_data = user_data;
+}
+