Contrib: upnp/win32, remove strerror use, and other small hacks...
[vlc/asuraparaju-public.git] / modules / video_output / caca.c
blob6de1ca19a27898fd10f6947bd3a7eecb2d6bb392
1 /*****************************************************************************
2 * caca.c: Color ASCII Art "vout display" module using libcaca
3 *****************************************************************************
4 * Copyright (C) 2003-2009 the VideoLAN team
5 * $Id$
7 * Authors: Sam Hocevar <sam@zoy.org>
8 * Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
26 * Preamble
27 *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_vout_display.h>
36 #include <vlc_picture_pool.h>
38 #include <caca.h>
40 /*****************************************************************************
41 * Module descriptor
42 *****************************************************************************/
43 static int Open (vlc_object_t *);
44 static void Close(vlc_object_t *);
46 vlc_module_begin()
47 set_shortname("Caca")
48 set_category(CAT_VIDEO)
49 set_subcategory(SUBCAT_VIDEO_VOUT)
50 set_description(N_("Color ASCII art video output"))
51 set_capability("vout display", 12)
52 set_callbacks(Open, Close)
53 vlc_module_end()
55 /*****************************************************************************
56 * Local prototypes
57 *****************************************************************************/
58 static picture_pool_t *Pool (vout_display_t *, unsigned);
59 static void Prepare(vout_display_t *, picture_t *);
60 static void Display(vout_display_t *, picture_t *);
61 static int Control(vout_display_t *, int, va_list);
63 /* */
64 static void Manage(vout_display_t *);
65 static void Refresh(vout_display_t *);
66 static void Place(vout_display_t *, vout_display_place_t *);
68 /* */
69 struct vout_display_sys_t {
70 cucul_canvas_t *cv;
71 caca_display_t *dp;
72 cucul_dither_t *dither;
74 picture_pool_t *pool;
77 /**
78 * This function initializes libcaca vout method.
80 static int Open(vlc_object_t *object)
82 vout_display_t *vd = (vout_display_t *)object;
83 vout_display_sys_t *sys;
85 #if defined(WIN32) && !defined(UNDER_CE)
86 CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
87 SMALL_RECT rect;
88 COORD coord;
89 HANDLE hstdout;
91 if (!AllocConsole()) {
92 msg_Err(vd, "cannot create console");
93 return VLC_EGENERIC;
96 hstdout =
97 CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
98 FILE_SHARE_READ | FILE_SHARE_WRITE,
99 NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
100 if (!hstdout || hstdout == INVALID_HANDLE_VALUE) {
101 msg_Err(vd, "cannot create screen buffer");
102 FreeConsole();
103 return VLC_EGENERIC;
106 if (!SetConsoleActiveScreenBuffer(hstdout)) {
107 msg_Err(vd, "cannot set active screen buffer");
108 FreeConsole();
109 return VLC_EGENERIC;
112 coord = GetLargestConsoleWindowSize(hstdout);
113 msg_Dbg(vd, "SetConsoleWindowInfo: %ix%i", coord.X, coord.Y);
115 /* Force size for now */
116 coord.X = 100;
117 coord.Y = 40;
119 if (!SetConsoleScreenBufferSize(hstdout, coord))
120 msg_Warn(vd, "SetConsoleScreenBufferSize %i %i",
121 coord.X, coord.Y);
123 /* Get the current screen buffer size and window position. */
124 if (GetConsoleScreenBufferInfo(hstdout, &csbiInfo)) {
125 rect.Top = 0; rect.Left = 0;
126 rect.Right = csbiInfo.dwMaximumWindowSize.X - 1;
127 rect.Bottom = csbiInfo.dwMaximumWindowSize.Y - 1;
128 if (!SetConsoleWindowInfo(hstdout, TRUE, &rect))
129 msg_Dbg(vd, "SetConsoleWindowInfo failed: %ix%i",
130 rect.Right, rect.Bottom);
132 #endif
134 /* Allocate structure */
135 vd->sys = sys = calloc(1, sizeof(*sys));
136 if (!sys)
137 goto error;
139 sys->cv = cucul_create_canvas(0, 0);
140 if (!sys->cv) {
141 msg_Err(vd, "cannot initialize libcucul");
142 goto error;
145 const char *driver = NULL;
146 #ifdef __APPLE__
147 // Make sure we don't try to open a window.
148 driver = "ncurses";
149 #endif
151 sys->dp = caca_create_display_with_driver(sys->cv, driver);
152 if (!sys->dp) {
153 msg_Err(vd, "cannot initialize libcaca");
154 goto error;
156 vout_display_DeleteWindow(vd, NULL);
158 if (vd->cfg->display.title)
159 caca_set_display_title(sys->dp,
160 vd->cfg->display.title);
161 else
162 caca_set_display_title(sys->dp,
163 VOUT_TITLE "(Colour AsCii Art)");
165 /* Fix format */
166 video_format_t fmt = vd->fmt;
167 if (fmt.i_chroma != VLC_CODEC_RGB32) {
168 fmt.i_chroma = VLC_CODEC_RGB32;
169 fmt.i_rmask = 0x00ff0000;
170 fmt.i_gmask = 0x0000ff00;
171 fmt.i_bmask = 0x000000ff;
174 /* TODO */
175 vout_display_info_t info = vd->info;
177 /* Setup vout_display now that everything is fine */
178 vd->fmt = fmt;
179 vd->info = info;
181 vd->pool = Pool;
182 vd->prepare = Prepare;
183 vd->display = Display;
184 vd->control = Control;
185 vd->manage = Manage;
187 /* Fix initial state */
188 vout_display_SendEventFullscreen(vd, false);
189 Refresh(vd);
191 return VLC_SUCCESS;
193 error:
194 if (sys) {
195 if (sys->pool)
196 picture_pool_Delete(sys->pool);
197 if (sys->dither)
198 cucul_free_dither(sys->dither);
199 if (sys->dp)
200 caca_free_display(sys->dp);
201 if (sys->cv)
202 cucul_free_canvas(sys->cv);
204 free(sys);
206 #if defined(WIN32) && !defined(UNDER_CE)
207 FreeConsole();
208 #endif
209 return VLC_EGENERIC;
213 * Close a libcaca video output
215 static void Close(vlc_object_t *object)
217 vout_display_t *vd = (vout_display_t *)object;
218 vout_display_sys_t *sys = vd->sys;
220 if (sys->pool)
221 picture_pool_Delete(sys->pool);
222 if (sys->dither)
223 cucul_free_dither(sys->dither);
224 caca_free_display(sys->dp);
225 cucul_free_canvas(sys->cv);
227 #if defined(WIN32) && !defined(UNDER_CE)
228 FreeConsole();
229 #endif
231 free(sys);
235 * Return a pool of direct buffers
237 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
239 vout_display_sys_t *sys = vd->sys;
241 if (!sys->pool)
242 sys->pool = picture_pool_NewFromFormat(&vd->fmt, count);
243 return sys->pool;
247 * Prepare a picture for display */
248 static void Prepare(vout_display_t *vd, picture_t *picture)
250 vout_display_sys_t *sys = vd->sys;
252 if (!sys->dither) {
253 /* Create the libcaca dither object */
254 sys->dither = cucul_create_dither(32,
255 vd->source.i_visible_width,
256 vd->source.i_visible_height,
257 picture->p[0].i_pitch,
258 vd->fmt.i_rmask,
259 vd->fmt.i_gmask,
260 vd->fmt.i_bmask,
261 0x00000000);
263 if (!sys->dither) {
264 msg_Err(vd, "could not create libcaca dither object");
265 return;
269 vout_display_place_t place;
270 Place(vd, &place);
272 cucul_set_color_ansi(sys->cv, CUCUL_COLOR_DEFAULT, CUCUL_COLOR_BLACK);
273 cucul_clear_canvas(sys->cv);
275 const int crop_offset = vd->source.i_y_offset * picture->p->i_pitch +
276 vd->source.i_x_offset * picture->p->i_pixel_pitch;
277 cucul_dither_bitmap(sys->cv, place.x, place.y,
278 place.width, place.height,
279 sys->dither,
280 &picture->p->p_pixels[crop_offset]);
284 * Display a picture
286 static void Display(vout_display_t *vd, picture_t *picture)
288 Refresh(vd);
289 picture_Release(picture);
293 * Control for vout display
295 static int Control(vout_display_t *vd, int query, va_list args)
297 vout_display_sys_t *sys = vd->sys;
299 switch (query) {
300 case VOUT_DISPLAY_HIDE_MOUSE:
301 caca_set_mouse(sys->dp, 0);
302 return VLC_SUCCESS;
304 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: {
305 const vout_display_cfg_t *cfg = va_arg(args, const vout_display_cfg_t *);
307 caca_refresh_display(sys->dp);
309 /* Not quite good but not sure how to resize it */
310 if (cfg->display.width != caca_get_display_width(sys->dp) ||
311 cfg->display.height != caca_get_display_height(sys->dp))
312 return VLC_EGENERIC;
313 return VLC_SUCCESS;
316 case VOUT_DISPLAY_CHANGE_ZOOM:
317 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
318 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
319 return VLC_SUCCESS;
321 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
322 if (sys->dither)
323 cucul_free_dither(sys->dither);
324 sys->dither = NULL;
325 return VLC_SUCCESS;
327 default:
328 msg_Err(vd, "Unsupported query in vout display caca");
329 return VLC_EGENERIC;
334 * Refresh the display and send resize event
336 static void Refresh(vout_display_t *vd)
338 vout_display_sys_t *sys = vd->sys;
340 /* */
341 caca_refresh_display(sys->dp);
343 /* */
344 const unsigned width = caca_get_display_width(sys->dp);
345 const unsigned height = caca_get_display_height(sys->dp);
347 if (width != vd->cfg->display.width ||
348 height != vd->cfg->display.height)
349 vout_display_SendEventDisplaySize(vd, width, height, false);
353 * Compute the place in canvas unit.
355 static void Place(vout_display_t *vd, vout_display_place_t *place)
357 vout_display_sys_t *sys = vd->sys;
359 vout_display_PlacePicture(place, &vd->source, vd->cfg, false);
361 const int canvas_width = cucul_get_canvas_width(sys->cv);
362 const int canvas_height = cucul_get_canvas_height(sys->cv);
363 const int display_width = caca_get_display_width(sys->dp);
364 const int display_height = caca_get_display_height(sys->dp);
366 if (display_width > 0 && display_height > 0) {
367 place->x = place->x * canvas_width / display_width;
368 place->y = place->y * canvas_height / display_height;
369 place->width = (place->width * canvas_width + display_width/2) / display_width;
370 place->height = (place->height * canvas_height + display_height/2) / display_height;
371 } else {
372 place->x = 0;
373 place->y = 0;
374 place->width = canvas_width;
375 place->height = display_height;
379 /* */
380 static const struct {
381 int caca;
382 int vlc;
383 } keys[] = {
385 { CACA_KEY_CTRL_A, KEY_MODIFIER_CTRL | 'a' },
386 { CACA_KEY_CTRL_B, KEY_MODIFIER_CTRL | 'b' },
387 { CACA_KEY_CTRL_C, KEY_MODIFIER_CTRL | 'c' },
388 { CACA_KEY_CTRL_D, KEY_MODIFIER_CTRL | 'd' },
389 { CACA_KEY_CTRL_E, KEY_MODIFIER_CTRL | 'e' },
390 { CACA_KEY_CTRL_F, KEY_MODIFIER_CTRL | 'f' },
391 { CACA_KEY_CTRL_G, KEY_MODIFIER_CTRL | 'g' },
392 { CACA_KEY_BACKSPACE, KEY_BACKSPACE },
393 { CACA_KEY_TAB, KEY_TAB },
394 { CACA_KEY_CTRL_J, KEY_MODIFIER_CTRL | 'j' },
395 { CACA_KEY_CTRL_K, KEY_MODIFIER_CTRL | 'k' },
396 { CACA_KEY_CTRL_L, KEY_MODIFIER_CTRL | 'l' },
397 { CACA_KEY_RETURN, KEY_ENTER },
399 { CACA_KEY_CTRL_N, KEY_MODIFIER_CTRL | 'n' },
400 { CACA_KEY_CTRL_O, KEY_MODIFIER_CTRL | 'o' },
401 { CACA_KEY_CTRL_P, KEY_MODIFIER_CTRL | 'p' },
402 { CACA_KEY_CTRL_Q, KEY_MODIFIER_CTRL | 'q' },
403 { CACA_KEY_CTRL_R, KEY_MODIFIER_CTRL | 'r' },
405 { CACA_KEY_PAUSE, -1 },
406 { CACA_KEY_CTRL_T, KEY_MODIFIER_CTRL | 't' },
407 { CACA_KEY_CTRL_U, KEY_MODIFIER_CTRL | 'u' },
408 { CACA_KEY_CTRL_V, KEY_MODIFIER_CTRL | 'v' },
409 { CACA_KEY_CTRL_W, KEY_MODIFIER_CTRL | 'w' },
410 { CACA_KEY_CTRL_X, KEY_MODIFIER_CTRL | 'x' },
411 { CACA_KEY_CTRL_Y, KEY_MODIFIER_CTRL | 'y' },
412 { CACA_KEY_CTRL_Z, KEY_MODIFIER_CTRL | 'z' },
414 { CACA_KEY_ESCAPE, KEY_ESC },
415 { CACA_KEY_DELETE, KEY_DELETE },
417 { CACA_KEY_F1, KEY_F1 },
418 { CACA_KEY_F2, KEY_F2 },
419 { CACA_KEY_F3, KEY_F3 },
420 { CACA_KEY_F4, KEY_F4 },
421 { CACA_KEY_F5, KEY_F5 },
422 { CACA_KEY_F6, KEY_F6 },
423 { CACA_KEY_F7, KEY_F7 },
424 { CACA_KEY_F8, KEY_F8 },
425 { CACA_KEY_F9, KEY_F9 },
426 { CACA_KEY_F10, KEY_F10 },
427 { CACA_KEY_F11, KEY_F11 },
428 { CACA_KEY_F12, KEY_F12 },
429 { CACA_KEY_F13, -1 },
430 { CACA_KEY_F14, -1 },
431 { CACA_KEY_F15, -1 },
433 { CACA_KEY_UP, KEY_UP },
434 { CACA_KEY_DOWN, KEY_DOWN },
435 { CACA_KEY_LEFT, KEY_LEFT },
436 { CACA_KEY_RIGHT, KEY_RIGHT },
438 { CACA_KEY_INSERT, KEY_INSERT },
439 { CACA_KEY_HOME, KEY_HOME },
440 { CACA_KEY_END, KEY_END },
441 { CACA_KEY_PAGEUP, KEY_PAGEUP },
442 { CACA_KEY_PAGEDOWN,KEY_PAGEDOWN },
444 /* */
445 { -1, -1 }
448 static const struct {
449 int caca;
450 int vlc;
451 } mouses[] = {
452 { 1, MOUSE_BUTTON_LEFT },
453 { 2, MOUSE_BUTTON_CENTER },
454 { 3, MOUSE_BUTTON_RIGHT },
455 { 4, MOUSE_BUTTON_WHEEL_UP },
456 { 5, MOUSE_BUTTON_WHEEL_DOWN },
458 /* */
459 { -1, -1 }
463 * Proccess pending event
465 static void Manage(vout_display_t *vd)
467 vout_display_sys_t *sys = vd->sys;
469 struct caca_event ev;
470 while (caca_get_event(sys->dp, CACA_EVENT_ANY, &ev, 0) > 0) {
471 switch (caca_get_event_type(&ev)) {
472 case CACA_EVENT_KEY_PRESS: {
473 const int caca = caca_get_event_key_ch(&ev);
475 for (int i = 0; keys[i].caca != -1; i++) {
476 if (keys[i].caca == caca) {
477 const int vlc = keys[i].vlc;
479 if (vlc >= 0)
480 vout_display_SendEventKey(vd, vlc);
481 return;
484 if (caca >= 0x20 && caca <= 0x7f) {
485 vout_display_SendEventKey(vd, caca);
486 return;
488 break;
490 case CACA_EVENT_RESIZE:
491 vout_display_SendEventDisplaySize(vd, caca_get_event_resize_width(&ev),
492 caca_get_event_resize_height(&ev), false);
493 break;
494 case CACA_EVENT_MOUSE_MOTION: {
495 vout_display_place_t place;
496 Place(vd, &place);
498 const unsigned x = vd->source.i_x_offset +
499 (int64_t)(caca_get_event_mouse_x(&ev) - place.x) *
500 vd->source.i_visible_width / place.width;
501 const unsigned y = vd->source.i_y_offset +
502 (int64_t)(caca_get_event_mouse_y(&ev) - place.y) *
503 vd->source.i_visible_height / place.height;
505 caca_set_mouse(sys->dp, 1);
506 vout_display_SendEventMouseMoved(vd, x, y);
507 break;
509 case CACA_EVENT_MOUSE_PRESS:
510 case CACA_EVENT_MOUSE_RELEASE: {
511 caca_set_mouse(sys->dp, 1);
512 const int caca = caca_get_event_mouse_button(&ev);
513 for (int i = 0; mouses[i].caca != -1; i++) {
514 if (mouses[i].caca == caca) {
515 if (caca_get_event_type(&ev) == CACA_EVENT_MOUSE_PRESS)
516 vout_display_SendEventMousePressed(vd, mouses[i].vlc);
517 else
518 vout_display_SendEventMouseReleased(vd, mouses[i].vlc);
519 return;
522 break;
524 case CACA_EVENT_QUIT:
525 vout_display_SendEventClose(vd);
526 break;
527 default:
528 break;