source: libcaca/trunk/caca/driver/win32.c @ 3480

Last change on this file since 3480 was 3412, checked in by Sam Hocevar, 12 years ago

Apparently \033 is more common than \x1b for ESC. Use the former in our
code for clarity.

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