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>
41 #include SDL_INCLUDE_FILE
43 /* FIXME add a configure check */
44 #if !SDL_VERSION_ATLEAST(1,2,10)
48 /*****************************************************************************
50 *****************************************************************************/
51 static int Open (vlc_object_t *);
52 static void Close(vlc_object_t *);
54 #define CHROMA_TEXT N_("SDL chroma format")
55 #define CHROMA_LONGTEXT N_(\
56 "Force the SDL renderer to use a specific chroma format instead of " \
57 "trying to improve performances by using the most efficient one.")
59 #define DRIVER_TEXT N_("SDL video driver name")
60 #define DRIVER_LONGTEXT N_(\
61 "Force a specific SDL video output driver.")
65 set_category(CAT_VIDEO)
66 set_subcategory(SUBCAT_VIDEO_VOUT)
67 set_description(N_("Simple DirectMedia Layer video output"))
68 set_capability("vout display", 60)
70 add_string("sdl-chroma", NULL, NULL, CHROMA_TEXT, CHROMA_LONGTEXT, true)
72 add_string("sdl-video-driver", NULL, NULL, DRIVER_TEXT, DRIVER_LONGTEXT, true)
74 set_callbacks(Open, Close)
75 #if defined(__i386__) || defined(__x86_64__)
76 /* On i386, SDL is linked against svgalib */
77 linked_with_a_crap_library_which_uses_atexit()
82 /*****************************************************************************
84 *****************************************************************************/
85 static picture_t *Get (vout_display_t *);
86 static void Display(vout_display_t *, picture_t *);
87 static int Control(vout_display_t *, int, va_list);
88 static void Manage(vout_display_t *);
91 static int ConvertKey(SDLKey);
94 static vlc_mutex_t sdl_lock = VLC_STATIC_MUTEX;
97 struct vout_display_sys_t {
98 vout_display_place_t place;
100 SDL_Surface *display;
102 uint32_t display_flags;
104 unsigned int desktop_width;
105 unsigned int desktop_height;
108 SDL_Overlay *overlay;
112 picture_pool_t *pool;
116 * This function initializes SDL vout method.
118 static int Open(vlc_object_t *object)
120 vout_display_t *vd = (vout_display_t *)object;
121 vout_display_sys_t *sys;
123 /* XXX: check for conflicts with the SDL audio output */
124 vlc_mutex_lock(&sdl_lock);
126 /* Check if SDL video module has been initialized */
127 if (SDL_WasInit(SDL_INIT_VIDEO) != 0) {
128 vlc_mutex_unlock(&sdl_lock);
132 vd->sys = sys = calloc(1, sizeof(*sys));
134 vlc_mutex_unlock(&sdl_lock);
139 char *psz_driver = var_CreateGetNonEmptyString(vd, "sdl-video-driver");
141 setenv("SDL_VIDEODRIVER", psz_driver, 1);
147 int sdl_flags = SDL_INIT_VIDEO;
149 /* Win32 SDL implementation doesn't support SDL_INIT_EVENTTHREAD yet*/
150 sdl_flags |= SDL_INIT_EVENTTHREAD;
153 /* In debug mode you may want vlc to dump a core instead of staying stuck */
154 sdl_flags |= SDL_INIT_NOPARACHUTE;
157 /* Initialize library */
158 if (SDL_Init(sdl_flags) < 0) {
159 vlc_mutex_unlock(&sdl_lock);
161 msg_Err(vd, "cannot initialize SDL (%s)", SDL_GetError());
165 vlc_mutex_unlock(&sdl_lock);
167 /* Translate keys into unicode */
168 SDL_EnableUNICODE(1);
170 /* Get the desktop resolution */
171 /* FIXME: SDL has a problem with virtual desktop */
172 sys->desktop_width = SDL_GetVideoInfo()->current_w;
173 sys->desktop_height = SDL_GetVideoInfo()->current_h;
176 video_format_t fmt = vd->fmt;
179 vout_display_info_t info = vd->info;
181 /* Set main window's size */
184 if (vd->cfg->is_fullscreen) {
185 display_width = sys->desktop_width;
186 display_height = sys->desktop_height;
188 display_width = vd->cfg->display.width;
189 display_height = vd->cfg->display.height;
192 /* Initialize flags and cursor */
193 sys->display_flags = SDL_ANYFORMAT | SDL_HWPALETTE | SDL_HWSURFACE | SDL_DOUBLEBUF;
194 sys->display_flags |= vd->cfg->is_fullscreen ? SDL_FULLSCREEN : SDL_RESIZABLE;
196 sys->display_bpp = SDL_VideoModeOK(display_width, display_height,
197 16, sys->display_flags);
198 if (sys->display_bpp == 0) {
199 msg_Err(vd, "no video mode available");
203 sys->display = SDL_SetVideoMode(display_width, display_height,
204 sys->display_bpp, sys->display_flags);
206 msg_Err(vd, "cannot set video mode");
210 /* We keep the surface locked forever */
211 SDL_LockSurface(sys->display);
214 vlc_fourcc_t forced_chroma = 0;
215 char *psz_chroma = var_CreateGetNonEmptyString(vd, "sdl-chroma");
217 forced_chroma = vlc_fourcc_GetCodecFromString(VIDEO_ES, psz_chroma);
219 msg_Dbg(vd, "Forcing chroma to 0x%.8x (%4.4s)",
220 forced_chroma, (const char*)&forced_chroma);
224 /* Try to open an overlay if requested */
226 const bool is_overlay = var_CreateGetBool(vd, "overlay");
233 { VLC_CODEC_YV12, SDL_YV12_OVERLAY },
234 { VLC_CODEC_I420, SDL_IYUV_OVERLAY },
235 { VLC_CODEC_YUYV, SDL_YUY2_OVERLAY },
236 { VLC_CODEC_UYVY, SDL_UYVY_OVERLAY },
237 { VLC_CODEC_YVYU, SDL_YVYU_OVERLAY },
241 const vlc_fourcc_t forced_chromas[] = {
244 const vlc_fourcc_t *fallback_chromas =
245 vlc_fourcc_GetYUVFallback(fmt.i_chroma);
246 const vlc_fourcc_t *chromas = forced_chroma ? forced_chromas : fallback_chromas;
248 for (int pass = forced_chroma ? 1 : 0; pass < 2 && !sys->overlay; pass++) {
249 for (int i = 0; chromas[i] != 0; i++) {
250 const vlc_fourcc_t vlc = chromas[i];
253 for (int j = 0; vlc_to_sdl[j].vlc != 0 && !sdl; j++) {
254 if (vlc_to_sdl[j].vlc == vlc)
255 sdl = vlc_to_sdl[j].sdl;
260 sys->overlay = SDL_CreateYUVOverlay(fmt.i_width, fmt.i_height,
262 if (sys->overlay && !sys->overlay->hw_overlay && pass == 0) {
263 /* Ignore non hardware overlay surface in first pass */
264 SDL_FreeYUVOverlay(sys->overlay);
268 /* We keep the surface locked forever */
269 SDL_LockYUVOverlay(sys->overlay);
272 sys->is_uv_swapped = vlc_fourcc_AreUVPlanesSwapped(fmt.i_chroma,
274 if (sys->is_uv_swapped)
275 fmt.i_chroma = vd->fmt.i_chroma;
281 msg_Warn(vd, "SDL overlay disabled by the user");
285 vout_display_cfg_t place_cfg = *vd->cfg;
286 place_cfg.display.width = display_width;
287 place_cfg.display.height = display_height;
288 vout_display_PlacePicture(&sys->place, &vd->source, &place_cfg, !sys->overlay);
290 /* If no overlay, fallback to software output */
293 switch (sys->display->format->BitsPerPixel) {
295 fmt.i_chroma = VLC_CODEC_RGB8;
298 fmt.i_chroma = VLC_CODEC_RGB15;
301 fmt.i_chroma = VLC_CODEC_RGB16;
304 fmt.i_chroma = VLC_CODEC_RGB24;
307 fmt.i_chroma = VLC_CODEC_RGB32;
310 msg_Err(vd, "unknown screen depth %i",
311 sys->display->format->BitsPerPixel);
315 /* All we have is an RGB image with square pixels */
316 fmt.i_width = display_width;
317 fmt.i_height = display_height;
318 fmt.i_rmask = sys->display->format->Rmask;
319 fmt.i_gmask = sys->display->format->Gmask;
320 fmt.i_bmask = sys->display->format->Bmask;
322 info.has_pictures_invalid = true;
325 if (vd->cfg->display.title)
326 SDL_WM_SetCaption(vd->cfg->display.title,
327 vd->cfg->display.title);
328 else if (!sys->overlay)
329 SDL_WM_SetCaption(VOUT_TITLE " (software RGB SDL output)",
330 VOUT_TITLE " (software RGB SDL output)");
331 else if (sys->overlay->hw_overlay)
332 SDL_WM_SetCaption(VOUT_TITLE " (hardware YUV SDL output)",
333 VOUT_TITLE " (hardware YUV SDL output)");
335 SDL_WM_SetCaption(VOUT_TITLE " (software YUV SDL output)",
336 VOUT_TITLE " (software YUV SDL output)");
339 SDL_EventState(SDL_KEYUP, SDL_IGNORE); /* ignore keys up */
341 /* Setup vout_display now that everything is fine */
347 vd->display = Display;
348 vd->control = Control;
352 vout_display_SendEventDisplaySize(vd, display_width, display_height);
356 msg_Err(vd, "cannot set up SDL (%s)", SDL_GetError());
359 SDL_UnlockSurface(sys->display);
360 SDL_FreeSurface(sys->display);
363 vlc_mutex_lock(&sdl_lock);
364 SDL_QuitSubSystem(SDL_INIT_VIDEO);
365 vlc_mutex_unlock(&sdl_lock);
372 * Close a SDL video output
374 static void Close(vlc_object_t *object)
376 vout_display_t *vd = (vout_display_t *)object;
377 vout_display_sys_t *sys = vd->sys;
380 picture_pool_Delete(sys->pool);
383 SDL_LockYUVOverlay(sys->overlay);
384 SDL_FreeYUVOverlay(sys->overlay);
386 SDL_UnlockSurface (sys->display);
387 SDL_FreeSurface(sys->display);
389 vlc_mutex_lock(&sdl_lock);
390 SDL_QuitSubSystem(SDL_INIT_VIDEO);
391 vlc_mutex_unlock(&sdl_lock);
397 * Return a direct buffer
399 static picture_t *Get(vout_display_t *vd)
401 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);
442 return picture_pool_Get(sys->pool);
448 static void Display(vout_display_t *vd, picture_t *p_pic)
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);
471 * Control for vout display
473 static int Control(vout_display_t *vd, int query, va_list args)
475 vout_display_sys_t *sys = vd->sys;
479 case VOUT_DISPLAY_HIDE_MOUSE:
483 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: {
484 const vout_display_cfg_t *cfg = va_arg(args, const vout_display_cfg_t *);
487 sys->display = SDL_SetVideoMode(cfg->display.width,
489 sys->display_bpp, sys->display_flags);
491 sys->display = SDL_SetVideoMode(vd->cfg->display.width,
492 vd->cfg->display.height,
493 sys->display_bpp, sys->display_flags);
497 vout_display_PlacePicture(&sys->place, &vd->source, cfg, !sys->overlay);
499 vout_display_SendEventPicturesInvalid(vd);
502 case VOUT_DISPLAY_CHANGE_FULLSCREEN: {
503 vout_display_cfg_t cfg = *va_arg(args, const vout_display_cfg_t *);
506 sys->display_flags &= ~(SDL_FULLSCREEN | SDL_RESIZABLE);
507 sys->display_flags |= cfg.is_fullscreen ? SDL_FULLSCREEN : SDL_RESIZABLE;
509 if (cfg.is_fullscreen) {
510 cfg.display.width = sys->desktop_width;
511 cfg.display.height = sys->desktop_height;
515 sys->display = SDL_SetVideoMode(cfg.display.width, cfg.display.height,
516 sys->display_bpp, sys->display_flags);
518 vout_display_PlacePicture(&sys->place, &vd->source, &cfg, !sys->overlay);
520 vout_display_SendEventDisplaySize(vd, cfg.display.width, cfg.display.height);
523 case VOUT_DISPLAY_CHANGE_ZOOM:
524 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
525 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: {
526 const vout_display_cfg_t *cfg;
527 const video_format_t *source;
529 if (query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT) {
530 source = va_arg(args, const video_format_t *);
533 source = &vd->source;
534 cfg = va_arg(args, const vout_display_cfg_t *);
537 sys->display = SDL_SetVideoMode(cfg->display.width, cfg->display.height,
538 sys->display_bpp, sys->display_flags);
540 vout_display_PlacePicture(&sys->place, source, cfg, !sys->overlay);
542 vout_display_SendEventPicturesInvalid(vd);
547 case VOUT_DISPLAY_RESET_PICTURES: {
549 assert(!sys->overlay);
553 picture_pool_Delete(sys->pool);
556 vout_display_PlacePicture(&sys->place, &vd->source, vd->cfg, !sys->overlay);
559 vd->fmt.i_width = sys->place.width;
560 vd->fmt.i_height = sys->place.height;
564 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
565 case VOUT_DISPLAY_CHANGE_ON_TOP:
566 /* I don't think it is possible to support with SDL:
573 msg_Err(vd, "Unsupported query in vout display SDL");
579 * Proccess pending event
581 static void Manage(vout_display_t *vd)
583 vout_display_sys_t *sys = vd->sys;
587 while (SDL_PollEvent(&event)) {
588 switch (event.type) {
590 vout_display_SendEventClose(vd);
594 /* convert the key if possible */
595 int key = ConvertKey(event.key.keysym.sym);
598 /* Find the right caracter */
599 if ((event.key.keysym.unicode & 0xff80) == 0) {
600 key = event.key.keysym.unicode & 0x7f;
601 /* FIXME: find a better solution than this
602 hack to find the right caracter */
603 if (key >= 1 && key <= 26)
605 else if (key >= 65 && key <= 90)
612 if (event.key.keysym.mod & KMOD_SHIFT)
613 key |= KEY_MODIFIER_SHIFT;
614 if (event.key.keysym.mod & KMOD_CTRL)
615 key |= KEY_MODIFIER_CTRL;
616 if (event.key.keysym.mod & KMOD_ALT)
617 key |= KEY_MODIFIER_ALT;
618 vout_display_SendEventKey(vd, key);
622 case SDL_MOUSEBUTTONDOWN:
623 case SDL_MOUSEBUTTONUP: {
624 static const struct { int sdl; int vlc; } buttons[] = {
625 { SDL_BUTTON_LEFT, MOUSE_BUTTON_LEFT },
626 { SDL_BUTTON_MIDDLE, MOUSE_BUTTON_CENTER },
627 { SDL_BUTTON_RIGHT, MOUSE_BUTTON_RIGHT },
628 { SDL_BUTTON_WHEELUP, MOUSE_BUTTON_WHEEL_UP },
629 { SDL_BUTTON_WHEELDOWN, MOUSE_BUTTON_WHEEL_DOWN },
634 for (int i = 0; buttons[i].sdl != -1; i++) {
635 if (buttons[i].sdl == event.button.button) {
636 if (event.type == SDL_MOUSEBUTTONDOWN)
637 vout_display_SendEventMousePressed(vd, buttons[i].vlc);
639 vout_display_SendEventMouseReleased(vd, buttons[i].vlc);
645 case SDL_MOUSEMOTION: {
646 if (sys->place.width <= 0 || sys->place.height <= 0)
649 const int x = (int64_t)(event.motion.x - sys->place.x) * vd->source.i_width / sys->place.width;
650 const int y = (int64_t)(event.motion.y - sys->place.y) * vd->source.i_height / sys->place.height;
653 if (x >= 0 && x < vd->source.i_width &&
654 y >= 0 && y < vd->source.i_height)
655 vout_display_SendEventMouseMoved(vd, x, y);
659 case SDL_VIDEORESIZE:
660 vout_display_SendEventDisplaySize(vd, event.resize.w, event.resize.h);
670 static const struct {
674 } sdlkeys_to_vlckeys[] = {
684 { SDLK_F10, KEY_F10 },
685 { SDLK_F11, KEY_F11 },
686 { SDLK_F12, KEY_F12 },
688 { SDLK_RETURN, KEY_ENTER },
689 { SDLK_KP_ENTER, KEY_ENTER },
690 { SDLK_SPACE, KEY_SPACE },
691 { SDLK_ESCAPE, KEY_ESC },
693 { SDLK_MENU, KEY_MENU },
694 { SDLK_LEFT, KEY_LEFT },
695 { SDLK_RIGHT, KEY_RIGHT },
697 { SDLK_DOWN, KEY_DOWN },
699 { SDLK_HOME, KEY_HOME },
700 { SDLK_END, KEY_END },
701 { SDLK_PAGEUP, KEY_PAGEUP },
702 { SDLK_PAGEDOWN, KEY_PAGEDOWN },
704 { SDLK_INSERT, KEY_INSERT },
705 { SDLK_DELETE, KEY_DELETE },
706 /*TODO: find a equivalent for SDL
707 { , KEY_MEDIA_NEXT_TRACK }
708 { , KEY_MEDIA_PREV_TRACK }
709 { , KEY_VOLUME_MUTE }
710 { , KEY_VOLUME_DOWN }
712 { , KEY_MEDIA_PLAY_PAUSE }
713 { , KEY_MEDIA_PLAY_PAUSE }*/
718 static int ConvertKey(SDLKey sdl_key)
720 for (int i = 0; sdlkeys_to_vlckeys[i].sdl_key != 0; i++) {
721 if (sdlkeys_to_vlckeys[i].sdl_key == sdl_key)
722 return sdlkeys_to_vlckeys[i].vlckey;