您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 
 

522 行
18 KiB

  1. /*
  2. * libcaca Colour ASCII-Art library
  3. * Copyright © 2002—2021 Sam Hocevar <sam@hocevar.net>
  4. * 2012 Bastian Märkisch <bmaerkisch@web.de>
  5. * All Rights Reserved
  6. *
  7. * This library is free software. It comes without any warranty, to
  8. * the extent permitted by applicable law. You can redistribute it
  9. * and/or modify it under the terms of the Do What the Fuck You Want
  10. * to Public License, Version 2, as published by Sam Hocevar. See
  11. * http://www.wtfpl.net/ for more details.
  12. */
  13. /*
  14. * This file contains the libcaca Win32 input and output driver
  15. */
  16. #include "config.h"
  17. #if defined(USE_WIN32)
  18. #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x500 /* _WIN32_WINNT_WIN2K */
  19. #define _WIN32_WINNT 0x500 /* Require WinXP or later */
  20. #endif
  21. #define WIN32_LEAN_AND_MEAN
  22. #include <windows.h>
  23. #if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
  24. /* This is missing from the MinGW headers. */
  25. # if (_WIN32_WINNT >= 0x0500)
  26. BOOL WINAPI GetCurrentConsoleFont(HANDLE hConsoleOutput, BOOL bMaximumWindow,
  27. PCONSOLE_FONT_INFO lpConsoleCurrentFont);
  28. # endif
  29. #endif
  30. #ifndef MOUSE_HWHEELED
  31. # define MOUSE_HWHEELED 0x0008
  32. #endif
  33. #include <stdlib.h>
  34. #include <stdio.h>
  35. #include "caca.h"
  36. #include "caca_internals.h"
  37. /*
  38. * Global variables
  39. */
  40. static int const win32_fg_palette[] =
  41. {
  42. 0,
  43. FOREGROUND_BLUE,
  44. FOREGROUND_GREEN,
  45. FOREGROUND_GREEN | FOREGROUND_BLUE,
  46. FOREGROUND_RED,
  47. FOREGROUND_RED | FOREGROUND_BLUE,
  48. FOREGROUND_RED | FOREGROUND_GREEN,
  49. FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
  50. FOREGROUND_INTENSITY,
  51. FOREGROUND_INTENSITY | FOREGROUND_BLUE,
  52. FOREGROUND_INTENSITY | FOREGROUND_GREEN,
  53. FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE,
  54. FOREGROUND_INTENSITY | FOREGROUND_RED,
  55. FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE,
  56. FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN,
  57. FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
  58. };
  59. static int const win32_bg_palette[] =
  60. {
  61. 0,
  62. BACKGROUND_BLUE,
  63. BACKGROUND_GREEN,
  64. BACKGROUND_GREEN | BACKGROUND_BLUE,
  65. BACKGROUND_RED,
  66. BACKGROUND_RED | BACKGROUND_BLUE,
  67. BACKGROUND_RED | BACKGROUND_GREEN,
  68. BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE,
  69. BACKGROUND_INTENSITY,
  70. BACKGROUND_INTENSITY | BACKGROUND_BLUE,
  71. BACKGROUND_INTENSITY | BACKGROUND_GREEN,
  72. BACKGROUND_INTENSITY | BACKGROUND_GREEN | BACKGROUND_BLUE,
  73. BACKGROUND_INTENSITY | BACKGROUND_RED,
  74. BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_BLUE,
  75. BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN,
  76. BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
  77. };
  78. struct driver_private
  79. {
  80. HANDLE hin, hout, screen;
  81. CHAR_INFO *buffer;
  82. CONSOLE_CURSOR_INFO cci;
  83. DWORD mouse_state;
  84. DWORD mode;
  85. BOOL new_console;
  86. };
  87. static int win32_init_graphics(caca_display_t *dp)
  88. {
  89. int width = caca_get_canvas_width(dp->cv);
  90. int height = caca_get_canvas_height(dp->cv);
  91. CONSOLE_SCREEN_BUFFER_INFO csbi;
  92. CONSOLE_CURSOR_INFO cci_screen;
  93. SMALL_RECT rect;
  94. COORD size;
  95. #if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
  96. CREATEFILE2_EXTENDED_PARAMETERS createExParams;
  97. #endif
  98. dp->drv.p = malloc(sizeof(struct driver_private));
  99. /* This call is allowed to fail in case we already have a console. */
  100. dp->drv.p->new_console = AllocConsole();
  101. dp->drv.p->hin = GetStdHandle(STD_INPUT_HANDLE);
  102. #if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
  103. ZeroMemory(&createExParams, sizeof(createExParams));
  104. createExParams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
  105. createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
  106. dp->drv.p->hout = CreateFile2(L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
  107. FILE_SHARE_READ | FILE_SHARE_WRITE,
  108. OPEN_EXISTING, &createExParams);
  109. #else
  110. dp->drv.p->hout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
  111. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
  112. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  113. #endif
  114. if(dp->drv.p->hout == INVALID_HANDLE_VALUE)
  115. return -1;
  116. /* Preserve state information */
  117. GetConsoleCursorInfo(dp->drv.p->hout, &dp->drv.p->cci);
  118. dp->drv.p->screen =
  119. CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
  120. 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
  121. if(!dp->drv.p->screen || dp->drv.p->screen == INVALID_HANDLE_VALUE)
  122. return -1;
  123. dp->drv.p->mouse_state = 0;
  124. /* Set the new console size, default to current screen size. */
  125. size.X = width ? width : 80;
  126. size.Y = height ? height : 25;
  127. if ((width <= 0) && (height <= 0))
  128. {
  129. CONSOLE_SCREEN_BUFFER_INFO info;
  130. if (GetConsoleScreenBufferInfo(dp->drv.p->hout, &info) != 0)
  131. size = info.dwSize;
  132. }
  133. SetConsoleScreenBufferSize(dp->drv.p->screen, size);
  134. rect.Left = rect.Top = 0;
  135. rect.Right = size.X - 1;
  136. rect.Bottom = size.Y - 1;
  137. SetConsoleWindowInfo(dp->drv.p->screen, TRUE, &rect);
  138. /* Report our new size to libcaca */
  139. if(!GetConsoleScreenBufferInfo(dp->drv.p->screen, &csbi))
  140. return -1;
  141. dp->resize.allow = 1;
  142. caca_set_canvas_size(dp->cv,
  143. csbi.srWindow.Right - csbi.srWindow.Left + 1,
  144. csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
  145. width = caca_get_canvas_width(dp->cv);
  146. height = caca_get_canvas_height(dp->cv);
  147. dp->resize.allow = 0;
  148. SetConsoleMode(dp->drv.p->screen, 0);
  149. /* We want mouse and window resize events. */
  150. GetConsoleMode(dp->drv.p->hin, &dp->drv.p->mode);
  151. SetConsoleMode(dp->drv.p->hin, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
  152. cci_screen.dwSize = 1; /* must be > 0, see MSDN */
  153. cci_screen.bVisible = FALSE;
  154. SetConsoleCursorInfo(dp->drv.p->screen, &cci_screen);
  155. SetConsoleActiveScreenBuffer(dp->drv.p->screen);
  156. dp->drv.p->buffer = _caca_alloc2d(width, height, sizeof(CHAR_INFO));
  157. if(dp->drv.p->buffer == NULL)
  158. return -1;
  159. return 0;
  160. }
  161. static int win32_end_graphics(caca_display_t *dp)
  162. {
  163. SetConsoleActiveScreenBuffer(dp->drv.p->hout);
  164. CloseHandle(dp->drv.p->screen);
  165. /* Reset console parameters */
  166. SetConsoleMode(dp->drv.p->hin, dp->drv.p->mode);
  167. SetConsoleCursorInfo(dp->drv.p->hout, &dp->drv.p->cci);
  168. CloseHandle(dp->drv.p->hout);
  169. /* We only free the console if we successfully created a new one before */
  170. if (dp->drv.p->new_console)
  171. FreeConsole();
  172. free(dp->drv.p);
  173. return 0;
  174. }
  175. static int win32_set_display_title(caca_display_t *dp, char const *title)
  176. {
  177. SetConsoleTitleA(title);
  178. return 0;
  179. }
  180. static int win32_get_display_width(caca_display_t const *dp)
  181. {
  182. /* Fallback to a 6x10 font */
  183. int font_width = 6;
  184. #if (_WIN32_WINNT >= 0x500)
  185. CONSOLE_FONT_INFO info;
  186. if (GetCurrentConsoleFont(dp->drv.p->screen, FALSE, &info) != 0)
  187. font_width = info.dwFontSize.X;
  188. #endif
  189. return font_width * caca_get_canvas_width(dp->cv);
  190. }
  191. static int win32_get_display_height(caca_display_t const *dp)
  192. {
  193. /* Fallback to a 6x10 font */
  194. int font_height = 10;
  195. #if (_WIN32_WINNT >= 0x500)
  196. CONSOLE_FONT_INFO info;
  197. if (GetCurrentConsoleFont(dp->drv.p->screen, FALSE, &info) != 0)
  198. font_height = info.dwFontSize.Y;
  199. #endif
  200. return font_height * caca_get_canvas_height(dp->cv);
  201. }
  202. static void win32_display(caca_display_t *dp)
  203. {
  204. COORD size, pos;
  205. SMALL_RECT rect;
  206. CHAR_INFO *buffer = dp->drv.p->buffer;
  207. uint32_t const *cvchars = caca_get_canvas_chars(dp->cv);
  208. uint32_t const *cvattrs = caca_get_canvas_attrs(dp->cv);
  209. int width = caca_get_canvas_width(dp->cv);
  210. int height = caca_get_canvas_height(dp->cv);
  211. int n;
  212. /* Render everything to our screen buffer */
  213. for(n = height * width; n--; )
  214. {
  215. uint32_t ch = *cvchars++;
  216. uint16_t bgfg = caca_attr_to_ansi(*cvattrs);
  217. uint8_t fg = bgfg & 0xf;
  218. uint8_t bg = bgfg >> 4;
  219. #if 0
  220. if(ch > 0x00000020 && ch < 0x00000080)
  221. dp->drv.p->buffer[i].Char.AsciiChar = (uint8_t)ch;
  222. else
  223. dp->drv.p->buffer[i].Char.AsciiChar = ' ';
  224. #else
  225. if(n && *cvchars == CACA_MAGIC_FULLWIDTH)
  226. ;
  227. else if(ch > 0x00000020 && ch < 0x00010000)
  228. buffer->Char.UnicodeChar = (uint16_t)ch;
  229. else
  230. buffer->Char.UnicodeChar = (uint16_t)' ';
  231. #endif
  232. buffer->Attributes = win32_fg_palette[fg < 0x10 ? fg : CACA_LIGHTGRAY]
  233. | win32_bg_palette[bg < 0x10 ? bg : CACA_BLACK];
  234. cvattrs++;
  235. buffer++;
  236. }
  237. /* Blit the screen buffer */
  238. size.X = width;
  239. size.Y = height;
  240. pos.X = pos.Y = 0;
  241. rect.Left = rect.Top = 0;
  242. rect.Right = width - 1;
  243. rect.Bottom = height - 1;
  244. #if 0
  245. WriteConsoleOutput(dp->drv.p->screen, dp->drv.p->buffer, size, pos, &rect);
  246. #else
  247. /* FIXME: would this benefit from dirty rectangles? */
  248. WriteConsoleOutputW(dp->drv.p->screen, dp->drv.p->buffer, size, pos, &rect);
  249. #endif
  250. }
  251. static void win32_handle_resize(caca_display_t *dp)
  252. {
  253. /* We only need to resize the internal buffer. */
  254. dp->drv.p->buffer = realloc(dp->drv.p->buffer,
  255. dp->resize.w * dp->resize.h * sizeof(CHAR_INFO));
  256. }
  257. static int win32_get_event(caca_display_t *dp, caca_privevent_t *ev)
  258. {
  259. INPUT_RECORD rec;
  260. DWORD num;
  261. for( ; ; )
  262. {
  263. HANDLE hin = GetStdHandle(STD_INPUT_HANDLE);
  264. if(hin == INVALID_HANDLE_VALUE)
  265. break;
  266. GetNumberOfConsoleInputEvents(hin, &num);
  267. if(num == 0)
  268. break;
  269. if(!ReadConsoleInput(hin, &rec, 1, &num) || (num == 0))
  270. break;
  271. if(rec.EventType == KEY_EVENT)
  272. {
  273. if(rec.Event.KeyEvent.bKeyDown)
  274. ev->type = CACA_EVENT_KEY_PRESS;
  275. else
  276. ev->type = CACA_EVENT_KEY_RELEASE;
  277. if(rec.Event.KeyEvent.uChar.AsciiChar)
  278. {
  279. ev->data.key.ch = rec.Event.KeyEvent.uChar.AsciiChar;
  280. ev->data.key.utf32 = (uint32_t)ev->data.key.ch;
  281. ev->data.key.utf8[0] = ev->data.key.ch;
  282. ev->data.key.utf8[1] = '\0';
  283. return 1;
  284. }
  285. else
  286. {
  287. switch (rec.Event.KeyEvent.wVirtualKeyCode)
  288. {
  289. case VK_TAB: ev->data.key.ch = '\t'; break;
  290. case VK_RETURN: ev->data.key.ch = '\r'; break;
  291. case VK_ESCAPE: ev->data.key.ch = '\033'; break;
  292. case VK_SPACE: ev->data.key.ch = ' '; break;
  293. case VK_DELETE: ev->data.key.ch = '\x7f'; break;
  294. case VK_LEFT: ev->data.key.ch = CACA_KEY_LEFT; break;
  295. case VK_RIGHT: ev->data.key.ch = CACA_KEY_RIGHT; break;
  296. case VK_UP: ev->data.key.ch = CACA_KEY_UP; break;
  297. case VK_DOWN: ev->data.key.ch = CACA_KEY_DOWN; break;
  298. case VK_INSERT: ev->data.key.ch = CACA_KEY_INSERT; break;
  299. case VK_HOME: ev->data.key.ch = CACA_KEY_HOME; break;
  300. case VK_END: ev->data.key.ch = CACA_KEY_END; break;
  301. case VK_PRIOR: ev->data.key.ch = CACA_KEY_PAGEUP; break;
  302. case VK_NEXT: ev->data.key.ch = CACA_KEY_PAGEDOWN; break;
  303. case VK_F1: ev->data.key.ch = CACA_KEY_F1; break;
  304. case VK_F2: ev->data.key.ch = CACA_KEY_F2; break;
  305. case VK_F3: ev->data.key.ch = CACA_KEY_F3; break;
  306. case VK_F4: ev->data.key.ch = CACA_KEY_F4; break;
  307. case VK_F5: ev->data.key.ch = CACA_KEY_F5; break;
  308. case VK_F6: ev->data.key.ch = CACA_KEY_F6; break;
  309. case VK_F7: ev->data.key.ch = CACA_KEY_F7; break;
  310. case VK_F8: ev->data.key.ch = CACA_KEY_F8; break;
  311. case VK_F9: ev->data.key.ch = CACA_KEY_F9; break;
  312. case VK_F10: ev->data.key.ch = CACA_KEY_F10; break;
  313. case VK_F11: ev->data.key.ch = CACA_KEY_F11; break;
  314. case VK_F12: ev->data.key.ch = CACA_KEY_F12; break;
  315. case VK_F13: ev->data.key.ch = CACA_KEY_F13; break;
  316. case VK_F14: ev->data.key.ch = CACA_KEY_F14; break;
  317. case VK_F15: ev->data.key.ch = CACA_KEY_F15; break;
  318. case VK_NUMPAD0: ev->data.key.ch = '0'; break;
  319. case VK_NUMPAD1: ev->data.key.ch = '1'; break;
  320. case VK_NUMPAD2: ev->data.key.ch = '2'; break;
  321. case VK_NUMPAD3: ev->data.key.ch = '3'; break;
  322. case VK_NUMPAD4: ev->data.key.ch = '4'; break;
  323. case VK_NUMPAD5: ev->data.key.ch = '5'; break;
  324. case VK_NUMPAD6: ev->data.key.ch = '6'; break;
  325. case VK_NUMPAD7: ev->data.key.ch = '7'; break;
  326. case VK_NUMPAD8: ev->data.key.ch = '8'; break;
  327. case VK_NUMPAD9: ev->data.key.ch = '9'; break;
  328. case VK_MULTIPLY: ev->data.key.ch = '*'; break;
  329. case VK_ADD: ev->data.key.ch = '+'; break;
  330. case VK_SEPARATOR: ev->data.key.ch = ','; break;
  331. case VK_SUBTRACT: ev->data.key.ch = '-'; break;
  332. case VK_DECIMAL: ev->data.key.ch = '.'; break;
  333. case VK_DIVIDE: ev->data.key.ch = '/'; break;
  334. default: ev->type = CACA_EVENT_NONE; return 0;
  335. }
  336. if((ev->data.key.ch > 0)
  337. &&
  338. (ev->data.key.ch <= 0x7f))
  339. {
  340. ev->data.key.utf32 = (uint32_t)ev->data.key.ch;
  341. ev->data.key.utf8[0] = ev->data.key.ch;
  342. ev->data.key.utf8[1] = '\0';
  343. }
  344. else
  345. {
  346. ev->data.key.utf32 = 0;
  347. ev->data.key.utf8[0] = '\0';
  348. }
  349. return 1;
  350. }
  351. }
  352. else if(rec.EventType == MOUSE_EVENT)
  353. {
  354. if((rec.Event.MouseEvent.dwEventFlags == 0) ||
  355. (rec.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK))
  356. {
  357. DWORD mouse_state_changed =
  358. dp->drv.p->mouse_state ^ rec.Event.MouseEvent.dwButtonState;
  359. int button;
  360. DWORD mask = 0x01;
  361. int const mapping[] = {1, 3, 2, 8, 9};
  362. for (button = 1; button <= 5; button++)
  363. {
  364. if(mouse_state_changed == mask)
  365. {
  366. if(rec.Event.MouseEvent.dwButtonState & mask)
  367. ev->type = CACA_EVENT_MOUSE_PRESS;
  368. else
  369. ev->type = CACA_EVENT_MOUSE_RELEASE;
  370. ev->data.mouse.button = mapping[button - 1];
  371. dp->drv.p->mouse_state = rec.Event.MouseEvent.dwButtonState;
  372. return 1;
  373. }
  374. mask <<= 1;
  375. }
  376. }
  377. else if(rec.Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
  378. {
  379. COORD pos = rec.Event.MouseEvent.dwMousePosition;
  380. if(dp->mouse.x == pos.X && dp->mouse.y == pos.Y)
  381. continue;
  382. dp->mouse.x = pos.X;
  383. dp->mouse.y = pos.Y;
  384. ev->type = CACA_EVENT_MOUSE_MOTION;
  385. ev->data.mouse.x = dp->mouse.x;
  386. ev->data.mouse.y = dp->mouse.y;
  387. return 1;
  388. }
  389. else if(rec.Event.MouseEvent.dwEventFlags == MOUSE_WHEELED)
  390. {
  391. ev->type = CACA_EVENT_MOUSE_PRESS;
  392. if((int16_t)HIWORD(rec.Event.MouseEvent.dwButtonState) > 0)
  393. ev->data.mouse.button = 4; /* up */
  394. else
  395. ev->data.mouse.button = 5; /* down */
  396. return 1;
  397. }
  398. else if(rec.Event.MouseEvent.dwEventFlags == MOUSE_HWHEELED)
  399. {
  400. ev->type = CACA_EVENT_MOUSE_PRESS;
  401. if((int16_t)HIWORD(rec.Event.MouseEvent.dwButtonState) > 0)
  402. ev->data.mouse.button = 7; /* right */
  403. else
  404. ev->data.mouse.button = 6; /* left */
  405. return 1;
  406. }
  407. }
  408. else if(rec.EventType == WINDOW_BUFFER_SIZE_EVENT)
  409. {
  410. int width = caca_get_canvas_width(dp->cv);
  411. int height = caca_get_canvas_height(dp->cv);
  412. int w = rec.Event.WindowBufferSizeEvent.dwSize.X;
  413. int h = rec.Event.WindowBufferSizeEvent.dwSize.Y;
  414. if((w == 0) || (h == 0) || ((w == width) && (h == height)))
  415. continue;
  416. /* resize the canvas accordingly */
  417. ev->type = CACA_EVENT_RESIZE;
  418. dp->resize.w = w;
  419. dp->resize.h = h;
  420. dp->resize.resized = 1;
  421. return 1;
  422. }
  423. /* Unknown event */
  424. ev->type = CACA_EVENT_NONE;
  425. return 0;
  426. }
  427. /* No event */
  428. ev->type = CACA_EVENT_NONE;
  429. return 0;
  430. }
  431. /*
  432. * Driver initialisation
  433. */
  434. int win32_install(caca_display_t *dp)
  435. {
  436. dp->drv.id = CACA_DRIVER_WIN32;
  437. dp->drv.driver = "win32";
  438. dp->drv.init_graphics = win32_init_graphics;
  439. dp->drv.end_graphics = win32_end_graphics;
  440. dp->drv.set_display_title = win32_set_display_title;
  441. dp->drv.get_display_width = win32_get_display_width;
  442. dp->drv.get_display_height = win32_get_display_height;
  443. dp->drv.display = win32_display;
  444. dp->drv.handle_resize = win32_handle_resize;
  445. dp->drv.get_event = win32_get_event;
  446. dp->drv.set_mouse = NULL;
  447. dp->drv.set_cursor = NULL;
  448. return 0;
  449. }
  450. #endif /* USE_WIN32 */