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>
44 # ifdef X_DISPLAY_MISSING
45 # error Xlib required due to XInitThreads
47 # include <vlc_xlib.h>
50 /*****************************************************************************
52 *****************************************************************************/
53 static int Open (vlc_object_t
*);
54 static void Close(vlc_object_t
*);
56 #define CHROMA_TEXT N_("SDL chroma format")
57 #define CHROMA_LONGTEXT N_(\
58 "Force the SDL renderer to use a specific chroma format instead of " \
59 "trying to improve performances by using the most efficient one.")
63 set_category(CAT_VIDEO
)
64 set_subcategory(SUBCAT_VIDEO_VOUT
)
65 set_description(N_("Simple DirectMedia Layer video output"))
66 set_capability("vout display", 60)
68 add_string("sdl-chroma", NULL
, CHROMA_TEXT
, CHROMA_LONGTEXT
, true)
69 add_obsolete_string("sdl-video-driver") /* obsolete since 1.1.0 */
70 set_callbacks(Open
, Close
)
71 #if defined(__i386__) || defined(__x86_64__)
72 /* On i386, SDL is linked against svgalib */
73 cannot_unload_broken_library()
78 /*****************************************************************************
80 *****************************************************************************/
81 static picture_pool_t
*Pool (vout_display_t
*, unsigned);
82 static void PictureDisplay(vout_display_t
*, picture_t
*);
83 static int Control(vout_display_t
*, int, va_list);
84 static void Manage(vout_display_t
*);
87 static int ConvertKey(SDLKey
);
90 static vlc_mutex_t sdl_lock
= VLC_STATIC_MUTEX
;
93 struct vout_display_sys_t
{
94 vout_display_place_t place
;
98 uint32_t display_flags
;
100 unsigned int desktop_width
;
101 unsigned int desktop_height
;
104 SDL_Overlay
*overlay
;
108 picture_pool_t
*pool
;
112 * This function initializes SDL vout method.
114 static int Open(vlc_object_t
*object
)
116 vout_display_t
*vd
= (vout_display_t
*)object
;
117 vout_display_sys_t
*sys
;
120 if (!vlc_xlib_init (object
))
124 /* XXX: check for conflicts with the SDL audio output */
125 vlc_mutex_lock(&sdl_lock
);
127 /* Check if SDL video module has been initialized */
128 if (SDL_WasInit(SDL_INIT_VIDEO
) != 0) {
129 vlc_mutex_unlock(&sdl_lock
);
133 vd
->sys
= sys
= calloc(1, sizeof(*sys
));
135 vlc_mutex_unlock(&sdl_lock
);
140 int sdl_flags
= SDL_INIT_VIDEO
;
142 /* Win32 SDL implementation doesn't support SDL_INIT_EVENTTHREAD yet*/
143 sdl_flags
|= SDL_INIT_EVENTTHREAD
;
146 /* In debug mode you may want vlc to dump a core instead of staying stuck */
147 sdl_flags
|= SDL_INIT_NOPARACHUTE
;
150 /* Initialize library */
151 if (SDL_Init(sdl_flags
) < 0) {
152 vlc_mutex_unlock(&sdl_lock
);
154 msg_Err(vd
, "cannot initialize SDL (%s)", SDL_GetError());
158 vlc_mutex_unlock(&sdl_lock
);
160 /* Translate keys into unicode */
161 SDL_EnableUNICODE(1);
163 /* Get the desktop resolution */
164 /* FIXME: SDL has a problem with virtual desktop */
165 sys
->desktop_width
= SDL_GetVideoInfo()->current_w
;
166 sys
->desktop_height
= SDL_GetVideoInfo()->current_h
;
169 video_format_t fmt
= vd
->fmt
;
172 vout_display_info_t info
= vd
->info
;
174 /* Set main window's size */
177 if (vd
->cfg
->is_fullscreen
) {
178 display_width
= sys
->desktop_width
;
179 display_height
= sys
->desktop_height
;
181 display_width
= vd
->cfg
->display
.width
;
182 display_height
= vd
->cfg
->display
.height
;
185 /* Initialize flags and cursor */
186 sys
->display_flags
= SDL_ANYFORMAT
| SDL_HWPALETTE
| SDL_HWSURFACE
| SDL_DOUBLEBUF
;
187 sys
->display_flags
|= vd
->cfg
->is_fullscreen
? SDL_FULLSCREEN
: SDL_RESIZABLE
;
189 sys
->display_bpp
= SDL_VideoModeOK(display_width
, display_height
,
190 16, sys
->display_flags
);
191 if (sys
->display_bpp
== 0) {
192 msg_Err(vd
, "no video mode available");
195 vout_display_DeleteWindow(vd
, NULL
);
197 sys
->display
= SDL_SetVideoMode(display_width
, display_height
,
198 sys
->display_bpp
, sys
->display_flags
);
200 msg_Err(vd
, "cannot set video mode");
204 /* We keep the surface locked forever */
205 SDL_LockSurface(sys
->display
);
208 vlc_fourcc_t forced_chroma
= 0;
209 char *psz_chroma
= var_InheritString(vd
, "sdl-chroma");
211 forced_chroma
= vlc_fourcc_GetCodecFromString(VIDEO_ES
, psz_chroma
);
213 msg_Dbg(vd
, "Forcing chroma to 0x%.8x (%4.4s)",
214 forced_chroma
, (const char*)&forced_chroma
);
218 /* Try to open an overlay if requested */
220 const bool is_overlay
= var_InheritBool(vd
, "overlay");
227 { VLC_CODEC_YV12
, SDL_YV12_OVERLAY
},
228 { VLC_CODEC_I420
, SDL_IYUV_OVERLAY
},
229 { VLC_CODEC_YUYV
, SDL_YUY2_OVERLAY
},
230 { VLC_CODEC_UYVY
, SDL_UYVY_OVERLAY
},
231 { VLC_CODEC_YVYU
, SDL_YVYU_OVERLAY
},
235 const vlc_fourcc_t forced_chromas
[] = {
238 const vlc_fourcc_t
*fallback_chromas
=
239 vlc_fourcc_GetYUVFallback(fmt
.i_chroma
);
240 const vlc_fourcc_t
*chromas
= forced_chroma
? forced_chromas
: fallback_chromas
;
242 for (int pass
= forced_chroma
? 1 : 0; pass
< 2 && !sys
->overlay
; pass
++) {
243 for (int i
= 0; chromas
[i
] != 0; i
++) {
244 const vlc_fourcc_t vlc
= chromas
[i
];
247 for (int j
= 0; vlc_to_sdl
[j
].vlc
!= 0 && !sdl
; j
++) {
248 if (vlc_to_sdl
[j
].vlc
== vlc
)
249 sdl
= vlc_to_sdl
[j
].sdl
;
254 sys
->overlay
= SDL_CreateYUVOverlay(fmt
.i_width
, fmt
.i_height
,
256 if (sys
->overlay
&& !sys
->overlay
->hw_overlay
&& pass
== 0) {
257 /* Ignore non hardware overlay surface in first pass */
258 SDL_FreeYUVOverlay(sys
->overlay
);
262 /* We keep the surface locked forever */
263 SDL_LockYUVOverlay(sys
->overlay
);
266 sys
->is_uv_swapped
= vlc_fourcc_AreUVPlanesSwapped(fmt
.i_chroma
,
268 if (sys
->is_uv_swapped
)
269 fmt
.i_chroma
= vd
->fmt
.i_chroma
;
275 msg_Warn(vd
, "SDL overlay disabled by the user");
279 vout_display_cfg_t place_cfg
= *vd
->cfg
;
280 place_cfg
.display
.width
= display_width
;
281 place_cfg
.display
.height
= display_height
;
282 vout_display_PlacePicture(&sys
->place
, &vd
->source
, &place_cfg
, !sys
->overlay
);
284 /* If no overlay, fallback to software output */
287 switch (sys
->display
->format
->BitsPerPixel
) {
289 fmt
.i_chroma
= VLC_CODEC_RGB8
;
292 fmt
.i_chroma
= VLC_CODEC_RGB15
;
295 fmt
.i_chroma
= VLC_CODEC_RGB16
;
298 fmt
.i_chroma
= VLC_CODEC_RGB24
;
301 fmt
.i_chroma
= VLC_CODEC_RGB32
;
304 msg_Err(vd
, "unknown screen depth %i",
305 sys
->display
->format
->BitsPerPixel
);
309 /* All we have is an RGB image with square pixels */
310 fmt
.i_width
= display_width
;
311 fmt
.i_height
= display_height
;
312 fmt
.i_rmask
= sys
->display
->format
->Rmask
;
313 fmt
.i_gmask
= sys
->display
->format
->Gmask
;
314 fmt
.i_bmask
= sys
->display
->format
->Bmask
;
316 info
.has_pictures_invalid
= true;
319 if (vd
->cfg
->display
.title
)
320 SDL_WM_SetCaption(vd
->cfg
->display
.title
,
321 vd
->cfg
->display
.title
);
322 else if (!sys
->overlay
)
323 SDL_WM_SetCaption(VOUT_TITLE
" (software RGB SDL output)",
324 VOUT_TITLE
" (software RGB SDL output)");
325 else if (sys
->overlay
->hw_overlay
)
326 SDL_WM_SetCaption(VOUT_TITLE
" (hardware YUV SDL output)",
327 VOUT_TITLE
" (hardware YUV SDL output)");
329 SDL_WM_SetCaption(VOUT_TITLE
" (software YUV SDL output)",
330 VOUT_TITLE
" (software YUV SDL output)");
333 SDL_EventState(SDL_KEYUP
, SDL_IGNORE
); /* ignore keys up */
335 /* Setup vout_display now that everything is fine */
341 vd
->display
= PictureDisplay
;
342 vd
->control
= Control
;
346 vout_display_SendEventDisplaySize(vd
, display_width
, display_height
, vd
->cfg
->is_fullscreen
);
350 msg_Err(vd
, "cannot set up SDL (%s)", SDL_GetError());
353 SDL_UnlockSurface(sys
->display
);
354 SDL_FreeSurface(sys
->display
);
357 vlc_mutex_lock(&sdl_lock
);
358 SDL_QuitSubSystem(SDL_INIT_VIDEO
);
359 vlc_mutex_unlock(&sdl_lock
);
366 * Close a SDL video output
368 static void Close(vlc_object_t
*object
)
370 vout_display_t
*vd
= (vout_display_t
*)object
;
371 vout_display_sys_t
*sys
= vd
->sys
;
374 picture_pool_Delete(sys
->pool
);
377 SDL_LockYUVOverlay(sys
->overlay
);
378 SDL_FreeYUVOverlay(sys
->overlay
);
380 SDL_UnlockSurface (sys
->display
);
381 SDL_FreeSurface(sys
->display
);
383 vlc_mutex_lock(&sdl_lock
);
384 SDL_QuitSubSystem(SDL_INIT_VIDEO
);
385 vlc_mutex_unlock(&sdl_lock
);
391 * Return a pool of direct buffers
393 static picture_pool_t
*Pool(vout_display_t
*vd
, unsigned count
)
395 vout_display_sys_t
*sys
= vd
->sys
;
399 picture_resource_t rsc
;
401 memset(&rsc
, 0, sizeof(rsc
));
404 SDL_Overlay
*ol
= sys
->overlay
;
406 for (int i
= 0; i
< ol
->planes
; i
++) {
407 rsc
.p
[i
].p_pixels
= ol
->pixels
[ i
> 0 && sys
->is_uv_swapped
? (3-i
) : i
];
408 rsc
.p
[i
].i_pitch
= ol
->pitches
[i
> 0 && sys
->is_uv_swapped
? (3-i
) : i
];
409 rsc
.p
[i
].i_lines
= ol
->h
;
410 if (ol
->format
== SDL_YV12_OVERLAY
||
411 ol
->format
== SDL_IYUV_OVERLAY
)
412 rsc
.p
[i
].i_lines
/= 2;
416 const int x
= sys
->place
.x
;
417 const int y
= sys
->place
.y
;
419 SDL_Surface
*sf
= sys
->display
;
420 SDL_FillRect(sf
, NULL
, 0);
422 assert(x
>= 0 && y
>= 0);
423 rsc
.p
[0].p_pixels
= (uint8_t*)sf
->pixels
+ y
* sf
->pitch
+ x
* ((sf
->format
->BitsPerPixel
+ 7) / 8);
424 rsc
.p
[0].i_pitch
= sf
->pitch
;
425 rsc
.p
[0].i_lines
= vd
->fmt
.i_height
;
428 picture_t
*picture
= picture_NewFromResource(&vd
->fmt
, &rsc
);;
432 sys
->pool
= picture_pool_New(1, &picture
);
441 static void PictureDisplay(vout_display_t
*vd
, picture_t
*p_pic
)
443 vout_display_sys_t
*sys
= vd
->sys
;
447 disp
.x
= sys
->place
.x
;
448 disp
.y
= sys
->place
.y
;
449 disp
.w
= sys
->place
.width
;
450 disp
.h
= sys
->place
.height
;
452 SDL_UnlockYUVOverlay(sys
->overlay
);
453 SDL_DisplayYUVOverlay(sys
->overlay
, &disp
);
454 SDL_LockYUVOverlay(sys
->overlay
);
456 SDL_Flip(sys
->display
);
459 picture_Release(p_pic
);
464 * Control for vout display
466 static int Control(vout_display_t
*vd
, int query
, va_list args
)
468 vout_display_sys_t
*sys
= vd
->sys
;
472 case VOUT_DISPLAY_HIDE_MOUSE
:
476 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
: {
477 const vout_display_cfg_t
*cfg
= va_arg(args
, const vout_display_cfg_t
*);
480 sys
->display
= SDL_SetVideoMode(cfg
->display
.width
,
482 sys
->display_bpp
, sys
->display_flags
);
484 sys
->display
= SDL_SetVideoMode(vd
->cfg
->display
.width
,
485 vd
->cfg
->display
.height
,
486 sys
->display_bpp
, sys
->display_flags
);
490 vout_display_PlacePicture(&sys
->place
, &vd
->source
, cfg
, !sys
->overlay
);
492 vout_display_SendEventPicturesInvalid(vd
);
495 case VOUT_DISPLAY_CHANGE_FULLSCREEN
: {
496 vout_display_cfg_t cfg
= *va_arg(args
, const vout_display_cfg_t
*);
499 sys
->display_flags
&= ~(SDL_FULLSCREEN
| SDL_RESIZABLE
);
500 sys
->display_flags
|= cfg
.is_fullscreen
? SDL_FULLSCREEN
: SDL_RESIZABLE
;
502 if (cfg
.is_fullscreen
) {
503 cfg
.display
.width
= sys
->desktop_width
;
504 cfg
.display
.height
= sys
->desktop_height
;
508 sys
->display
= SDL_SetVideoMode(cfg
.display
.width
, cfg
.display
.height
,
509 sys
->display_bpp
, sys
->display_flags
);
511 vout_display_PlacePicture(&sys
->place
, &vd
->source
, &cfg
, !sys
->overlay
);
513 vout_display_SendEventDisplaySize(vd
, cfg
.display
.width
, cfg
.display
.height
, cfg
.is_fullscreen
);
516 case VOUT_DISPLAY_CHANGE_ZOOM
:
517 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED
:
518 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
: {
519 const vout_display_cfg_t
*cfg
;
520 const video_format_t
*source
;
522 if (query
== VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
) {
523 source
= va_arg(args
, const video_format_t
*);
526 source
= &vd
->source
;
527 cfg
= va_arg(args
, const vout_display_cfg_t
*);
530 sys
->display
= SDL_SetVideoMode(cfg
->display
.width
, cfg
->display
.height
,
531 sys
->display_bpp
, sys
->display_flags
);
533 vout_display_PlacePicture(&sys
->place
, source
, cfg
, !sys
->overlay
);
535 vout_display_SendEventPicturesInvalid(vd
);
540 case VOUT_DISPLAY_RESET_PICTURES
: {
542 assert(!sys
->overlay
);
546 picture_pool_Delete(sys
->pool
);
549 vout_display_PlacePicture(&sys
->place
, &vd
->source
, vd
->cfg
, !sys
->overlay
);
552 vd
->fmt
.i_width
= sys
->place
.width
;
553 vd
->fmt
.i_height
= sys
->place
.height
;
557 case VOUT_DISPLAY_CHANGE_SOURCE_CROP
:
558 case VOUT_DISPLAY_CHANGE_WINDOW_STATE
:
559 /* I don't think it is possible to support with SDL:
566 msg_Err(vd
, "Unsupported query in vout display SDL");
572 * Proccess pending event
574 static void Manage(vout_display_t
*vd
)
576 vout_display_sys_t
*sys
= vd
->sys
;
580 while (SDL_PollEvent(&event
)) {
581 switch (event
.type
) {
583 vout_display_SendEventClose(vd
);
587 /* convert the key if possible */
588 int key
= ConvertKey(event
.key
.keysym
.sym
);
591 /* Find the right caracter */
592 if ((event
.key
.keysym
.unicode
& 0xff80) == 0) {
593 key
= event
.key
.keysym
.unicode
& 0x7f;
594 /* FIXME: find a better solution than this
595 hack to find the right caracter */
596 if (key
>= 1 && key
<= 26)
598 else if (key
>= 65 && key
<= 90)
605 if (event
.key
.keysym
.mod
& KMOD_SHIFT
)
606 key
|= KEY_MODIFIER_SHIFT
;
607 if (event
.key
.keysym
.mod
& KMOD_CTRL
)
608 key
|= KEY_MODIFIER_CTRL
;
609 if (event
.key
.keysym
.mod
& KMOD_ALT
)
610 key
|= KEY_MODIFIER_ALT
;
611 vout_display_SendEventKey(vd
, key
);
615 case SDL_MOUSEBUTTONDOWN
:
616 case SDL_MOUSEBUTTONUP
: {
617 static const struct { int sdl
; int vlc
; } buttons
[] = {
618 { SDL_BUTTON_LEFT
, MOUSE_BUTTON_LEFT
},
619 { SDL_BUTTON_MIDDLE
, MOUSE_BUTTON_CENTER
},
620 { SDL_BUTTON_RIGHT
, MOUSE_BUTTON_RIGHT
},
621 { SDL_BUTTON_WHEELUP
, MOUSE_BUTTON_WHEEL_UP
},
622 { SDL_BUTTON_WHEELDOWN
, MOUSE_BUTTON_WHEEL_DOWN
},
627 for (int i
= 0; buttons
[i
].sdl
!= -1; i
++) {
628 if (buttons
[i
].sdl
== event
.button
.button
) {
629 if (event
.type
== SDL_MOUSEBUTTONDOWN
)
630 vout_display_SendEventMousePressed(vd
, buttons
[i
].vlc
);
632 vout_display_SendEventMouseReleased(vd
, buttons
[i
].vlc
);
638 case SDL_MOUSEMOTION
: {
639 if (sys
->place
.width
<= 0 || sys
->place
.height
<= 0)
642 const int x
= (int64_t)(event
.motion
.x
- sys
->place
.x
) * vd
->source
.i_width
/ sys
->place
.width
;
643 const int y
= (int64_t)(event
.motion
.y
- sys
->place
.y
) * vd
->source
.i_height
/ sys
->place
.height
;
646 vout_display_SendEventMouseMoved(vd
, x
, y
);
650 case SDL_VIDEORESIZE
:
651 vout_display_SendEventDisplaySize(vd
, event
.resize
.w
, event
.resize
.h
, vd
->cfg
->is_fullscreen
);
661 static const struct {
665 } sdlkeys_to_vlckeys
[] = {
675 { SDLK_F10
, KEY_F10
},
676 { SDLK_F11
, KEY_F11
},
677 { SDLK_F12
, KEY_F12
},
679 { SDLK_RETURN
, KEY_ENTER
},
680 { SDLK_KP_ENTER
, KEY_ENTER
},
682 { SDLK_ESCAPE
, KEY_ESC
},
684 { SDLK_MENU
, KEY_MENU
},
685 { SDLK_LEFT
, KEY_LEFT
},
686 { SDLK_RIGHT
, KEY_RIGHT
},
688 { SDLK_DOWN
, KEY_DOWN
},
690 { SDLK_HOME
, KEY_HOME
},
691 { SDLK_END
, KEY_END
},
692 { SDLK_PAGEUP
, KEY_PAGEUP
},
693 { SDLK_PAGEDOWN
, KEY_PAGEDOWN
},
695 { SDLK_INSERT
, KEY_INSERT
},
696 { SDLK_DELETE
, KEY_DELETE
},
697 /*TODO: find a equivalent for SDL
698 { , KEY_MEDIA_NEXT_TRACK }
699 { , KEY_MEDIA_PREV_TRACK }
700 { , KEY_VOLUME_MUTE }
701 { , KEY_VOLUME_DOWN }
703 { , KEY_MEDIA_PLAY_PAUSE }
704 { , KEY_MEDIA_PLAY_PAUSE }*/
709 static int ConvertKey(SDLKey sdl_key
)
711 for (int i
= 0; sdlkeys_to_vlckeys
[i
].sdl_key
!= 0; i
++) {
712 if (sdlkeys_to_vlckeys
[i
].sdl_key
== sdl_key
)
713 return sdlkeys_to_vlckeys
[i
].vlckey
;