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

Last change on this file since 4146 was 4146, checked in by Sam Hocevar, 10 years ago

Fix an alignment issue in caca_get_canvas_chars() and caca_get_canvas_attrs().
Fixes #41.

  • Property svn:keywords set to Id
File size: 14.9 KB
Line 
1/*
2 *  libcaca       Colour ASCII-Art library
3 *  Copyright (c) 2002-2009 Sam Hocevar <sam@hocevar.net>
4 *                All Rights Reserved
5 *
6 *  $Id: win32.c 4146 2009-12-18 21:50:37Z 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 = caca_get_canvas_chars(dp->cv);
199    uint32_t const *cvattrs = 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    /* FIXME: would this benefit from dirty rectangles? */
243    WriteConsoleOutputW(dp->drv.p->screen, dp->drv.p->buffer, size, pos, &rect);
244#endif
245}
246
247static void win32_handle_resize(caca_display_t *dp)
248{
249    /* FIXME: I don't know what to do here. */
250    dp->resize.w = caca_get_canvas_width(dp->cv);
251    dp->resize.h = caca_get_canvas_height(dp->cv);
252}
253
254static int win32_get_event(caca_display_t *dp, caca_privevent_t *ev)
255{
256    INPUT_RECORD rec;
257    DWORD num;
258
259    for( ; ; )
260    {
261        GetNumberOfConsoleInputEvents(dp->drv.p->hin, &num);
262        if(num == 0)
263            break;
264
265        ReadConsoleInput(dp->drv.p->hin, &rec, 1, &num);
266        if(rec.EventType == KEY_EVENT)
267        {
268            if(rec.Event.KeyEvent.bKeyDown)
269                ev->type = CACA_EVENT_KEY_PRESS;
270            else
271                ev->type = CACA_EVENT_KEY_RELEASE;
272
273            if(rec.Event.KeyEvent.uChar.AsciiChar)
274            {
275                ev->data.key.ch = rec.Event.KeyEvent.uChar.AsciiChar;
276                ev->data.key.utf32 = (uint32_t)ev->data.key.ch;
277                ev->data.key.utf8[0] = ev->data.key.ch;
278                ev->data.key.utf8[1] = '\0';
279
280                return 1;
281            }
282            else
283            {
284                switch (rec.Event.KeyEvent.wVirtualKeyCode)
285                {
286                case VK_TAB:        ev->data.key.ch = '\t';              break;
287                case VK_RETURN:     ev->data.key.ch = '\r';              break;
288                case VK_ESCAPE:     ev->data.key.ch = '\033';            break;
289                case VK_SPACE:      ev->data.key.ch = ' ';               break;
290                case VK_DELETE:     ev->data.key.ch = '\x7f';            break;
291
292                case VK_LEFT:       ev->data.key.ch = CACA_KEY_LEFT;     break;
293                case VK_RIGHT:      ev->data.key.ch = CACA_KEY_RIGHT;    break;
294                case VK_UP:         ev->data.key.ch = CACA_KEY_UP;       break;
295                case VK_DOWN:       ev->data.key.ch = CACA_KEY_DOWN;     break;
296
297                case VK_INSERT:     ev->data.key.ch = CACA_KEY_INSERT;   break;
298                case VK_HOME:       ev->data.key.ch = CACA_KEY_HOME;     break;
299                case VK_END:        ev->data.key.ch = CACA_KEY_END;      break;
300                case VK_PRIOR:      ev->data.key.ch = CACA_KEY_PAGEUP;   break;
301                case VK_NEXT:       ev->data.key.ch = CACA_KEY_PAGEDOWN; break;
302
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
319                case VK_NUMPAD0:    ev->data.key.ch = '0';               break;
320                case VK_NUMPAD1:    ev->data.key.ch = '1';               break;
321                case VK_NUMPAD2:    ev->data.key.ch = '2';               break;
322                case VK_NUMPAD3:    ev->data.key.ch = '3';               break;
323                case VK_NUMPAD4:    ev->data.key.ch = '4';               break;
324                case VK_NUMPAD5:    ev->data.key.ch = '5';               break;
325                case VK_NUMPAD6:    ev->data.key.ch = '6';               break;
326                case VK_NUMPAD7:    ev->data.key.ch = '7';               break;
327                case VK_NUMPAD8:    ev->data.key.ch = '8';               break;
328                case VK_NUMPAD9:    ev->data.key.ch = '9';               break;
329                case VK_MULTIPLY:   ev->data.key.ch = '*';               break;
330                case VK_ADD:        ev->data.key.ch = '+';               break;
331                case VK_SEPARATOR:  ev->data.key.ch = ',';               break;
332                case VK_SUBTRACT:   ev->data.key.ch = '-';               break;
333                case VK_DECIMAL:    ev->data.key.ch = '.';               break;
334                case VK_DIVIDE:     ev->data.key.ch = '/';               break;
335
336                default: ev->type = CACA_EVENT_NONE; return 0;
337                }
338
339                if ((ev->data.key.ch > 0)
340                    &&
341                    (ev->data.key.ch <= 0x7f))
342                {
343                    ev->data.key.utf32 = (uint32_t)ev->data.key.ch;
344                    ev->data.key.utf8[0] = ev->data.key.ch;
345                    ev->data.key.utf8[1] = '\0';
346                }
347                else
348                {
349                    ev->data.key.utf32 = 0;
350                    ev->data.key.utf8[0] = '\0';
351                }
352
353                return 1;
354            }
355        }
356
357        if(rec.EventType == MOUSE_EVENT)
358        {
359            if(rec.Event.MouseEvent.dwEventFlags == 0)
360            {
361                if(rec.Event.MouseEvent.dwButtonState & 0x01)
362                {
363                    ev->type = CACA_EVENT_MOUSE_PRESS;
364                    ev->data.mouse.button = 1;
365                    return 1;
366                }
367
368                if(rec.Event.MouseEvent.dwButtonState & 0x02)
369                {
370                    ev->type = CACA_EVENT_MOUSE_PRESS;
371                    ev->data.mouse.button = 2;
372                    return 1;
373                }
374            }
375            else if(rec.Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
376            {
377                COORD pos = rec.Event.MouseEvent.dwMousePosition;
378
379                if(dp->mouse.x == pos.X && dp->mouse.y == pos.Y)
380                    continue;
381
382                dp->mouse.x = pos.X;
383                dp->mouse.y = pos.Y;
384
385                ev->type = CACA_EVENT_MOUSE_MOTION;
386                ev->data.mouse.x = dp->mouse.x;
387                ev->data.mouse.y = dp->mouse.y;
388                return 1;
389            }
390#if 0
391            else if(rec.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK)
392            {
393                cout << rec.Event.MouseEvent.dwMousePosition.X << "," <<
394                        rec.Event.MouseEvent.dwMousePosition.Y << "  " << flush;
395            }
396            else if(rec.Event.MouseEvent.dwEventFlags == MOUSE_WHEELED)
397            {
398                SetConsoleCursorPosition(hOut,
399                                         WheelWhere);
400                if(rec.Event.MouseEvent.dwButtonState & 0xFF000000)
401                    cout << "Down" << flush;
402                else
403                    cout << "Up  " << flush;
404            }
405#endif
406        }
407
408        /* Unknown event */
409        ev->type = CACA_EVENT_NONE;
410        return 0;
411    }
412
413    /* No event */
414    ev->type = CACA_EVENT_NONE;
415    return 0;
416}
417
418/*
419 * Driver initialisation
420 */
421
422int win32_install(caca_display_t *dp)
423{
424    dp->drv.id = CACA_DRIVER_WIN32;
425    dp->drv.driver = "win32";
426
427    dp->drv.init_graphics = win32_init_graphics;
428    dp->drv.end_graphics = win32_end_graphics;
429    dp->drv.set_display_title = win32_set_display_title;
430    dp->drv.get_display_width = win32_get_display_width;
431    dp->drv.get_display_height = win32_get_display_height;
432    dp->drv.display = win32_display;
433    dp->drv.handle_resize = win32_handle_resize;
434    dp->drv.get_event = win32_get_event;
435    dp->drv.set_mouse = NULL;
436    dp->drv.set_cursor = NULL;
437
438    return 0;
439}
440
441#endif /* USE_WIN32 */
442
Note: See TracBrowser for help on using the repository browser.