RetroArch/gfx/xvideo.c

804 lines
23 KiB
C
Raw Normal View History

2012-04-21 21:13:50 +00:00
/* RetroArch - A frontend for libretro.
2013-01-01 00:37:37 +00:00
* Copyright (C) 2010-2013 - Hans-Kristian Arntzen
*
2012-04-21 21:13:50 +00:00
* RetroArch is free software: you can redistribute it and/or modify it under the terms
2011-03-13 03:51:09 +00:00
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
2012-04-21 21:13:50 +00:00
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
2011-03-13 03:51:09 +00:00
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
2012-04-21 21:31:57 +00:00
* You should have received a copy of the GNU General Public License along with RetroArch.
2011-03-13 03:51:09 +00:00
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "driver.h"
#include "general.h"
#include <stdlib.h>
2011-03-13 12:11:33 +00:00
#include <string.h>
2011-03-13 13:12:43 +00:00
#include <signal.h>
2011-03-26 17:34:58 +00:00
#include <math.h>
2012-09-26 13:52:25 +00:00
#include "gfx_common.h"
2012-05-27 00:04:50 +00:00
#include "fonts/fonts.h"
2012-09-26 13:52:25 +00:00
#include "context/x11_common.h"
2011-03-13 03:51:09 +00:00
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>
#include <X11/extensions/Xv.h>
#include <X11/extensions/Xvlib.h>
2011-03-13 17:09:27 +00:00
// Adapted from bSNES and MPlayer source.
2011-03-13 13:12:43 +00:00
2011-12-24 12:46:12 +00:00
typedef struct xv
2011-03-13 03:51:09 +00:00
{
Display *display;
GC gc;
Window window;
Colormap colormap;
XShmSegmentInfo shminfo;
2011-03-13 13:12:43 +00:00
Atom quit_atom;
2011-03-13 19:52:06 +00:00
bool focus;
2011-03-13 13:12:43 +00:00
XvPortID port;
2011-03-13 03:51:09 +00:00
int depth;
int visualid;
XvImage *image;
2011-03-13 12:11:33 +00:00
uint32_t fourcc;
2011-03-13 03:51:09 +00:00
unsigned width;
unsigned height;
2011-03-13 12:11:33 +00:00
bool keep_aspect;
struct rarch_viewport vp;
2011-03-13 03:51:09 +00:00
uint8_t *ytable;
uint8_t *utable;
uint8_t *vtable;
2011-03-13 19:52:06 +00:00
2012-12-14 21:33:04 +00:00
void *font;
const font_renderer_driver_t *font_driver;
2011-09-06 15:56:42 +00:00
unsigned luma_index[2];
unsigned chroma_u_index;
unsigned chroma_v_index;
uint8_t font_y;
uint8_t font_u;
uint8_t font_v;
2011-03-13 14:11:53 +00:00
2011-12-24 12:46:12 +00:00
void (*render_func)(struct xv*, const void *frame, unsigned width, unsigned height, unsigned pitch);
} xv_t;
2011-03-13 03:51:09 +00:00
2011-03-13 17:09:27 +00:00
static void xv_set_nonblock_state(void *data, bool state)
{
2011-12-24 12:46:12 +00:00
xv_t *xv = (xv_t*)data;
2011-03-13 17:09:27 +00:00
Atom atom = XInternAtom(xv->display, "XV_SYNC_TO_VBLANK", true);
2011-04-08 18:53:11 +00:00
if (atom != None && xv->port)
2011-03-13 17:09:27 +00:00
XvSetPortAttribute(xv->display, xv->port, atom, !state);
else
2012-04-21 21:25:32 +00:00
RARCH_WARN("Failed to set SYNC_TO_VBLANK attribute.\n");
2011-03-13 17:09:27 +00:00
}
2011-03-13 13:12:43 +00:00
static volatile sig_atomic_t g_quit = 0;
2011-03-13 13:12:43 +00:00
static void sighandler(int sig)
{
g_quit = 1;
2011-03-13 13:12:43 +00:00
}
2011-09-06 15:56:42 +00:00
static inline void calculate_yuv(uint8_t *y, uint8_t *u, uint8_t *v, unsigned r, unsigned g, unsigned b)
{
int y_ = (int)(+((double)r * 0.257) + ((double)g * 0.504) + ((double)b * 0.098) + 16.0);
int u_ = (int)(-((double)r * 0.148) - ((double)g * 0.291) + ((double)b * 0.439) + 128.0);
int v_ = (int)(+((double)r * 0.439) - ((double)g * 0.368) - ((double)b * 0.071) + 128.0);
*y = y_ < 0 ? 0 : (y_ > 255 ? 255 : y_);
*u = y_ < 0 ? 0 : (u_ > 255 ? 255 : u_);
*v = v_ < 0 ? 0 : (v_ > 255 ? 255 : v_);
}
2011-03-13 03:51:09 +00:00
static void init_yuv_tables(xv_t *xv)
{
2012-10-21 19:59:25 +00:00
xv->ytable = (uint8_t*)malloc(0x10000);
xv->utable = (uint8_t*)malloc(0x10000);
xv->vtable = (uint8_t*)malloc(0x10000);
2011-03-13 03:51:09 +00:00
2012-10-21 19:59:25 +00:00
for (unsigned i = 0; i < 0x10000; i++)
2011-03-13 03:51:09 +00:00
{
2012-10-21 19:59:25 +00:00
// Extract RGB565 color data from i
unsigned r = (i >> 11) & 0x1f, g = (i >> 5) & 0x3f, b = (i >> 0) & 0x1f;
2011-09-06 15:56:42 +00:00
r = (r << 3) | (r >> 2); // R5->R8
2012-10-21 19:59:25 +00:00
g = (g << 2) | (g >> 4); // G6->G8
2011-09-06 15:56:42 +00:00
b = (b << 3) | (b >> 2); // B5->B8
calculate_yuv(&xv->ytable[i], &xv->utable[i], &xv->vtable[i], r, g, b);
2011-03-13 03:51:09 +00:00
}
}
2011-03-13 19:52:06 +00:00
static void xv_init_font(xv_t *xv, const char *font_path, unsigned font_size)
{
2011-11-09 23:15:41 +00:00
if (!g_settings.video.font_enable)
return;
if (font_renderer_create_default(&xv->font_driver, &xv->font))
2011-03-13 19:52:06 +00:00
{
int r = g_settings.video.msg_color_r * 255;
r = (r < 0 ? 0 : (r > 255 ? 255 : r));
int g = g_settings.video.msg_color_g * 255;
g = (g < 0 ? 0 : (g > 255 ? 255 : g));
int b = g_settings.video.msg_color_b * 255;
b = (b < 0 ? 0 : (b > 255 ? 255 : b));
calculate_yuv(&xv->font_y, &xv->font_u, &xv->font_v,
r, g, b);
2011-03-13 19:52:06 +00:00
}
2011-10-06 19:12:11 +00:00
else
RARCH_LOG("Could not initialize fonts.\n");
2011-03-13 19:52:06 +00:00
}
// We render @ 2x scale to combat chroma downsampling. Also makes fonts more bearable :)
static void render16_yuy2(xv_t *xv, const void *input_, unsigned width, unsigned height, unsigned pitch)
2011-03-13 03:51:09 +00:00
{
2011-12-24 12:46:12 +00:00
const uint16_t *input = (const uint16_t*)input_;
2011-03-13 14:49:00 +00:00
uint8_t *output = (uint8_t*)xv->image->data;
2011-03-13 03:51:09 +00:00
for (unsigned y = 0; y < height; y++)
2011-03-13 03:51:09 +00:00
{
for (unsigned x = 0; x < width; x++)
2011-03-13 03:51:09 +00:00
{
uint16_t p = *input++;
uint8_t y0 = xv->ytable[p];
uint8_t u = xv->utable[p];
uint8_t v = xv->vtable[p];
unsigned img_width = xv->width << 1;
output[0] = output[img_width] = y0;
output[1] = output[img_width + 1] = u;
output[2] = output[img_width + 2] = y0;
output[3] = output[img_width + 3] = v;
output += 4;
2011-03-13 03:51:09 +00:00
}
input += (pitch >> 1) - width;
output += (xv->width - width) << 2;
2011-03-13 03:51:09 +00:00
}
}
static void render16_uyvy(xv_t *xv, const void *input_, unsigned width, unsigned height, unsigned pitch)
2011-03-13 14:28:57 +00:00
{
2011-12-24 12:46:12 +00:00
const uint16_t *input = (const uint16_t*)input_;
2011-03-13 14:49:00 +00:00
uint8_t *output = (uint8_t*)xv->image->data;
2011-03-13 14:28:57 +00:00
for (unsigned y = 0; y < height; y++)
2011-03-13 14:28:57 +00:00
{
for (unsigned x = 0; x < width; x++)
2011-03-13 14:28:57 +00:00
{
uint16_t p = *input++;
uint8_t y0 = xv->ytable[p];
uint8_t u = xv->utable[p];
uint8_t v = xv->vtable[p];
unsigned img_width = xv->width << 1;
output[0] = output[img_width] = u;
output[1] = output[img_width + 1] = y0;
output[2] = output[img_width + 2] = v;
output[3] = output[img_width + 3] = y0;
output += 4;
2011-03-13 14:28:57 +00:00
}
input += (pitch >> 1) - width;
output += (xv->width - width) << 2;
2011-03-13 14:28:57 +00:00
}
}
static void render32_yuy2(xv_t *xv, const void *input_, unsigned width, unsigned height, unsigned pitch)
2011-03-13 14:28:57 +00:00
{
2011-12-24 12:46:12 +00:00
const uint32_t *input = (const uint32_t*)input_;
2011-03-13 14:49:00 +00:00
uint8_t *output = (uint8_t*)xv->image->data;
2011-03-13 14:28:57 +00:00
for (unsigned y = 0; y < height; y++)
2011-03-13 14:28:57 +00:00
{
for (unsigned x = 0; x < width; x++)
2011-03-13 14:28:57 +00:00
{
uint32_t p = *input++;
2012-10-21 19:59:25 +00:00
p = ((p >> 8) & 0xf800) | ((p >> 5) & 0x07e0) | ((p >> 3) & 0x1f); // ARGB -> RGB16
uint8_t y0 = xv->ytable[p];
uint8_t u = xv->utable[p];
uint8_t v = xv->vtable[p];
unsigned img_width = xv->width << 1;
output[0] = output[img_width] = y0;
output[1] = output[img_width + 1] = u;
output[2] = output[img_width + 2] = y0;
output[3] = output[img_width + 3] = v;
output += 4;
2011-03-13 14:28:57 +00:00
}
input += (pitch >> 2) - width;
output += (xv->width - width) << 2;
2011-03-13 14:28:57 +00:00
}
}
static void render32_uyvy(xv_t *xv, const void *input_, unsigned width, unsigned height, unsigned pitch)
2011-03-13 14:28:57 +00:00
{
2011-12-24 12:46:12 +00:00
const uint32_t *input = (const uint32_t*)input_;
2011-03-13 14:28:57 +00:00
uint16_t *output = (uint16_t*)xv->image->data;
for (unsigned y = 0; y < height; y++)
2011-03-13 14:28:57 +00:00
{
for (unsigned x = 0; x < width; x++)
2011-03-13 14:28:57 +00:00
{
uint32_t p = *input++;
2012-10-21 19:59:25 +00:00
p = ((p >> 8) & 0xf800) | ((p >> 5) & 0x07e0) | ((p >> 3) & 0x1f); // ARGB -> RGB16
uint8_t y0 = xv->ytable[p];
uint8_t u = xv->utable[p];
uint8_t v = xv->vtable[p];
unsigned img_width = xv->width << 1;
output[0] = output[img_width] = u;
output[1] = output[img_width + 1] = y0;
output[2] = output[img_width + 2] = v;
output[3] = output[img_width + 3] = y0;
output += 4;
2011-03-13 14:28:57 +00:00
}
input += (pitch >> 2) - width;
output += (xv->width - width) << 2;
2011-03-13 14:28:57 +00:00
}
}
struct format_desc
{
void (*render_16)(xv_t *xv, const void *input,
unsigned width, unsigned height, unsigned pitch);
void (*render_32)(xv_t *xv, const void *input,
unsigned width, unsigned height, unsigned pitch);
char components[4];
unsigned luma_index[2];
unsigned u_index;
unsigned v_index;
};
static const struct format_desc formats[] = {
{
2011-12-24 12:46:12 +00:00
render16_yuy2,
render32_yuy2,
{ 'Y', 'U', 'Y', 'V' },
{ 0, 2 },
1,
3,
},
{
2011-12-24 12:46:12 +00:00
render16_uyvy,
render32_uyvy,
{ 'U', 'Y', 'V', 'Y' },
{ 1, 3 },
0,
2,
},
};
static bool adaptor_set_format(xv_t *xv, Display *dpy, XvPortID port, const video_info_t *video)
{
int format_count;
XvImageFormatValues *format = XvListImageFormats(xv->display, port, &format_count);
if (!format)
return false;
for (int i = 0; i < format_count; i++)
{
for (unsigned j = 0; j < sizeof(formats) / sizeof(formats[0]); j++)
{
if (format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked)
{
if (format[i].component_order[0] == formats[j].components[0] &&
format[i].component_order[1] == formats[j].components[1] &&
format[i].component_order[2] == formats[j].components[2] &&
format[i].component_order[3] == formats[j].components[3])
{
xv->fourcc = format[i].id;
xv->render_func = video->rgb32 ? formats[j].render_32 : formats[j].render_16;
xv->luma_index[0] = formats[j].luma_index[0];
xv->luma_index[1] = formats[j].luma_index[1];
xv->chroma_u_index = formats[j].u_index;
xv->chroma_v_index = formats[j].v_index;
XFree(format);
return true;
}
}
}
}
XFree(format);
return false;
}
static void *xv_init(const video_info_t *video, const input_driver_t **input, void **input_data)
2011-03-13 03:51:09 +00:00
{
2011-12-24 12:46:12 +00:00
xv_t *xv = (xv_t*)calloc(1, sizeof(*xv));
2011-03-13 03:51:09 +00:00
if (!xv)
return NULL;
XInitThreads();
2011-03-13 03:51:09 +00:00
xv->display = XOpenDisplay(NULL);
2011-12-24 12:46:12 +00:00
struct sigaction sa;
unsigned adaptor_count = 0;
int visualmatches = 0;
XSetWindowAttributes attributes = {0};
unsigned width = 0, height = 0;
char buf[128], buf_fps[128];
2011-12-24 12:46:12 +00:00
Atom atom = 0;
void *xinput = NULL;
XVisualInfo *visualinfo = NULL;
XVisualInfo visualtemplate = {0};
2012-04-09 20:30:29 +00:00
const struct retro_game_geometry *geom = &g_extern.system.av_info.geometry;
2011-03-13 03:51:09 +00:00
if (!XShmQueryExtension(xv->display))
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("XVideo: XShm extension not found.\n");
2011-03-13 03:51:09 +00:00
goto error;
}
2011-03-13 12:11:33 +00:00
xv->keep_aspect = video->force_aspect;
2011-03-13 03:51:09 +00:00
// Find an appropriate Xv port.
2011-04-08 18:53:11 +00:00
xv->port = 0;
2011-03-13 03:51:09 +00:00
XvAdaptorInfo *adaptor_info;
XvQueryAdaptors(xv->display, DefaultRootWindow(xv->display), &adaptor_count, &adaptor_info);
for (unsigned i = 0; i < adaptor_count; i++)
2011-03-13 03:51:09 +00:00
{
// Find adaptor that supports both input (memory->drawable) and image (drawable->screen) masks.
if (adaptor_info[i].num_formats < 1) continue;
if (!(adaptor_info[i].type & XvInputMask)) continue;
if (!(adaptor_info[i].type & XvImageMask)) continue;
if (!adaptor_set_format(xv, xv->display, adaptor_info[i].base_id, video)) continue;
2011-03-13 03:51:09 +00:00
xv->port = adaptor_info[i].base_id;
xv->depth = adaptor_info[i].formats->depth;
xv->visualid = adaptor_info[i].formats->visual_id;
2012-04-21 21:25:32 +00:00
RARCH_LOG("XVideo: Found suitable XvPort #%u\n", (unsigned)xv->port);
2011-03-13 03:51:09 +00:00
break;
}
XvFreeAdaptorInfo(adaptor_info);
if (xv->port == 0)
2011-03-13 03:51:09 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("XVideo: Failed to find valid XvPort or format.\n");
2011-03-13 03:51:09 +00:00
goto error;
}
visualtemplate.visualid = xv->visualid;
visualtemplate.screen = DefaultScreen(xv->display);
visualtemplate.depth = xv->depth;
visualtemplate.visual = 0;
2011-12-24 12:46:12 +00:00
visualinfo = XGetVisualInfo(xv->display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualtemplate, &visualmatches);
if (visualmatches < 1 || !visualinfo->visual)
2011-03-13 03:51:09 +00:00
{
if (visualinfo) XFree(visualinfo);
2012-04-21 21:25:32 +00:00
RARCH_ERR("XVideo: Unable to find Xv-compatible visual.\n");
2011-03-13 03:51:09 +00:00
goto error;
}
xv->colormap = XCreateColormap(xv->display, DefaultRootWindow(xv->display), visualinfo->visual, AllocNone);
attributes.colormap = xv->colormap;
attributes.border_pixel = 0;
2012-12-15 11:06:21 +00:00
attributes.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | DestroyNotify | ClientMessage;
2011-03-13 12:11:33 +00:00
2012-04-07 09:55:37 +00:00
width = video->fullscreen ? ((video->width == 0) ? geom->base_width : video->width) : video->width;
height = video->fullscreen ? ((video->height == 0) ? geom->base_height : video->height) : video->height;
2011-03-13 03:51:09 +00:00
xv->window = XCreateWindow(xv->display, DefaultRootWindow(xv->display),
2011-03-13 13:46:27 +00:00
0, 0, width, height,
2011-03-13 12:11:33 +00:00
0, xv->depth, InputOutput, visualinfo->visual,
2011-03-13 03:51:09 +00:00
CWColormap | CWBorderPixel | CWEventMask, &attributes);
2011-03-13 12:11:33 +00:00
2011-03-13 03:51:09 +00:00
XFree(visualinfo);
2011-03-13 12:11:33 +00:00
XSetWindowBackground(xv->display, xv->window, 0);
2011-03-13 13:46:27 +00:00
2011-03-13 03:51:09 +00:00
XMapWindow(xv->display, xv->window);
2011-03-26 17:34:58 +00:00
if (gfx_get_fps(buf, sizeof(buf), false, buf_fps, sizeof(buf_fps)))
2011-03-26 17:34:58 +00:00
XStoreName(xv->display, xv->window, buf);
2012-10-26 21:01:32 +00:00
x11_set_window_attr(xv->display, xv->window);
2012-10-13 10:51:37 +00:00
2011-03-13 13:46:27 +00:00
if (video->fullscreen)
2012-10-13 14:58:29 +00:00
{
x11_windowed_fullscreen(xv->display, xv->window);
2013-03-29 12:46:11 +00:00
x11_show_mouse(xv->display, xv->window, false);
2012-10-13 14:58:29 +00:00
}
2011-03-13 03:51:09 +00:00
xv->gc = XCreateGC(xv->display, xv->window, 0, 0);
// Set colorkey to auto paint, so that Xv video output is always visible
2011-12-24 12:46:12 +00:00
atom = XInternAtom(xv->display, "XV_AUTOPAINT_COLORKEY", true);
2011-09-04 19:11:26 +00:00
if (atom != None) XvSetPortAttribute(xv->display, xv->port, atom, 1);
2011-03-13 03:51:09 +00:00
2012-04-07 09:55:37 +00:00
xv->width = geom->max_width;
xv->height = geom->max_height;
2011-03-13 03:51:09 +00:00
2011-03-13 12:11:33 +00:00
xv->image = XvShmCreateImage(xv->display, xv->port, xv->fourcc, NULL, xv->width, xv->height, &xv->shminfo);
if (!xv->image)
2011-03-13 03:51:09 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("XVideo: XShmCreateImage failed.\n");
2011-03-13 03:51:09 +00:00
goto error;
}
2011-03-13 12:11:33 +00:00
xv->width = xv->image->width;
xv->height = xv->image->height;
2011-03-13 03:51:09 +00:00
2011-03-13 12:11:33 +00:00
xv->shminfo.shmid = shmget(IPC_PRIVATE, xv->image->data_size, IPC_CREAT | 0777);
2011-12-24 12:46:12 +00:00
xv->shminfo.shmaddr = xv->image->data = (char*)shmat(xv->shminfo.shmid, NULL, 0);
2011-03-13 03:51:09 +00:00
xv->shminfo.readOnly = false;
if (!XShmAttach(xv->display, &xv->shminfo))
2011-03-13 03:51:09 +00:00
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("XVideo: XShmAttach failed.\n");
2011-03-13 03:51:09 +00:00
goto error;
}
2011-03-13 12:11:33 +00:00
XSync(xv->display, False);
memset(xv->image->data, 128, xv->image->data_size);
2011-03-13 03:51:09 +00:00
2011-03-13 13:12:43 +00:00
xv->quit_atom = XInternAtom(xv->display, "WM_DELETE_WINDOW", False);
if (xv->quit_atom)
XSetWMProtocols(xv->display, xv->window, &xv->quit_atom, 1);
sa.sa_handler = sighandler;
sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
2011-03-13 17:09:27 +00:00
xv_set_nonblock_state(xv, !video->vsync);
2011-03-13 19:52:06 +00:00
xv->focus = true;
2012-09-28 20:38:42 +00:00
driver.display_type = RARCH_DISPLAY_X11;
driver.video_display = (uintptr_t)xv->display;
driver.video_window = (Window)xv->window;
if (input && input_data)
2011-03-13 03:51:09 +00:00
{
xinput = input_x.init();
if (xinput)
{
*input = &input_x;
*input_data = xinput;
}
else
*input = NULL;
2011-03-13 03:51:09 +00:00
}
init_yuv_tables(xv);
2011-09-06 15:56:42 +00:00
xv_init_font(xv, g_settings.video.font_path, g_settings.video.font_size);
2011-03-13 03:51:09 +00:00
return xv;
error:
free(xv);
return NULL;
}
2011-03-13 12:11:33 +00:00
static bool check_resize(xv_t *xv, unsigned width, unsigned height)
{
// We render @ 2x scale to combat chroma downsampling.
if (xv->width != (width << 1) || xv->height != (height << 1))
2011-03-13 12:11:33 +00:00
{
xv->width = width << 1;
xv->height = height << 1;
2011-03-13 12:11:33 +00:00
XShmDetach(xv->display, &xv->shminfo);
shmdt(xv->shminfo.shmaddr);
shmctl(xv->shminfo.shmid, IPC_RMID, NULL);
XFree(xv->image);
memset(&xv->shminfo, 0, sizeof(xv->shminfo));
xv->image = XvShmCreateImage(xv->display, xv->port, xv->fourcc, NULL, xv->width, xv->height, &xv->shminfo);
if (xv->image == None)
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Failed to create image.\n");
2011-03-13 12:11:33 +00:00
return false;
}
xv->width = xv->image->width;
xv->height = xv->image->height;
xv->shminfo.shmid = shmget(IPC_PRIVATE, xv->image->data_size, IPC_CREAT | 0777);
if (xv->shminfo.shmid < 0)
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Failed to init SHM.\n");
2011-03-13 12:11:33 +00:00
return false;
}
2011-12-24 12:46:12 +00:00
xv->shminfo.shmaddr = xv->image->data = (char*)shmat(xv->shminfo.shmid, NULL, 0);
2011-03-13 12:11:33 +00:00
xv->shminfo.readOnly = false;
2011-03-13 12:11:33 +00:00
if (!XShmAttach(xv->display, &xv->shminfo))
{
2012-04-21 21:25:32 +00:00
RARCH_ERR("Failed to reattch XvShm image.\n");
2011-03-13 12:11:33 +00:00
return false;
}
XSync(xv->display, False);
memset(xv->image->data, 128, xv->image->data_size);
}
return true;
}
static void calc_out_rect(bool keep_aspect, struct rarch_viewport *vp, unsigned vp_width, unsigned vp_height)
2011-03-13 12:11:33 +00:00
{
2013-01-11 15:23:04 +00:00
vp->full_width = vp_width;
vp->full_height = vp_height;
2013-01-29 18:28:33 +00:00
if (g_settings.video.scale_integer)
{
gfx_scale_integer(vp, vp_width, vp_height, g_extern.system.aspect_ratio, keep_aspect);
2013-01-29 18:28:33 +00:00
}
else if (!keep_aspect)
2011-03-13 12:11:33 +00:00
{
vp->x = 0; vp->y = 0;
vp->width = vp_width;
vp->height = vp_height;
2011-03-13 12:11:33 +00:00
}
else
{
float desired_aspect = g_extern.system.aspect_ratio;
2011-03-13 12:11:33 +00:00
float device_aspect = (float)vp_width / vp_height;
// If the aspect ratios of screen and desired aspect ratio are sufficiently equal (floating point stuff),
2011-03-13 12:11:33 +00:00
// assume they are actually equal.
2011-03-26 17:34:58 +00:00
if (fabs(device_aspect - desired_aspect) < 0.0001)
{
vp->x = 0; vp->y = 0;
vp->width = vp_width;
vp->height = vp_height;
2011-03-26 17:34:58 +00:00
}
else if (device_aspect > desired_aspect)
2011-03-13 12:11:33 +00:00
{
float delta = (desired_aspect / device_aspect - 1.0) / 2.0 + 0.5;
vp->x = vp_width * (0.5 - delta);
vp->y = 0;
vp->width = 2.0 * vp_width * delta;
vp->height = vp_height;
2011-03-13 12:11:33 +00:00
}
2011-03-26 17:34:58 +00:00
else
2011-03-13 12:11:33 +00:00
{
float delta = (device_aspect / desired_aspect - 1.0) / 2.0 + 0.5;
vp->x = 0;
vp->y = vp_height * (0.5 - delta);
vp->width = vp_width;
vp->height = 2.0 * vp_height * delta;
2011-03-13 12:11:33 +00:00
}
}
}
2011-03-13 19:52:06 +00:00
// TODO: Is there some way to render directly like GL? :(
2011-09-06 15:56:42 +00:00
// Hacky C code is hacky :D Yay.
2011-03-13 19:52:06 +00:00
static void xv_render_msg(xv_t *xv, const char *msg, unsigned width, unsigned height)
{
2011-03-26 17:04:18 +00:00
if (!xv->font)
return;
2011-03-13 19:52:06 +00:00
struct font_output_list out;
xv->font_driver->render_msg(xv->font, msg, &out);
2011-03-13 19:52:06 +00:00
struct font_output *head = out.head;
int msg_base_x = g_settings.video.msg_pos_x * width;
int msg_base_y = height * (1.0 - g_settings.video.msg_pos_y);
2011-03-13 19:52:06 +00:00
2011-09-06 15:56:42 +00:00
unsigned luma_index[2] = { xv->luma_index[0], xv->luma_index[1] };
unsigned chroma_u_index = xv->chroma_u_index;
unsigned chroma_v_index = xv->chroma_v_index;
unsigned pitch = width << 1; // YUV formats used are 16 bpp.
2011-03-13 19:52:06 +00:00
for (; head; head = head->next)
2011-03-13 19:52:06 +00:00
{
int base_x = (msg_base_x + head->off_x) & ~1; // Make sure we always start on the correct boundary so the indices are correct.
int base_y = msg_base_y - head->off_y - head->height;
2011-09-06 15:56:42 +00:00
int glyph_width = head->width;
int glyph_height = head->height;
const uint8_t *src = head->output;
if (base_x < 0)
2011-03-13 19:52:06 +00:00
{
src -= base_x;
glyph_width += base_x;
base_x = 0;
}
2011-03-13 19:52:06 +00:00
if (base_y < 0)
{
src -= base_y * (int)head->pitch;
glyph_height += base_y;
base_y = 0;
}
int max_width = width - base_x;
int max_height = height - base_y;
if (max_width <= 0 || max_height <= 0)
continue;
2011-09-06 15:56:42 +00:00
if (glyph_width > max_width)
glyph_width = max_width;
if (glyph_height > max_height)
glyph_height = max_height;
2011-09-06 15:56:42 +00:00
uint8_t *out = (uint8_t*)xv->image->data + base_y * pitch + (base_x << 1);
2011-09-06 15:56:42 +00:00
for (int y = 0; y < glyph_height; y++, src += head->pitch, out += pitch)
{
// 2 input pixels => 4 bytes (2Y, 1U, 1V).
for (int x = 0; x < glyph_width; x += 2)
{
int out_x = x << 1;
unsigned alpha[2];
alpha[0] = src[x + 0];
if (x + 1 < glyph_width)
alpha[1] = src[x + 1];
else
alpha[1] = 0;
2011-09-06 15:56:42 +00:00
unsigned alpha_sub = (alpha[0] + alpha[1]) >> 1; // Blended alpha for the sub-sampled U/V channels.
2011-09-06 15:56:42 +00:00
for (unsigned i = 0; i < 2; i++)
{
unsigned blended = (xv->font_y * alpha[i] + ((256 - alpha[i]) * out[out_x + luma_index[i]])) >> 8;
out[out_x + luma_index[i]] = blended;
}
// Blend chroma channels
unsigned blended = (xv->font_u * alpha_sub + ((256 - alpha_sub) * out[out_x + chroma_u_index])) >> 8;
out[out_x + chroma_u_index] = blended;
blended = (xv->font_v * alpha_sub + ((256 - alpha_sub) * out[out_x + chroma_v_index])) >> 8;
out[out_x + chroma_v_index] = blended;
2011-03-13 19:52:06 +00:00
}
}
}
xv->font_driver->free_output(xv->font, &out);
2011-03-13 19:52:06 +00:00
}
2011-11-02 18:31:36 +00:00
static bool xv_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
2011-03-13 03:51:09 +00:00
{
if (!frame)
return true;
2011-12-24 12:46:12 +00:00
xv_t *xv = (xv_t*)data;
2011-03-13 03:51:09 +00:00
2011-03-13 12:11:33 +00:00
if (!check_resize(xv, width, height))
return false;
2011-03-13 03:51:09 +00:00
XWindowAttributes target;
XGetWindowAttributes(xv->display, xv->window, &target);
2011-03-13 14:11:53 +00:00
xv->render_func(xv, frame, width, height, pitch);
2011-03-13 12:11:33 +00:00
calc_out_rect(xv->keep_aspect, &xv->vp, target.width, target.height);
2013-01-29 18:28:33 +00:00
xv->vp.full_width = target.width;
xv->vp.full_height = target.width;
2011-09-06 15:56:42 +00:00
if (msg)
xv_render_msg(xv, msg, width << 1, height << 1);
2011-03-13 03:51:09 +00:00
XvShmPutImage(xv->display, xv->port, xv->window, xv->gc, xv->image,
0, 0, width << 1, height << 1,
xv->vp.x, xv->vp.y, xv->vp.width, xv->vp.height,
2011-03-13 03:51:09 +00:00
true);
2011-03-19 16:19:51 +00:00
XSync(xv->display, False);
2011-03-13 03:51:09 +00:00
char buf[128], buf_fps[128];
if (gfx_get_fps(buf, sizeof(buf), false, buf_fps, sizeof(buf_fps)))
2011-03-26 17:34:58 +00:00
XStoreName(xv->display, xv->window, buf);
g_extern.frame_count++;
2011-03-13 03:51:09 +00:00
return true;
}
static bool xv_alive(void *data)
{
2011-12-24 12:46:12 +00:00
xv_t *xv = (xv_t*)data;
2011-03-13 13:12:43 +00:00
XEvent event;
while (XPending(xv->display))
{
XNextEvent(xv->display, &event);
switch (event.type)
{
case ClientMessage:
2011-12-24 12:46:12 +00:00
if ((Atom)event.xclient.data.l[0] == xv->quit_atom)
2011-03-13 13:12:43 +00:00
return false;
break;
case DestroyNotify:
return false;
2011-03-13 19:52:06 +00:00
case MapNotify: // Find something that works better.
xv->focus = true;
break;
case UnmapNotify:
xv->focus = false;
break;
2012-12-15 11:06:21 +00:00
case KeyPress:
case KeyRelease:
x11_handle_key_event(&event);
break;
2011-03-13 13:12:43 +00:00
default:
break;
}
}
return !g_quit;
2011-03-13 03:51:09 +00:00
}
static bool xv_focus(void *data)
{
2011-12-24 12:46:12 +00:00
xv_t *xv = (xv_t*)data;
2011-03-13 19:52:06 +00:00
return xv->focus;
2011-03-13 03:51:09 +00:00
}
static void xv_free(void *data)
{
2011-12-24 12:46:12 +00:00
xv_t *xv = (xv_t*)data;
2011-03-13 03:51:09 +00:00
XShmDetach(xv->display, &xv->shminfo);
shmdt(xv->shminfo.shmaddr);
shmctl(xv->shminfo.shmid, IPC_RMID, NULL);
XFree(xv->image);
if (xv->window)
2011-03-13 03:51:09 +00:00
XUnmapWindow(xv->display, xv->window);
if (xv->colormap)
2011-03-13 03:51:09 +00:00
XFreeColormap(xv->display, xv->colormap);
XCloseDisplay(xv->display);
free(xv->ytable);
free(xv->utable);
free(xv->vtable);
2011-03-13 19:52:06 +00:00
if (xv->font)
xv->font_driver->free(xv->font);
2011-03-13 19:52:06 +00:00
2011-03-13 03:51:09 +00:00
free(xv);
}
static void xv_viewport_info(void *data, struct rarch_viewport *vp)
{
xv_t *xv = (xv_t*)data;
*vp = xv->vp;
}
2011-03-13 03:51:09 +00:00
const video_driver_t video_xvideo = {
2011-12-24 12:46:12 +00:00
xv_init,
xv_frame,
xv_set_nonblock_state,
xv_alive,
xv_focus,
NULL,
xv_free,
"xvideo",
2013-03-24 01:24:53 +00:00
#ifdef HAVE_RGUI
NULL,
NULL,
#endif
NULL,
xv_viewport_info,
2011-03-13 03:51:09 +00:00
};