/*
* Mesa 3-D graphics library
* Version: 7.8
*
* Copyright (C) 2009-2010 Chia-I Wu <olv@0xlab.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <xf86drm.h>
#include <X11/Xlibint.h>
#include <X11/extensions/XShm.h>
#include "util/u_memory.h"
#include "egllog.h"
#include "x11_screen.h"
#include "dri2.h"
#include "glxinit.h"
struct x11_screen {
Display *dpy;
int number;
/*
* This is used to fetch GLX visuals/fbconfigs. It steals code from GLX.
* It might be better to rewrite the part in Xlib or XCB.
*/
__GLXdisplayPrivate *glx_dpy;
int dri_major, dri_minor;
char *dri_driver;
char *dri_device;
int dri_fd;
x11_drawable_invalidate_buffers dri_invalidate_buffers;
void *dri_user_data;
XVisualInfo *visuals;
int num_visuals;
/* cached values for x11_drawable_get_depth */
Drawable last_drawable;
unsigned int last_depth;
};
/**
* Create a X11 screen.
*/
struct x11_screen *
x11_screen_create(Display *dpy, int screen)
{
struct x11_screen *xscr;
if (screen >= ScreenCount(dpy))
return NULL;
xscr = CALLOC_STRUCT(x11_screen);
if (xscr) {
xscr->dpy = dpy;
xscr->number = screen;
xscr->dri_major = -1;
xscr->dri_fd = -1;
}
return xscr;
}
/**
* Destroy a X11 screen.
*/
void
x11_screen_destroy(struct x11_screen *xscr)
{
if (xscr->dri_fd >= 0)
close(xscr->dri_fd);
if (xscr->dri_driver)
Xfree(xscr->dri_driver);
if (xscr->dri_device)
Xfree(xscr->dri_device);
#ifdef GLX_DIRECT_RENDERING
/* xscr->glx_dpy will be destroyed with the X display */
if (xscr->glx_dpy)
xscr->glx_dpy->xscr = NULL;
#endif
if (xscr->visuals)
XFree(xscr->visuals);
FREE(xscr);
}
#ifdef GLX_DIRECT_RENDERING
static boolean
x11_screen_init_dri2(struct x11_screen *xscr)
{
if (xscr->dri_major < 0) {
int eventBase, errorBase;
if (!DRI2QueryExtension(xscr->dpy, &eventBase, &errorBase) ||
!DRI2QueryVersion(xscr->dpy, &xscr->dri_major, &xscr->dri_minor))
xscr->dri_major = -1;
}
return (xscr->dri_major >= 0);
}
static boolean
x11_screen_init_glx(struct x11_screen *xscr)
{
if (!xscr->glx_dpy)
xscr->glx_dpy = __glXInitialize(xscr->dpy);
return (xscr->glx_dpy != NULL);
}
#endif /* GLX_DIRECT_RENDERING */
/**
* Return true if the screen supports the extension.
*/
boolean
x11_screen_support(struct x11_screen *xscr, enum x11_screen_extension ext)
{
boolean supported = FALSE;
switch (ext) {
case X11_SCREEN_EXTENSION_XSHM:
supported = XShmQueryExtension(xscr->dpy);
break;
#ifdef GLX_DIRECT_RENDERING
case X11_SCREEN_EXTENSION_GLX:
supported = x11_screen_init_glx(xscr);
break;
case X11_SCREEN_EXTENSION_DRI2:
supported = x11_screen_init_dri2(xscr);
break;
#endif
default:
break;
}
return supported;
}
/**
* Return the X visuals.
*/
const XVisualInfo *
x11_screen_get_visuals(struct x11_screen *xscr, int *num_visuals)
{
if (!xscr->visuals) {
XVisualInfo vinfo_template;
vinfo_template.screen = xscr->number;
xscr->visuals = XGetVisualInfo(xscr->dpy, VisualScreenMask,
&vinfo_template, &xscr->num_visuals);
}
if (num_visuals)
*num_visuals = xscr->num_visuals;
return xscr->visuals;
}
/**
* Return the depth of a drawable.
*
* Unlike other drawable functions, the drawable needs not be a DRI2 drawable.
*/
uint
x11_drawable_get_depth(struct x11_screen *xscr, Drawable drawable)
{
unsigned int depth;
if (drawable != xscr->last_drawable) {
Window root;
int x, y;
unsigned int w, h, border;
Status ok;
ok = XGetGeometry(xscr->dpy, drawable, &root,
&x, &y, &w, &h, &border, &depth);
if (!ok)
depth = 0;
xscr->last_drawable = drawable;
xscr->last_depth = depth;
}
else {
depth = xscr->last_depth;
}
return depth;
}
#ifdef GLX_DIRECT_RENDERING
/**
* Return the GLX fbconfigs.
*/
const __GLcontextModes *
x11_screen_get_glx_configs(struct x11_screen *xscr)
{
return (x11_screen_init_glx(xscr))
? xscr->glx_dpy->screenConfigs[xscr->number]->configs
: NULL;
}
/**
* Probe the screen for the DRI2 driver name.
*/
const char *
x11_screen_probe_dri2(struct x11_screen *xscr, int *major, int *minor)
{
if (!x11_screen_init_dri2(xscr))
return NULL;
/* get the driver name and the device name */
if (!xscr->dri_driver) {
if (!DRI2Connect(xscr->dpy, RootWindow(xscr->dpy, xscr->number),
&xscr->dri_driver, &xscr->dri_device))
xscr->dri_driver = xscr->dri_device = NULL;
}
if (major)
*major = xscr->dri_major;
if (minor)
*minor = xscr->dri_minor;
return xscr->dri_driver;
}
/**
* Enable DRI2 and returns the file descriptor of the DRM device. The file
* descriptor will be closed automatically when the screen is destoryed.
*/
int
x11_screen_enable_dri2(struct x11_screen *xscr,
x11_drawable_invalidate_buffers invalidate_buffers,
void *user_data)
{
if (xscr->dri_fd < 0) {
int fd;
drm_magic_t magic;
/* get the driver name and the device name first */
if (!x11_screen_probe_dri2(xscr, NULL, NULL))
return -1;
#ifdef O_CLOEXEC
fd = open(xscr->dri_device, O_RDWR | O_CLOEXEC);
if (fd == -1 && errno == EINVAL)
#endif
{
fd = open(xscr->dri_device, O_RDWR);
if (fd != -1)
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
}
if (fd < 0) {
_eglLog(_EGL_WARNING, "failed to open %s", xscr->dri_device);
return -1;
}
memset(&magic, 0, sizeof(magic));
if (drmGetMagic(fd, &magic)) {
_eglLog(_EGL_WARNING, "failed to get magic");
close(fd);
return -1;
}
if (!DRI2Authenticate(xscr->dpy,
RootWindow(xscr->dpy, xscr->number), magic)) {
_eglLog(_EGL_WARNING, "failed to authenticate magic");
close(fd);
return -1;
}
if (!x11_screen_init_glx(xscr)) {
_eglLog(_EGL_WARNING, "failed to initialize GLX");
close(fd);
return -1;
}
if (xscr->glx_dpy->xscr) {
_eglLog(_EGL_WARNING,
"display is already managed by another x11 screen");
close(fd);
return -1;
}
xscr->glx_dpy->xscr = xscr;
xscr->dri_invalidate_buffers = invalidate_buffers;
xscr->dri_user_data = user_data;
xscr->dri_fd = fd;
}
return xscr->dri_fd;
}
char *
x11_screen_get_device_name(struct x11_screen *xscr)
{
return xscr->dri_device;
}
int
x11_screen_authenticate(struct x11_screen *xscr, uint32_t id)
{
boolean authenticated;
authenticated = DRI2Authenticate(xscr->dpy,
RootWindow(xscr->dpy, xscr->number), id);
return authenticated ? 0 : -1;
}
/**
* Create/Destroy the DRI drawable.
*/
void
x11_drawable_enable_dri2(struct x11_screen *xscr,
Drawable drawable, boolean on)
{
if (on)
DRI2CreateDrawable(xscr->dpy, drawable);
else
DRI2DestroyDrawable(xscr->dpy, drawable);
}
/**
* Copy between buffers of the DRI2 drawable.
*/
void
x11_drawable_copy_buffers_region(struct x11_screen *xscr, Drawable drawable,
int num_rects, const int *rects,
int src_buf, int dst_buf)
{
XserverRegion region;
XRectangle *rectangles = CALLOC(num_rects, sizeof(XRectangle));
for (int i = 0; i < num_rects; i++) {
rectangles[i].x = rects[i * 4 + 0];
rectangles[i].y = rects[i * 4 + 1];
rectangles[i].width = rects[i * 4 + 2];
rectangles[i].height = rects[i * 4 + 3];
}
region = XFixesCreateRegion(xscr->dpy, rectangles, num_rects);
DRI2CopyRegion(xscr->dpy, drawable, region, dst_buf, src_buf);
XFixesDestroyRegion(xscr->dpy, region);
FREE(rectangles);
}
/**
* Get the buffers of the DRI2 drawable. The returned array should be freed.
*/
struct x11_drawable_buffer *
x11_drawable_get_buffers(struct x11_screen *xscr, Drawable drawable,
int *width, int *height, unsigned int *attachments,
boolean with_format, int num_ins, int *num_outs)
{
DRI2Buffer *dri2bufs;
if (with_format)
dri2bufs = DRI2GetBuffersWithFormat(xscr->dpy, drawable, width, height,
attachments, num_ins, num_outs);
else
dri2bufs = DRI2GetBuffers(xscr->dpy, drawable, width, height,
attachments, num_ins, num_outs);
return (struct x11_drawable_buffer *) dri2bufs;
}
/**
* Create a mode list of the given size.
*/
__GLcontextModes *
x11_context_modes_create(unsigned count)
{
const size_t size = sizeof(__GLcontextModes);
__GLcontextModes *base = NULL;
__GLcontextModes **next;
unsigned i;
next = &base;
for (i = 0; i < count; i++) {
*next = (__GLcontextModes *) CALLOC(1, size);
if (*next == NULL) {
x11_context_modes_destroy(base);
base = NULL;
break;
}
next = &((*next)->next);
}
return base;
}
/**
* Destroy a mode list.
*/
void
x11_context_modes_destroy(__GLcontextModes *modes)
{
while (modes != NULL) {
__GLcontextModes *next = modes->next;
FREE(modes);
modes = next;
}
}
/**
* Return the number of the modes in the mode list.
*/
unsigned
x11_context_modes_count(const __GLcontextModes *modes)
{
const __GLcontextModes *mode;
int count = 0;
for (mode = modes; mode; mode = mode->next)
count++;
return count;
}
extern void
dri2InvalidateBuffers(Display *dpy, XID drawable);
/**
* This is called from src/glx/dri2.c.
*/
void
dri2InvalidateBuffers(Display *dpy, XID drawable)
{
__GLXdisplayPrivate *priv = __glXInitialize(dpy);
struct x11_screen *xscr = NULL;
if (priv && priv->xscr)
xscr = priv->xscr;
if (!xscr || !xscr->dri_invalidate_buffers)
return;
xscr->dri_invalidate_buffers(xscr, drawable, xscr->dri_user_data);
}
extern unsigned
dri2GetSwapEventType(Display *dpy, XID drawable);
extern void *
dri2GetGlxDrawableFromXDrawableId(Display *dpy, XID id);
extern void *
GetGLXDrawable(Display *dpy, XID drawable);
/**
* This is also called from src/glx/dri2.c.
*/
unsigned dri2GetSwapEventType(Display *dpy, XID drawable)
{
return 0;
}
void *
dri2GetGlxDrawableFromXDrawableId(Display *dpy, XID id)
{
return NULL;
}
void *
GetGLXDrawable(Display *dpy, XID drawable)
{
return NULL;
}
#endif /* GLX_DIRECT_RENDERING */