txt_sdl.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  1. //
  2. // Copyright(C) 2005-2014 Simon Howard
  3. //
  4. // This program is free software; you can redistribute it and/or
  5. // modify it under the terms of the GNU General Public License
  6. // as published by the Free Software Foundation; either version 2
  7. // of the License, or (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. //
  15. // Text mode emulation in SDL
  16. //
  17. #include "SDL.h"
  18. #include <ctype.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include "doomkeys.h"
  23. #include "txt_main.h"
  24. #include "txt_sdl.h"
  25. #if defined(_MSC_VER) && !defined(__cplusplus)
  26. #define inline __inline
  27. #endif
  28. typedef struct
  29. {
  30. unsigned char *data;
  31. unsigned int w;
  32. unsigned int h;
  33. } txt_font_t;
  34. // Fonts:
  35. #include "txt_font.h"
  36. #include "txt_largefont.h"
  37. #include "txt_smallfont.h"
  38. // Time between character blinks in ms
  39. #define BLINK_PERIOD 250
  40. SDL_Window *TXT_SDLWindow;
  41. static SDL_Surface *screenbuffer;
  42. static unsigned char *screendata;
  43. static int key_mapping = 1;
  44. static TxtSDLEventCallbackFunc event_callback;
  45. static void *event_callback_data;
  46. static int modifier_state[TXT_NUM_MODIFIERS];
  47. // Font we are using:
  48. static txt_font_t *font;
  49. //#define TANGO
  50. #ifndef TANGO
  51. static SDL_Color ega_colors[] =
  52. {
  53. {0x00, 0x00, 0x00, 0xff}, // 0: Black
  54. {0x00, 0x00, 0xa8, 0xff}, // 1: Blue
  55. {0x00, 0xa8, 0x00, 0xff}, // 2: Green
  56. {0x00, 0xa8, 0xa8, 0xff}, // 3: Cyan
  57. {0xa8, 0x00, 0x00, 0xff}, // 4: Red
  58. {0xa8, 0x00, 0xa8, 0xff}, // 5: Magenta
  59. {0xa8, 0x54, 0x00, 0xff}, // 6: Brown
  60. {0xa8, 0xa8, 0xa8, 0xff}, // 7: Grey
  61. {0x54, 0x54, 0x54, 0xff}, // 8: Dark grey
  62. {0x54, 0x54, 0xfe, 0xff}, // 9: Bright blue
  63. {0x54, 0xfe, 0x54, 0xff}, // 10: Bright green
  64. {0x54, 0xfe, 0xfe, 0xff}, // 11: Bright cyan
  65. {0xfe, 0x54, 0x54, 0xff}, // 12: Bright red
  66. {0xfe, 0x54, 0xfe, 0xff}, // 13: Bright magenta
  67. {0xfe, 0xfe, 0x54, 0xff}, // 14: Yellow
  68. {0xfe, 0xfe, 0xfe, 0xff}, // 15: Bright white
  69. };
  70. #else
  71. // Colors that fit the Tango desktop guidelines: see
  72. // http://tango.freedesktop.org/ also
  73. // http://uwstopia.nl/blog/2006/07/tango-terminal
  74. static SDL_Color ega_colors[] =
  75. {
  76. {0x2e, 0x34, 0x36, 0xff}, // 0: Black
  77. {0x34, 0x65, 0xa4, 0xff}, // 1: Blue
  78. {0x4e, 0x9a, 0x06, 0xff}, // 2: Green
  79. {0x06, 0x98, 0x9a, 0xff}, // 3: Cyan
  80. {0xcc, 0x00, 0x00, 0xff}, // 4: Red
  81. {0x75, 0x50, 0x7b, 0xff}, // 5: Magenta
  82. {0xc4, 0xa0, 0x00, 0xff}, // 6: Brown
  83. {0xd3, 0xd7, 0xcf, 0xff}, // 7: Grey
  84. {0x55, 0x57, 0x53, 0xff}, // 8: Dark grey
  85. {0x72, 0x9f, 0xcf, 0xff}, // 9: Bright blue
  86. {0x8a, 0xe2, 0x34, 0xff}, // 10: Bright green
  87. {0x34, 0xe2, 0xe2, 0xff}, // 11: Bright cyan
  88. {0xef, 0x29, 0x29, 0xff}, // 12: Bright red
  89. {0x34, 0xe2, 0xe2, 0xff}, // 13: Bright magenta
  90. {0xfc, 0xe9, 0x4f, 0xff}, // 14: Yellow
  91. {0xee, 0xee, 0xec, 0xff}, // 15: Bright white
  92. };
  93. #endif
  94. #ifdef _WIN32
  95. #define WIN32_LEAN_AND_MEAN
  96. #include <windows.h>
  97. // Examine system DPI settings to determine whether to use the large font.
  98. static int Win32_UseLargeFont(void)
  99. {
  100. HDC hdc = GetDC(NULL);
  101. int dpix;
  102. if (!hdc)
  103. {
  104. return 0;
  105. }
  106. dpix = GetDeviceCaps(hdc, LOGPIXELSX);
  107. ReleaseDC(NULL, hdc);
  108. // 144 is the DPI when using "150%" scaling. If the user has this set
  109. // then consider this an appropriate threshold for using the large font.
  110. return dpix >= 144;
  111. }
  112. #endif
  113. static txt_font_t *FontForName(char *name)
  114. {
  115. if (!strcmp(name, "small"))
  116. {
  117. return &small_font;
  118. }
  119. else if (!strcmp(name, "normal"))
  120. {
  121. return &main_font;
  122. }
  123. else if (!strcmp(name, "large"))
  124. {
  125. return &large_font;
  126. }
  127. else
  128. {
  129. return NULL;
  130. }
  131. }
  132. //
  133. // Select the font to use, based on screen resolution
  134. //
  135. // If the highest screen resolution available is less than
  136. // 640x480, use the small font.
  137. //
  138. static void ChooseFont(void)
  139. {
  140. SDL_DisplayMode desktop_info;
  141. char *env;
  142. // Allow normal selection to be overridden from an environment variable:
  143. env = getenv("TEXTSCREEN_FONT");
  144. if (env != NULL)
  145. {
  146. font = FontForName(env);
  147. if (font != NULL)
  148. {
  149. return;
  150. }
  151. }
  152. // Get desktop resolution.
  153. // If in doubt and we can't get a list, always prefer to
  154. // fall back to the normal font:
  155. if (!SDL_GetCurrentDisplayMode(0, &desktop_info))
  156. {
  157. font = &main_font;
  158. return;
  159. }
  160. // On tiny low-res screens (eg. palmtops) use the small font.
  161. // If the screen resolution is at least 1920x1080, this is
  162. // a modern high-resolution display, and we can use the
  163. // large font.
  164. if (desktop_info.w < 640 || desktop_info.h < 480)
  165. {
  166. font = &small_font;
  167. }
  168. #ifdef _WIN32
  169. // On Windows we can use the system DPI settings to make a
  170. // more educated guess about whether to use the large font.
  171. else if (Win32_UseLargeFont())
  172. {
  173. font = &large_font;
  174. }
  175. #endif
  176. // TODO: Detect high DPI on Linux by inquiring about Gtk+ scale
  177. // settings. This looks like it should just be a case of shelling
  178. // out to invoke the 'gsettings' command, eg.
  179. // gsettings get org.gnome.desktop.interface text-scaling-factor
  180. // and using large_font if the result is >= 2.
  181. else
  182. {
  183. font = &main_font;
  184. }
  185. }
  186. //
  187. // Initialize text mode screen
  188. //
  189. // Returns 1 if successful, 0 if an error occurred
  190. //
  191. int TXT_Init(void)
  192. {
  193. if (SDL_Init(SDL_INIT_VIDEO) < 0)
  194. {
  195. return 0;
  196. }
  197. ChooseFont();
  198. // Always create the screen at the native screen depth (bpp=0);
  199. // some systems nowadays don't seem to support true 8-bit palettized
  200. // screen modes very well and we end up with screwed up colors.
  201. TXT_SDLWindow =
  202. SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
  203. TXT_SCREEN_W * font->w, TXT_SCREEN_H * font->h,
  204. 0);
  205. if (TXT_SDLWindow == NULL)
  206. return 0;
  207. // Instead, we draw everything into an intermediate 8-bit surface
  208. // the same dimensions as the screen. SDL then takes care of all the
  209. // 8->32 bit (or whatever depth) color conversions for us.
  210. screenbuffer = SDL_CreateRGBSurface(0,
  211. TXT_SCREEN_W * font->w,
  212. TXT_SCREEN_H * font->h,
  213. 8, 0, 0, 0, 0);
  214. SDL_LockSurface(screenbuffer);
  215. SDL_SetPaletteColors(screenbuffer->format->palette, ega_colors, 0, 16);
  216. SDL_UnlockSurface(screenbuffer);
  217. // SDL2-TODO SDL_EnableUNICODE(1);
  218. screendata = malloc(TXT_SCREEN_W * TXT_SCREEN_H * 2);
  219. memset(screendata, 0, TXT_SCREEN_W * TXT_SCREEN_H * 2);
  220. // Ignore all mouse motion events
  221. // SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
  222. // Repeat key presses so we can hold down arrows to scroll down the
  223. // menu, for example. This is what setup.exe does.
  224. // SDL2-TODO SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
  225. return 1;
  226. }
  227. void TXT_Shutdown(void)
  228. {
  229. free(screendata);
  230. screendata = NULL;
  231. SDL_FreeSurface(screenbuffer);
  232. screenbuffer = NULL;
  233. SDL_QuitSubSystem(SDL_INIT_VIDEO);
  234. }
  235. unsigned char *TXT_GetScreenData(void)
  236. {
  237. return screendata;
  238. }
  239. static inline void UpdateCharacter(int x, int y)
  240. {
  241. unsigned char character;
  242. unsigned char *p;
  243. unsigned char *s, *s1;
  244. unsigned int bit, bytes;
  245. int bg, fg;
  246. unsigned int x1, y1;
  247. p = &screendata[(y * TXT_SCREEN_W + x) * 2];
  248. character = p[0];
  249. fg = p[1] & 0xf;
  250. bg = (p[1] >> 4) & 0xf;
  251. if (bg & 0x8)
  252. {
  253. // blinking
  254. bg &= ~0x8;
  255. if (((SDL_GetTicks() / BLINK_PERIOD) % 2) == 0)
  256. {
  257. fg = bg;
  258. }
  259. }
  260. // How many bytes per line?
  261. bytes = (font->w + 7) / 8;
  262. p = &font->data[character * font->h * bytes];
  263. s = ((unsigned char *) screenbuffer->pixels)
  264. + (y * font->h * screenbuffer->pitch)
  265. + (x * font->w);
  266. for (y1=0; y1<font->h; ++y1)
  267. {
  268. s1 = s;
  269. bit = 0;
  270. for (x1=0; x1<font->w; ++x1)
  271. {
  272. if (*p & (1 << (7-bit)))
  273. {
  274. *s1++ = fg;
  275. }
  276. else
  277. {
  278. *s1++ = bg;
  279. }
  280. ++bit;
  281. if (bit == 8)
  282. {
  283. ++p;
  284. bit = 0;
  285. }
  286. }
  287. if (bit != 0)
  288. {
  289. ++p;
  290. }
  291. s += screenbuffer->pitch;
  292. }
  293. }
  294. static int LimitToRange(int val, int min, int max)
  295. {
  296. if (val < min)
  297. {
  298. return min;
  299. }
  300. else if (val > max)
  301. {
  302. return max;
  303. }
  304. else
  305. {
  306. return val;
  307. }
  308. }
  309. void TXT_UpdateScreenArea(int x, int y, int w, int h)
  310. {
  311. SDL_Rect rect;
  312. int x1, y1;
  313. int x_end;
  314. int y_end;
  315. SDL_LockSurface(screenbuffer);
  316. x_end = LimitToRange(x + w, 0, TXT_SCREEN_W);
  317. y_end = LimitToRange(y + h, 0, TXT_SCREEN_H);
  318. x = LimitToRange(x, 0, TXT_SCREEN_W);
  319. y = LimitToRange(y, 0, TXT_SCREEN_H);
  320. for (y1=y; y1<y_end; ++y1)
  321. {
  322. for (x1=x; x1<x_end; ++x1)
  323. {
  324. UpdateCharacter(x1, y1);
  325. }
  326. }
  327. rect.x = x * font->w;
  328. rect.y = y * font->h;
  329. rect.w = (x_end - x) * font->w;
  330. rect.h = (y_end - y) * font->h;
  331. SDL_UnlockSurface(screenbuffer);
  332. SDL_BlitSurface(screenbuffer, &rect,
  333. SDL_GetWindowSurface(TXT_SDLWindow), &rect);
  334. SDL_UpdateWindowSurfaceRects(TXT_SDLWindow, &rect, 1);
  335. }
  336. void TXT_UpdateScreen(void)
  337. {
  338. TXT_UpdateScreenArea(0, 0, TXT_SCREEN_W, TXT_SCREEN_H);
  339. }
  340. void TXT_GetMousePosition(int *x, int *y)
  341. {
  342. SDL_GetMouseState(x, y);
  343. *x /= font->w;
  344. *y /= font->h;
  345. }
  346. //
  347. // Translates the SDL key
  348. //
  349. static int TranslateKey(SDL_Keysym *sym)
  350. {
  351. switch(sym->sym)
  352. {
  353. case SDLK_LEFT: return KEY_LEFTARROW;
  354. case SDLK_RIGHT: return KEY_RIGHTARROW;
  355. case SDLK_DOWN: return KEY_DOWNARROW;
  356. case SDLK_UP: return KEY_UPARROW;
  357. case SDLK_ESCAPE: return KEY_ESCAPE;
  358. case SDLK_RETURN: return KEY_ENTER;
  359. case SDLK_TAB: return KEY_TAB;
  360. case SDLK_F1: return KEY_F1;
  361. case SDLK_F2: return KEY_F2;
  362. case SDLK_F3: return KEY_F3;
  363. case SDLK_F4: return KEY_F4;
  364. case SDLK_F5: return KEY_F5;
  365. case SDLK_F6: return KEY_F6;
  366. case SDLK_F7: return KEY_F7;
  367. case SDLK_F8: return KEY_F8;
  368. case SDLK_F9: return KEY_F9;
  369. case SDLK_F10: return KEY_F10;
  370. case SDLK_F11: return KEY_F11;
  371. case SDLK_F12: return KEY_F12;
  372. case SDLK_PRINTSCREEN: return KEY_PRTSCR;
  373. case SDLK_BACKSPACE: return KEY_BACKSPACE;
  374. case SDLK_DELETE: return KEY_DEL;
  375. case SDLK_PAUSE: return KEY_PAUSE;
  376. case SDLK_LSHIFT:
  377. case SDLK_RSHIFT:
  378. return KEY_RSHIFT;
  379. case SDLK_LCTRL:
  380. case SDLK_RCTRL:
  381. return KEY_RCTRL;
  382. case SDLK_LALT:
  383. case SDLK_RALT:
  384. return KEY_RALT;
  385. case SDLK_CAPSLOCK: return KEY_CAPSLOCK;
  386. case SDLK_SCROLLLOCK: return KEY_SCRLCK;
  387. case SDLK_HOME: return KEY_HOME;
  388. case SDLK_INSERT: return KEY_INS;
  389. case SDLK_END: return KEY_END;
  390. case SDLK_PAGEUP: return KEY_PGUP;
  391. case SDLK_PAGEDOWN: return KEY_PGDN;
  392. #ifdef SDL_HAVE_APP_KEYS
  393. case SDLK_APP1: return KEY_F1;
  394. case SDLK_APP2: return KEY_F2;
  395. case SDLK_APP3: return KEY_F3;
  396. case SDLK_APP4: return KEY_F4;
  397. case SDLK_APP5: return KEY_F5;
  398. case SDLK_APP6: return KEY_F6;
  399. #endif
  400. default: break;
  401. }
  402. // Returned value is different, depending on whether key mapping is
  403. // enabled. Key mapping is preferable most of the time, for typing
  404. // in text, etc. However, when we want to read raw keyboard codes
  405. // for the setup keyboard configuration dialog, we want the raw
  406. // key code.
  407. if (key_mapping)
  408. {
  409. // Unicode characters beyond the ASCII range need to be
  410. // mapped up into textscreen's Unicode range.
  411. #if 0
  412. // SDL2-TODO
  413. if (sym->unicode < 128)
  414. {
  415. return sym->unicode;
  416. }
  417. else
  418. {
  419. return sym->unicode - 128 + TXT_UNICODE_BASE;
  420. }
  421. #endif
  422. return 0;
  423. }
  424. else
  425. {
  426. // Keypad mapping is only done when we want a raw value:
  427. // most of the time, the keypad should behave as it normally
  428. // does.
  429. switch (sym->sym)
  430. {
  431. case SDLK_KP_0: return KEYP_0;
  432. case SDLK_KP_1: return KEYP_1;
  433. case SDLK_KP_2: return KEYP_2;
  434. case SDLK_KP_3: return KEYP_3;
  435. case SDLK_KP_4: return KEYP_4;
  436. case SDLK_KP_5: return KEYP_5;
  437. case SDLK_KP_6: return KEYP_6;
  438. case SDLK_KP_7: return KEYP_7;
  439. case SDLK_KP_8: return KEYP_8;
  440. case SDLK_KP_9: return KEYP_9;
  441. case SDLK_KP_PERIOD: return KEYP_PERIOD;
  442. case SDLK_KP_MULTIPLY: return KEYP_MULTIPLY;
  443. case SDLK_KP_PLUS: return KEYP_PLUS;
  444. case SDLK_KP_MINUS: return KEYP_MINUS;
  445. case SDLK_KP_DIVIDE: return KEYP_DIVIDE;
  446. case SDLK_KP_EQUALS: return KEYP_EQUALS;
  447. case SDLK_KP_ENTER: return KEYP_ENTER;
  448. default:
  449. return tolower(sym->sym);
  450. }
  451. }
  452. }
  453. // Convert an SDL button index to textscreen button index.
  454. //
  455. // Note special cases because 2 == mid in SDL, 3 == mid in textscreen/setup
  456. static int SDLButtonToTXTButton(int button)
  457. {
  458. switch (button)
  459. {
  460. case SDL_BUTTON_LEFT:
  461. return TXT_MOUSE_LEFT;
  462. case SDL_BUTTON_RIGHT:
  463. return TXT_MOUSE_RIGHT;
  464. case SDL_BUTTON_MIDDLE:
  465. return TXT_MOUSE_MIDDLE;
  466. default:
  467. return TXT_MOUSE_BASE + button - 1;
  468. }
  469. }
  470. static int MouseHasMoved(void)
  471. {
  472. static int last_x = 0, last_y = 0;
  473. int x, y;
  474. TXT_GetMousePosition(&x, &y);
  475. if (x != last_x || y != last_y)
  476. {
  477. last_x = x; last_y = y;
  478. return 1;
  479. }
  480. else
  481. {
  482. return 0;
  483. }
  484. }
  485. // Examine a key press/release and update the modifier key state
  486. // if necessary.
  487. static void UpdateModifierState(SDL_Keysym *sym, int pressed)
  488. {
  489. txt_modifier_t mod;
  490. switch (sym->sym)
  491. {
  492. case SDLK_LSHIFT:
  493. case SDLK_RSHIFT:
  494. mod = TXT_MOD_SHIFT;
  495. break;
  496. case SDLK_LCTRL:
  497. case SDLK_RCTRL:
  498. mod = TXT_MOD_CTRL;
  499. break;
  500. case SDLK_LALT:
  501. case SDLK_RALT:
  502. mod = TXT_MOD_ALT;
  503. break;
  504. default:
  505. return;
  506. }
  507. if (pressed)
  508. {
  509. ++modifier_state[mod];
  510. }
  511. else
  512. {
  513. --modifier_state[mod];
  514. }
  515. }
  516. signed int TXT_GetChar(void)
  517. {
  518. SDL_Event ev;
  519. while (SDL_PollEvent(&ev))
  520. {
  521. // If there is an event callback, allow it to intercept this
  522. // event.
  523. if (event_callback != NULL)
  524. {
  525. if (event_callback(&ev, event_callback_data))
  526. {
  527. continue;
  528. }
  529. }
  530. // Process the event.
  531. switch (ev.type)
  532. {
  533. case SDL_MOUSEBUTTONDOWN:
  534. if (ev.button.button < TXT_MAX_MOUSE_BUTTONS)
  535. {
  536. return SDLButtonToTXTButton(ev.button.button);
  537. }
  538. break;
  539. case SDL_KEYDOWN:
  540. UpdateModifierState(&ev.key.keysym, 1);
  541. return TranslateKey(&ev.key.keysym);
  542. case SDL_KEYUP:
  543. UpdateModifierState(&ev.key.keysym, 0);
  544. break;
  545. case SDL_QUIT:
  546. // Quit = escape
  547. return 27;
  548. case SDL_MOUSEMOTION:
  549. if (MouseHasMoved())
  550. {
  551. return 0;
  552. }
  553. default:
  554. break;
  555. }
  556. }
  557. return -1;
  558. }
  559. int TXT_GetModifierState(txt_modifier_t mod)
  560. {
  561. if (mod < TXT_NUM_MODIFIERS)
  562. {
  563. return modifier_state[mod] > 0;
  564. }
  565. return 0;
  566. }
  567. static const char *SpecialKeyName(int key)
  568. {
  569. switch (key)
  570. {
  571. case ' ': return "SPACE";
  572. case KEY_RIGHTARROW: return "RIGHT";
  573. case KEY_LEFTARROW: return "LEFT";
  574. case KEY_UPARROW: return "UP";
  575. case KEY_DOWNARROW: return "DOWN";
  576. case KEY_ESCAPE: return "ESC";
  577. case KEY_ENTER: return "ENTER";
  578. case KEY_TAB: return "TAB";
  579. case KEY_F1: return "F1";
  580. case KEY_F2: return "F2";
  581. case KEY_F3: return "F3";
  582. case KEY_F4: return "F4";
  583. case KEY_F5: return "F5";
  584. case KEY_F6: return "F6";
  585. case KEY_F7: return "F7";
  586. case KEY_F8: return "F8";
  587. case KEY_F9: return "F9";
  588. case KEY_F10: return "F10";
  589. case KEY_F11: return "F11";
  590. case KEY_F12: return "F12";
  591. case KEY_BACKSPACE: return "BKSP";
  592. case KEY_PAUSE: return "PAUSE";
  593. case KEY_EQUALS: return "EQUALS";
  594. case KEY_MINUS: return "MINUS";
  595. case KEY_RSHIFT: return "SHIFT";
  596. case KEY_RCTRL: return "CTRL";
  597. case KEY_RALT: return "ALT";
  598. case KEY_CAPSLOCK: return "CAPS";
  599. case KEY_SCRLCK: return "SCRLCK";
  600. case KEY_HOME: return "HOME";
  601. case KEY_END: return "END";
  602. case KEY_PGUP: return "PGUP";
  603. case KEY_PGDN: return "PGDN";
  604. case KEY_INS: return "INS";
  605. case KEY_DEL: return "DEL";
  606. case KEY_PRTSCR: return "PRTSC";
  607. /*
  608. case KEYP_0: return "PAD0";
  609. case KEYP_1: return "PAD1";
  610. case KEYP_2: return "PAD2";
  611. case KEYP_3: return "PAD3";
  612. case KEYP_4: return "PAD4";
  613. case KEYP_5: return "PAD5";
  614. case KEYP_6: return "PAD6";
  615. case KEYP_7: return "PAD7";
  616. case KEYP_8: return "PAD8";
  617. case KEYP_9: return "PAD9";
  618. case KEYP_UPARROW: return "PAD_U";
  619. case KEYP_DOWNARROW: return "PAD_D";
  620. case KEYP_LEFTARROW: return "PAD_L";
  621. case KEYP_RIGHTARROW: return "PAD_R";
  622. case KEYP_MULTIPLY: return "PAD*";
  623. case KEYP_PLUS: return "PAD+";
  624. case KEYP_MINUS: return "PAD-";
  625. case KEYP_DIVIDE: return "PAD/";
  626. */
  627. default: return NULL;
  628. }
  629. }
  630. void TXT_GetKeyDescription(int key, char *buf, size_t buf_len)
  631. {
  632. const char *keyname;
  633. keyname = SpecialKeyName(key);
  634. if (keyname != NULL)
  635. {
  636. TXT_StringCopy(buf, keyname, buf_len);
  637. }
  638. else if (isprint(key))
  639. {
  640. TXT_snprintf(buf, buf_len, "%c", toupper(key));
  641. }
  642. else
  643. {
  644. TXT_snprintf(buf, buf_len, "??%i", key);
  645. }
  646. }
  647. // Searches the desktop screen buffer to determine whether there are any
  648. // blinking characters.
  649. int TXT_ScreenHasBlinkingChars(void)
  650. {
  651. int x, y;
  652. unsigned char *p;
  653. // Check all characters in screen buffer
  654. for (y=0; y<TXT_SCREEN_H; ++y)
  655. {
  656. for (x=0; x<TXT_SCREEN_W; ++x)
  657. {
  658. p = &screendata[(y * TXT_SCREEN_W + x) * 2];
  659. if (p[1] & 0x80)
  660. {
  661. // This character is blinking
  662. return 1;
  663. }
  664. }
  665. }
  666. // None found
  667. return 0;
  668. }
  669. // Sleeps until an event is received, the screen needs to be redrawn,
  670. // or until timeout expires (if timeout != 0)
  671. void TXT_Sleep(int timeout)
  672. {
  673. unsigned int start_time;
  674. if (TXT_ScreenHasBlinkingChars())
  675. {
  676. int time_to_next_blink;
  677. time_to_next_blink = BLINK_PERIOD - (SDL_GetTicks() % BLINK_PERIOD);
  678. // There are blinking characters on the screen, so we
  679. // must time out after a while
  680. if (timeout == 0 || timeout > time_to_next_blink)
  681. {
  682. // Add one so it is always positive
  683. timeout = time_to_next_blink + 1;
  684. }
  685. }
  686. if (timeout == 0)
  687. {
  688. // We can just wait forever until an event occurs
  689. SDL_WaitEvent(NULL);
  690. }
  691. else
  692. {
  693. // Sit in a busy loop until the timeout expires or we have to
  694. // redraw the blinking screen
  695. start_time = SDL_GetTicks();
  696. while (SDL_GetTicks() < start_time + timeout)
  697. {
  698. if (SDL_PollEvent(NULL) != 0)
  699. {
  700. // Received an event, so stop waiting
  701. break;
  702. }
  703. // Don't hog the CPU
  704. SDL_Delay(1);
  705. }
  706. }
  707. }
  708. void TXT_EnableKeyMapping(int enable)
  709. {
  710. key_mapping = enable;
  711. }
  712. void TXT_SetWindowTitle(char *title)
  713. {
  714. SDL_SetWindowTitle(TXT_SDLWindow, title);
  715. }
  716. void TXT_SDL_SetEventCallback(TxtSDLEventCallbackFunc callback, void *user_data)
  717. {
  718. event_callback = callback;
  719. event_callback_data = user_data;
  720. }
  721. // Safe string functions.
  722. void TXT_StringCopy(char *dest, const char *src, size_t dest_len)
  723. {
  724. if (dest_len < 1)
  725. {
  726. return;
  727. }
  728. dest[dest_len - 1] = '\0';
  729. strncpy(dest, src, dest_len - 1);
  730. }
  731. void TXT_StringConcat(char *dest, const char *src, size_t dest_len)
  732. {
  733. size_t offset;
  734. offset = strlen(dest);
  735. if (offset > dest_len)
  736. {
  737. offset = dest_len;
  738. }
  739. TXT_StringCopy(dest + offset, src, dest_len - offset);
  740. }
  741. // On Windows, vsnprintf() is _vsnprintf().
  742. #ifdef _WIN32
  743. #if _MSC_VER < 1400 /* not needed for Visual Studio 2008 */
  744. #define vsnprintf _vsnprintf
  745. #endif
  746. #endif
  747. // Safe, portable vsnprintf().
  748. int TXT_vsnprintf(char *buf, size_t buf_len, const char *s, va_list args)
  749. {
  750. int result;
  751. if (buf_len < 1)
  752. {
  753. return 0;
  754. }
  755. // Windows (and other OSes?) has a vsnprintf() that doesn't always
  756. // append a trailing \0. So we must do it, and write into a buffer
  757. // that is one byte shorter; otherwise this function is unsafe.
  758. result = vsnprintf(buf, buf_len, s, args);
  759. // If truncated, change the final char in the buffer to a \0.
  760. // A negative result indicates a truncated buffer on Windows.
  761. if (result < 0 || (size_t)result >= buf_len)
  762. {
  763. buf[buf_len - 1] = '\0';
  764. result = buf_len - 1;
  765. }
  766. return result;
  767. }
  768. // Safe, portable snprintf().
  769. int TXT_snprintf(char *buf, size_t buf_len, const char *s, ...)
  770. {
  771. va_list args;
  772. int result;
  773. va_start(args, s);
  774. result = TXT_vsnprintf(buf, buf_len, s, args);
  775. va_end(args);
  776. return result;
  777. }