123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946 |
- //
- // Copyright(C) 2005-2014 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.
- //
- //
- // Text mode emulation in SDL
- //
- #include "SDL.h"
- #include <ctype.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include "doomkeys.h"
- #include "txt_main.h"
- #include "txt_sdl.h"
- #if defined(_MSC_VER) && !defined(__cplusplus)
- #define inline __inline
- #endif
- typedef struct
- {
- unsigned char *data;
- unsigned int w;
- unsigned int h;
- } txt_font_t;
- // Fonts:
- #include "txt_font.h"
- #include "txt_largefont.h"
- #include "txt_smallfont.h"
- // Time between character blinks in ms
- #define BLINK_PERIOD 250
- SDL_Window *TXT_SDLWindow;
- static SDL_Surface *screenbuffer;
- static unsigned char *screendata;
- static int key_mapping = 1;
- static TxtSDLEventCallbackFunc event_callback;
- static void *event_callback_data;
- static int modifier_state[TXT_NUM_MODIFIERS];
- // Font we are using:
- static txt_font_t *font;
- //#define TANGO
- #ifndef TANGO
- static SDL_Color ega_colors[] =
- {
- {0x00, 0x00, 0x00, 0xff}, // 0: Black
- {0x00, 0x00, 0xa8, 0xff}, // 1: Blue
- {0x00, 0xa8, 0x00, 0xff}, // 2: Green
- {0x00, 0xa8, 0xa8, 0xff}, // 3: Cyan
- {0xa8, 0x00, 0x00, 0xff}, // 4: Red
- {0xa8, 0x00, 0xa8, 0xff}, // 5: Magenta
- {0xa8, 0x54, 0x00, 0xff}, // 6: Brown
- {0xa8, 0xa8, 0xa8, 0xff}, // 7: Grey
- {0x54, 0x54, 0x54, 0xff}, // 8: Dark grey
- {0x54, 0x54, 0xfe, 0xff}, // 9: Bright blue
- {0x54, 0xfe, 0x54, 0xff}, // 10: Bright green
- {0x54, 0xfe, 0xfe, 0xff}, // 11: Bright cyan
- {0xfe, 0x54, 0x54, 0xff}, // 12: Bright red
- {0xfe, 0x54, 0xfe, 0xff}, // 13: Bright magenta
- {0xfe, 0xfe, 0x54, 0xff}, // 14: Yellow
- {0xfe, 0xfe, 0xfe, 0xff}, // 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, 0xff}, // 0: Black
- {0x34, 0x65, 0xa4, 0xff}, // 1: Blue
- {0x4e, 0x9a, 0x06, 0xff}, // 2: Green
- {0x06, 0x98, 0x9a, 0xff}, // 3: Cyan
- {0xcc, 0x00, 0x00, 0xff}, // 4: Red
- {0x75, 0x50, 0x7b, 0xff}, // 5: Magenta
- {0xc4, 0xa0, 0x00, 0xff}, // 6: Brown
- {0xd3, 0xd7, 0xcf, 0xff}, // 7: Grey
- {0x55, 0x57, 0x53, 0xff}, // 8: Dark grey
- {0x72, 0x9f, 0xcf, 0xff}, // 9: Bright blue
- {0x8a, 0xe2, 0x34, 0xff}, // 10: Bright green
- {0x34, 0xe2, 0xe2, 0xff}, // 11: Bright cyan
- {0xef, 0x29, 0x29, 0xff}, // 12: Bright red
- {0x34, 0xe2, 0xe2, 0xff}, // 13: Bright magenta
- {0xfc, 0xe9, 0x4f, 0xff}, // 14: Yellow
- {0xee, 0xee, 0xec, 0xff}, // 15: Bright white
- };
- #endif
- #ifdef _WIN32
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- // Examine system DPI settings to determine whether to use the large font.
- static int Win32_UseLargeFont(void)
- {
- HDC hdc = GetDC(NULL);
- int dpix;
- if (!hdc)
- {
- return 0;
- }
- dpix = GetDeviceCaps(hdc, LOGPIXELSX);
- ReleaseDC(NULL, hdc);
- // 144 is the DPI when using "150%" scaling. If the user has this set
- // then consider this an appropriate threshold for using the large font.
- return dpix >= 144;
- }
- #endif
- static txt_font_t *FontForName(char *name)
- {
- if (!strcmp(name, "small"))
- {
- return &small_font;
- }
- else if (!strcmp(name, "normal"))
- {
- return &main_font;
- }
- else if (!strcmp(name, "large"))
- {
- return &large_font;
- }
- else
- {
- return NULL;
- }
- }
- //
- // Select the font to use, based on screen resolution
- //
- // If the highest screen resolution available is less than
- // 640x480, use the small font.
- //
- static void ChooseFont(void)
- {
- SDL_DisplayMode desktop_info;
- char *env;
- // Allow normal selection to be overridden from an environment variable:
- env = getenv("TEXTSCREEN_FONT");
- if (env != NULL)
- {
- font = FontForName(env);
- if (font != NULL)
- {
- return;
- }
- }
- // Get desktop resolution.
- // If in doubt and we can't get a list, always prefer to
- // fall back to the normal font:
- if (!SDL_GetCurrentDisplayMode(0, &desktop_info))
- {
- font = &main_font;
- return;
- }
- // On tiny low-res screens (eg. palmtops) use the small font.
- // If the screen resolution is at least 1920x1080, this is
- // a modern high-resolution display, and we can use the
- // large font.
- if (desktop_info.w < 640 || desktop_info.h < 480)
- {
- font = &small_font;
- }
- #ifdef _WIN32
- // On Windows we can use the system DPI settings to make a
- // more educated guess about whether to use the large font.
- else if (Win32_UseLargeFont())
- {
- font = &large_font;
- }
- #endif
- // TODO: Detect high DPI on Linux by inquiring about Gtk+ scale
- // settings. This looks like it should just be a case of shelling
- // out to invoke the 'gsettings' command, eg.
- // gsettings get org.gnome.desktop.interface text-scaling-factor
- // and using large_font if the result is >= 2.
- else
- {
- font = &main_font;
- }
- }
- //
- // Initialize text mode screen
- //
- // Returns 1 if successful, 0 if an error occurred
- //
- int TXT_Init(void)
- {
- if (SDL_Init(SDL_INIT_VIDEO) < 0)
- {
- return 0;
- }
- ChooseFont();
- // Always create the screen at the native screen depth (bpp=0);
- // some systems nowadays don't seem to support true 8-bit palettized
- // screen modes very well and we end up with screwed up colors.
- TXT_SDLWindow =
- SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
- TXT_SCREEN_W * font->w, TXT_SCREEN_H * font->h,
- 0);
- if (TXT_SDLWindow == NULL)
- return 0;
- // Instead, we draw everything into an intermediate 8-bit surface
- // the same dimensions as the screen. SDL then takes care of all the
- // 8->32 bit (or whatever depth) color conversions for us.
- screenbuffer = SDL_CreateRGBSurface(0,
- TXT_SCREEN_W * font->w,
- TXT_SCREEN_H * font->h,
- 8, 0, 0, 0, 0);
- SDL_LockSurface(screenbuffer);
- SDL_SetPaletteColors(screenbuffer->format->palette, ega_colors, 0, 16);
- SDL_UnlockSurface(screenbuffer);
- // SDL2-TODO 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.
- // SDL2-TODO SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
- return 1;
- }
- void TXT_Shutdown(void)
- {
- free(screendata);
- screendata = NULL;
- SDL_FreeSurface(screenbuffer);
- screenbuffer = NULL;
- 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;
- unsigned int bit, bytes;
- int bg, fg;
- unsigned 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;
- }
- }
- // How many bytes per line?
- bytes = (font->w + 7) / 8;
- p = &font->data[character * font->h * bytes];
- s = ((unsigned char *) screenbuffer->pixels)
- + (y * font->h * screenbuffer->pitch)
- + (x * font->w);
- for (y1=0; y1<font->h; ++y1)
- {
- s1 = s;
- bit = 0;
- for (x1=0; x1<font->w; ++x1)
- {
- if (*p & (1 << (7-bit)))
- {
- *s1++ = fg;
- }
- else
- {
- *s1++ = bg;
- }
- ++bit;
- if (bit == 8)
- {
- ++p;
- bit = 0;
- }
- }
- if (bit != 0)
- {
- ++p;
- }
- s += screenbuffer->pitch;
- }
- }
- static int LimitToRange(int val, int min, int max)
- {
- if (val < min)
- {
- return min;
- }
- else if (val > max)
- {
- return max;
- }
- else
- {
- return val;
- }
- }
- void TXT_UpdateScreenArea(int x, int y, int w, int h)
- {
- SDL_Rect rect;
- int x1, y1;
- int x_end;
- int y_end;
- SDL_LockSurface(screenbuffer);
- x_end = LimitToRange(x + w, 0, TXT_SCREEN_W);
- y_end = LimitToRange(y + h, 0, TXT_SCREEN_H);
- x = LimitToRange(x, 0, TXT_SCREEN_W);
- y = LimitToRange(y, 0, TXT_SCREEN_H);
- for (y1=y; y1<y_end; ++y1)
- {
- for (x1=x; x1<x_end; ++x1)
- {
- UpdateCharacter(x1, y1);
- }
- }
- rect.x = x * font->w;
- rect.y = y * font->h;
- rect.w = (x_end - x) * font->w;
- rect.h = (y_end - y) * font->h;
- SDL_UnlockSurface(screenbuffer);
- SDL_BlitSurface(screenbuffer, &rect,
- SDL_GetWindowSurface(TXT_SDLWindow), &rect);
- SDL_UpdateWindowSurfaceRects(TXT_SDLWindow, &rect, 1);
- }
- 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 /= font->w;
- *y /= font->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_PRINTSCREEN: return KEY_PRTSCR;
- case SDLK_BACKSPACE: return KEY_BACKSPACE;
- case SDLK_DELETE: return KEY_DEL;
- case SDLK_PAUSE: return KEY_PAUSE;
- case SDLK_LSHIFT:
- case SDLK_RSHIFT:
- return KEY_RSHIFT;
- case SDLK_LCTRL:
- case SDLK_RCTRL:
- return KEY_RCTRL;
- case SDLK_LALT:
- case SDLK_RALT:
- return KEY_RALT;
- case SDLK_CAPSLOCK: return KEY_CAPSLOCK;
- case SDLK_SCROLLLOCK: return KEY_SCRLCK;
- 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;
- #ifdef SDL_HAVE_APP_KEYS
- case SDLK_APP1: return KEY_F1;
- case SDLK_APP2: return KEY_F2;
- case SDLK_APP3: return KEY_F3;
- case SDLK_APP4: return KEY_F4;
- case SDLK_APP5: return KEY_F5;
- case SDLK_APP6: return KEY_F6;
- #endif
- 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)
- {
- // Unicode characters beyond the ASCII range need to be
- // mapped up into textscreen's Unicode range.
- #if 0
- // SDL2-TODO
- if (sym->unicode < 128)
- {
- return sym->unicode;
- }
- else
- {
- return sym->unicode - 128 + TXT_UNICODE_BASE;
- }
- #endif
- return 0;
- }
- else
- {
- // Keypad mapping is only done when we want a raw value:
- // most of the time, the keypad should behave as it normally
- // does.
- switch (sym->sym)
- {
- case SDLK_KP_0: return KEYP_0;
- case SDLK_KP_1: return KEYP_1;
- case SDLK_KP_2: return KEYP_2;
- case SDLK_KP_3: return KEYP_3;
- case SDLK_KP_4: return KEYP_4;
- case SDLK_KP_5: return KEYP_5;
- case SDLK_KP_6: return KEYP_6;
- case SDLK_KP_7: return KEYP_7;
- case SDLK_KP_8: return KEYP_8;
- case SDLK_KP_9: return KEYP_9;
- case SDLK_KP_PERIOD: return KEYP_PERIOD;
- 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;
- case SDLK_KP_EQUALS: return KEYP_EQUALS;
- case SDLK_KP_ENTER: return KEYP_ENTER;
- default:
- return tolower(sym->sym);
- }
- }
- }
- // Convert an SDL button index to textscreen button index.
- //
- // Note special cases because 2 == mid in SDL, 3 == mid in textscreen/setup
- static int SDLButtonToTXTButton(int button)
- {
- switch (button)
- {
- case SDL_BUTTON_LEFT:
- return TXT_MOUSE_LEFT;
- case SDL_BUTTON_RIGHT:
- return TXT_MOUSE_RIGHT;
- case SDL_BUTTON_MIDDLE:
- return TXT_MOUSE_MIDDLE;
- default:
- return TXT_MOUSE_BASE + button - 1;
- }
- }
- static int MouseHasMoved(void)
- {
- static int last_x = 0, last_y = 0;
- int x, y;
- TXT_GetMousePosition(&x, &y);
- if (x != last_x || y != last_y)
- {
- last_x = x; last_y = y;
- return 1;
- }
- else
- {
- return 0;
- }
- }
- // Examine a key press/release and update the modifier key state
- // if necessary.
- static void UpdateModifierState(SDL_Keysym *sym, int pressed)
- {
- txt_modifier_t mod;
- switch (sym->sym)
- {
- case SDLK_LSHIFT:
- case SDLK_RSHIFT:
- mod = TXT_MOD_SHIFT;
- break;
- case SDLK_LCTRL:
- case SDLK_RCTRL:
- mod = TXT_MOD_CTRL;
- break;
- case SDLK_LALT:
- case SDLK_RALT:
- mod = TXT_MOD_ALT;
- break;
- default:
- return;
- }
- if (pressed)
- {
- ++modifier_state[mod];
- }
- else
- {
- --modifier_state[mod];
- }
- }
- 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 < TXT_MAX_MOUSE_BUTTONS)
- {
- return SDLButtonToTXTButton(ev.button.button);
- }
- break;
- case SDL_KEYDOWN:
- UpdateModifierState(&ev.key.keysym, 1);
- return TranslateKey(&ev.key.keysym);
- case SDL_KEYUP:
- UpdateModifierState(&ev.key.keysym, 0);
- break;
- case SDL_QUIT:
- // Quit = escape
- return 27;
- case SDL_MOUSEMOTION:
- if (MouseHasMoved())
- {
- return 0;
- }
- default:
- break;
- }
- }
- return -1;
- }
- int TXT_GetModifierState(txt_modifier_t mod)
- {
- if (mod < TXT_NUM_MODIFIERS)
- {
- return modifier_state[mod] > 0;
- }
- return 0;
- }
- static const 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 KEY_PRTSCR: return "PRTSC";
- /*
- 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, size_t buf_len)
- {
- const char *keyname;
- keyname = SpecialKeyName(key);
- if (keyname != NULL)
- {
- TXT_StringCopy(buf, keyname, buf_len);
- }
- else if (isprint(key))
- {
- TXT_snprintf(buf, buf_len, "%c", toupper(key));
- }
- else
- {
- TXT_snprintf(buf, buf_len, "??%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_SetWindowTitle(TXT_SDLWindow, title);
- }
- void TXT_SDL_SetEventCallback(TxtSDLEventCallbackFunc callback, void *user_data)
- {
- event_callback = callback;
- event_callback_data = user_data;
- }
- // Safe string functions.
- void TXT_StringCopy(char *dest, const char *src, size_t dest_len)
- {
- if (dest_len < 1)
- {
- return;
- }
- dest[dest_len - 1] = '\0';
- strncpy(dest, src, dest_len - 1);
- }
- void TXT_StringConcat(char *dest, const char *src, size_t dest_len)
- {
- size_t offset;
- offset = strlen(dest);
- if (offset > dest_len)
- {
- offset = dest_len;
- }
- TXT_StringCopy(dest + offset, src, dest_len - offset);
- }
- // On Windows, vsnprintf() is _vsnprintf().
- #ifdef _WIN32
- #if _MSC_VER < 1400 /* not needed for Visual Studio 2008 */
- #define vsnprintf _vsnprintf
- #endif
- #endif
- // Safe, portable vsnprintf().
- int TXT_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args)
- {
- int result;
- if (buf_len < 1)
- {
- return 0;
- }
- // Windows (and other OSes?) has a vsnprintf() that doesn't always
- // append a trailing \0. So we must do it, and write into a buffer
- // that is one byte shorter; otherwise this function is unsafe.
- result = vsnprintf(buf, buf_len, s, args);
- // If truncated, change the final char in the buffer to a \0.
- // A negative result indicates a truncated buffer on Windows.
- if (result < 0 || (size_t)result >= buf_len)
- {
- buf[buf_len - 1] = '\0';
- result = buf_len - 1;
- }
- return result;
- }
- // Safe, portable snprintf().
- int TXT_snprintf(char *buf, size_t buf_len, const char *s, ...)
- {
- va_list args;
- int result;
- va_start(args, s);
- result = TXT_vsnprintf(buf, buf_len, s, args);
- va_end(args);
- return result;
- }
|