2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "video_out.h"
25 #include "video_out_internal.h"
30 #include <X11/Xutil.h>
34 #include "x11_common.h"
39 #include <X11/extensions/XShm.h>
41 static int Shmem_Flag
;
43 //static int Quiet_Flag; Here also what is this for. It's used but isn't initialized?
44 static XShmSegmentInfo Shminfo
[1];
45 static int gXErrorFlag
;
46 static int CompletionType
= -1;
51 #include "libswscale/swscale.h"
52 #include "libmpcodecs/vf_scale.h"
58 static const vo_info_t info
= {
61 "Aaron Holtzman <aholtzma@ess.engr.uvic.ca>",
65 const LIBVO_EXTERN(x11
)
66 /* private prototypes */
67 static void (*draw_alpha_fnc
) (int x0
, int y0
, int w
, int h
,
68 unsigned char *src
, unsigned char *srca
,
72 static unsigned char *ImageData
;
73 //! original unaligned pointer for free
74 static unsigned char *ImageDataOrig
;
76 /* X11 related variables */
77 static XImage
*myximage
= NULL
;
78 static int depth
, bpp
;
79 static XWindowAttributes attribs
;
88 static uint32_t image_width
;
89 static uint32_t image_height
;
90 static uint32_t in_format
;
91 static uint32_t out_format
= 0;
92 static int out_offset
;
96 static int old_vo_dwidth
= -1;
97 static int old_vo_dheight
= -1;
99 static void check_events(void)
101 int ret
= vo_x11_check_events(mDisplay
);
103 if (ret
& VO_EVENT_RESIZE
)
104 vo_x11_clearwindow(mDisplay
, vo_window
);
105 else if (ret
& VO_EVENT_EXPOSE
)
106 vo_x11_clearwindow_part(mDisplay
, vo_window
, myximage
->width
,
108 if (ret
& VO_EVENT_EXPOSE
&& int_pause
)
112 static void draw_alpha_32(int x0
, int y0
, int w
, int h
, unsigned char *src
,
113 unsigned char *srca
, int stride
)
115 vo_draw_alpha_rgb32(w
, h
, src
, srca
, stride
,
116 ImageData
+ 4 * (y0
* image_width
+ x0
),
120 static void draw_alpha_24(int x0
, int y0
, int w
, int h
, unsigned char *src
,
121 unsigned char *srca
, int stride
)
123 vo_draw_alpha_rgb24(w
, h
, src
, srca
, stride
,
124 ImageData
+ 3 * (y0
* image_width
+ x0
),
128 static void draw_alpha_16(int x0
, int y0
, int w
, int h
, unsigned char *src
,
129 unsigned char *srca
, int stride
)
131 vo_draw_alpha_rgb16(w
, h
, src
, srca
, stride
,
132 ImageData
+ 2 * (y0
* image_width
+ x0
),
136 static void draw_alpha_15(int x0
, int y0
, int w
, int h
, unsigned char *src
,
137 unsigned char *srca
, int stride
)
139 vo_draw_alpha_rgb15(w
, h
, src
, srca
, stride
,
140 ImageData
+ 2 * (y0
* image_width
+ x0
),
144 static void draw_alpha_null(int x0
, int y0
, int w
, int h
,
145 unsigned char *src
, unsigned char *srca
,
150 static struct SwsContext
*swsContext
= NULL
;
151 static int dst_width
;
152 extern int sws_flags
;
154 static XVisualInfo vinfo
;
157 * Scan the available visuals on this Display/Screen. Try to find
158 * the 'best' available TrueColor visual that has a decent color
159 * depth (at least 15bit). If there are multiple visuals with depth
160 * >= 15bit, we prefer visuals with a smaller color depth.
162 static int vo_find_depth_from_visuals(Display
* dpy
, int screen
,
163 Visual
** visual_return
)
165 XVisualInfo visual_tmpl
;
166 XVisualInfo
*visuals
;
169 int bestvisual_depth
= -1;
171 visual_tmpl
.screen
= screen
;
172 visual_tmpl
.class = TrueColor
;
173 visuals
= XGetVisualInfo(dpy
,
174 VisualScreenMask
| VisualClassMask
,
175 &visual_tmpl
, &nvisuals
);
178 for (i
= 0; i
< nvisuals
; i
++)
180 mp_msg(MSGT_VO
, MSGL_V
,
181 "vo: X11 truecolor visual %#lx, depth %d, R:%lX G:%lX B:%lX\n",
182 visuals
[i
].visualid
, visuals
[i
].depth
,
183 visuals
[i
].red_mask
, visuals
[i
].green_mask
,
184 visuals
[i
].blue_mask
);
186 * Save the visual index and its depth, if this is the first
187 * truecolor visul, or a visual that is 'preferred' over the
188 * previous 'best' visual.
190 if (bestvisual_depth
== -1
191 || (visuals
[i
].depth
>= 15
192 && (visuals
[i
].depth
< bestvisual_depth
193 || bestvisual_depth
< 15)))
196 bestvisual_depth
= visuals
[i
].depth
;
200 if (bestvisual
!= -1 && visual_return
!= NULL
)
201 *visual_return
= visuals
[bestvisual
].visual
;
205 return bestvisual_depth
;
208 static void getMyXImage(void)
211 if (mLocalDisplay
&& XShmQueryExtension(mDisplay
))
216 mp_msg(MSGT_VO
, MSGL_WARN
,
217 "Shared memory not supported\nReverting to normal Xlib\n");
220 CompletionType
= XShmGetEventBase(mDisplay
) + ShmCompletion
;
225 XShmCreateImage(mDisplay
, vinfo
.visual
, depth
, ZPixmap
, NULL
,
226 &Shminfo
[0], image_width
, image_height
);
227 if (myximage
== NULL
)
229 mp_msg(MSGT_VO
, MSGL_WARN
,
230 "Shared memory error,disabling ( Ximage error )\n");
233 Shminfo
[0].shmid
= shmget(IPC_PRIVATE
,
234 myximage
->bytes_per_line
*
235 myximage
->height
, IPC_CREAT
| 0777);
236 if (Shminfo
[0].shmid
< 0)
238 XDestroyImage(myximage
);
239 mp_msg(MSGT_VO
, MSGL_V
, "%s\n", strerror(errno
));
240 //perror( strerror( errno ) );
241 mp_msg(MSGT_VO
, MSGL_WARN
,
242 "Shared memory error,disabling ( seg id error )\n");
245 Shminfo
[0].shmaddr
= (char *) shmat(Shminfo
[0].shmid
, 0, 0);
247 if (Shminfo
[0].shmaddr
== ((char *) -1))
249 XDestroyImage(myximage
);
250 if (Shminfo
[0].shmaddr
!= ((char *) -1))
251 shmdt(Shminfo
[0].shmaddr
);
252 mp_msg(MSGT_VO
, MSGL_WARN
,
253 "Shared memory error,disabling ( address error )\n");
256 myximage
->data
= Shminfo
[0].shmaddr
;
257 ImageData
= (unsigned char *) myximage
->data
;
258 Shminfo
[0].readOnly
= False
;
259 XShmAttach(mDisplay
, &Shminfo
[0]);
261 XSync(mDisplay
, False
);
265 XDestroyImage(myximage
);
266 shmdt(Shminfo
[0].shmaddr
);
267 mp_msg(MSGT_VO
, MSGL_WARN
, "Shared memory error,disabling.\n");
271 shmctl(Shminfo
[0].shmid
, IPC_RMID
, 0);
274 static int firstTime
= 1;
278 mp_msg(MSGT_VO
, MSGL_V
, "Sharing memory.\n");
287 myximage
= XCreateImage(mDisplay
, vinfo
.visual
, depth
, ZPixmap
,
288 0, NULL
, image_width
, image_height
, 8, 0);
289 ImageDataOrig
= malloc(myximage
->bytes_per_line
* image_height
+ 32);
290 myximage
->data
= ImageDataOrig
+ 16 - ((long)ImageDataOrig
& 15);
291 memset(myximage
->data
, 0, myximage
->bytes_per_line
* image_height
);
292 ImageData
= myximage
->data
;
298 static void freeMyXImage(void)
303 XShmDetach(mDisplay
, &Shminfo
[0]);
304 XDestroyImage(myximage
);
305 shmdt(Shminfo
[0].shmaddr
);
309 myximage
->data
= ImageDataOrig
;
310 XDestroyImage(myximage
);
311 ImageDataOrig
= NULL
;
318 #define BO_NATIVE MSBFirst
319 #define BO_NONNATIVE LSBFirst
321 #define BO_NATIVE LSBFirst
322 #define BO_NONNATIVE MSBFirst
324 const struct fmt2Xfmtentry_s
{
331 {IMGFMT_RGB8
, BO_NATIVE
, 0x00000007, 0x00000038, 0x000000C0},
332 {IMGFMT_RGB8
, BO_NONNATIVE
, 0x00000007, 0x00000038, 0x000000C0},
333 {IMGFMT_BGR8
, BO_NATIVE
, 0x000000E0, 0x0000001C, 0x00000003},
334 {IMGFMT_BGR8
, BO_NONNATIVE
, 0x000000E0, 0x0000001C, 0x00000003},
335 {IMGFMT_RGB15
, BO_NATIVE
, 0x0000001F, 0x000003E0, 0x00007C00},
336 {IMGFMT_BGR15
, BO_NATIVE
, 0x00007C00, 0x000003E0, 0x0000001F},
337 {IMGFMT_RGB16
, BO_NATIVE
, 0x0000001F, 0x000007E0, 0x0000F800},
338 {IMGFMT_BGR16
, BO_NATIVE
, 0x0000F800, 0x000007E0, 0x0000001F},
339 {IMGFMT_RGB24
, MSBFirst
, 0x00FF0000, 0x0000FF00, 0x000000FF},
340 {IMGFMT_RGB24
, LSBFirst
, 0x000000FF, 0x0000FF00, 0x00FF0000},
341 {IMGFMT_BGR24
, MSBFirst
, 0x000000FF, 0x0000FF00, 0x00FF0000},
342 {IMGFMT_BGR24
, LSBFirst
, 0x00FF0000, 0x0000FF00, 0x000000FF},
343 {IMGFMT_RGB32
, BO_NATIVE
, 0x000000FF, 0x0000FF00, 0x00FF0000},
344 {IMGFMT_RGB32
, BO_NONNATIVE
, 0xFF000000, 0x00FF0000, 0x0000FF00},
345 {IMGFMT_BGR32
, BO_NATIVE
, 0x00FF0000, 0x0000FF00, 0x000000FF},
346 {IMGFMT_BGR32
, BO_NONNATIVE
, 0x0000FF00, 0x00FF0000, 0xFF000000},
347 {IMGFMT_ARGB
, MSBFirst
, 0x00FF0000, 0x0000FF00, 0x000000FF},
348 {IMGFMT_ARGB
, LSBFirst
, 0x0000FF00, 0x00FF0000, 0xFF000000},
349 {IMGFMT_ABGR
, MSBFirst
, 0x000000FF, 0x0000FF00, 0x00FF0000},
350 {IMGFMT_ABGR
, LSBFirst
, 0xFF000000, 0x00FF0000, 0x0000FF00},
351 {IMGFMT_RGBA
, MSBFirst
, 0xFF000000, 0x00FF0000, 0x0000FF00},
352 {IMGFMT_RGBA
, LSBFirst
, 0x000000FF, 0x0000FF00, 0x00FF0000},
353 {IMGFMT_BGRA
, MSBFirst
, 0x0000FF00, 0x00FF0000, 0xFF000000},
354 {IMGFMT_BGRA
, LSBFirst
, 0x00FF0000, 0x0000FF00, 0x000000FF},
358 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
,
359 uint32_t d_height
, uint32_t flags
, char *title
,
363 unsigned long xswamask
;
364 const struct fmt2Xfmtentry_s
*fmte
= fmt2Xfmt
;
367 int vm
= flags
& VOFLAG_MODESWITCHING
;
369 Flip_Flag
= flags
& VOFLAG_FLIPPING
;
370 zoomFlag
= flags
& VOFLAG_SWSCALE
;
376 title
= "MPlayer X11 (XImage/Shm) render";
382 XGetWindowAttributes(mDisplay
, mRootWin
, &attribs
);
383 depth
= attribs
.depth
;
385 if (depth
!= 15 && depth
!= 16 && depth
!= 24 && depth
!= 32)
389 depth
= vo_find_depth_from_visuals(mDisplay
, mScreen
, &visual
);
391 if (!XMatchVisualInfo(mDisplay
, mScreen
, depth
, DirectColor
, &vinfo
) ||
393 && vinfo
.visualid
!= XVisualIDFromVisual(attribs
.visual
)))
394 XMatchVisualInfo(mDisplay
, mScreen
, depth
, TrueColor
, &vinfo
);
396 /* set image size (which is indeed neither the input nor output size),
397 if zoom is on it will be changed during draw_slice anyway so we don't duplicate the aspect code here
399 image_width
= (width
+ 7) & (~7);
400 image_height
= height
;
409 theCmap
= vo_x11_create_colormap(&vinfo
);
411 xswamask
= CWBackPixel
| CWBorderPixel
| CWColormap
;
415 xswamask
|= CWOverrideRedirect
;
418 vo_x11_create_vo_window(&vinfo
, vo_dx
, vo_dy
, vo_dwidth
, vo_dheight
,
419 flags
, theCmap
, "x11", title
);
421 depth
= vo_x11_update_geometry();
426 /* Grab the mouse pointer in our window */
428 XGrabPointer(mDisplay
, vo_window
, True
, 0,
429 GrabModeAsync
, GrabModeAsync
,
430 vo_window
, None
, CurrentTime
);
431 XSetInputFocus(mDisplay
, vo_window
, RevertToNone
, CurrentTime
);
439 sws_freeContext(swsContext
);
443 while (fmte
->mpfmt
) {
444 int depth
= IMGFMT_RGB_DEPTH(fmte
->mpfmt
);
445 /* bits_per_pixel in X seems to be set to 16 for 15 bit formats
446 => force depth to 16 so that only the color masks are used for the format check */
450 if (depth
== myximage
->bits_per_pixel
&&
451 fmte
->byte_order
== myximage
->byte_order
&&
452 fmte
->red_mask
== myximage
->red_mask
&&
453 fmte
->green_mask
== myximage
->green_mask
&&
454 fmte
->blue_mask
== myximage
->blue_mask
)
459 mp_msg(MSGT_VO
, MSGL_ERR
,
460 "X server image format not supported, please contact the developers\n");
463 out_format
= fmte
->mpfmt
;
464 switch ((bpp
= myximage
->bits_per_pixel
))
467 draw_alpha_fnc
= draw_alpha_24
;
470 draw_alpha_fnc
= draw_alpha_32
;
475 draw_alpha_fnc
= draw_alpha_15
;
477 draw_alpha_fnc
= draw_alpha_16
;
480 draw_alpha_fnc
= draw_alpha_null
;
483 // for these formats conversion is currently not support and
484 // we can easily "emulate" them.
485 if (out_format
& 64 && (IMGFMT_IS_RGB(out_format
) || IMGFMT_IS_BGR(out_format
))) {
494 /* always allocate swsContext as size could change between frames */
496 sws_getContextFromCmdLine(width
, height
, in_format
, width
, height
,
502 //printf( "X11 bpp: %d color mask: R:%lX G:%lX B:%lX\n",bpp,myximage->red_mask,myximage->green_mask,myximage->blue_mask );
507 static void Display_Image(XImage
* myximage
, uint8_t * ImageData
)
509 int x
= (vo_dwidth
- dst_width
) / 2;
510 int y
= (vo_dheight
- myximage
->height
) / 2;
512 // do not draw if the image needs rescaling
513 if ((old_vo_dwidth
!= vo_dwidth
|| old_vo_dheight
!= vo_dheight
) && zoomFlag
)
520 myximage
->data
+= out_offset
;
524 XShmPutImage(mDisplay
, vo_window
, vo_gc
, myximage
,
527 myximage
->height
, True
);
531 XPutImage(mDisplay
, vo_window
, vo_gc
, myximage
,
536 myximage
->data
-= out_offset
;
539 static void draw_osd(void)
541 vo_draw_text(image_width
, image_height
, draw_alpha_fnc
);
544 static void flip_page(void)
546 Display_Image(myximage
, ImageData
);
547 XSync(mDisplay
, False
);
550 static int draw_slice(uint8_t * src
[], int stride
[], int w
, int h
,
553 uint8_t *dst
[MP_MAX_PLANES
] = {NULL
};
554 int dstStride
[MP_MAX_PLANES
] = {0};
556 if ((old_vo_dwidth
!= vo_dwidth
557 || old_vo_dheight
!= vo_dheight
) /*&& y==0 */ && zoomFlag
)
559 int newW
= vo_dwidth
;
560 int newH
= vo_dheight
;
561 struct SwsContext
*oldContext
= swsContext
;
563 old_vo_dwidth
= vo_dwidth
;
564 old_vo_dheight
= vo_dheight
;
567 aspect(&newW
, &newH
, A_ZOOM
);
569 newW
&= (~31); // not needed but, if the user wants the FAST_BILINEAR SCALER, then its needed
571 swsContext
= sws_getContextFromCmdLine(srcW
, srcH
, in_format
,
572 newW
, newH
, out_format
);
575 image_width
= (newW
+ 7) & (~7);
580 sws_freeContext(oldContext
);
583 swsContext
= oldContext
;
588 dstStride
[0] = image_width
* ((bpp
+ 7) / 8);
592 dst
[0] += dstStride
[0] * (image_height
- 1);
593 dstStride
[0] = -dstStride
[0];
595 sws_scale(swsContext
, (const uint8_t **)src
, stride
, y
, h
, dst
, dstStride
);
599 static int draw_frame(uint8_t * src
[])
604 static uint32_t get_image(mp_image_t
* mpi
)
607 !IMGFMT_IS_BGR(mpi
->imgfmt
) ||
608 (IMGFMT_BGR_DEPTH(mpi
->imgfmt
) != vo_depthonscreen
) ||
609 ((mpi
->type
!= MP_IMGTYPE_STATIC
)
610 && (mpi
->type
!= MP_IMGTYPE_TEMP
))
611 || (mpi
->flags
& MP_IMGFLAG_PLANAR
)
612 || (mpi
->flags
& MP_IMGFLAG_YUV
) || (mpi
->width
!= image_width
)
613 || (mpi
->height
!= image_height
))
618 mpi
->stride
[0] = -image_width
* ((bpp
+ 7) / 8);
619 mpi
->planes
[0] = ImageData
- mpi
->stride
[0] * (image_height
- 1);
622 mpi
->stride
[0] = image_width
* ((bpp
+ 7) / 8);
623 mpi
->planes
[0] = ImageData
;
625 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
630 static int query_format(uint32_t format
)
632 mp_msg(MSGT_VO
, MSGL_DBG2
,
633 "vo_x11: query_format was called: %x (%s)\n", format
,
634 vo_format_name(format
));
635 if (IMGFMT_IS_BGR(format
))
637 if (IMGFMT_BGR_DEPTH(format
) <= 8)
638 return 0; // TODO 8bpp not yet fully implemented
639 if (IMGFMT_BGR_DEPTH(format
) == vo_depthonscreen
)
640 return VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
| VFCAP_OSD
| VFCAP_SWSCALE
| VFCAP_FLIP
|
643 return VFCAP_CSP_SUPPORTED
| VFCAP_OSD
| VFCAP_SWSCALE
| VFCAP_FLIP
|
650 // case IMGFMT_BGR15:
651 // case IMGFMT_BGR16:
652 // case IMGFMT_BGR24:
653 // case IMGFMT_BGR32:
659 return VFCAP_CSP_SUPPORTED
| VFCAP_OSD
| VFCAP_SWSCALE
| VFCAP_ACCEPT_STRIDE
;
665 static void uninit(void)
677 sws_freeContext(swsContext
);
680 static int preinit(const char *arg
)
684 mp_msg(MSGT_VO
, MSGL_ERR
, "vo_x11: Unknown subdevice: %s\n", arg
);
689 return -1; // Can't open X11
691 XWindowAttributes attribs
;
692 XGetWindowAttributes(mDisplay
, mRootWin
, &attribs
);
693 depth
= attribs
.depth
;
694 XImage
*mXImage
= NULL
;
695 if (depth
!= 15 && depth
!= 16 && depth
!= 24 && depth
!= 32) {
698 depth
= vo_find_depth_from_visuals(mDisplay
, mScreen
, &visual
);
700 mXImage
= XCreateImage(mDisplay
, visual
, depth
, ZPixmap
,
701 0, NULL
, 1, 1, 8, 1);
704 XGetImage(mDisplay
, mRootWin
, 0, 0, 1, 1, AllPlanes
, ZPixmap
);
706 vo_depthonscreen
= depth
; // display depth on screen
708 // get bits/pixel from XImage structure:
710 if (mXImage
== NULL
) {
714 * for the depth==24 case, the XImage structures might use
715 * 24 or 32 bits of data per pixel. The vo_depthonscreen
716 * field stores the amount of data per pixel in the
719 * Maybe we should rename vo_depthonscreen to (or add) vo_bpp?
721 bpp
= mXImage
->bits_per_pixel
;
722 if ((vo_depthonscreen
+ 7) / 8 != (bpp
+ 7) / 8)
723 vo_depthonscreen
= bpp
; // by A'rpi
725 mXImage
->red_mask
| mXImage
->green_mask
| mXImage
->blue_mask
;
726 mp_msg(MSGT_VO
, MSGL_V
,
727 "vo: X11 color mask: %X (R:%lX G:%lX B:%lX)\n", mask
,
728 mXImage
->red_mask
, mXImage
->green_mask
, mXImage
->blue_mask
);
729 XDestroyImage(mXImage
);
731 if (((vo_depthonscreen
+ 7) / 8) == 2)
734 vo_depthonscreen
= 15;
735 else if (mask
== 0xFFFF)
736 vo_depthonscreen
= 16;
741 static int control(uint32_t request
, void *data
)
746 return int_pause
= 1;
748 return int_pause
= 0;
749 case VOCTRL_QUERY_FORMAT
:
750 return query_format(*((uint32_t *) data
));
751 case VOCTRL_GET_IMAGE
:
752 return get_image(data
);
753 case VOCTRL_FULLSCREEN
:
755 vo_x11_clearwindow(mDisplay
, vo_window
);
757 case VOCTRL_SET_EQUALIZER
:
759 struct voctrl_set_equalizer_args
*args
= data
;
760 return vo_x11_set_equalizer(args
->name
, args
->value
);
762 case VOCTRL_GET_EQUALIZER
:
764 struct voctrl_get_equalizer_args
*args
= data
;
765 return vo_x11_get_equalizer(args
->name
, args
->valueptr
);
770 case VOCTRL_UPDATE_SCREENINFO
:
771 update_xinerama_info();