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