d3d11: handle VLC_CODEC_D3D11_OPAQUE_10B upload/download
[vlc.git] / modules / video_output / caca.c
blobffea9625cc1ed7b97c218e03fe6bba77ae4e98af
1 /*****************************************************************************
2 * caca.c: Color ASCII Art "vout display" module using libcaca
3 *****************************************************************************
4 * Copyright (C) 2003-2009 VLC authors and VideoLAN
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 it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * 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 <stdnoreturn.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <vlc_common.h>
37 #include <vlc_block.h>
38 #include <vlc_plugin.h>
39 #include <vlc_vout_display.h>
40 #include <vlc_picture_pool.h>
41 #if !defined(_WIN32) && !defined(__APPLE__)
42 # ifdef X_DISPLAY_MISSING
43 # error Xlib required due to XInitThreads
44 # endif
45 # include <vlc_xlib.h>
46 #endif
48 #include <caca.h>
50 /* */
51 struct vout_display_sys_t {
52 cucul_canvas_t *cv;
53 caca_display_t *dp;
54 cucul_dither_t *dither;
56 picture_pool_t *pool;
57 block_fifo_t *fifo;
58 vlc_thread_t thread;
61 noreturn static void *VoutDisplayEventKeyDispatch(void *data)
63 vout_display_t *vd = data;
64 vout_display_sys_t *sys = vd->sys;
65 block_fifo_t *fifo = sys->fifo;
67 for (;;) {
68 block_t *event = block_FifoGet(fifo);
70 int cancel = vlc_savecancel();
71 int key;
73 memcpy(&key, event->p_buffer, sizeof (key));
74 block_Release(event);
75 vout_display_SendEventKey(vd, key);
76 vlc_restorecancel(cancel);
80 static void VoutDisplayEventKey(vout_display_sys_t *sys, int key)
82 block_t *event = block_Alloc(sizeof (key));
83 if (likely(event != NULL)) {
84 memcpy(event->p_buffer, &key, sizeof (key));
85 block_FifoPut(sys->fifo, event);
89 /**
90 * Return a pool of direct buffers
92 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
94 vout_display_sys_t *sys = vd->sys;
96 if (!sys->pool)
97 sys->pool = picture_pool_NewFromFormat(&vd->fmt, count);
98 return sys->pool;
102 * Compute the place in canvas unit.
104 static void Place(vout_display_t *vd, vout_display_place_t *place)
106 vout_display_sys_t *sys = vd->sys;
108 vout_display_PlacePicture(place, &vd->source, vd->cfg, false);
110 const int canvas_width = cucul_get_canvas_width(sys->cv);
111 const int canvas_height = cucul_get_canvas_height(sys->cv);
112 const int display_width = caca_get_display_width(sys->dp);
113 const int display_height = caca_get_display_height(sys->dp);
115 if (display_width > 0 && display_height > 0) {
116 place->x = place->x * canvas_width / display_width;
117 place->y = place->y * canvas_height / display_height;
118 place->width = (place->width * canvas_width + display_width/2) / display_width;
119 place->height = (place->height * canvas_height + display_height/2) / display_height;
120 } else {
121 place->x = 0;
122 place->y = 0;
123 place->width = canvas_width;
124 place->height = display_height;
129 * Refresh the display and send resize event
131 static void Refresh(vout_display_t *vd)
133 vout_display_sys_t *sys = vd->sys;
135 /* */
136 caca_refresh_display(sys->dp);
138 /* */
139 const unsigned width = caca_get_display_width(sys->dp);
140 const unsigned height = caca_get_display_height(sys->dp);
142 if (width != vd->cfg->display.width ||
143 height != vd->cfg->display.height)
144 vout_display_SendEventDisplaySize(vd, width, height);
148 * Prepare a picture for display */
149 static void Prepare(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
151 vout_display_sys_t *sys = vd->sys;
153 if (!sys->dither) {
154 /* Create the libcaca dither object */
155 sys->dither = cucul_create_dither(32,
156 vd->source.i_visible_width,
157 vd->source.i_visible_height,
158 picture->p[0].i_pitch,
159 vd->fmt.i_rmask,
160 vd->fmt.i_gmask,
161 vd->fmt.i_bmask,
162 0x00000000);
164 if (!sys->dither) {
165 msg_Err(vd, "could not create libcaca dither object");
166 return;
170 vout_display_place_t place;
171 Place(vd, &place);
173 cucul_set_color_ansi(sys->cv, CUCUL_COLOR_DEFAULT, CUCUL_COLOR_BLACK);
174 cucul_clear_canvas(sys->cv);
176 const int crop_offset = vd->source.i_y_offset * picture->p->i_pitch +
177 vd->source.i_x_offset * picture->p->i_pixel_pitch;
178 cucul_dither_bitmap(sys->cv, place.x, place.y,
179 place.width, place.height,
180 sys->dither,
181 &picture->p->p_pixels[crop_offset]);
182 VLC_UNUSED(subpicture);
186 * Display a picture
188 static void PictureDisplay(vout_display_t *vd, picture_t *picture, subpicture_t *subpicture)
190 Refresh(vd);
191 picture_Release(picture);
192 VLC_UNUSED(subpicture);
196 * Control for vout display
198 static int Control(vout_display_t *vd, int query, va_list args)
200 vout_display_sys_t *sys = vd->sys;
202 (void) args;
204 switch (query) {
205 case VOUT_DISPLAY_HIDE_MOUSE:
206 caca_set_mouse(sys->dp, 0);
207 return VLC_SUCCESS;
209 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
210 case VOUT_DISPLAY_CHANGE_ZOOM:
211 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
212 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
213 return VLC_EGENERIC;
215 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
216 if (sys->dither)
217 cucul_free_dither(sys->dither);
218 sys->dither = NULL;
219 return VLC_SUCCESS;
221 default:
222 msg_Err(vd, "Unsupported query in vout display caca");
223 return VLC_EGENERIC;
227 /* */
228 static const struct {
229 int caca;
230 int vlc;
231 } keys[] = {
233 { CACA_KEY_CTRL_A, KEY_MODIFIER_CTRL | 'a' },
234 { CACA_KEY_CTRL_B, KEY_MODIFIER_CTRL | 'b' },
235 { CACA_KEY_CTRL_C, KEY_MODIFIER_CTRL | 'c' },
236 { CACA_KEY_CTRL_D, KEY_MODIFIER_CTRL | 'd' },
237 { CACA_KEY_CTRL_E, KEY_MODIFIER_CTRL | 'e' },
238 { CACA_KEY_CTRL_F, KEY_MODIFIER_CTRL | 'f' },
239 { CACA_KEY_CTRL_G, KEY_MODIFIER_CTRL | 'g' },
240 { CACA_KEY_BACKSPACE, KEY_BACKSPACE },
241 { CACA_KEY_TAB, KEY_TAB },
242 { CACA_KEY_CTRL_J, KEY_MODIFIER_CTRL | 'j' },
243 { CACA_KEY_CTRL_K, KEY_MODIFIER_CTRL | 'k' },
244 { CACA_KEY_CTRL_L, KEY_MODIFIER_CTRL | 'l' },
245 { CACA_KEY_RETURN, KEY_ENTER },
247 { CACA_KEY_CTRL_N, KEY_MODIFIER_CTRL | 'n' },
248 { CACA_KEY_CTRL_O, KEY_MODIFIER_CTRL | 'o' },
249 { CACA_KEY_CTRL_P, KEY_MODIFIER_CTRL | 'p' },
250 { CACA_KEY_CTRL_Q, KEY_MODIFIER_CTRL | 'q' },
251 { CACA_KEY_CTRL_R, KEY_MODIFIER_CTRL | 'r' },
253 { CACA_KEY_PAUSE, -1 },
254 { CACA_KEY_CTRL_T, KEY_MODIFIER_CTRL | 't' },
255 { CACA_KEY_CTRL_U, KEY_MODIFIER_CTRL | 'u' },
256 { CACA_KEY_CTRL_V, KEY_MODIFIER_CTRL | 'v' },
257 { CACA_KEY_CTRL_W, KEY_MODIFIER_CTRL | 'w' },
258 { CACA_KEY_CTRL_X, KEY_MODIFIER_CTRL | 'x' },
259 { CACA_KEY_CTRL_Y, KEY_MODIFIER_CTRL | 'y' },
260 { CACA_KEY_CTRL_Z, KEY_MODIFIER_CTRL | 'z' },
262 { CACA_KEY_ESCAPE, KEY_ESC },
263 { CACA_KEY_DELETE, KEY_DELETE },
265 { CACA_KEY_F1, KEY_F1 },
266 { CACA_KEY_F2, KEY_F2 },
267 { CACA_KEY_F3, KEY_F3 },
268 { CACA_KEY_F4, KEY_F4 },
269 { CACA_KEY_F5, KEY_F5 },
270 { CACA_KEY_F6, KEY_F6 },
271 { CACA_KEY_F7, KEY_F7 },
272 { CACA_KEY_F8, KEY_F8 },
273 { CACA_KEY_F9, KEY_F9 },
274 { CACA_KEY_F10, KEY_F10 },
275 { CACA_KEY_F11, KEY_F11 },
276 { CACA_KEY_F12, KEY_F12 },
277 { CACA_KEY_F13, -1 },
278 { CACA_KEY_F14, -1 },
279 { CACA_KEY_F15, -1 },
281 { CACA_KEY_UP, KEY_UP },
282 { CACA_KEY_DOWN, KEY_DOWN },
283 { CACA_KEY_LEFT, KEY_LEFT },
284 { CACA_KEY_RIGHT, KEY_RIGHT },
286 { CACA_KEY_INSERT, KEY_INSERT },
287 { CACA_KEY_HOME, KEY_HOME },
288 { CACA_KEY_END, KEY_END },
289 { CACA_KEY_PAGEUP, KEY_PAGEUP },
290 { CACA_KEY_PAGEDOWN,KEY_PAGEDOWN },
292 /* */
293 { -1, -1 }
296 static const struct {
297 int caca;
298 int vlc;
299 } mouses[] = {
300 { 1, MOUSE_BUTTON_LEFT },
301 { 2, MOUSE_BUTTON_CENTER },
302 { 3, MOUSE_BUTTON_RIGHT },
303 { 4, MOUSE_BUTTON_WHEEL_UP },
304 { 5, MOUSE_BUTTON_WHEEL_DOWN },
306 /* */
307 { -1, -1 }
311 * Proccess pending event
313 static void Manage(vout_display_t *vd)
315 vout_display_sys_t *sys = vd->sys;
317 struct caca_event ev;
318 while (caca_get_event(sys->dp, CACA_EVENT_ANY, &ev, 0) > 0) {
319 switch (caca_get_event_type(&ev)) {
320 case CACA_EVENT_KEY_PRESS: {
321 const int caca = caca_get_event_key_ch(&ev);
323 for (int i = 0; keys[i].caca != -1; i++) {
324 if (keys[i].caca == caca) {
325 const int vlc = keys[i].vlc;
327 if (vlc >= 0)
328 VoutDisplayEventKey(sys, vlc);
329 return;
332 if (caca >= 0x20 && caca <= 0x7f) {
333 VoutDisplayEventKey(sys, caca);
334 return;
336 break;
338 case CACA_EVENT_RESIZE:
339 vout_display_SendEventDisplaySize(vd, caca_get_event_resize_width(&ev),
340 caca_get_event_resize_height(&ev));
341 break;
342 case CACA_EVENT_MOUSE_MOTION: {
343 vout_display_place_t place;
344 Place(vd, &place);
346 const unsigned x = vd->source.i_x_offset +
347 (int64_t)(caca_get_event_mouse_x(&ev) - place.x) *
348 vd->source.i_visible_width / place.width;
349 const unsigned y = vd->source.i_y_offset +
350 (int64_t)(caca_get_event_mouse_y(&ev) - place.y) *
351 vd->source.i_visible_height / place.height;
353 caca_set_mouse(sys->dp, 1);
354 vout_display_SendEventMouseMoved(vd, x, y);
355 break;
357 case CACA_EVENT_MOUSE_PRESS:
358 case CACA_EVENT_MOUSE_RELEASE: {
359 caca_set_mouse(sys->dp, 1);
360 const int caca = caca_get_event_mouse_button(&ev);
361 for (int i = 0; mouses[i].caca != -1; i++) {
362 if (mouses[i].caca == caca) {
363 if (caca_get_event_type(&ev) == CACA_EVENT_MOUSE_PRESS)
364 vout_display_SendEventMousePressed(vd, mouses[i].vlc);
365 else
366 vout_display_SendEventMouseReleased(vd, mouses[i].vlc);
367 return;
370 break;
372 case CACA_EVENT_QUIT:
373 vout_display_SendEventClose(vd);
374 break;
375 default:
376 break;
382 * This function initializes libcaca vout method.
384 static int Open(vlc_object_t *object)
386 vout_display_t *vd = (vout_display_t *)object;
387 vout_display_sys_t *sys;
389 if (vout_display_IsWindowed(vd))
390 return VLC_EGENERIC;
391 #if !defined(__APPLE__) && !defined(_WIN32)
392 # ifndef X_DISPLAY_MISSING
393 if (!vlc_xlib_init(object))
394 return VLC_EGENERIC;
395 # endif
396 #endif
398 #if defined(_WIN32)
399 CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
400 SMALL_RECT rect;
401 COORD coord;
402 HANDLE hstdout;
404 if (!AllocConsole()) {
405 msg_Err(vd, "cannot create console");
406 return VLC_EGENERIC;
409 hstdout =
410 CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
411 FILE_SHARE_READ | FILE_SHARE_WRITE,
412 NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
413 if (!hstdout || hstdout == INVALID_HANDLE_VALUE) {
414 msg_Err(vd, "cannot create screen buffer");
415 FreeConsole();
416 return VLC_EGENERIC;
419 if (!SetConsoleActiveScreenBuffer(hstdout)) {
420 msg_Err(vd, "cannot set active screen buffer");
421 FreeConsole();
422 return VLC_EGENERIC;
425 coord = GetLargestConsoleWindowSize(hstdout);
426 msg_Dbg(vd, "SetConsoleWindowInfo: %ix%i", coord.X, coord.Y);
428 /* Force size for now */
429 coord.X = 100;
430 coord.Y = 40;
432 if (!SetConsoleScreenBufferSize(hstdout, coord))
433 msg_Warn(vd, "SetConsoleScreenBufferSize %i %i",
434 coord.X, coord.Y);
436 /* Get the current screen buffer size and window position. */
437 if (GetConsoleScreenBufferInfo(hstdout, &csbiInfo)) {
438 rect.Top = 0; rect.Left = 0;
439 rect.Right = csbiInfo.dwMaximumWindowSize.X - 1;
440 rect.Bottom = csbiInfo.dwMaximumWindowSize.Y - 1;
441 if (!SetConsoleWindowInfo(hstdout, TRUE, &rect))
442 msg_Dbg(vd, "SetConsoleWindowInfo failed: %ix%i",
443 rect.Right, rect.Bottom);
445 #endif
447 /* Allocate structure */
448 vd->sys = sys = calloc(1, sizeof(*sys));
449 if (!sys)
450 goto error;
452 sys->cv = cucul_create_canvas(0, 0);
453 if (!sys->cv) {
454 msg_Err(vd, "cannot initialize libcucul");
455 goto error;
458 const char *driver = NULL;
459 #ifdef __APPLE__
460 // Make sure we don't try to open a window.
461 driver = "ncurses";
462 #endif
464 sys->dp = caca_create_display_with_driver(sys->cv, driver);
465 if (!sys->dp) {
466 msg_Err(vd, "cannot initialize libcaca");
467 goto error;
470 if (vd->cfg->display.title)
471 caca_set_display_title(sys->dp,
472 vd->cfg->display.title);
473 else
474 caca_set_display_title(sys->dp,
475 VOUT_TITLE "(Colour AsCii Art)");
477 block_fifo_t *fifo = block_FifoNew();
478 if (likely(fifo != NULL)) {
479 sys->fifo = fifo;
481 if (vlc_clone(&sys->thread, VoutDisplayEventKeyDispatch, vd,
482 VLC_THREAD_PRIORITY_LOW)) {
483 block_FifoRelease(fifo);
484 sys->fifo = NULL;
488 /* Fix format */
489 video_format_t fmt = vd->fmt;
490 if (fmt.i_chroma != VLC_CODEC_RGB32) {
491 fmt.i_chroma = VLC_CODEC_RGB32;
492 fmt.i_rmask = 0x00ff0000;
493 fmt.i_gmask = 0x0000ff00;
494 fmt.i_bmask = 0x000000ff;
497 /* Setup vout_display now that everything is fine */
498 vd->fmt = fmt;
499 vd->info.needs_hide_mouse = true;
501 vd->pool = Pool;
502 vd->prepare = Prepare;
503 vd->display = PictureDisplay;
504 vd->control = Control;
505 vd->manage = Manage;
507 /* Fix initial state */
508 Refresh(vd);
510 return VLC_SUCCESS;
512 error:
513 if (sys) {
514 if (sys->pool)
515 picture_pool_Release(sys->pool);
516 if (sys->dither)
517 cucul_free_dither(sys->dither);
518 if (sys->dp)
519 caca_free_display(sys->dp);
520 if (sys->cv)
521 cucul_free_canvas(sys->cv);
523 free(sys);
525 #if defined(_WIN32)
526 FreeConsole();
527 #endif
528 return VLC_EGENERIC;
532 * Close a libcaca video output
534 static void Close(vlc_object_t *object)
536 vout_display_t *vd = (vout_display_t *)object;
537 vout_display_sys_t *sys = vd->sys;
539 if (sys->fifo != NULL) {
540 vlc_cancel(sys->thread);
541 vlc_join(sys->thread, NULL);
542 block_FifoRelease(sys->fifo);
544 if (sys->pool)
545 picture_pool_Release(sys->pool);
546 if (sys->dither)
547 cucul_free_dither(sys->dither);
548 caca_free_display(sys->dp);
549 cucul_free_canvas(sys->cv);
551 #if defined(_WIN32)
552 FreeConsole();
553 #endif
555 free(sys);
558 /*****************************************************************************
559 * Module descriptor
560 *****************************************************************************/
561 vlc_module_begin()
562 set_shortname("Caca")
563 set_category(CAT_VIDEO)
564 set_subcategory(SUBCAT_VIDEO_VOUT)
565 set_description(N_("Color ASCII art video output"))
566 set_capability("vout display", 15)
567 set_callbacks(Open, Close)
568 vlc_module_end()