You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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