source: libcaca/trunk/caca/driver_win32.c @ 2138

Last change on this file since 2138 was 2138, checked in by Sam Hocevar, 12 years ago
  • Implement caca_create_display_with_driver() to specify an output driver without using environment variables..
  • Implement caca_get_display_driver() to return the current display's output driver.
  • Add a simple example program to test caca_create_display_with_driver().
  • Property svn:keywords set to Id
File size: 10.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: driver_win32.c 2138 2007-12-16 01:50:41Z 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#include "common.h"
21
22#if defined(USE_WIN32)
23
24#include <windows.h>
25
26#include <stdlib.h>
27#include <stdio.h>
28
29#include "cucul.h"
30#include "caca.h"
31#include "caca_internals.h"
32
33/*
34 * Global variables
35 */
36
37static int const win32_fg_palette[] =
38{
39    0,
40    FOREGROUND_BLUE,
41    FOREGROUND_GREEN,
42    FOREGROUND_GREEN | FOREGROUND_BLUE,
43    FOREGROUND_RED,
44    FOREGROUND_RED | FOREGROUND_BLUE,
45    FOREGROUND_RED | FOREGROUND_GREEN,
46    FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
47    FOREGROUND_INTENSITY,
48    FOREGROUND_INTENSITY | FOREGROUND_BLUE,
49    FOREGROUND_INTENSITY | FOREGROUND_GREEN,
50    FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE,
51    FOREGROUND_INTENSITY | FOREGROUND_RED,
52    FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE,
53    FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN,
54    FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
55};
56
57static 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
77struct driver_private
78{
79    HANDLE hin, hout, screen;
80    CHAR_INFO *buffer;
81    CONSOLE_CURSOR_INFO cci;
82};
83
84static int win32_init_graphics(caca_display_t *dp)
85{
86    unsigned int width = cucul_get_canvas_width(dp->cv);
87    unsigned int height = cucul_get_canvas_height(dp->cv);
88    CONSOLE_SCREEN_BUFFER_INFO csbi;
89    SMALL_RECT rect;
90    COORD size;
91
92    dp->drv.p = malloc(sizeof(struct driver_private));
93
94    /* This call is allowed to fail in case we already have a console */
95    AllocConsole();
96
97    dp->drv.p->hin = GetStdHandle(STD_INPUT_HANDLE);
98    dp->drv.p->hout = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE,
99                                 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
100                                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
101    if(dp->drv.p->hout == INVALID_HANDLE_VALUE)
102        return -1;
103
104    GetConsoleCursorInfo(dp->drv.p->hout, &dp->drv.p->cci);
105    dp->drv.p->cci.bVisible = FALSE;
106    SetConsoleCursorInfo(dp->drv.p->hout, &dp->drv.p->cci);
107
108    SetConsoleMode(dp->drv.p->hout, ENABLE_MOUSE_INPUT);
109
110    dp->drv.p->screen =
111        CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
112                                  0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
113    if(!dp->drv.p->screen || dp->drv.p->screen == INVALID_HANDLE_VALUE)
114        return -1;
115
116    /* Set the new console size */
117    size.X = width ? width : 80;
118    size.Y = height ? height : 25;
119    SetConsoleScreenBufferSize(dp->drv.p->screen, size);
120
121    rect.Left = rect.Top = 0;
122    rect.Right = size.X - 1;
123    rect.Bottom = size.Y - 1;
124    SetConsoleWindowInfo(dp->drv.p->screen, TRUE, &rect);
125
126    /* Report our new size to libcucul */
127    if(!GetConsoleScreenBufferInfo(dp->drv.p->screen, &csbi))
128        return -1;
129
130    dp->resize.allow = 1;
131    cucul_set_canvas_size(dp->cv,
132                          csbi.srWindow.Right - csbi.srWindow.Left + 1,
133                          csbi.srWindow.Bottom - csbi.srWindow.Top + 1);
134    width = cucul_get_canvas_width(dp->cv);
135    height = cucul_get_canvas_height(dp->cv);
136    dp->resize.allow = 0;
137
138    SetConsoleMode(dp->drv.p->screen, 0);
139
140    GetConsoleCursorInfo(dp->drv.p->screen, &dp->drv.p->cci);
141    dp->drv.p->cci.dwSize = 0;
142    dp->drv.p->cci.bVisible = FALSE;
143    SetConsoleCursorInfo(dp->drv.p->screen, &dp->drv.p->cci);
144
145    SetConsoleActiveScreenBuffer(dp->drv.p->screen);
146
147    dp->drv.p->buffer = malloc(width * height
148                               * sizeof(CHAR_INFO));
149    if(dp->drv.p->buffer == NULL)
150        return -1;
151
152    return 0;
153}
154
155static int win32_end_graphics(caca_display_t *dp)
156{
157    SetConsoleActiveScreenBuffer(dp->drv.p->hout);
158    CloseHandle(dp->drv.p->screen);
159
160    SetConsoleTextAttribute(dp->drv.p->hout, FOREGROUND_INTENSITY
161                                             | FOREGROUND_RED
162                                             | FOREGROUND_GREEN
163                                             | FOREGROUND_BLUE);
164    dp->drv.p->cci.bVisible = TRUE;
165    SetConsoleCursorInfo(dp->drv.p->hout, &dp->drv.p->cci);
166    CloseHandle(dp->drv.p->hout);
167
168    free(dp->drv.p);
169
170    return 0;
171}
172
173static int win32_set_display_title(caca_display_t *dp, char const *title)
174{
175    SetConsoleTitle(title);
176    return 0;
177}
178
179static unsigned int win32_get_display_width(caca_display_t const *dp)
180{
181    /* FIXME */
182
183    /* Fallback to a 6x10 font */
184    return cucul_get_canvas_width(dp->cv) * 6;
185}
186
187static unsigned int win32_get_display_height(caca_display_t const *dp)
188{
189    /* FIXME */
190
191    /* Fallback to a 6x10 font */
192    return cucul_get_canvas_height(dp->cv) * 10;
193}
194
195static void win32_display(caca_display_t *dp)
196{
197    COORD size, pos;
198    SMALL_RECT rect;
199    CHAR_INFO *buffer = dp->drv.p->buffer;
200    uint32_t const *cvchars = (uint32_t const *)cucul_get_canvas_chars(dp->cv);
201    uint32_t const *cvattrs = (uint32_t const *)cucul_get_canvas_attrs(dp->cv);
202    unsigned int width = cucul_get_canvas_width(dp->cv);
203    unsigned int height = cucul_get_canvas_height(dp->cv);
204    unsigned int n;
205
206    /* Render everything to our screen buffer */
207    for(n = height * width; n--; )
208    {
209        uint32_t ch = *cvchars++;
210        uint8_t fg = cucul_attr_to_ansi_fg(*cvattrs);
211        uint8_t bg = cucul_attr_to_ansi_bg(*cvattrs);
212
213#if 0
214        if(ch > 0x00000020 && ch < 0x00000080)
215            dp->drv.p->buffer[i].Char.AsciiChar = (uint8_t)ch;
216        else
217            dp->drv.p->buffer[i].Char.AsciiChar = ' ';
218#else
219        if(n && *cvchars == CUCUL_MAGIC_FULLWIDTH)
220            ;
221        else if(ch > 0x00000020 && ch < 0x00010000)
222            buffer->Char.UnicodeChar = (uint16_t)ch;
223        else
224            buffer->Char.UnicodeChar = (uint16_t)' ';
225#endif
226
227        buffer->Attributes = win32_fg_palette[fg < 0x10 ? fg : CUCUL_LIGHTGRAY]
228                              | win32_bg_palette[bg < 0x10 ? bg : CUCUL_BLACK];
229        cvattrs++;
230        buffer++;
231    }
232
233    /* Blit the screen buffer */
234    size.X = width;
235    size.Y = height;
236    pos.X = pos.Y = 0;
237    rect.Left = rect.Top = 0;
238    rect.Right = width - 1;
239    rect.Bottom = height - 1;
240#if 0
241    WriteConsoleOutput(dp->drv.p->screen, dp->drv.p->buffer, size, pos, &rect);
242#else
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 = cucul_get_canvas_width(dp->cv);
251    dp->resize.h = cucul_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        }
283
284        if(rec.EventType == MOUSE_EVENT)
285        {
286            if(rec.Event.MouseEvent.dwEventFlags == 0)
287            {
288                if(rec.Event.MouseEvent.dwButtonState & 0x01)
289                {
290                    ev->type = CACA_EVENT_MOUSE_PRESS;
291                    ev->data.mouse.button = 1;
292                    return 1;
293                }
294
295                if(rec.Event.MouseEvent.dwButtonState & 0x02)
296                {
297                    ev->type = CACA_EVENT_MOUSE_PRESS;
298                    ev->data.mouse.button = 2;
299                    return 1;
300                }
301            }
302            else if(rec.Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
303            {
304                COORD pos = rec.Event.MouseEvent.dwMousePosition;
305
306                if(dp->mouse.x == (unsigned int)pos.X &&
307                   dp->mouse.y == (unsigned int)pos.Y)
308                    continue;
309
310                dp->mouse.x = pos.X;
311                dp->mouse.y = pos.Y;
312
313                ev->type = CACA_EVENT_MOUSE_MOTION;
314                ev->data.mouse.x = dp->mouse.x;
315                ev->data.mouse.y = dp->mouse.y;
316                return 1;
317            }
318#if 0
319            else if(rec.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK)
320            {
321                cout << rec.Event.MouseEvent.dwMousePosition.X << "," <<
322                        rec.Event.MouseEvent.dwMousePosition.Y << "  " << flush;
323            }
324            else if(rec.Event.MouseEvent.dwEventFlags == MOUSE_WHEELED)
325            {
326                SetConsoleCursorPosition(hOut,
327                                         WheelWhere);
328                if(rec.Event.MouseEvent.dwButtonState & 0xFF000000)
329                    cout << "Down" << flush;
330                else
331                    cout << "Up  " << flush;
332            }
333#endif
334        }
335
336        /* Unknown event */
337        ev->type = CACA_EVENT_NONE;
338        return 0;
339    }
340
341    /* No event */
342    ev->type = CACA_EVENT_NONE;
343    return 0;
344}
345
346/*
347 * Driver initialisation
348 */
349
350int win32_install(caca_display_t *dp)
351{
352    dp->drv.id = CACA_DRIVER_WIN32;
353    dp->drv.driver = "win32";
354
355    dp->drv.init_graphics = win32_init_graphics;
356    dp->drv.end_graphics = win32_end_graphics;
357    dp->drv.set_display_title = win32_set_display_title;
358    dp->drv.get_display_width = win32_get_display_width;
359    dp->drv.get_display_height = win32_get_display_height;
360    dp->drv.display = win32_display;
361    dp->drv.handle_resize = win32_handle_resize;
362    dp->drv.get_event = win32_get_event;
363    dp->drv.set_mouse = NULL;
364    dp->drv.set_cursor = NULL;
365
366    return 0;
367}
368
369#endif /* USE_WIN32 */
370
Note: See TracBrowser for help on using the repository browser.