1 /*****************************************************************************
2 * sdl.c: SDL video output display method
3 *****************************************************************************
4 * Copyright (C) 1998-2009 the VideoLAN team
7 * Authors: Samuel Hocevar <sam@zoy.org>
8 * Pierre Baillet <oct@zoy.org>
9 * Arnaud de Bossoreille de Ribou <bozo@via.ecp.fr>
10 * Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_vout_display.h>
37 #include <vlc_picture_pool.h>
38 #include "keythread.h"
45 # ifdef X_DISPLAY_MISSING
46 # error Xlib required due to XInitThreads
48 # include <vlc_xlib.h>
51 /*****************************************************************************
53 *****************************************************************************/
54 static int Open (vlc_object_t
*);
55 static void Close(vlc_object_t
*);
57 #define CHROMA_TEXT N_("SDL chroma format")
58 #define CHROMA_LONGTEXT N_(\
59 "Force the SDL renderer to use a specific chroma format instead of " \
60 "trying to improve performances by using the most efficient one.")
64 set_category(CAT_VIDEO
)
65 set_subcategory(SUBCAT_VIDEO_VOUT
)
66 set_description(N_("Simple DirectMedia Layer video output"))
67 set_capability("vout display", 60)
69 add_string("sdl-chroma", NULL
, NULL
, CHROMA_TEXT
, CHROMA_LONGTEXT
, true)
70 add_obsolete_string("sdl-video-driver") /* obsolete since 1.1.0 */
71 set_callbacks(Open
, Close
)
72 #if defined(__i386__) || defined(__x86_64__)
73 /* On i386, SDL is linked against svgalib */
74 cannot_unload_broken_library()
79 /*****************************************************************************
81 *****************************************************************************/
82 static picture_pool_t
*Pool (vout_display_t
*, unsigned);
83 static void PictureDisplay(vout_display_t
*, picture_t
*);
84 static int Control(vout_display_t
*, int, va_list);
85 static void Manage(vout_display_t
*);
88 static int ConvertKey(SDLKey
);
91 static vlc_mutex_t sdl_lock
= VLC_STATIC_MUTEX
;
94 struct vout_display_sys_t
{
95 vout_display_place_t place
;
99 uint32_t display_flags
;
101 unsigned int desktop_width
;
102 unsigned int desktop_height
;
105 SDL_Overlay
*overlay
;
109 picture_pool_t
*pool
;
114 * This function initializes SDL vout method.
116 static int Open(vlc_object_t
*object
)
118 vout_display_t
*vd
= (vout_display_t
*)object
;
119 vout_display_sys_t
*sys
;
122 if (!vlc_xlib_init (object
))
126 /* XXX: check for conflicts with the SDL audio output */
127 vlc_mutex_lock(&sdl_lock
);
129 /* Check if SDL video module has been initialized */
130 if (SDL_WasInit(SDL_INIT_VIDEO
) != 0) {
131 vlc_mutex_unlock(&sdl_lock
);
135 vd
->sys
= sys
= calloc(1, sizeof(*sys
));
137 vlc_mutex_unlock(&sdl_lock
);
142 int sdl_flags
= SDL_INIT_VIDEO
;
144 /* Win32 SDL implementation doesn't support SDL_INIT_EVENTTHREAD yet*/
145 sdl_flags
|= SDL_INIT_EVENTTHREAD
;
148 /* In debug mode you may want vlc to dump a core instead of staying stuck */
149 sdl_flags
|= SDL_INIT_NOPARACHUTE
;
152 /* Initialize library */
153 if (SDL_Init(sdl_flags
) < 0) {
154 vlc_mutex_unlock(&sdl_lock
);
156 msg_Err(vd
, "cannot initialize SDL (%s)", SDL_GetError());
160 vlc_mutex_unlock(&sdl_lock
);
162 /* Translate keys into unicode */
163 SDL_EnableUNICODE(1);
165 /* Get the desktop resolution */
166 /* FIXME: SDL has a problem with virtual desktop */
167 sys
->desktop_width
= SDL_GetVideoInfo()->current_w
;
168 sys
->desktop_height
= SDL_GetVideoInfo()->current_h
;
171 video_format_t fmt
= vd
->fmt
;
174 vout_display_info_t info
= vd
->info
;
176 /* Set main window's size */
179 if (vd
->cfg
->is_fullscreen
) {
180 display_width
= sys
->desktop_width
;
181 display_height
= sys
->desktop_height
;
183 display_width
= vd
->cfg
->display
.width
;
184 display_height
= vd
->cfg
->display
.height
;
187 /* Initialize flags and cursor */
188 sys
->display_flags
= SDL_ANYFORMAT
| SDL_HWPALETTE
| SDL_HWSURFACE
| SDL_DOUBLEBUF
;
189 sys
->display_flags
|= vd
->cfg
->is_fullscreen
? SDL_FULLSCREEN
: SDL_RESIZABLE
;
191 sys
->display_bpp
= SDL_VideoModeOK(display_width
, display_height
,
192 16, sys
->display_flags
);
193 if (sys
->display_bpp
== 0) {
194 msg_Err(vd
, "no video mode available");
198 sys
->display
= SDL_SetVideoMode(display_width
, display_height
,
199 sys
->display_bpp
, sys
->display_flags
);
201 msg_Err(vd
, "cannot set video mode");
205 /* We keep the surface locked forever */
206 SDL_LockSurface(sys
->display
);
209 vlc_fourcc_t forced_chroma
= 0;
210 char *psz_chroma
= var_CreateGetNonEmptyString(vd
, "sdl-chroma");
212 forced_chroma
= vlc_fourcc_GetCodecFromString(VIDEO_ES
, psz_chroma
);
214 msg_Dbg(vd
, "Forcing chroma to 0x%.8x (%4.4s)",
215 forced_chroma
, (const char*)&forced_chroma
);
219 /* Try to open an overlay if requested */
221 const bool is_overlay
= var_CreateGetBool(vd
, "overlay");
228 { VLC_CODEC_YV12
, SDL_YV12_OVERLAY
},
229 { VLC_CODEC_I420
, SDL_IYUV_OVERLAY
},
230 { VLC_CODEC_YUYV
, SDL_YUY2_OVERLAY
},
231 { VLC_CODEC_UYVY
, SDL_UYVY_OVERLAY
},
232 { VLC_CODEC_YVYU
, SDL_YVYU_OVERLAY
},
236 const vlc_fourcc_t forced_chromas
[] = {
239 const vlc_fourcc_t
*fallback_chromas
=
240 vlc_fourcc_GetYUVFallback(fmt
.i_chroma
);
241 const vlc_fourcc_t
*chromas
= forced_chroma
? forced_chromas
: fallback_chromas
;
243 for (int pass
= forced_chroma
? 1 : 0; pass
< 2 && !sys
->overlay
; pass
++) {
244 for (int i
= 0; chromas
[i
] != 0; i
++) {
245 const vlc_fourcc_t vlc
= chromas
[i
];
248 for (int j
= 0; vlc_to_sdl
[j
].vlc
!= 0 && !sdl
; j
++) {
249 if (vlc_to_sdl
[j
].vlc
== vlc
)
250 sdl
= vlc_to_sdl
[j
].sdl
;
255 sys
->overlay
= SDL_CreateYUVOverlay(fmt
.i_width
, fmt
.i_height
,
257 if (sys
->overlay
&& !sys
->overlay
->hw_overlay
&& pass
== 0) {
258 /* Ignore non hardware overlay surface in first pass */
259 SDL_FreeYUVOverlay(sys
->overlay
);
263 /* We keep the surface locked forever */
264 SDL_LockYUVOverlay(sys
->overlay
);
267 sys
->is_uv_swapped
= vlc_fourcc_AreUVPlanesSwapped(fmt
.i_chroma
,
269 if (sys
->is_uv_swapped
)
270 fmt
.i_chroma
= vd
->fmt
.i_chroma
;
276 msg_Warn(vd
, "SDL overlay disabled by the user");
280 vout_display_cfg_t place_cfg
= *vd
->cfg
;
281 place_cfg
.display
.width
= display_width
;
282 place_cfg
.display
.height
= display_height
;
283 vout_display_PlacePicture(&sys
->place
, &vd
->source
, &place_cfg
, !sys
->overlay
);
285 /* If no overlay, fallback to software output */
288 switch (sys
->display
->format
->BitsPerPixel
) {
290 fmt
.i_chroma
= VLC_CODEC_RGB8
;
293 fmt
.i_chroma
= VLC_CODEC_RGB15
;
296 fmt
.i_chroma
= VLC_CODEC_RGB16
;
299 fmt
.i_chroma
= VLC_CODEC_RGB24
;
302 fmt
.i_chroma
= VLC_CODEC_RGB32
;
305 msg_Err(vd
, "unknown screen depth %i",
306 sys
->display
->format
->BitsPerPixel
);
310 /* All we have is an RGB image with square pixels */
311 fmt
.i_width
= display_width
;
312 fmt
.i_height
= display_height
;
313 fmt
.i_rmask
= sys
->display
->format
->Rmask
;
314 fmt
.i_gmask
= sys
->display
->format
->Gmask
;
315 fmt
.i_bmask
= sys
->display
->format
->Bmask
;
317 info
.has_pictures_invalid
= true;
320 if (vd
->cfg
->display
.title
)
321 SDL_WM_SetCaption(vd
->cfg
->display
.title
,
322 vd
->cfg
->display
.title
);
323 else if (!sys
->overlay
)
324 SDL_WM_SetCaption(VOUT_TITLE
" (software RGB SDL output)",
325 VOUT_TITLE
" (software RGB SDL output)");
326 else if (sys
->overlay
->hw_overlay
)
327 SDL_WM_SetCaption(VOUT_TITLE
" (hardware YUV SDL output)",
328 VOUT_TITLE
" (hardware YUV SDL output)");
330 SDL_WM_SetCaption(VOUT_TITLE
" (software YUV SDL output)",
331 VOUT_TITLE
" (software YUV SDL output)");
334 SDL_EventState(SDL_KEYUP
, SDL_IGNORE
); /* ignore keys up */
336 /* Setup vout_display now that everything is fine */
342 vd
->display
= PictureDisplay
;
343 vd
->control
= Control
;
347 vout_display_SendEventDisplaySize(vd
, display_width
, display_height
, vd
->cfg
->is_fullscreen
);
349 sys
->keys
= vlc_CreateKeyThread (vd
);
353 msg_Err(vd
, "cannot set up SDL (%s)", SDL_GetError());
356 SDL_UnlockSurface(sys
->display
);
357 SDL_FreeSurface(sys
->display
);
360 vlc_mutex_lock(&sdl_lock
);
361 SDL_QuitSubSystem(SDL_INIT_VIDEO
);
362 vlc_mutex_unlock(&sdl_lock
);
369 * Close a SDL video output
371 static void Close(vlc_object_t
*object
)
373 vout_display_t
*vd
= (vout_display_t
*)object
;
374 vout_display_sys_t
*sys
= vd
->sys
;
376 vlc_DestroyKeyThread(sys
->keys
);
379 picture_pool_Delete(sys
->pool
);
382 SDL_LockYUVOverlay(sys
->overlay
);
383 SDL_FreeYUVOverlay(sys
->overlay
);
385 SDL_UnlockSurface (sys
->display
);
386 SDL_FreeSurface(sys
->display
);
388 vlc_mutex_lock(&sdl_lock
);
389 SDL_QuitSubSystem(SDL_INIT_VIDEO
);
390 vlc_mutex_unlock(&sdl_lock
);
396 * Return a pool of direct buffers
398 static picture_pool_t
*Pool(vout_display_t
*vd
, unsigned count
)
400 vout_display_sys_t
*sys
= vd
->sys
;
404 picture_resource_t rsc
;
406 memset(&rsc
, 0, sizeof(rsc
));
409 SDL_Overlay
*ol
= sys
->overlay
;
411 for (int i
= 0; i
< ol
->planes
; i
++) {
412 rsc
.p
[i
].p_pixels
= ol
->pixels
[ i
> 0 && sys
->is_uv_swapped
? (3-i
) : i
];
413 rsc
.p
[i
].i_pitch
= ol
->pitches
[i
> 0 && sys
->is_uv_swapped
? (3-i
) : i
];
414 rsc
.p
[i
].i_lines
= ol
->h
;
415 if (ol
->format
== SDL_YV12_OVERLAY
||
416 ol
->format
== SDL_IYUV_OVERLAY
)
417 rsc
.p
[i
].i_lines
/= 2;
421 const int x
= sys
->place
.x
;
422 const int y
= sys
->place
.y
;
424 SDL_Surface
*sf
= sys
->display
;
425 SDL_FillRect(sf
, NULL
, 0);
427 assert(x
>= 0 && y
>= 0);
428 rsc
.p
[0].p_pixels
= (uint8_t*)sf
->pixels
+ y
* sf
->pitch
+ x
* ((sf
->format
->BitsPerPixel
+ 7) / 8);
429 rsc
.p
[0].i_pitch
= sf
->pitch
;
430 rsc
.p
[0].i_lines
= vd
->fmt
.i_height
;
433 picture_t
*picture
= picture_NewFromResource(&vd
->fmt
, &rsc
);;
437 sys
->pool
= picture_pool_New(1, &picture
);
446 static void PictureDisplay(vout_display_t
*vd
, picture_t
*p_pic
)
448 vout_display_sys_t
*sys
= vd
->sys
;
452 disp
.x
= sys
->place
.x
;
453 disp
.y
= sys
->place
.y
;
454 disp
.w
= sys
->place
.width
;
455 disp
.h
= sys
->place
.height
;
457 SDL_UnlockYUVOverlay(sys
->overlay
);
458 SDL_DisplayYUVOverlay(sys
->overlay
, &disp
);
459 SDL_LockYUVOverlay(sys
->overlay
);
461 SDL_Flip(sys
->display
);
464 picture_Release(p_pic
);
469 * Control for vout display
471 static int Control(vout_display_t
*vd
, int query
, va_list args
)
473 vout_display_sys_t
*sys
= vd
->sys
;
477 case VOUT_DISPLAY_HIDE_MOUSE
:
481 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
: {
482 const vout_display_cfg_t
*cfg
= va_arg(args
, const vout_display_cfg_t
*);
485 sys
->display
= SDL_SetVideoMode(cfg
->display
.width
,
487 sys
->display_bpp
, sys
->display_flags
);
489 sys
->display
= SDL_SetVideoMode(vd
->cfg
->display
.width
,
490 vd
->cfg
->display
.height
,
491 sys
->display_bpp
, sys
->display_flags
);
495 vout_display_PlacePicture(&sys
->place
, &vd
->source
, cfg
, !sys
->overlay
);
497 vout_display_SendEventPicturesInvalid(vd
);
500 case VOUT_DISPLAY_CHANGE_FULLSCREEN
: {
501 vout_display_cfg_t cfg
= *va_arg(args
, const vout_display_cfg_t
*);
504 sys
->display_flags
&= ~(SDL_FULLSCREEN
| SDL_RESIZABLE
);
505 sys
->display_flags
|= cfg
.is_fullscreen
? SDL_FULLSCREEN
: SDL_RESIZABLE
;
507 if (cfg
.is_fullscreen
) {
508 cfg
.display
.width
= sys
->desktop_width
;
509 cfg
.display
.height
= sys
->desktop_height
;
513 sys
->display
= SDL_SetVideoMode(cfg
.display
.width
, cfg
.display
.height
,
514 sys
->display_bpp
, sys
->display_flags
);
516 vout_display_PlacePicture(&sys
->place
, &vd
->source
, &cfg
, !sys
->overlay
);
518 vout_display_SendEventDisplaySize(vd
, cfg
.display
.width
, cfg
.display
.height
, cfg
.is_fullscreen
);
521 case VOUT_DISPLAY_CHANGE_ZOOM
:
522 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED
:
523 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
: {
524 const vout_display_cfg_t
*cfg
;
525 const video_format_t
*source
;
527 if (query
== VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
) {
528 source
= va_arg(args
, const video_format_t
*);
531 source
= &vd
->source
;
532 cfg
= va_arg(args
, const vout_display_cfg_t
*);
535 sys
->display
= SDL_SetVideoMode(cfg
->display
.width
, cfg
->display
.height
,
536 sys
->display_bpp
, sys
->display_flags
);
538 vout_display_PlacePicture(&sys
->place
, source
, cfg
, !sys
->overlay
);
540 vout_display_SendEventPicturesInvalid(vd
);
545 case VOUT_DISPLAY_RESET_PICTURES
: {
547 assert(!sys
->overlay
);
551 picture_pool_Delete(sys
->pool
);
554 vout_display_PlacePicture(&sys
->place
, &vd
->source
, vd
->cfg
, !sys
->overlay
);
557 vd
->fmt
.i_width
= sys
->place
.width
;
558 vd
->fmt
.i_height
= sys
->place
.height
;
562 case VOUT_DISPLAY_CHANGE_SOURCE_CROP
:
563 case VOUT_DISPLAY_CHANGE_WINDOW_STATE
:
564 /* I don't think it is possible to support with SDL:
571 msg_Err(vd
, "Unsupported query in vout display SDL");
577 * Proccess pending event
579 static void Manage(vout_display_t
*vd
)
581 vout_display_sys_t
*sys
= vd
->sys
;
585 while (SDL_PollEvent(&event
)) {
586 switch (event
.type
) {
588 vout_display_SendEventClose(vd
);
592 /* convert the key if possible */
593 int key
= ConvertKey(event
.key
.keysym
.sym
);
596 /* Find the right caracter */
597 if ((event
.key
.keysym
.unicode
& 0xff80) == 0) {
598 key
= event
.key
.keysym
.unicode
& 0x7f;
599 /* FIXME: find a better solution than this
600 hack to find the right caracter */
601 if (key
>= 1 && key
<= 26)
603 else if (key
>= 65 && key
<= 90)
610 if (event
.key
.keysym
.mod
& KMOD_SHIFT
)
611 key
|= KEY_MODIFIER_SHIFT
;
612 if (event
.key
.keysym
.mod
& KMOD_CTRL
)
613 key
|= KEY_MODIFIER_CTRL
;
614 if (event
.key
.keysym
.mod
& KMOD_ALT
)
615 key
|= KEY_MODIFIER_ALT
;
616 vlc_EmitKey(sys
->keys
, key
);
620 case SDL_MOUSEBUTTONDOWN
:
621 case SDL_MOUSEBUTTONUP
: {
622 static const struct { int sdl
; int vlc
; } buttons
[] = {
623 { SDL_BUTTON_LEFT
, MOUSE_BUTTON_LEFT
},
624 { SDL_BUTTON_MIDDLE
, MOUSE_BUTTON_CENTER
},
625 { SDL_BUTTON_RIGHT
, MOUSE_BUTTON_RIGHT
},
626 { SDL_BUTTON_WHEELUP
, MOUSE_BUTTON_WHEEL_UP
},
627 { SDL_BUTTON_WHEELDOWN
, MOUSE_BUTTON_WHEEL_DOWN
},
632 for (int i
= 0; buttons
[i
].sdl
!= -1; i
++) {
633 if (buttons
[i
].sdl
== event
.button
.button
) {
634 if (event
.type
== SDL_MOUSEBUTTONDOWN
)
635 vout_display_SendEventMousePressed(vd
, buttons
[i
].vlc
);
637 vout_display_SendEventMouseReleased(vd
, buttons
[i
].vlc
);
643 case SDL_MOUSEMOTION
: {
644 if (sys
->place
.width
<= 0 || sys
->place
.height
<= 0)
647 const int x
= (int64_t)(event
.motion
.x
- sys
->place
.x
) * vd
->source
.i_width
/ sys
->place
.width
;
648 const int y
= (int64_t)(event
.motion
.y
- sys
->place
.y
) * vd
->source
.i_height
/ sys
->place
.height
;
651 vout_display_SendEventMouseMoved(vd
, x
, y
);
655 case SDL_VIDEORESIZE
:
656 vout_display_SendEventDisplaySize(vd
, event
.resize
.w
, event
.resize
.h
, vd
->cfg
->is_fullscreen
);
666 static const struct {
670 } sdlkeys_to_vlckeys
[] = {
680 { SDLK_F10
, KEY_F10
},
681 { SDLK_F11
, KEY_F11
},
682 { SDLK_F12
, KEY_F12
},
684 { SDLK_RETURN
, KEY_ENTER
},
685 { SDLK_KP_ENTER
, KEY_ENTER
},
687 { SDLK_ESCAPE
, KEY_ESC
},
689 { SDLK_MENU
, KEY_MENU
},
690 { SDLK_LEFT
, KEY_LEFT
},
691 { SDLK_RIGHT
, KEY_RIGHT
},
693 { SDLK_DOWN
, KEY_DOWN
},
695 { SDLK_HOME
, KEY_HOME
},
696 { SDLK_END
, KEY_END
},
697 { SDLK_PAGEUP
, KEY_PAGEUP
},
698 { SDLK_PAGEDOWN
, KEY_PAGEDOWN
},
700 { SDLK_INSERT
, KEY_INSERT
},
701 { SDLK_DELETE
, KEY_DELETE
},
702 /*TODO: find a equivalent for SDL
703 { , KEY_MEDIA_NEXT_TRACK }
704 { , KEY_MEDIA_PREV_TRACK }
705 { , KEY_VOLUME_MUTE }
706 { , KEY_VOLUME_DOWN }
708 { , KEY_MEDIA_PLAY_PAUSE }
709 { , KEY_MEDIA_PLAY_PAUSE }*/
714 static int ConvertKey(SDLKey sdl_key
)
716 for (int i
= 0; sdlkeys_to_vlckeys
[i
].sdl_key
!= 0; i
++) {
717 if (sdlkeys_to_vlckeys
[i
].sdl_key
== sdl_key
)
718 return sdlkeys_to_vlckeys
[i
].vlckey
;