1 /*****************************************************************************
2 * sdl.c: SDL video output display method
3 *****************************************************************************
4 * Copyright (C) 1998-2009 VLC authors and VideoLAN
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 it
13 * under the terms of the GNU Lesser General Public License as published by
14 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public License
23 * along with this program; if not, write to the Free Software Foundation,
24 * 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>
43 #if !defined(_WIN32) && !defined(__OS2__)
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.")
61 #define OVERLAY_TEXT N_("YUV overlay")
62 #define OVERLAY_LONGTEXT N_(\
63 "Use the hardware YUV overlay of the graphic card (if available).")
67 set_category(CAT_VIDEO
)
68 set_subcategory(SUBCAT_VIDEO_VOUT
)
69 set_description(N_("Simple DirectMedia Layer video output"))
70 set_capability("vout display", 70)
72 add_bool("sdl-overlay", true, OVERLAY_TEXT
, OVERLAY_LONGTEXT
, false)
73 add_string("sdl-chroma", NULL
, CHROMA_TEXT
, CHROMA_LONGTEXT
, true)
74 add_obsolete_string("sdl-video-driver") /* obsolete since 1.1.0 */
75 set_callbacks(Open
, Close
)
76 #if defined(__i386__) || defined(__x86_64__)
77 /* On i386, SDL is linked against svgalib */
78 cannot_unload_broken_library()
83 /*****************************************************************************
85 *****************************************************************************/
86 static picture_pool_t
*Pool (vout_display_t
*, unsigned);
87 static void PictureDisplay(vout_display_t
*, picture_t
*, subpicture_t
*);
88 static int Control(vout_display_t
*, int, va_list);
89 static void Manage(vout_display_t
*);
92 static int ConvertKey(SDLKey
);
95 static vlc_mutex_t sdl_lock
= VLC_STATIC_MUTEX
;
98 struct vout_display_sys_t
{
99 vout_display_place_t place
;
101 SDL_Surface
*display
;
103 uint32_t display_flags
;
105 unsigned int desktop_width
;
106 unsigned int desktop_height
;
109 SDL_Overlay
*overlay
;
113 picture_pool_t
*pool
;
117 * This function initializes SDL vout method.
119 static int Open(vlc_object_t
*object
)
121 vout_display_t
*vd
= (vout_display_t
*)object
;
122 vout_display_sys_t
*sys
;
124 if (vout_display_IsWindowed(vd
))
126 #if !defined(_WIN32) && !defined(__OS2__)
127 if (!vlc_xlib_init (object
))
131 /* XXX: check for conflicts with the SDL audio output */
132 vlc_mutex_lock(&sdl_lock
);
134 /* Check if SDL video module has been initialized */
135 if (SDL_WasInit(SDL_INIT_VIDEO
) != 0) {
136 vlc_mutex_unlock(&sdl_lock
);
140 vd
->sys
= sys
= calloc(1, sizeof(*sys
));
142 vlc_mutex_unlock(&sdl_lock
);
147 int sdl_flags
= SDL_INIT_VIDEO
;
149 /* Win32 SDL implementation doesn't support SDL_INIT_EVENTTHREAD yet*/
150 sdl_flags
|= SDL_INIT_EVENTTHREAD
;
152 /* In debug mode you may want vlc to dump a core instead of staying stuck */
153 sdl_flags
|= SDL_INIT_NOPARACHUTE
;
155 /* Initialize library */
156 if (SDL_Init(sdl_flags
) < 0) {
157 vlc_mutex_unlock(&sdl_lock
);
159 msg_Err(vd
, "cannot initialize SDL (%s)", SDL_GetError());
163 vlc_mutex_unlock(&sdl_lock
);
165 /* Translate keys into unicode */
166 SDL_EnableUNICODE(1);
168 /* Get the desktop resolution */
169 /* FIXME: SDL has a problem with virtual desktop */
170 sys
->desktop_width
= SDL_GetVideoInfo()->current_w
;
171 sys
->desktop_height
= SDL_GetVideoInfo()->current_h
;
175 video_format_ApplyRotation(&fmt
, &vd
->fmt
);
178 vout_display_info_t info
= vd
->info
;
180 info
.needs_event_thread
= true;
182 /* Set main window's size */
185 if (vd
->cfg
->is_fullscreen
) {
186 display_width
= sys
->desktop_width
;
187 display_height
= sys
->desktop_height
;
189 display_width
= vd
->cfg
->display
.width
;
190 display_height
= vd
->cfg
->display
.height
;
193 /* Initialize flags and cursor */
194 sys
->display_flags
= SDL_ANYFORMAT
| SDL_HWPALETTE
| SDL_HWSURFACE
| SDL_DOUBLEBUF
;
195 sys
->display_flags
|= vd
->cfg
->is_fullscreen
? SDL_FULLSCREEN
: SDL_RESIZABLE
;
197 sys
->display_bpp
= SDL_VideoModeOK(display_width
, display_height
,
198 16, sys
->display_flags
);
199 if (sys
->display_bpp
== 0) {
200 msg_Err(vd
, "no video mode available");
204 sys
->display
= SDL_SetVideoMode(display_width
, display_height
,
205 sys
->display_bpp
, sys
->display_flags
);
207 msg_Err(vd
, "cannot set video mode");
211 /* We keep the surface locked forever */
212 SDL_LockSurface(sys
->display
);
215 vlc_fourcc_t forced_chroma
= 0;
216 char *psz_chroma
= var_InheritString(vd
, "sdl-chroma");
218 forced_chroma
= vlc_fourcc_GetCodecFromString(VIDEO_ES
, psz_chroma
);
220 msg_Dbg(vd
, "Forcing chroma to 0x%.8x (%4.4s)",
221 forced_chroma
, (const char*)&forced_chroma
);
225 /* Try to open an overlay if requested */
227 const bool is_overlay
= var_InheritBool(vd
, "sdl-overlay");
234 { VLC_CODEC_YV12
, SDL_YV12_OVERLAY
},
235 { VLC_CODEC_I420
, SDL_IYUV_OVERLAY
},
236 { VLC_CODEC_YUYV
, SDL_YUY2_OVERLAY
},
237 { VLC_CODEC_UYVY
, SDL_UYVY_OVERLAY
},
238 { VLC_CODEC_YVYU
, SDL_YVYU_OVERLAY
},
242 const vlc_fourcc_t forced_chromas
[] = {
245 const vlc_fourcc_t
*fallback_chromas
=
246 vlc_fourcc_GetYUVFallback(fmt
.i_chroma
);
247 const vlc_fourcc_t
*chromas
= forced_chroma
? forced_chromas
: fallback_chromas
;
249 for (int pass
= forced_chroma
? 1 : 0; pass
< 2 && !sys
->overlay
; pass
++) {
250 for (int i
= 0; chromas
[i
] != 0; i
++) {
251 const vlc_fourcc_t vlc
= chromas
[i
];
254 for (int j
= 0; vlc_to_sdl
[j
].vlc
!= 0 && !sdl
; j
++) {
255 if (vlc_to_sdl
[j
].vlc
== vlc
)
256 sdl
= vlc_to_sdl
[j
].sdl
;
261 sys
->overlay
= SDL_CreateYUVOverlay(fmt
.i_width
, fmt
.i_height
,
263 if (sys
->overlay
&& !sys
->overlay
->hw_overlay
&& pass
== 0) {
264 /* Ignore non hardware overlay surface in first pass */
265 SDL_FreeYUVOverlay(sys
->overlay
);
269 /* We keep the surface locked forever */
270 SDL_LockYUVOverlay(sys
->overlay
);
273 sys
->is_uv_swapped
= vlc_fourcc_AreUVPlanesSwapped(fmt
.i_chroma
,
275 if (sys
->is_uv_swapped
)
276 fmt
.i_chroma
= vd
->fmt
.i_chroma
;
282 msg_Warn(vd
, "SDL overlay disabled by the user");
286 vout_display_cfg_t place_cfg
= *vd
->cfg
;
287 place_cfg
.display
.width
= display_width
;
288 place_cfg
.display
.height
= display_height
;
289 vout_display_PlacePicture(&sys
->place
, &vd
->source
, &place_cfg
, !sys
->overlay
);
291 /* If no overlay, fallback to software output */
294 switch (sys
->display
->format
->BitsPerPixel
) {
296 fmt
.i_chroma
= VLC_CODEC_RGB8
;
299 fmt
.i_chroma
= VLC_CODEC_RGB15
;
302 fmt
.i_chroma
= VLC_CODEC_RGB16
;
305 fmt
.i_chroma
= VLC_CODEC_RGB24
;
308 fmt
.i_chroma
= VLC_CODEC_RGB32
;
311 msg_Err(vd
, "unknown screen depth %i",
312 sys
->display
->format
->BitsPerPixel
);
316 /* All we have is an RGB image with square pixels */
317 fmt
.i_width
= display_width
;
318 fmt
.i_height
= display_height
;
319 fmt
.i_rmask
= sys
->display
->format
->Rmask
;
320 fmt
.i_gmask
= sys
->display
->format
->Gmask
;
321 fmt
.i_bmask
= sys
->display
->format
->Bmask
;
323 info
.has_pictures_invalid
= true;
326 if (vd
->cfg
->display
.title
)
327 SDL_WM_SetCaption(vd
->cfg
->display
.title
,
328 vd
->cfg
->display
.title
);
329 else if (!sys
->overlay
)
330 SDL_WM_SetCaption(VOUT_TITLE
" (software RGB SDL output)",
331 VOUT_TITLE
" (software RGB SDL output)");
332 else if (sys
->overlay
->hw_overlay
)
333 SDL_WM_SetCaption(VOUT_TITLE
" (hardware YUV SDL output)",
334 VOUT_TITLE
" (hardware YUV SDL output)");
336 SDL_WM_SetCaption(VOUT_TITLE
" (software YUV SDL output)",
337 VOUT_TITLE
" (software YUV SDL output)");
340 SDL_EventState(SDL_KEYUP
, SDL_IGNORE
); /* ignore keys up */
342 /* Setup vout_display now that everything is fine */
348 vd
->display
= PictureDisplay
;
349 vd
->control
= Control
;
353 vout_display_SendEventDisplaySize(vd
, display_width
, display_height
);
357 msg_Err(vd
, "cannot set up SDL (%s)", SDL_GetError());
360 SDL_UnlockSurface(sys
->display
);
361 SDL_FreeSurface(sys
->display
);
364 vlc_mutex_lock(&sdl_lock
);
365 SDL_QuitSubSystem(SDL_INIT_VIDEO
);
366 vlc_mutex_unlock(&sdl_lock
);
373 * Close a SDL video output
375 static void Close(vlc_object_t
*object
)
377 vout_display_t
*vd
= (vout_display_t
*)object
;
378 vout_display_sys_t
*sys
= vd
->sys
;
381 picture_pool_Release(sys
->pool
);
384 SDL_LockYUVOverlay(sys
->overlay
);
385 SDL_FreeYUVOverlay(sys
->overlay
);
387 SDL_UnlockSurface (sys
->display
);
388 SDL_FreeSurface(sys
->display
);
390 vlc_mutex_lock(&sdl_lock
);
391 SDL_QuitSubSystem(SDL_INIT_VIDEO
);
392 vlc_mutex_unlock(&sdl_lock
);
398 * Return a pool of direct buffers
400 static picture_pool_t
*Pool(vout_display_t
*vd
, unsigned count
)
402 vout_display_sys_t
*sys
= vd
->sys
;
406 picture_resource_t rsc
;
408 memset(&rsc
, 0, sizeof(rsc
));
411 SDL_Overlay
*ol
= sys
->overlay
;
413 for (int i
= 0; i
< ol
->planes
; i
++) {
414 rsc
.p
[i
].p_pixels
= ol
->pixels
[ i
> 0 && sys
->is_uv_swapped
? (3-i
) : i
];
415 rsc
.p
[i
].i_pitch
= ol
->pitches
[i
> 0 && sys
->is_uv_swapped
? (3-i
) : i
];
416 rsc
.p
[i
].i_lines
= ol
->h
;
417 if (ol
->format
== SDL_YV12_OVERLAY
||
418 ol
->format
== SDL_IYUV_OVERLAY
)
419 rsc
.p
[i
].i_lines
/= 2;
423 const int x
= sys
->place
.x
;
424 const int y
= sys
->place
.y
;
426 SDL_Surface
*sf
= sys
->display
;
427 SDL_FillRect(sf
, NULL
, 0);
429 assert(x
>= 0 && y
>= 0);
430 rsc
.p
[0].p_pixels
= (uint8_t*)sf
->pixels
+ y
* sf
->pitch
+ x
* ((sf
->format
->BitsPerPixel
+ 7) / 8);
431 rsc
.p
[0].i_pitch
= sf
->pitch
;
432 rsc
.p
[0].i_lines
= vd
->fmt
.i_height
;
435 picture_t
*picture
= picture_NewFromResource(&vd
->fmt
, &rsc
);;
439 sys
->pool
= picture_pool_New(1, &picture
);
448 static void PictureDisplay(vout_display_t
*vd
, picture_t
*p_pic
, subpicture_t
*p_subpicture
)
450 vout_display_sys_t
*sys
= vd
->sys
;
454 disp
.x
= sys
->place
.x
;
455 disp
.y
= sys
->place
.y
;
456 disp
.w
= sys
->place
.width
;
457 disp
.h
= sys
->place
.height
;
459 SDL_UnlockYUVOverlay(sys
->overlay
);
460 SDL_DisplayYUVOverlay(sys
->overlay
, &disp
);
461 SDL_LockYUVOverlay(sys
->overlay
);
463 SDL_Flip(sys
->display
);
466 picture_Release(p_pic
);
467 VLC_UNUSED(p_subpicture
);
472 * Control for vout display
474 static int Control(vout_display_t
*vd
, int query
, va_list args
)
476 vout_display_sys_t
*sys
= vd
->sys
;
480 case VOUT_DISPLAY_HIDE_MOUSE
:
484 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE
: {
485 const vout_display_cfg_t
*cfg
= va_arg(args
, const vout_display_cfg_t
*);
488 sys
->display
= SDL_SetVideoMode(cfg
->display
.width
,
490 sys
->display_bpp
, sys
->display_flags
);
492 sys
->display
= SDL_SetVideoMode(vd
->cfg
->display
.width
,
493 vd
->cfg
->display
.height
,
494 sys
->display_bpp
, sys
->display_flags
);
498 vout_display_PlacePicture(&sys
->place
, &vd
->source
, cfg
, !sys
->overlay
);
500 vout_display_SendEventPicturesInvalid(vd
);
503 case VOUT_DISPLAY_CHANGE_FULLSCREEN
: {
504 bool fs
= va_arg(args
, int);
507 sys
->display_flags
&= ~(SDL_FULLSCREEN
| SDL_RESIZABLE
);
508 sys
->display_flags
|= fs
? SDL_FULLSCREEN
: SDL_RESIZABLE
;
511 sys
->display
= SDL_SetVideoMode(sys
->desktop_width
, sys
->desktop_height
,
512 sys
->display_bpp
, sys
->display_flags
);
514 vout_display_SendEventDisplaySize(vd
, sys
->desktop_width
, sys
->desktop_height
);
517 case VOUT_DISPLAY_CHANGE_ZOOM
:
518 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED
:
519 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
: {
520 const vout_display_cfg_t
*cfg
;
521 const video_format_t
*source
;
523 if (query
== VOUT_DISPLAY_CHANGE_SOURCE_ASPECT
) {
524 source
= va_arg(args
, const video_format_t
*);
527 source
= &vd
->source
;
528 cfg
= va_arg(args
, const vout_display_cfg_t
*);
531 sys
->display
= SDL_SetVideoMode(cfg
->display
.width
, cfg
->display
.height
,
532 sys
->display_bpp
, sys
->display_flags
);
534 vout_display_PlacePicture(&sys
->place
, source
, cfg
, !sys
->overlay
);
536 vout_display_SendEventPicturesInvalid(vd
);
541 case VOUT_DISPLAY_RESET_PICTURES
: {
543 assert(!sys
->overlay
);
547 picture_pool_Release(sys
->pool
);
550 vout_display_PlacePicture(&sys
->place
, &vd
->source
, vd
->cfg
, !sys
->overlay
);
553 vd
->fmt
.i_width
= sys
->place
.width
;
554 vd
->fmt
.i_height
= sys
->place
.height
;
558 case VOUT_DISPLAY_CHANGE_SOURCE_CROP
:
559 /* I don't think it is possible to support with SDL:
565 msg_Err(vd
, "Unsupported query in vout display SDL");
571 * Proccess pending event
573 static void Manage(vout_display_t
*vd
)
575 vout_display_sys_t
*sys
= vd
->sys
;
579 while (SDL_PollEvent(&event
)) {
580 switch (event
.type
) {
582 vout_display_SendEventClose(vd
);
586 /* convert the key if possible */
587 int key
= ConvertKey(event
.key
.keysym
.sym
);
590 /* Find the right caracter */
591 if ((event
.key
.keysym
.unicode
& 0xff80) == 0) {
592 key
= event
.key
.keysym
.unicode
& 0x7f;
593 /* FIXME: find a better solution than this
594 hack to find the right caracter */
595 if (key
>= 1 && key
<= 26)
597 else if (key
>= 65 && key
<= 90)
604 if (event
.key
.keysym
.mod
& KMOD_SHIFT
)
605 key
|= KEY_MODIFIER_SHIFT
;
606 if (event
.key
.keysym
.mod
& KMOD_CTRL
)
607 key
|= KEY_MODIFIER_CTRL
;
608 if (event
.key
.keysym
.mod
& KMOD_ALT
)
609 key
|= KEY_MODIFIER_ALT
;
610 vout_display_SendEventKey(vd
, key
);
614 case SDL_MOUSEBUTTONDOWN
:
615 case SDL_MOUSEBUTTONUP
: {
616 static const struct { int sdl
; int vlc
; } buttons
[] = {
617 { SDL_BUTTON_LEFT
, MOUSE_BUTTON_LEFT
},
618 { SDL_BUTTON_MIDDLE
, MOUSE_BUTTON_CENTER
},
619 { SDL_BUTTON_RIGHT
, MOUSE_BUTTON_RIGHT
},
620 { SDL_BUTTON_WHEELUP
, MOUSE_BUTTON_WHEEL_UP
},
621 { SDL_BUTTON_WHEELDOWN
, MOUSE_BUTTON_WHEEL_DOWN
},
626 for (int i
= 0; buttons
[i
].sdl
!= -1; i
++) {
627 if (buttons
[i
].sdl
== event
.button
.button
) {
628 if (event
.type
== SDL_MOUSEBUTTONDOWN
)
629 vout_display_SendEventMousePressed(vd
, buttons
[i
].vlc
);
631 vout_display_SendEventMouseReleased(vd
, buttons
[i
].vlc
);
637 case SDL_MOUSEMOTION
: {
638 if (sys
->place
.width
<= 0 || sys
->place
.height
<= 0)
643 vout_display_SendMouseMovedDisplayCoordinates(vd
, ORIENT_NORMAL
,
644 event
.motion
.x
, event
.motion
.y
,
649 case SDL_VIDEORESIZE
:
650 vout_display_SendEventDisplaySize(vd
, event
.resize
.w
, event
.resize
.h
);
660 static const struct {
664 } sdlkeys_to_vlckeys
[] = {
674 { SDLK_F10
, KEY_F10
},
675 { SDLK_F11
, KEY_F11
},
676 { SDLK_F12
, KEY_F12
},
678 { SDLK_RETURN
, KEY_ENTER
},
679 { SDLK_KP_ENTER
, KEY_ENTER
},
681 { SDLK_ESCAPE
, KEY_ESC
},
683 { SDLK_MENU
, KEY_MENU
},
684 { SDLK_LEFT
, KEY_LEFT
},
685 { SDLK_RIGHT
, KEY_RIGHT
},
687 { SDLK_DOWN
, KEY_DOWN
},
689 { SDLK_HOME
, KEY_HOME
},
690 { SDLK_END
, KEY_END
},
691 { SDLK_PAGEUP
, KEY_PAGEUP
},
692 { SDLK_PAGEDOWN
, KEY_PAGEDOWN
},
694 { SDLK_INSERT
, KEY_INSERT
},
695 { SDLK_DELETE
, KEY_DELETE
},
696 /*TODO: find a equivalent for SDL
697 { , KEY_MEDIA_NEXT_TRACK }
698 { , KEY_MEDIA_PREV_TRACK }
699 { , KEY_VOLUME_MUTE }
700 { , KEY_VOLUME_DOWN }
702 { , KEY_MEDIA_PLAY_PAUSE }
703 { , KEY_MEDIA_PLAY_PAUSE }*/
708 static int ConvertKey(SDLKey sdl_key
)
710 for (int i
= 0; sdlkeys_to_vlckeys
[i
].sdl_key
!= 0; i
++) {
711 if (sdlkeys_to_vlckeys
[i
].sdl_key
== sdl_key
)
712 return sdlkeys_to_vlckeys
[i
].vlckey
;