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.
 
 
 
 
 
 

441 lines
15 KiB

  1. /*
  2. * libcaca Colour ASCII-Art library
  3. * Copyright (c) 2002-2010 Sam Hocevar <sam@hocevar.net>
  4. * All Rights Reserved
  5. *
  6. * This library is free software. It comes without any warranty, to
  7. * the extent permitted by applicable law. You can redistribute it
  8. * and/or modify it under the terms of the Do What The Fuck You Want
  9. * To Public License, Version 2, as published by Sam Hocevar. See
  10. * http://sam.zoy.org/wtfpl/COPYING for more details.
  11. */
  12. /*
  13. * This file contains the libcaca Win32 input and output driver
  14. */
  15. #include "config.h"
  16. #if defined(USE_WIN32)
  17. #include <windows.h>
  18. #include <stdlib.h>
  19. #include <stdio.h>
  20. #include "caca.h"
  21. #include "caca_internals.h"
  22. /*
  23. * Global variables
  24. */
  25. static int const win32_fg_palette[] =
  26. {
  27. 0,
  28. FOREGROUND_BLUE,
  29. FOREGROUND_GREEN,
  30. FOREGROUND_GREEN | FOREGROUND_BLUE,
  31. FOREGROUND_RED,
  32. FOREGROUND_RED | FOREGROUND_BLUE,
  33. FOREGROUND_RED | FOREGROUND_GREEN,
  34. FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
  35. FOREGROUND_INTENSITY,
  36. FOREGROUND_INTENSITY | FOREGROUND_BLUE,
  37. FOREGROUND_INTENSITY | FOREGROUND_GREEN,
  38. FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE,
  39. FOREGROUND_INTENSITY | FOREGROUND_RED,
  40. FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE,
  41. FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN,
  42. FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
  43. };
  44. static int const win32_bg_palette[] =
  45. {
  46. 0,
  47. BACKGROUND_BLUE,
  48. BACKGROUND_GREEN,
  49. BACKGROUND_GREEN | BACKGROUND_BLUE,
  50. BACKGROUND_RED,
  51. BACKGROUND_RED | BACKGROUND_BLUE,
  52. BACKGROUND_RED | BACKGROUND_GREEN,
  53. BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE,
  54. BACKGROUND_INTENSITY,
  55. BACKGROUND_INTENSITY | BACKGROUND_BLUE,
  56. BACKGROUND_INTENSITY | BACKGROUND_GREEN,
  57. BACKGROUND_INTENSITY | BACKGROUND_GREEN | BACKGROUND_BLUE,
  58. BACKGROUND_INTENSITY | BACKGROUND_RED,
  59. BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_BLUE,
  60. BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN,
  61. BACKGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
  62. };
  63. struct driver_private
  64. {
  65. HANDLE hin, hout, screen;
  66. CHAR_INFO *buffer;
  67. CONSOLE_CURSOR_INFO cci;
  68. };
  69. static int win32_init_graphics(caca_display_t *dp)
  70. {
  71. int width = caca_get_canvas_width(dp->cv);
  72. int height = caca_get_canvas_height(dp->cv);
  73. CONSOLE_SCREEN_BUFFER_INFO csbi;
  74. SMALL_RECT rect;
  75. COORD size;
  76. dp->drv.p = malloc(sizeof(struct driver_private));
  77. /* This call is allowed to fail in case we already have a console */
  78. AllocConsole();
  79. dp->drv.p->hin = GetStdHandle(STD_INPUT_HANDLE);
  80. dp->drv.p->hout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
  81. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
  82. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  83. if(dp->drv.p->hout == INVALID_HANDLE_VALUE)
  84. return -1;
  85. GetConsoleCursorInfo(dp->drv.p->hout, &dp->drv.p->cci);
  86. dp->drv.p->cci.bVisible = FALSE;
  87. SetConsoleCursorInfo(dp->drv.p->hout, &dp->drv.p->cci);
  88. SetConsoleMode(dp->drv.p->hout, ENABLE_MOUSE_INPUT);
  89. dp->drv.p->screen =
  90. CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
  91. 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
  92. if(!dp->drv.p->screen || dp->drv.p->screen == INVALID_HANDLE_VALUE)
  93. return -1;
  94. /* Set the new console size */
  95. size.X = width ? width : 80;
  96. size.Y = height ? height : 25;
  97. SetConsoleScreenBufferSize(dp->drv.p->screen, size);
  98. rect.Left = rect.Top = 0;
  99. rect.Right = size.X - 1;
  100. rect.Bottom = size.Y - 1;
  101. SetConsoleWindowInfo(dp->drv.p->screen, TRUE, &rect);
  102. /* Report our new size to libcaca */
  103. if(!GetConsoleScreenBufferInfo(dp->drv.p->screen, &csbi))
  104. return -1;
  105. dp->resize.allow = 1;
  106. caca_set_canvas_size(dp->cv,
  107. csbi.srWindow.Right - csbi.srWindow.Left + 1,
  108. csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
  109. width = caca_get_canvas_width(dp->cv);
  110. height = caca_get_canvas_height(dp->cv);
  111. dp->resize.allow = 0;
  112. SetConsoleMode(dp->drv.p->screen, 0);
  113. GetConsoleCursorInfo(dp->drv.p->screen, &dp->drv.p->cci);
  114. dp->drv.p->cci.dwSize = 0;
  115. dp->drv.p->cci.bVisible = FALSE;
  116. SetConsoleCursorInfo(dp->drv.p->screen, &dp->drv.p->cci);
  117. SetConsoleActiveScreenBuffer(dp->drv.p->screen);
  118. dp->drv.p->buffer = malloc(width * height
  119. * sizeof(CHAR_INFO));
  120. if(dp->drv.p->buffer == NULL)
  121. return -1;
  122. return 0;
  123. }
  124. static int win32_end_graphics(caca_display_t *dp)
  125. {
  126. SetConsoleActiveScreenBuffer(dp->drv.p->hout);
  127. CloseHandle(dp->drv.p->screen);
  128. SetConsoleTextAttribute(dp->drv.p->hout, FOREGROUND_INTENSITY
  129. | FOREGROUND_RED
  130. | FOREGROUND_GREEN
  131. | FOREGROUND_BLUE);
  132. dp->drv.p->cci.bVisible = TRUE;
  133. SetConsoleCursorInfo(dp->drv.p->hout, &dp->drv.p->cci);
  134. CloseHandle(dp->drv.p->hout);
  135. free(dp->drv.p);
  136. return 0;
  137. }
  138. static int win32_set_display_title(caca_display_t *dp, char const *title)
  139. {
  140. SetConsoleTitle(title);
  141. return 0;
  142. }
  143. static int win32_get_display_width(caca_display_t const *dp)
  144. {
  145. /* FIXME */
  146. /* Fallback to a 6x10 font */
  147. return caca_get_canvas_width(dp->cv) * 6;
  148. }
  149. static int win32_get_display_height(caca_display_t const *dp)
  150. {
  151. /* FIXME */
  152. /* Fallback to a 6x10 font */
  153. return caca_get_canvas_height(dp->cv) * 10;
  154. }
  155. static void win32_display(caca_display_t *dp)
  156. {
  157. COORD size, pos;
  158. SMALL_RECT rect;
  159. CHAR_INFO *buffer = dp->drv.p->buffer;
  160. uint32_t const *cvchars = caca_get_canvas_chars(dp->cv);
  161. uint32_t const *cvattrs = caca_get_canvas_attrs(dp->cv);
  162. int width = caca_get_canvas_width(dp->cv);
  163. int height = caca_get_canvas_height(dp->cv);
  164. int n;
  165. /* Render everything to our screen buffer */
  166. for(n = height * width; n--; )
  167. {
  168. uint32_t ch = *cvchars++;
  169. uint16_t bgfg = caca_attr_to_ansi(*cvattrs);
  170. uint8_t fg = bgfg & 0xf;
  171. uint8_t bg = bgfg >> 4;
  172. #if 0
  173. if(ch > 0x00000020 && ch < 0x00000080)
  174. dp->drv.p->buffer[i].Char.AsciiChar = (uint8_t)ch;
  175. else
  176. dp->drv.p->buffer[i].Char.AsciiChar = ' ';
  177. #else
  178. if(n && *cvchars == CACA_MAGIC_FULLWIDTH)
  179. ;
  180. else if(ch > 0x00000020 && ch < 0x00010000)
  181. buffer->Char.UnicodeChar = (uint16_t)ch;
  182. else
  183. buffer->Char.UnicodeChar = (uint16_t)' ';
  184. #endif
  185. buffer->Attributes = win32_fg_palette[fg < 0x10 ? fg : CACA_LIGHTGRAY]
  186. | win32_bg_palette[bg < 0x10 ? bg : CACA_BLACK];
  187. cvattrs++;
  188. buffer++;
  189. }
  190. /* Blit the screen buffer */
  191. size.X = width;
  192. size.Y = height;
  193. pos.X = pos.Y = 0;
  194. rect.Left = rect.Top = 0;
  195. rect.Right = width - 1;
  196. rect.Bottom = height - 1;
  197. #if 0
  198. WriteConsoleOutput(dp->drv.p->screen, dp->drv.p->buffer, size, pos, &rect);
  199. #else
  200. /* FIXME: would this benefit from dirty rectangles? */
  201. WriteConsoleOutputW(dp->drv.p->screen, dp->drv.p->buffer, size, pos, &rect);
  202. #endif
  203. }
  204. static void win32_handle_resize(caca_display_t *dp)
  205. {
  206. /* FIXME: I don't know what to do here. */
  207. dp->resize.w = caca_get_canvas_width(dp->cv);
  208. dp->resize.h = caca_get_canvas_height(dp->cv);
  209. }
  210. static int win32_get_event(caca_display_t *dp, caca_privevent_t *ev)
  211. {
  212. INPUT_RECORD rec;
  213. DWORD num;
  214. for( ; ; )
  215. {
  216. GetNumberOfConsoleInputEvents(dp->drv.p->hin, &num);
  217. if(num == 0)
  218. break;
  219. ReadConsoleInput(dp->drv.p->hin, &rec, 1, &num);
  220. if(rec.EventType == KEY_EVENT)
  221. {
  222. if(rec.Event.KeyEvent.bKeyDown)
  223. ev->type = CACA_EVENT_KEY_PRESS;
  224. else
  225. ev->type = CACA_EVENT_KEY_RELEASE;
  226. if(rec.Event.KeyEvent.uChar.AsciiChar)
  227. {
  228. ev->data.key.ch = rec.Event.KeyEvent.uChar.AsciiChar;
  229. ev->data.key.utf32 = (uint32_t)ev->data.key.ch;
  230. ev->data.key.utf8[0] = ev->data.key.ch;
  231. ev->data.key.utf8[1] = '\0';
  232. return 1;
  233. }
  234. else
  235. {
  236. switch (rec.Event.KeyEvent.wVirtualKeyCode)
  237. {
  238. case VK_TAB: ev->data.key.ch = '\t'; break;
  239. case VK_RETURN: ev->data.key.ch = '\r'; break;
  240. case VK_ESCAPE: ev->data.key.ch = '\033'; break;
  241. case VK_SPACE: ev->data.key.ch = ' '; break;
  242. case VK_DELETE: ev->data.key.ch = '\x7f'; break;
  243. case VK_LEFT: ev->data.key.ch = CACA_KEY_LEFT; break;
  244. case VK_RIGHT: ev->data.key.ch = CACA_KEY_RIGHT; break;
  245. case VK_UP: ev->data.key.ch = CACA_KEY_UP; break;
  246. case VK_DOWN: ev->data.key.ch = CACA_KEY_DOWN; break;
  247. case VK_INSERT: ev->data.key.ch = CACA_KEY_INSERT; break;
  248. case VK_HOME: ev->data.key.ch = CACA_KEY_HOME; break;
  249. case VK_END: ev->data.key.ch = CACA_KEY_END; break;
  250. case VK_PRIOR: ev->data.key.ch = CACA_KEY_PAGEUP; break;
  251. case VK_NEXT: ev->data.key.ch = CACA_KEY_PAGEDOWN; break;
  252. case VK_F1: ev->data.key.ch = CACA_KEY_F1; break;
  253. case VK_F2: ev->data.key.ch = CACA_KEY_F2; break;
  254. case VK_F3: ev->data.key.ch = CACA_KEY_F3; break;
  255. case VK_F4: ev->data.key.ch = CACA_KEY_F4; break;
  256. case VK_F5: ev->data.key.ch = CACA_KEY_F5; break;
  257. case VK_F6: ev->data.key.ch = CACA_KEY_F6; break;
  258. case VK_F7: ev->data.key.ch = CACA_KEY_F7; break;
  259. case VK_F8: ev->data.key.ch = CACA_KEY_F8; break;
  260. case VK_F9: ev->data.key.ch = CACA_KEY_F9; break;
  261. case VK_F10: ev->data.key.ch = CACA_KEY_F10; break;
  262. case VK_F11: ev->data.key.ch = CACA_KEY_F11; break;
  263. case VK_F12: ev->data.key.ch = CACA_KEY_F12; break;
  264. case VK_F13: ev->data.key.ch = CACA_KEY_F13; break;
  265. case VK_F14: ev->data.key.ch = CACA_KEY_F14; break;
  266. case VK_F15: ev->data.key.ch = CACA_KEY_F15; break;
  267. case VK_NUMPAD0: ev->data.key.ch = '0'; break;
  268. case VK_NUMPAD1: ev->data.key.ch = '1'; break;
  269. case VK_NUMPAD2: ev->data.key.ch = '2'; break;
  270. case VK_NUMPAD3: ev->data.key.ch = '3'; break;
  271. case VK_NUMPAD4: ev->data.key.ch = '4'; break;
  272. case VK_NUMPAD5: ev->data.key.ch = '5'; break;
  273. case VK_NUMPAD6: ev->data.key.ch = '6'; break;
  274. case VK_NUMPAD7: ev->data.key.ch = '7'; break;
  275. case VK_NUMPAD8: ev->data.key.ch = '8'; break;
  276. case VK_NUMPAD9: ev->data.key.ch = '9'; break;
  277. case VK_MULTIPLY: ev->data.key.ch = '*'; break;
  278. case VK_ADD: ev->data.key.ch = '+'; break;
  279. case VK_SEPARATOR: ev->data.key.ch = ','; break;
  280. case VK_SUBTRACT: ev->data.key.ch = '-'; break;
  281. case VK_DECIMAL: ev->data.key.ch = '.'; break;
  282. case VK_DIVIDE: ev->data.key.ch = '/'; break;
  283. default: ev->type = CACA_EVENT_NONE; return 0;
  284. }
  285. if ((ev->data.key.ch > 0)
  286. &&
  287. (ev->data.key.ch <= 0x7f))
  288. {
  289. ev->data.key.utf32 = (uint32_t)ev->data.key.ch;
  290. ev->data.key.utf8[0] = ev->data.key.ch;
  291. ev->data.key.utf8[1] = '\0';
  292. }
  293. else
  294. {
  295. ev->data.key.utf32 = 0;
  296. ev->data.key.utf8[0] = '\0';
  297. }
  298. return 1;
  299. }
  300. }
  301. if(rec.EventType == MOUSE_EVENT)
  302. {
  303. if(rec.Event.MouseEvent.dwEventFlags == 0)
  304. {
  305. if(rec.Event.MouseEvent.dwButtonState & 0x01)
  306. {
  307. ev->type = CACA_EVENT_MOUSE_PRESS;
  308. ev->data.mouse.button = 1;
  309. return 1;
  310. }
  311. if(rec.Event.MouseEvent.dwButtonState & 0x02)
  312. {
  313. ev->type = CACA_EVENT_MOUSE_PRESS;
  314. ev->data.mouse.button = 2;
  315. return 1;
  316. }
  317. }
  318. else if(rec.Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
  319. {
  320. COORD pos = rec.Event.MouseEvent.dwMousePosition;
  321. if(dp->mouse.x == pos.X && dp->mouse.y == pos.Y)
  322. continue;
  323. dp->mouse.x = pos.X;
  324. dp->mouse.y = pos.Y;
  325. ev->type = CACA_EVENT_MOUSE_MOTION;
  326. ev->data.mouse.x = dp->mouse.x;
  327. ev->data.mouse.y = dp->mouse.y;
  328. return 1;
  329. }
  330. #if 0
  331. else if(rec.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK)
  332. {
  333. cout << rec.Event.MouseEvent.dwMousePosition.X << "," <<
  334. rec.Event.MouseEvent.dwMousePosition.Y << " " << flush;
  335. }
  336. else if(rec.Event.MouseEvent.dwEventFlags == MOUSE_WHEELED)
  337. {
  338. SetConsoleCursorPosition(hOut,
  339. WheelWhere);
  340. if(rec.Event.MouseEvent.dwButtonState & 0xFF000000)
  341. cout << "Down" << flush;
  342. else
  343. cout << "Up " << flush;
  344. }
  345. #endif
  346. }
  347. /* Unknown event */
  348. ev->type = CACA_EVENT_NONE;
  349. return 0;
  350. }
  351. /* No event */
  352. ev->type = CACA_EVENT_NONE;
  353. return 0;
  354. }
  355. /*
  356. * Driver initialisation
  357. */
  358. int win32_install(caca_display_t *dp)
  359. {
  360. dp->drv.id = CACA_DRIVER_WIN32;
  361. dp->drv.driver = "win32";
  362. dp->drv.init_graphics = win32_init_graphics;
  363. dp->drv.end_graphics = win32_end_graphics;
  364. dp->drv.set_display_title = win32_set_display_title;
  365. dp->drv.get_display_width = win32_get_display_width;
  366. dp->drv.get_display_height = win32_get_display_height;
  367. dp->drv.display = win32_display;
  368. dp->drv.handle_resize = win32_handle_resize;
  369. dp->drv.get_event = win32_get_event;
  370. dp->drv.set_mouse = NULL;
  371. dp->drv.set_cursor = NULL;
  372. return 0;
  373. }
  374. #endif /* USE_WIN32 */