1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3  *  Copyright (C) 2011-2017 - Daniel De Matteis
4  *
5  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
6  *  of the GNU General Public License as published by the Free Software Found-
7  *  ation, either version 3 of the License, or (at your option) any later version.
8  *
9  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11  *  PURPOSE.  See the GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License along with RetroArch.
14  *  If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include <stdlib.h>
18 #include <string.h>
19 #include <signal.h>
20 #include <math.h>
21 
22 #include <X11/Xlib.h>
23 #include <X11/Xutil.h>
24 #include <X11/Xatom.h>
25 #include <sys/ipc.h>
26 #include <sys/shm.h>
27 #include <X11/extensions/XShm.h>
28 #include <X11/extensions/Xv.h>
29 #include <X11/extensions/Xvlib.h>
30 
31 #include <retro_inline.h>
32 
33 #ifdef HAVE_CONFIG_H
34 #include "../../config.h"
35 #endif
36 
37 #ifdef HAVE_MENU
38 #include "../../menu/menu_driver.h"
39 #endif
40 
41 #include "../font_driver.h"
42 
43 #include "../../configuration.h"
44 #include "../../frontend/frontend_driver.h"
45 #include "../../input/input_driver.h"
46 #include "../../verbosity.h"
47 
48 #include "../common/x11_common.h"
49 
50 /* Adapted from bSNES and MPlayer source. */
51 
52 typedef struct xv
53 {
54    GC gc;
55    XShmSegmentInfo shminfo;
56 
57    XvPortID port;
58    int depth;
59    int visualid;
60 
61    XvImage *image;
62    uint32_t fourcc;
63 
64    unsigned width;
65    unsigned height;
66    bool keep_aspect;
67    struct video_viewport vp;
68 
69    uint8_t *ytable;
70    uint8_t *utable;
71    uint8_t *vtable;
72 
73    void *font;
74    const font_renderer_driver_t *font_driver;
75 
76    unsigned luma_index[2];
77    unsigned chroma_u_index;
78    unsigned chroma_v_index;
79 
80    uint8_t font_y;
81    uint8_t font_u;
82    uint8_t font_v;
83 
84    void (*render_func)(struct xv*, const void *frame,
85          unsigned width, unsigned height, unsigned pitch);
86 
87    void (*render_glyph)(struct xv*, int base_x, int base_y,
88 			const uint8_t *glyph, int atlas_width,
89 			int glyph_width, int glyph_height);
90 } xv_t;
91 
xv_set_nonblock_state(void * data,bool state,bool c,unsigned d)92 static void xv_set_nonblock_state(void *data, bool state, bool c, unsigned d)
93 {
94    xv_t *xv  = (xv_t*)data;
95    Atom atom = XInternAtom(g_x11_dpy, "XV_SYNC_TO_VBLANK", true);
96 
97    if (atom != None && xv->port)
98       XvSetPortAttribute(g_x11_dpy, xv->port, atom, !state);
99    else
100       RARCH_WARN("Failed to set SYNC_TO_VBLANK attribute.\n");
101 }
102 
xv_calculate_yuv(uint8_t * y,uint8_t * u,uint8_t * v,unsigned r,unsigned g,unsigned b)103 static INLINE void xv_calculate_yuv(uint8_t *y, uint8_t *u, uint8_t *v,
104       unsigned r, unsigned g, unsigned b)
105 {
106    int y_ = (int)(+((double)r * 0.257) + ((double)g * 0.504)
107          + ((double)b * 0.098) +  16.0);
108    int u_ = (int)(-((double)r * 0.148) - ((double)g * 0.291)
109          + ((double)b * 0.439) + 128.0);
110    int v_ = (int)(+((double)r * 0.439) - ((double)g * 0.368)
111          - ((double)b * 0.071) + 128.0);
112 
113    *y     = y_ < 0 ? 0 : (y_ > 255 ? 255 : y_);
114    *u     = y_ < 0 ? 0 : (u_ > 255 ? 255 : u_);
115    *v     = v_ < 0 ? 0 : (v_ > 255 ? 255 : v_);
116 }
117 
xv_init_yuv_tables(xv_t * xv)118 static void xv_init_yuv_tables(xv_t *xv)
119 {
120    unsigned i;
121    xv->ytable = (uint8_t*)malloc(0x10000);
122    xv->utable = (uint8_t*)malloc(0x10000);
123    xv->vtable = (uint8_t*)malloc(0x10000);
124 
125    for (i = 0; i < 0x10000; i++)
126    {
127       /* Extract RGB565 color data from i */
128       unsigned r = (i >> 11) & 0x1f;
129       unsigned g = (i >> 5)  & 0x3f;
130       unsigned b = (i >> 0)  & 0x1f;
131       r          = (r << 3) | (r >> 2);  /* R5->R8 */
132       g          = (g << 2) | (g >> 4);  /* G6->G8 */
133       b          = (b << 3) | (b >> 2);  /* B5->B8 */
134 
135       xv_calculate_yuv(&xv->ytable[i],
136             &xv->utable[i], &xv->vtable[i], r, g, b);
137    }
138 }
139 
xv_init_font(xv_t * xv,const char * font_path,unsigned font_size)140 static void xv_init_font(xv_t *xv, const char *font_path, unsigned font_size)
141 {
142    settings_t *settings   = config_get_ptr();
143    bool video_font_enable = settings->bools.video_font_enable;
144    const char *path_font  = settings->paths.path_font;
145    float video_font_size  = settings->floats.video_font_size;
146    float msg_color_r      = settings->floats.video_msg_color_r;
147    float msg_color_g      = settings->floats.video_msg_color_g;
148    float msg_color_b      = settings->floats.video_msg_color_b;
149 
150    if (!video_font_enable)
151       return;
152 
153    if (font_renderer_create_default(
154             &xv->font_driver,
155             &xv->font, *path_font
156             ? path_font : NULL,
157             video_font_size))
158    {
159       int r = msg_color_r * 255;
160       int g = msg_color_g * 255;
161       int b = msg_color_b * 255;
162       r = (r < 0 ? 0 : (r > 255 ? 255 : r));
163       g = (g < 0 ? 0 : (g > 255 ? 255 : g));
164       b = (b < 0 ? 0 : (b > 255 ? 255 : b));
165 
166       xv_calculate_yuv(&xv->font_y, &xv->font_u, &xv->font_v,
167             r, g, b);
168    }
169    else
170       RARCH_LOG("[XVideo]: Could not initialize fonts.\n");
171 }
172 
173 /* We render @ 2x scale to combat chroma downsampling.
174  * Also makes fonts more bearable. */
render16_yuy2(xv_t * xv,const void * input_,unsigned width,unsigned height,unsigned pitch)175 static void render16_yuy2(xv_t *xv, const void *input_,
176       unsigned width, unsigned height, unsigned pitch)
177 {
178    unsigned x, y;
179    const uint16_t *input = (const uint16_t*)input_;
180    uint8_t *output       = (uint8_t*)xv->image->data;
181 
182    for (y = 0; y < height; y++)
183    {
184       for (x = 0; x < width; x++)
185       {
186          uint16_t p         = *input++;
187          uint8_t y0         = xv->ytable[p];
188          uint8_t u          = xv->utable[p];
189          uint8_t v          = xv->vtable[p];
190 
191          unsigned img_width = xv->width << 1;
192 
193          output[0] = output[img_width]     = y0;
194          output[1] = output[img_width + 1] = u;
195          output[2] = output[img_width + 2] = y0;
196          output[3] = output[img_width + 3] = v;
197          output += 4;
198       }
199 
200       input  += (pitch >> 1) - width;
201       output += (xv->width - width) << 2;
202    }
203 }
204 
render16_uyvy(xv_t * xv,const void * input_,unsigned width,unsigned height,unsigned pitch)205 static void render16_uyvy(xv_t *xv, const void *input_,
206       unsigned width, unsigned height, unsigned pitch)
207 {
208    unsigned x, y;
209    const uint16_t *input = (const uint16_t*)input_;
210    uint8_t       *output = (uint8_t*)xv->image->data;
211 
212    for (y = 0; y < height; y++)
213    {
214       for (x = 0; x < width; x++)
215       {
216          uint16_t p         = *input++;
217          uint8_t y0         = xv->ytable[p];
218          uint8_t u          = xv->utable[p];
219          uint8_t v          = xv->vtable[p];
220          unsigned img_width = xv->width << 1;
221 
222          output[0] = output[img_width]     = u;
223          output[1] = output[img_width + 1] = y0;
224          output[2] = output[img_width + 2] = v;
225          output[3] = output[img_width + 3] = y0;
226          output += 4;
227       }
228 
229       input  += (pitch >> 1) - width;
230       output += (xv->width - width) << 2;
231    }
232 }
233 
render32_yuy2(xv_t * xv,const void * input_,unsigned width,unsigned height,unsigned pitch)234 static void render32_yuy2(xv_t *xv, const void *input_,
235       unsigned width, unsigned height, unsigned pitch)
236 {
237    unsigned x, y;
238    const uint32_t *input = (const uint32_t*)input_;
239    uint8_t *output       = (uint8_t*)xv->image->data;
240 
241    for (y = 0; y < height; y++)
242    {
243       for (x = 0; x < width; x++)
244       {
245          uint8_t y0, u, v;
246          unsigned img_width;
247          uint32_t p = *input++;
248          p = ((p >> 8) & 0xf800) | ((p >> 5) & 0x07e0)
249             | ((p >> 3) & 0x1f); /* ARGB -> RGB16 */
250 
251          y0        = xv->ytable[p];
252          u         = xv->utable[p];
253          v         = xv->vtable[p];
254 
255          img_width = xv->width << 1;
256          output[0] = output[img_width] = y0;
257          output[1] = output[img_width + 1] = u;
258          output[2] = output[img_width + 2] = y0;
259          output[3] = output[img_width + 3] = v;
260          output += 4;
261       }
262 
263       input  += (pitch >> 2) - width;
264       output += (xv->width - width) << 2;
265    }
266 }
267 
render32_uyvy(xv_t * xv,const void * input_,unsigned width,unsigned height,unsigned pitch)268 static void render32_uyvy(xv_t *xv, const void *input_,
269       unsigned width, unsigned height, unsigned pitch)
270 {
271    unsigned x, y;
272    const uint32_t *input = (const uint32_t*)input_;
273    uint16_t *output      = (uint16_t*)xv->image->data;
274 
275    for (y = 0; y < height; y++)
276    {
277       for (x = 0; x < width; x++)
278       {
279          uint8_t y0, u, v;
280          unsigned img_width;
281          uint32_t p = *input++;
282          p = ((p >> 8) & 0xf800)
283             | ((p >> 5) & 0x07e0) | ((p >> 3) & 0x1f); /* ARGB -> RGB16 */
284 
285          y0        = xv->ytable[p];
286          u         = xv->utable[p];
287          v         = xv->vtable[p];
288 
289          img_width = xv->width << 1;
290          output[0] = output[img_width] = u;
291          output[1] = output[img_width + 1] = y0;
292          output[2] = output[img_width + 2] = v;
293          output[3] = output[img_width + 3] = y0;
294          output += 4;
295       }
296 
297       input  += (pitch >> 2) - width;
298       output += (xv->width - width) << 2;
299    }
300 }
301 
render32_yuv12(xv_t * xv,const void * input_,unsigned width,unsigned height,unsigned pitch)302 static void render32_yuv12(xv_t *xv, const void *input_,
303       unsigned width, unsigned height, unsigned pitch)
304 {
305    unsigned x, y;
306    const uint32_t *input = (const uint32_t*)input_;
307    unsigned w0 = xv->width >> 1;
308    unsigned w1 = w0 << 1;
309    unsigned h0 = xv->height >> 1;
310    uint8_t *output       = (uint8_t*)xv->image->data;
311    uint8_t *outputu       = (uint8_t*)xv->image->data + 4 * w0 * h0;
312    uint8_t *outputv       = (uint8_t*)xv->image->data + 5 * w0 * h0;
313 
314    for (y = 0; y < height; y++)
315    {
316       for (x = 0; x < width; x++)
317       {
318          uint8_t y0, u, v;
319          unsigned img_width;
320          uint32_t p = *input++;
321          p = ((p >> 8) & 0xf800) | ((p >> 5) & 0x07e0)
322             | ((p >> 3) & 0x1f); /* ARGB -> RGB16 */
323 
324          y0        = xv->ytable[p];
325          u         = xv->utable[p];
326          v         = xv->vtable[p];
327 
328          output[0] = output[w1] = y0;
329 	 output[1] = output[w1+1] = y0;
330          output+=2;
331 	 *outputu++ = u;
332 	 *outputv++ = v;
333       }
334 
335       input  += (pitch >> 2) - width;
336       output += 4 * w0 - 2 * width;
337       outputu += (w0 - width);
338       outputv += (w0 - width);
339    }
340 }
341 
render16_yuv12(xv_t * xv,const void * input_,unsigned width,unsigned height,unsigned pitch)342 static void render16_yuv12(xv_t *xv, const void *input_,
343       unsigned width, unsigned height, unsigned pitch)
344 {
345    unsigned x, y;
346    const uint16_t *input = (const uint16_t*)input_;
347    unsigned w0 = xv->width >> 1;
348    unsigned w1 = w0 << 1;
349    unsigned h0 = xv->height >> 1;
350    uint8_t *output       = (uint8_t*)xv->image->data;
351    uint8_t *outputu       = (uint8_t*)xv->image->data + 4 * w0 * h0;
352    uint8_t *outputv       = (uint8_t*)xv->image->data + 5 * w0 * h0;
353 
354    for (y = 0; y < height; y++)
355    {
356       for (x = 0; x < width; x++)
357       {
358 	 uint16_t p         = *input++;
359          uint8_t y0         = xv->ytable[p];
360          uint8_t u          = xv->utable[p];
361          uint8_t v          = xv->vtable[p];
362 
363          output[0] = output[w1] = y0;
364 	 output[1] = output[w1+1] = y0;
365          output+=2;
366 	 *outputu++ = u;
367 	 *outputv++ = v;
368       }
369 
370       input  += (pitch >> 1) - width;
371       output += 4 * w0 - 2 * width;
372       outputu += (w0 - width);
373       outputv += (w0 - width);
374    }
375 }
376 
render_glyph_yuv12(xv_t * xv,int base_x,int base_y,const uint8_t * glyph,int atlas_width,int glyph_width,int glyph_height)377 static INLINE void render_glyph_yuv12(xv_t *xv, int base_x, int base_y,
378 				      const uint8_t *glyph, int atlas_width,
379 				      int glyph_width, int glyph_height)
380 {
381    uint8_t *out_luma, *out_u, *out_v;
382    int x, y, i;
383 
384    out_luma = (uint8_t*)xv->image->data + base_y * xv->width + (base_x);
385    out_u = (uint8_t*)xv->image->data + xv->width * xv->height + (base_y / 2) * xv->width / 2 + (base_x / 2);
386    out_v= (uint8_t*)xv->image->data + xv->width * xv->height * 5 / 4 + (base_y / 2) * xv->width / 2 + (base_x / 2);
387 
388    for (y = 0; y < glyph_height; y++, glyph += atlas_width, out_luma += xv->width)
389    {
390       /* 2 input pixels => 4 bytes (2Y, 1U, 1V). */
391 
392       for (x = 0; x < glyph_width; x += 2)
393       {
394 	 unsigned alpha[2], alpha_sub, blended;
395 
396 	 alpha[0] = glyph[x + 0];
397 	 alpha[1] = 0;
398 
399 	 if (x + 1 < glyph_width)
400 	    alpha[1] = glyph[x + 1];
401 
402 	 /* Blended alpha for the sub-sampled U/V channels. */
403 	 alpha_sub = (alpha[0] + alpha[1]) >> 1;
404 
405 	 for (i = 0; i < 2; i++)
406 	 {
407 	    unsigned blended = (xv->font_y * alpha[i]
408 				+ ((256 - alpha[i]) * out_luma[x+i])) >> 8;
409 	    out_luma[x+i] = blended;
410 	 }
411 
412 	 /* Blend chroma channels */
413 	 if (y & 1)
414 	 {
415 	    blended = (xv->font_u * alpha_sub
416 		       + ((256 - alpha_sub) * out_u[x/2])) >> 8;
417 	    out_u[x / 2] = blended;
418 
419 	    blended = (xv->font_v * alpha_sub
420 		       + ((256 - alpha_sub) * out_v[x/2])) >> 8;
421 	    out_v[x/2] = blended;
422 	 }
423       }
424 
425       if (y & 1)
426       {
427 	 out_u += xv->width / 2;
428 	 out_v += xv->width / 2;
429       }
430    }
431 }
432 
render_glyph_yuv_packed(xv_t * xv,int base_x,int base_y,const uint8_t * glyph,int atlas_width,int glyph_width,int glyph_height)433 static INLINE void render_glyph_yuv_packed(xv_t *xv, int base_x, int base_y,
434 					   const uint8_t *glyph, int atlas_width,
435 					   int glyph_width, int glyph_height)
436 {
437    uint8_t *out                   = NULL;
438    int x, y, i;
439    unsigned luma_index[2], pitch;
440    unsigned chroma_u_index, chroma_v_index;
441 
442    luma_index[0]  = xv->luma_index[0];
443    luma_index[1]  = xv->luma_index[1];
444 
445    chroma_u_index = xv->chroma_u_index;
446    chroma_v_index = xv->chroma_v_index;
447 
448    pitch          = xv->width << 1; /* YUV formats used are 16 bpp. */
449    out = (uint8_t*)xv->image->data + base_y * pitch + (base_x << 1);
450 
451    for (y = 0; y < glyph_height; y++, glyph += atlas_width, out += pitch)
452    {
453       /* 2 input pixels => 4 bytes (2Y, 1U, 1V). */
454 
455       for (x = 0; x < glyph_width; x += 2)
456       {
457 	 unsigned alpha[2], alpha_sub, blended;
458 	 int out_x = x << 1;
459 
460 	 alpha[0] = glyph[x + 0];
461 	 alpha[1] = 0;
462 
463 	 if (x + 1 < glyph_width)
464 	    alpha[1] = glyph[x + 1];
465 
466 	 /* Blended alpha for the sub-sampled U/V channels. */
467 	 alpha_sub = (alpha[0] + alpha[1]) >> 1;
468 
469 	 for (i = 0; i < 2; i++)
470 	 {
471 	    unsigned blended = (xv->font_y * alpha[i]
472 				+ ((256 - alpha[i]) * out[out_x + luma_index[i]])) >> 8;
473 	    out[out_x + luma_index[i]] = blended;
474 	 }
475 
476 	 /* Blend chroma channels */
477 	 blended = (xv->font_u * alpha_sub
478 		    + ((256 - alpha_sub) * out[out_x + chroma_u_index])) >> 8;
479 	 out[out_x + chroma_u_index] = blended;
480 
481 	 blended = (xv->font_v * alpha_sub
482 		    + ((256 - alpha_sub) * out[out_x + chroma_v_index])) >> 8;
483 	 out[out_x + chroma_v_index] = blended;
484       }
485    }
486 }
487 
488 struct format_desc
489 {
490    void (*render_16)(xv_t *xv, const void *input,
491          unsigned width, unsigned height, unsigned pitch);
492    void (*render_32)(xv_t *xv, const void *input,
493          unsigned width, unsigned height, unsigned pitch);
494    void (*render_glyph)(xv_t *xv, int base_x, int base_y,
495 			const uint8_t *glyph, int atlas_width,
496 			int glyph_width, int glyph_height);
497    char components[4];
498    unsigned luma_index[2];
499    unsigned u_index;
500    unsigned v_index;
501    unsigned bits;
502    int format;
503 };
504 
505 static const struct format_desc formats[] = {
506    {
507       render16_yuy2,
508       render32_yuy2,
509       render_glyph_yuv_packed,
510       { 'Y', 'U', 'Y', 'V' },
511       { 0, 2 },
512       1,
513       3,
514       16,
515       XvPacked
516    },
517    {
518       render16_uyvy,
519       render32_uyvy,
520       render_glyph_yuv_packed,
521       { 'U', 'Y', 'V', 'Y' },
522       { 1, 3 },
523       0,
524       2,
525       16,
526       XvPacked
527    },
528    {
529       render16_yuv12,
530       render32_yuv12,
531       render_glyph_yuv12,
532       { 'Y', 'U', 'V', 0 },
533       { 1, 3 },
534       0,
535       2,
536       12,
537       XvPlanar
538    },
539 };
540 
xv_adaptor_set_format(xv_t * xv,Display * dpy,XvPortID port,const video_info_t * video)541 static bool xv_adaptor_set_format(xv_t *xv, Display *dpy,
542       XvPortID port, const video_info_t *video)
543 {
544    int i;
545    unsigned j;
546    int format_count;
547    XvImageFormatValues *format = XvListImageFormats(
548          g_x11_dpy, port, &format_count);
549 
550    if (!format)
551       return false;
552 
553    for (i = 0; i < format_count; i++)
554    {
555       for (j = 0; j < ARRAY_SIZE(formats); j++)
556       {
557          if (format[i].type == XvYUV
558                && format[i].bits_per_pixel == formats[j].bits
559                && format[i].format == formats[j].format)
560          {
561             if (format[i].component_order[0] == formats[j].components[0] &&
562                   format[i].component_order[1] == formats[j].components[1] &&
563                   format[i].component_order[2] == formats[j].components[2] &&
564                   format[i].component_order[3] == formats[j].components[3])
565             {
566                xv->fourcc         = format[i].id;
567                xv->render_func    = video->rgb32
568                   ? formats[j].render_32 : formats[j].render_16;
569                xv->render_glyph    = formats[j].render_glyph;
570 
571                xv->luma_index[0]  = formats[j].luma_index[0];
572                xv->luma_index[1]  = formats[j].luma_index[1];
573                xv->chroma_u_index = formats[j].u_index;
574                xv->chroma_v_index = formats[j].v_index;
575                XFree(format);
576                return true;
577             }
578          }
579       }
580    }
581 
582    XFree(format);
583    return false;
584 }
585 
xv_calc_out_rect(bool keep_aspect,struct video_viewport * vp,unsigned vp_width,unsigned vp_height)586 static void xv_calc_out_rect(bool keep_aspect,
587       struct video_viewport *vp,
588       unsigned vp_width, unsigned vp_height)
589 {
590    settings_t *settings = config_get_ptr();
591    bool scale_integer   = settings->bools.video_scale_integer;
592 
593    vp->full_width       = vp_width;
594    vp->full_height      = vp_height;
595 
596    if (scale_integer)
597       video_viewport_get_scaled_integer(vp, vp_width, vp_height,
598             video_driver_get_aspect_ratio(), keep_aspect);
599    else if (!keep_aspect)
600    {
601       vp->x      = 0;
602       vp->y      = 0;
603       vp->width  = vp_width;
604       vp->height = vp_height;
605    }
606    else
607    {
608       float desired_aspect = video_driver_get_aspect_ratio();
609       float device_aspect  = (float)vp_width / vp_height;
610 
611       /* If the aspect ratios of screen and desired aspect ratio
612        * are sufficiently equal (floating point stuff),
613        * assume they are actually equal.
614        */
615       if (fabs(device_aspect - desired_aspect) < 0.0001)
616       {
617          vp->x       = 0;
618          vp->y       = 0;
619          vp->width   = vp_width;
620          vp->height  = vp_height;
621       }
622       else if (device_aspect > desired_aspect)
623       {
624          float delta = (desired_aspect / device_aspect - 1.0) / 2.0 + 0.5;
625          vp->x       = vp_width * (0.5 - delta);
626          vp->y       = 0;
627          vp->width   = 2.0 * vp_width * delta;
628          vp->height  = vp_height;
629       }
630       else
631       {
632          float delta = (device_aspect / desired_aspect - 1.0) / 2.0 + 0.5;
633          vp->x       = 0;
634          vp->y       = vp_height * (0.5 - delta);
635          vp->width   = vp_width;
636          vp->height  = 2.0 * vp_height * delta;
637       }
638    }
639 }
640 
xv_init(const video_info_t * video,input_driver_t ** input,void ** input_data)641 static void *xv_init(const video_info_t *video,
642       input_driver_t **input, void **input_data)
643 {
644    unsigned i;
645    int ret;
646    XWindowAttributes target;
647    char title[128]                        = {0};
648    XSetWindowAttributes attributes        = {0};
649    XVisualInfo visualtemplate             = {0};
650    unsigned width                         = 0;
651    unsigned height                        = 0;
652    unsigned adaptor_count                 = 0;
653    int visualmatches                      = 0;
654    Atom atom                              = 0;
655    void *xinput                           = NULL;
656    XVisualInfo *visualinfo                = NULL;
657    XvAdaptorInfo *adaptor_info            = NULL;
658    const struct retro_game_geometry *geom = NULL;
659    struct retro_system_av_info *av_info   = NULL;
660    settings_t *settings                   = config_get_ptr();
661    bool video_disable_composition         = settings->bools.video_disable_composition;
662    xv_t                               *xv = (xv_t*)calloc(1, sizeof(*xv));
663    if (!xv)
664       return NULL;
665 
666    XInitThreads();
667 
668    g_x11_dpy = XOpenDisplay(NULL);
669 
670    if (!g_x11_dpy)
671    {
672       RARCH_ERR("[XVideo]: Cannot connect to the X server.\n");
673       RARCH_ERR("[XVideo]: Check DISPLAY variable and if X is running.\n");
674       goto error;
675    }
676 
677    av_info = video_viewport_get_system_av_info();
678 
679    if (av_info)
680       geom        = &av_info->geometry;
681 
682    if (!XShmQueryExtension(g_x11_dpy))
683    {
684       RARCH_ERR("[XVideo]: XShm extension not found.\n");
685       goto error;
686    }
687 
688    xv->keep_aspect = video->force_aspect;
689 
690    /* Find an appropriate Xv port. */
691    xv->port = 0;
692    ret = XvQueryAdaptors(g_x11_dpy,
693          DefaultRootWindow(g_x11_dpy), &adaptor_count, &adaptor_info);
694 
695    if (ret != Success)
696    {
697       if (ret == XvBadExtension)
698          RARCH_ERR("[XVideo]: Xv extension not found.\n");
699       else if (ret == XvBadAlloc)
700          RARCH_ERR("[XVideo]: XvQueryAdaptors() failed to allocate memory.\n");
701       else
702          RARCH_ERR("[XVideo]: Unkown error in XvQueryAdaptors().\n");
703 
704       goto error;
705    }
706 
707    if (adaptor_count == 0)
708    {
709       RARCH_ERR("[XVideo]: XvQueryAdaptors() found 0 adaptors.\n");
710       goto error;
711    }
712 
713    for (i = 0; i < adaptor_count; i++)
714    {
715       /* Find adaptor that supports both input (memory->drawable)
716        * and image (drawable->screen) masks. */
717 
718       if (adaptor_info[i].num_formats < 1)
719          continue;
720       if (!(adaptor_info[i].type & XvInputMask))
721          continue;
722       if (!(adaptor_info[i].type & XvImageMask))
723          continue;
724       if (!xv_adaptor_set_format(xv, g_x11_dpy,
725                adaptor_info[i].base_id, video))
726          continue;
727 
728       xv->port     = adaptor_info[i].base_id;
729       xv->depth    = adaptor_info[i].formats->depth;
730       xv->visualid = adaptor_info[i].formats->visual_id;
731 
732       RARCH_LOG("[XVideo]: Found suitable XvPort #%u\n", (unsigned)xv->port);
733       break;
734    }
735    XvFreeAdaptorInfo(adaptor_info);
736 
737    if (xv->port == 0)
738    {
739       RARCH_ERR("[XVideo]: Failed to find valid XvPort or format.\n");
740       goto error;
741    }
742 
743    visualtemplate.visualid = xv->visualid;
744    visualtemplate.screen   = DefaultScreen(g_x11_dpy);
745    visualtemplate.depth    = xv->depth;
746    visualtemplate.visual   = 0;
747    visualinfo              = XGetVisualInfo(g_x11_dpy, VisualIDMask |
748          VisualScreenMask | VisualDepthMask, &visualtemplate, &visualmatches);
749 
750    if (!visualinfo)
751       goto error;
752 
753    if (visualmatches < 1 || !visualinfo->visual)
754    {
755       RARCH_ERR("[XVideo]: Unable to find Xv-compatible visual.\n");
756       goto error;
757    }
758 
759    g_x11_cmap = XCreateColormap(g_x11_dpy,
760          DefaultRootWindow(g_x11_dpy), visualinfo->visual, AllocNone);
761 
762    attributes.colormap     = g_x11_cmap;
763    attributes.border_pixel = 0;
764    attributes.event_mask   = StructureNotifyMask | KeyPressMask |
765       KeyReleaseMask | ButtonReleaseMask | ButtonPressMask | DestroyNotify | ClientMessage;
766 
767    if (video->fullscreen)
768    {
769       width      = (((video->width  == 0) && geom) ? geom->base_width : video->width);
770       height     = (((video->height == 0) && geom) ? geom->base_height : video->height);
771    }
772    else
773    {
774       width      = video->width;
775       height     = video->height;
776    }
777    g_x11_win  = XCreateWindow(g_x11_dpy, DefaultRootWindow(g_x11_dpy),
778          0, 0, width, height,
779          0, xv->depth, InputOutput, visualinfo->visual,
780          CWColormap | CWBorderPixel | CWEventMask, &attributes);
781 
782    XFree(visualinfo);
783    XSetWindowBackground(g_x11_dpy, g_x11_win, 0);
784 
785    if (video->fullscreen && video_disable_composition)
786    {
787       uint32_t value = 1;
788       Atom cardinal = XInternAtom(g_x11_dpy, "CARDINAL", False);
789       Atom net_wm_bypass_compositor = XInternAtom(g_x11_dpy,
790             "_NET_WM_BYPASS_COMPOSITOR", False);
791 
792       RARCH_LOG("[XVideo]: Requesting compositor bypass.\n");
793       XChangeProperty(g_x11_dpy, g_x11_win,
794             net_wm_bypass_compositor, cardinal, 32,
795             PropModeReplace, (const unsigned char*)&value, 1);
796    }
797 
798    XMapWindow(g_x11_dpy, g_x11_win);
799 
800    video_driver_get_window_title(title, sizeof(title));
801 
802    if (title[0])
803       XStoreName(g_x11_dpy, g_x11_win, title);
804 
805    x11_set_window_attr(g_x11_dpy, g_x11_win);
806 
807    if (video->fullscreen)
808    {
809       x11_set_net_wm_fullscreen(g_x11_dpy, g_x11_win);
810       x11_show_mouse(g_x11_dpy, g_x11_win, false);
811    }
812 
813    xv->gc = XCreateGC(g_x11_dpy, g_x11_win, 0, 0);
814 
815    /* Set colorkey to auto paint, so that Xv video output is always visible. */
816    atom = XInternAtom(g_x11_dpy, "XV_AUTOPAINT_COLORKEY", true);
817    if (atom != None)
818       XvSetPortAttribute(g_x11_dpy, xv->port, atom, 1);
819 
820    if (geom)
821    {
822       xv->width  = geom->max_width;
823       xv->height = geom->max_height;
824    }
825 
826    xv->image = XvShmCreateImage(g_x11_dpy, xv->port, xv->fourcc,
827          NULL, xv->width, xv->height, &xv->shminfo);
828 
829    if (!xv->image)
830    {
831       RARCH_ERR("[XVideo]: XShmCreateImage failed.\n");
832       goto error;
833    }
834 
835    xv->width            = xv->image->width;
836    xv->height           = xv->image->height;
837    xv->shminfo.shmid    = shmget(IPC_PRIVATE, xv->image->data_size, IPC_CREAT | 0777);
838    xv->shminfo.shmaddr  = xv->image->data = (char*)shmat(xv->shminfo.shmid, NULL, 0);
839    xv->shminfo.readOnly = false;
840 
841    if (!XShmAttach(g_x11_dpy, &xv->shminfo))
842    {
843       RARCH_ERR("[XVideo]: XShmAttach failed.\n");
844       goto error;
845    }
846    XSync(g_x11_dpy, False);
847    memset(xv->image->data, 128, xv->image->data_size);
848 
849    x11_install_quit_atom();
850 
851    frontend_driver_install_signal_handler();
852 
853    xv_init_yuv_tables(xv);
854    xv_init_font(xv, settings->paths.path_font, settings->floats.video_font_size);
855 
856    if (!x11_input_ctx_new(true))
857       goto error;
858 
859    if (input && input_data)
860    {
861       xinput = input_driver_init_wrap(&input_x,
862             settings->arrays.input_joypad_driver);
863       if (xinput)
864       {
865          *input = &input_x;
866          *input_data = xinput;
867       }
868       else
869          *input = NULL;
870    }
871 
872    XGetWindowAttributes(g_x11_dpy, g_x11_win, &target);
873    xv_calc_out_rect(xv->keep_aspect, &xv->vp, target.width, target.height);
874    xv->vp.full_width = target.width;
875    xv->vp.full_height = target.height;
876 
877    return xv;
878 
879 error:
880    if (visualinfo)
881       XFree(visualinfo);
882    free(xv);
883    return NULL;
884 }
885 
xv_check_resize(xv_t * xv,unsigned width,unsigned height)886 static bool xv_check_resize(xv_t *xv, unsigned width, unsigned height)
887 {
888    /* We render @ 2x scale to combat chroma downsampling. */
889    if (xv->width != (width << 1) || xv->height != (height << 1))
890    {
891       xv->width  = width << 1;
892       xv->height = height << 1;
893 
894       XShmDetach(g_x11_dpy, &xv->shminfo);
895       shmdt(xv->shminfo.shmaddr);
896       shmctl(xv->shminfo.shmid, IPC_RMID, NULL);
897       XFree(xv->image);
898 
899       memset(&xv->shminfo, 0, sizeof(xv->shminfo));
900       xv->image = XvShmCreateImage(g_x11_dpy, xv->port, xv->fourcc,
901             NULL, xv->width, xv->height, &xv->shminfo);
902 
903       if (xv->image == None)
904       {
905          RARCH_ERR("[XVideo]: Failed to create image.\n");
906          return false;
907       }
908 
909       xv->width  = xv->image->width;
910       xv->height = xv->image->height;
911 
912       xv->shminfo.shmid =
913          shmget(IPC_PRIVATE, xv->image->data_size, IPC_CREAT | 0777);
914 
915       if (xv->shminfo.shmid < 0)
916       {
917          RARCH_ERR("[XVideo]: Failed to init SHM.\n");
918          return false;
919       }
920 
921       xv->shminfo.shmaddr  = xv->image->data =
922          (char*)shmat(xv->shminfo.shmid, NULL, 0);
923       xv->shminfo.readOnly = false;
924 
925       if (!XShmAttach(g_x11_dpy, &xv->shminfo))
926       {
927          RARCH_ERR("[XVideo]: Failed to reattch XvShm image.\n");
928          return false;
929       }
930       XSync(g_x11_dpy, False);
931       memset(xv->image->data, 128, xv->image->data_size);
932    }
933    return true;
934 }
935 
936 /* TODO: Is there some way to render directly like GL?
937  * Hacky C code is hacky. */
xv_render_msg(xv_t * xv,const char * msg,unsigned width,unsigned height)938 static void xv_render_msg(xv_t *xv, const char *msg,
939       unsigned width, unsigned height)
940 {
941    int msg_base_x, msg_base_y;
942    const struct font_atlas *atlas = NULL;
943    settings_t           *settings = config_get_ptr();
944    float video_msg_pos_x          = settings->floats.video_msg_pos_x;
945    float video_msg_pos_y          = settings->floats.video_msg_pos_y;
946 
947    if (!xv->font)
948       return;
949 
950    atlas          = xv->font_driver->get_atlas(xv->font);
951 
952    msg_base_x     = video_msg_pos_x * width;
953    msg_base_y     = height * (1.0f - video_msg_pos_y);
954 
955    for (; *msg; msg++)
956    {
957       int base_x, base_y, glyph_width, glyph_height, max_width, max_height;
958       const uint8_t *src             = NULL;
959       const struct font_glyph *glyph =
960          xv->font_driver->get_glyph(xv->font, (uint8_t)*msg);
961 
962       if (!glyph)
963          continue;
964 
965       /* Make sure we always start on the correct boundary
966        * so the indices are correct. */
967       base_x          = (msg_base_x + glyph->draw_offset_x + 1) & ~1;
968       base_y          = msg_base_y + glyph->draw_offset_y;
969 
970       glyph_width     = glyph->width;
971       glyph_height    = glyph->height;
972 
973       src             = atlas->buffer + glyph->atlas_offset_x +
974                         glyph->atlas_offset_y * atlas->width;
975 
976       if (base_x < 0)
977       {
978          src          -= base_x;
979          glyph_width  += base_x;
980          base_x = 0;
981       }
982 
983       if (base_y < 0)
984       {
985          src          -= base_y * (int)atlas->width;
986          glyph_height += base_y;
987          base_y = 0;
988       }
989 
990       max_width        = width - base_x;
991       max_height       = height - base_y;
992 
993       if (max_width <= 0 || max_height <= 0)
994          continue;
995 
996       if (glyph_width > max_width)
997          glyph_width   = max_width;
998       if (glyph_height > max_height)
999          glyph_height  = max_height;
1000 
1001       xv->render_glyph(xv, base_x, base_y, src, atlas->width, glyph_width, glyph_height);
1002 
1003       msg_base_x += glyph->advance_x;
1004       msg_base_y += glyph->advance_y;
1005    }
1006 }
1007 
xv_frame(void * data,const void * frame,unsigned width,unsigned height,uint64_t frame_count,unsigned pitch,const char * msg,video_frame_info_t * video_info)1008 static bool xv_frame(void *data, const void *frame, unsigned width,
1009       unsigned height, uint64_t frame_count,
1010       unsigned pitch, const char *msg, video_frame_info_t *video_info)
1011 {
1012    XWindowAttributes target;
1013    xv_t *xv                  = (xv_t*)data;
1014 #ifdef HAVE_MENU
1015    bool menu_is_alive        = video_info->menu_is_alive;
1016 #endif
1017 
1018    if (!frame)
1019       return true;
1020 
1021    if (!xv_check_resize(xv, width, height))
1022       return false;
1023 
1024    XGetWindowAttributes(g_x11_dpy, g_x11_win, &target);
1025    xv->render_func(xv, frame, width, height, pitch);
1026 
1027    xv_calc_out_rect(xv->keep_aspect, &xv->vp, target.width, target.height);
1028    xv->vp.full_width  = target.width;
1029    xv->vp.full_height = target.height;
1030 
1031 #ifdef HAVE_MENU
1032    menu_driver_frame(menu_is_alive, video_info);
1033 #endif
1034 
1035    if (msg)
1036       xv_render_msg(xv, msg, width << 1, height << 1);
1037 
1038    XvShmPutImage(g_x11_dpy, xv->port, g_x11_win, xv->gc, xv->image,
1039          0, 0, width << 1, height << 1,
1040          xv->vp.x, xv->vp.y, xv->vp.width, xv->vp.height,
1041          true);
1042    XSync(g_x11_dpy, False);
1043 
1044    x11_update_title(NULL);
1045 
1046    return true;
1047 }
1048 
xv_suppress_screensaver(void * data,bool enable)1049 static bool xv_suppress_screensaver(void *data, bool enable)
1050 {
1051    if (video_driver_display_type_get() != RARCH_DISPLAY_X11)
1052       return false;
1053 
1054    x11_suspend_screensaver(video_driver_window_get(), enable);
1055    return true;
1056 }
1057 
xv_has_windowed(void * data)1058 static bool xv_has_windowed(void *data) { return true; }
1059 
xv_free(void * data)1060 static void xv_free(void *data)
1061 {
1062    xv_t *xv = (xv_t*)data;
1063 
1064    if (!xv)
1065       return;
1066 
1067    x11_input_ctx_destroy();
1068 
1069    XShmDetach(g_x11_dpy, &xv->shminfo);
1070    shmdt(xv->shminfo.shmaddr);
1071    shmctl(xv->shminfo.shmid, IPC_RMID, NULL);
1072    XFree(xv->image);
1073 
1074    x11_window_destroy(true);
1075    x11_colormap_destroy();
1076 
1077    XCloseDisplay(g_x11_dpy);
1078 
1079    free(xv->ytable);
1080    free(xv->utable);
1081    free(xv->vtable);
1082 
1083    if (xv->font)
1084       xv->font_driver->free(xv->font);
1085 
1086    free(xv);
1087 }
1088 
xv_viewport_info(void * data,struct video_viewport * vp)1089 static void xv_viewport_info(void *data, struct video_viewport *vp)
1090 {
1091    xv_t *xv = (xv_t*)data;
1092    *vp = xv->vp;
1093 }
1094 
xv_get_flags(void * data)1095 static uint32_t xv_get_flags(void *data) { return 0; }
1096 
1097 static video_poke_interface_t xv_video_poke_interface = {
1098    xv_get_flags,
1099    NULL,
1100    NULL,
1101    NULL,
1102    x11_get_refresh_rate,
1103    NULL,
1104    NULL,
1105    NULL,
1106    NULL,
1107    NULL,
1108    NULL,
1109    NULL,
1110    NULL,
1111    NULL,
1112    NULL,
1113    NULL,
1114    NULL,
1115    NULL,
1116    NULL,
1117    NULL,
1118    NULL
1119 };
1120 
xv_get_poke_interface(void * data,const video_poke_interface_t ** iface)1121 static void xv_get_poke_interface(void *data,
1122       const video_poke_interface_t **iface)
1123 {
1124    (void)data;
1125    *iface = &xv_video_poke_interface;
1126 }
1127 
xv_set_shader(void * data,enum rarch_shader_type type,const char * path)1128 static bool xv_set_shader(void *data,
1129       enum rarch_shader_type type, const char *path)
1130 {
1131    (void)data;
1132    (void)type;
1133    (void)path;
1134 
1135    return false;
1136 }
1137 
1138 video_driver_t video_xvideo = {
1139    xv_init,
1140    xv_frame,
1141    xv_set_nonblock_state,
1142    x11_alive,
1143    x11_has_focus_internal,
1144    xv_suppress_screensaver,
1145    xv_has_windowed,
1146    xv_set_shader,
1147    xv_free,
1148    "xvideo",
1149    NULL, /* set_viewport */
1150    NULL, /* set_rotation */
1151    xv_viewport_info,
1152    NULL, /* read_viewport */
1153    NULL, /* read_frame_raw */
1154 #ifdef HAVE_OVERLAY
1155   NULL, /* overlay_interface */
1156 #endif
1157 #ifdef HAVE_VIDEO_LAYOUT
1158   NULL,
1159 #endif
1160   xv_get_poke_interface
1161 };
1162