1 /*****************************************************************************
2 * caca.c: Color ASCII Art "vout display" module using libcaca
3 *****************************************************************************
4 * Copyright (C) 2003-2009 the VideoLAN team
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 /*****************************************************************************
27 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_vout_display.h>
36 #include <vlc_picture_pool.h>
37 #include "keythread.h"
41 /*****************************************************************************
43 *****************************************************************************/
44 static int Open (vlc_object_t
*);
45 static void Close(vlc_object_t
*);
49 set_category(CAT_VIDEO
)
50 set_subcategory(SUBCAT_VIDEO_VOUT
)
51 set_description(N_("Color ASCII art video output"))
52 set_capability("vout display", 12)
53 set_callbacks(Open
, Close
)
56 /*****************************************************************************
58 *****************************************************************************/
59 static picture_pool_t
*Pool (vout_display_t
*, unsigned);
60 static void Prepare(vout_display_t
*, picture_t
*);
61 static void Display(vout_display_t
*, picture_t
*);
62 static int Control(vout_display_t
*, int, va_list);
65 static void Manage(vout_display_t
*);
66 static void Refresh(vout_display_t
*);
67 static void Place(vout_display_t
*, vout_display_place_t
*);
70 struct vout_display_sys_t
{
73 cucul_dither_t
*dither
;
80 * This function initializes libcaca vout method.
82 static int Open(vlc_object_t
*object
)
84 vout_display_t
*vd
= (vout_display_t
*)object
;
85 vout_display_sys_t
*sys
;
87 #if defined(WIN32) && !defined(UNDER_CE)
88 CONSOLE_SCREEN_BUFFER_INFO csbiInfo
;
93 if (!AllocConsole()) {
94 msg_Err(vd
, "cannot create console");
99 CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
100 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
101 NULL
, CONSOLE_TEXTMODE_BUFFER
, NULL
);
102 if (!hstdout
|| hstdout
== INVALID_HANDLE_VALUE
) {
103 msg_Err(vd
, "cannot create screen buffer");
108 if (!SetConsoleActiveScreenBuffer(hstdout
)) {
109 msg_Err(vd
, "cannot set active screen buffer");
114 coord
= GetLargestConsoleWindowSize(hstdout
);
115 msg_Dbg(vd
, "SetConsoleWindowInfo: %ix%i", coord
.X
, coord
.Y
);
117 /* Force size for now */
121 if (!SetConsoleScreenBufferSize(hstdout
, coord
))
122 msg_Warn(vd
, "SetConsoleScreenBufferSize %i %i",
125 /* Get the current screen buffer size and window position. */
126 if (GetConsoleScreenBufferInfo(hstdout
, &csbiInfo
)) {
127 rect
.Top
= 0; rect
.Left
= 0;
128 rect
.Right
= csbiInfo
.dwMaximumWindowSize
.X
- 1;
129 rect
.Bottom
= csbiInfo
.dwMaximumWindowSize
.Y
- 1;
130 if (!SetConsoleWindowInfo(hstdout
, TRUE
, &rect
))
131 msg_Dbg(vd
, "SetConsoleWindowInfo failed: %ix%i",
132 rect
.Right
, rect
.Bottom
);
136 /* Allocate structure */
137 vd
->sys
= sys
= calloc(1, sizeof(*sys
));
141 sys
->cv
= cucul_create_canvas(0, 0);
143 msg_Err(vd
, "cannot initialize libcucul");
147 const char *driver
= NULL
;
149 // Make sure we don't try to open a window.
153 sys
->dp
= caca_create_display_with_driver(sys
->cv
, driver
);
155 msg_Err(vd
, "cannot initialize libcaca");
158 vout_display_DeleteWindow(vd
, NULL
);
160 if (vd
->cfg
->display
.title
)
161 caca_set_display_title(sys
->dp
,
162 vd
->cfg
->display
.title
);
164 caca_set_display_title(sys
->dp
,
165 VOUT_TITLE
"(Colour AsCii Art)");
168 video_format_t fmt
= vd
->fmt
;
169 if (fmt
.i_chroma
!= VLC_CODEC_RGB32
) {
170 fmt
.i_chroma
= VLC_CODEC_RGB32
;
171 fmt
.i_rmask
= 0x00ff0000;
172 fmt
.i_gmask
= 0x0000ff00;
173 fmt
.i_bmask
= 0x000000ff;
177 vout_display_info_t info
= vd
->info
;
179 /* Setup vout_display now that everything is fine */
184 vd
->prepare
= Prepare
;
185 vd
->display
= Display
;
186 vd
->control
= Control
;
189 /* Fix initial state */
190 vout_display_SendEventFullscreen(vd
, false);
193 sys
->keys
= vlc_CreateKeyThread(vd
);
199 picture_pool_Delete(sys
->pool
);
201 cucul_free_dither(sys
->dither
);
203 caca_free_display(sys
->dp
);
205 cucul_free_canvas(sys
->cv
);
209 #if defined(WIN32) && !defined(UNDER_CE)
216 * Close a libcaca video output
218 static void Close(vlc_object_t
*object
)
220 vout_display_t
*vd
= (vout_display_t
*)object
;
221 vout_display_sys_t
*sys
= vd
->sys
;
223 vlc_DestroyKeyThread(sys
->keys
);
225 picture_pool_Delete(sys
->pool
);
227 cucul_free_dither(sys
->dither
);
228 caca_free_display(sys
->dp
);
229 cucul_free_canvas(sys
->cv
);
231 #if defined(WIN32) && !defined(UNDER_CE)
239 * Return a pool of direct buffers
241 static picture_pool_t
*Pool(vout_display_t
*vd
, unsigned count
)
243 vout_display_sys_t
*sys
= vd
->sys
;
246 sys
->pool
= picture_pool_NewFromFormat(&vd
->fmt
, count
);
251 * Prepare a picture for display */
252 static void Prepare(vout_display_t
*vd
, picture_t
*picture
)
254 vout_display_sys_t
*sys
= vd
->sys
;
257 /* Create the libcaca dither object */
258 sys
->dither
= cucul_create_dither(32,
259 vd
->source
.i_visible_width
,
260 vd
->source
.i_visible_height
,
261 picture
->p
[0].i_pitch
,
268 msg_Err(vd
, "could not create libcaca dither object");
273 vout_display_place_t place
;
276 cucul_set_color_ansi(sys
->cv
, CUCUL_COLOR_DEFAULT
, CUCUL_COLOR_BLACK
);
277 cucul_clear_canvas(sys
->cv
);
279 const int crop_offset
= vd
->source
.i_y_offset
* picture
->p
->i_pitch
+
280 vd
->source
.i_x_offset
* picture
->p
->i_pixel_pitch
;
281 cucul_dither_bitmap(sys
->cv
, place
.x
, place
.y
,
282 place
.width
, place
.height
,
284 &picture
->p
->p_pixels
[crop_offset
]);
290 static void Display(vout_display_t
*vd
, picture_t
*picture
)
293 picture_Release(picture
);
297 * Control for vout display
299 static int Control(vout_display_t
*vd
, int query
, va_list args
)
301 vout_display_sys_t
*sys
= vd
->sys
;
304 case VOUT_DISPLAY_HIDE_MOUSE
:
305 caca_set_mouse(sys
->dp
, 0);
308 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
: {
309 const vout_display_cfg_t
*cfg
= va_arg(args
, const vout_display_cfg_t
*);
311 caca_refresh_display(sys
->dp
);
313 /* Not quite good but not sure how to resize it */
314 if (cfg
->display
.width
!= caca_get_display_width(sys
->dp
) ||
315 cfg
->display
.height
!= caca_get_display_height(sys
->dp
))
320 case VOUT_DISPLAY_CHANGE_ZOOM
:
321 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED
:
322 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
:
325 case VOUT_DISPLAY_CHANGE_SOURCE_CROP
:
327 cucul_free_dither(sys
->dither
);
332 msg_Err(vd
, "Unsupported query in vout display caca");
338 * Refresh the display and send resize event
340 static void Refresh(vout_display_t
*vd
)
342 vout_display_sys_t
*sys
= vd
->sys
;
345 caca_refresh_display(sys
->dp
);
348 const unsigned width
= caca_get_display_width(sys
->dp
);
349 const unsigned height
= caca_get_display_height(sys
->dp
);
351 if (width
!= vd
->cfg
->display
.width
||
352 height
!= vd
->cfg
->display
.height
)
353 vout_display_SendEventDisplaySize(vd
, width
, height
, false);
357 * Compute the place in canvas unit.
359 static void Place(vout_display_t
*vd
, vout_display_place_t
*place
)
361 vout_display_sys_t
*sys
= vd
->sys
;
363 vout_display_PlacePicture(place
, &vd
->source
, vd
->cfg
, false);
365 const int canvas_width
= cucul_get_canvas_width(sys
->cv
);
366 const int canvas_height
= cucul_get_canvas_height(sys
->cv
);
367 const int display_width
= caca_get_display_width(sys
->dp
);
368 const int display_height
= caca_get_display_height(sys
->dp
);
370 if (display_width
> 0 && display_height
> 0) {
371 place
->x
= place
->x
* canvas_width
/ display_width
;
372 place
->y
= place
->y
* canvas_height
/ display_height
;
373 place
->width
= (place
->width
* canvas_width
+ display_width
/2) / display_width
;
374 place
->height
= (place
->height
* canvas_height
+ display_height
/2) / display_height
;
378 place
->width
= canvas_width
;
379 place
->height
= display_height
;
384 static const struct {
389 { CACA_KEY_CTRL_A
, KEY_MODIFIER_CTRL
| 'a' },
390 { CACA_KEY_CTRL_B
, KEY_MODIFIER_CTRL
| 'b' },
391 { CACA_KEY_CTRL_C
, KEY_MODIFIER_CTRL
| 'c' },
392 { CACA_KEY_CTRL_D
, KEY_MODIFIER_CTRL
| 'd' },
393 { CACA_KEY_CTRL_E
, KEY_MODIFIER_CTRL
| 'e' },
394 { CACA_KEY_CTRL_F
, KEY_MODIFIER_CTRL
| 'f' },
395 { CACA_KEY_CTRL_G
, KEY_MODIFIER_CTRL
| 'g' },
396 { CACA_KEY_BACKSPACE
, KEY_BACKSPACE
},
397 { CACA_KEY_TAB
, KEY_TAB
},
398 { CACA_KEY_CTRL_J
, KEY_MODIFIER_CTRL
| 'j' },
399 { CACA_KEY_CTRL_K
, KEY_MODIFIER_CTRL
| 'k' },
400 { CACA_KEY_CTRL_L
, KEY_MODIFIER_CTRL
| 'l' },
401 { CACA_KEY_RETURN
, KEY_ENTER
},
403 { CACA_KEY_CTRL_N
, KEY_MODIFIER_CTRL
| 'n' },
404 { CACA_KEY_CTRL_O
, KEY_MODIFIER_CTRL
| 'o' },
405 { CACA_KEY_CTRL_P
, KEY_MODIFIER_CTRL
| 'p' },
406 { CACA_KEY_CTRL_Q
, KEY_MODIFIER_CTRL
| 'q' },
407 { CACA_KEY_CTRL_R
, KEY_MODIFIER_CTRL
| 'r' },
409 { CACA_KEY_PAUSE
, -1 },
410 { CACA_KEY_CTRL_T
, KEY_MODIFIER_CTRL
| 't' },
411 { CACA_KEY_CTRL_U
, KEY_MODIFIER_CTRL
| 'u' },
412 { CACA_KEY_CTRL_V
, KEY_MODIFIER_CTRL
| 'v' },
413 { CACA_KEY_CTRL_W
, KEY_MODIFIER_CTRL
| 'w' },
414 { CACA_KEY_CTRL_X
, KEY_MODIFIER_CTRL
| 'x' },
415 { CACA_KEY_CTRL_Y
, KEY_MODIFIER_CTRL
| 'y' },
416 { CACA_KEY_CTRL_Z
, KEY_MODIFIER_CTRL
| 'z' },
418 { CACA_KEY_ESCAPE
, KEY_ESC
},
419 { CACA_KEY_DELETE
, KEY_DELETE
},
421 { CACA_KEY_F1
, KEY_F1
},
422 { CACA_KEY_F2
, KEY_F2
},
423 { CACA_KEY_F3
, KEY_F3
},
424 { CACA_KEY_F4
, KEY_F4
},
425 { CACA_KEY_F5
, KEY_F5
},
426 { CACA_KEY_F6
, KEY_F6
},
427 { CACA_KEY_F7
, KEY_F7
},
428 { CACA_KEY_F8
, KEY_F8
},
429 { CACA_KEY_F9
, KEY_F9
},
430 { CACA_KEY_F10
, KEY_F10
},
431 { CACA_KEY_F11
, KEY_F11
},
432 { CACA_KEY_F12
, KEY_F12
},
433 { CACA_KEY_F13
, -1 },
434 { CACA_KEY_F14
, -1 },
435 { CACA_KEY_F15
, -1 },
437 { CACA_KEY_UP
, KEY_UP
},
438 { CACA_KEY_DOWN
, KEY_DOWN
},
439 { CACA_KEY_LEFT
, KEY_LEFT
},
440 { CACA_KEY_RIGHT
, KEY_RIGHT
},
442 { CACA_KEY_INSERT
, KEY_INSERT
},
443 { CACA_KEY_HOME
, KEY_HOME
},
444 { CACA_KEY_END
, KEY_END
},
445 { CACA_KEY_PAGEUP
, KEY_PAGEUP
},
446 { CACA_KEY_PAGEDOWN
,KEY_PAGEDOWN
},
452 static const struct {
456 { 1, MOUSE_BUTTON_LEFT
},
457 { 2, MOUSE_BUTTON_CENTER
},
458 { 3, MOUSE_BUTTON_RIGHT
},
459 { 4, MOUSE_BUTTON_WHEEL_UP
},
460 { 5, MOUSE_BUTTON_WHEEL_DOWN
},
467 * Proccess pending event
469 static void Manage(vout_display_t
*vd
)
471 vout_display_sys_t
*sys
= vd
->sys
;
473 struct caca_event ev
;
474 while (caca_get_event(sys
->dp
, CACA_EVENT_ANY
, &ev
, 0) > 0) {
475 switch (caca_get_event_type(&ev
)) {
476 case CACA_EVENT_KEY_PRESS
: {
477 const int caca
= caca_get_event_key_ch(&ev
);
479 for (int i
= 0; keys
[i
].caca
!= -1; i
++) {
480 if (keys
[i
].caca
== caca
) {
481 const int vlc
= keys
[i
].vlc
;
484 vlc_EmitKey(sys
->keys
, vlc
);
488 if (caca
>= 0x20 && caca
<= 0x7f) {
489 vout_display_SendEventKey(vd
, caca
);
494 case CACA_EVENT_RESIZE
:
495 vout_display_SendEventDisplaySize(vd
, caca_get_event_resize_width(&ev
),
496 caca_get_event_resize_height(&ev
), false);
498 case CACA_EVENT_MOUSE_MOTION
: {
499 vout_display_place_t place
;
502 const unsigned x
= vd
->source
.i_x_offset
+
503 (int64_t)(caca_get_event_mouse_x(&ev
) - place
.x
) *
504 vd
->source
.i_visible_width
/ place
.width
;
505 const unsigned y
= vd
->source
.i_y_offset
+
506 (int64_t)(caca_get_event_mouse_y(&ev
) - place
.y
) *
507 vd
->source
.i_visible_height
/ place
.height
;
509 caca_set_mouse(sys
->dp
, 1);
510 vout_display_SendEventMouseMoved(vd
, x
, y
);
513 case CACA_EVENT_MOUSE_PRESS
:
514 case CACA_EVENT_MOUSE_RELEASE
: {
515 caca_set_mouse(sys
->dp
, 1);
516 const int caca
= caca_get_event_mouse_button(&ev
);
517 for (int i
= 0; mouses
[i
].caca
!= -1; i
++) {
518 if (mouses
[i
].caca
== caca
) {
519 if (caca_get_event_type(&ev
) == CACA_EVENT_MOUSE_PRESS
)
520 vout_display_SendEventMousePressed(vd
, mouses
[i
].vlc
);
522 vout_display_SendEventMouseReleased(vd
, mouses
[i
].vlc
);
528 case CACA_EVENT_QUIT
:
529 vout_display_SendEventClose(vd
);