2018-10-03 17:46:18 -04:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
/*
|
|
|
|
* Compton - a compositor for X11
|
|
|
|
*
|
|
|
|
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
|
|
|
|
*
|
|
|
|
* Copyright (c) 2011-2013, Christopher Jeffrey
|
2019-02-03 13:47:36 -05:00
|
|
|
* Copyright (c) 2019 Yuxuan Shui <yshuiv7@gmail.com>
|
2018-10-03 17:46:18 -04:00
|
|
|
* See LICENSE-mit for more information.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2019-02-03 11:41:04 -05:00
|
|
|
#include <X11/Xlib-xcb.h>
|
2019-01-27 14:34:26 -05:00
|
|
|
#include <assert.h>
|
2019-02-06 19:00:39 -05:00
|
|
|
#include <limits.h>
|
2019-01-27 14:34:26 -05:00
|
|
|
#include <pixman.h>
|
2019-01-20 11:53:39 -05:00
|
|
|
#include <stdbool.h>
|
2019-01-20 16:15:20 -05:00
|
|
|
#include <stdlib.h>
|
2019-01-27 14:34:26 -05:00
|
|
|
#include <string.h>
|
|
|
|
#include <xcb/composite.h>
|
|
|
|
#include <xcb/xcb.h>
|
2019-01-20 11:53:39 -05:00
|
|
|
|
2018-10-03 17:46:18 -04:00
|
|
|
#include "backend/backend.h"
|
2019-03-08 20:39:18 -05:00
|
|
|
#include "backend/backend_common.h"
|
2018-10-03 17:46:18 -04:00
|
|
|
#include "backend/gl/gl_common.h"
|
2019-02-03 11:41:04 -05:00
|
|
|
#include "backend/gl/glx.h"
|
2019-01-20 11:53:39 -05:00
|
|
|
#include "common.h"
|
|
|
|
#include "compiler.h"
|
2019-01-27 14:34:26 -05:00
|
|
|
#include "config.h"
|
2019-01-20 11:53:39 -05:00
|
|
|
#include "log.h"
|
|
|
|
#include "region.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "win.h"
|
2019-02-03 11:41:04 -05:00
|
|
|
#include "x.h"
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
struct _glx_image_data {
|
2018-10-03 17:46:18 -04:00
|
|
|
gl_texture_t texture;
|
|
|
|
GLXPixmap glpixmap;
|
|
|
|
xcb_pixmap_t pixmap;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _glx_data {
|
2019-03-08 20:39:18 -05:00
|
|
|
struct gl_data gl;
|
|
|
|
Display *display;
|
|
|
|
int screen;
|
|
|
|
int target_win;
|
2018-10-03 17:46:18 -04:00
|
|
|
int glx_event;
|
|
|
|
int glx_error;
|
|
|
|
GLXContext ctx;
|
|
|
|
};
|
|
|
|
|
2019-03-09 11:06:57 -05:00
|
|
|
#define glXGetFBConfigAttribChecked(a, b, attr, c) \
|
|
|
|
do { \
|
|
|
|
if (glXGetFBConfigAttrib(a, b, attr, c)) { \
|
|
|
|
log_info("Cannot get FBConfig attribute " #attr); \
|
|
|
|
continue; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
struct glx_fbconfig_info *glx_find_fbconfig(Display *dpy, int screen, struct xvisual_info m) {
|
2019-02-08 17:34:53 -05:00
|
|
|
log_debug("Looking for FBConfig for RGBA%d%d%d%d, depth %d", m.red_size,
|
|
|
|
m.blue_size, m.green_size, m.alpha_size, m.visual_depth);
|
2019-02-03 11:41:04 -05:00
|
|
|
|
|
|
|
int ncfg;
|
|
|
|
// clang-format off
|
|
|
|
GLXFBConfig *cfg =
|
|
|
|
glXChooseFBConfig(dpy, screen, (int[]){
|
|
|
|
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
|
|
|
GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
|
|
|
|
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
|
|
|
|
GLX_X_RENDERABLE, true,
|
|
|
|
GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, GLX_DONT_CARE,
|
2019-02-06 19:00:39 -05:00
|
|
|
GLX_BUFFER_SIZE, m.red_size + m.green_size +
|
|
|
|
m.blue_size + m.alpha_size,
|
|
|
|
GLX_RED_SIZE, m.red_size,
|
|
|
|
GLX_BLUE_SIZE, m.blue_size,
|
|
|
|
GLX_GREEN_SIZE, m.green_size,
|
|
|
|
GLX_ALPHA_SIZE, m.alpha_size,
|
2019-02-03 11:41:04 -05:00
|
|
|
GLX_STENCIL_SIZE, 0,
|
|
|
|
GLX_DEPTH_SIZE, 0,
|
|
|
|
0
|
|
|
|
}, &ncfg);
|
|
|
|
// clang-format on
|
|
|
|
|
|
|
|
int texture_tgts, y_inverted, texture_fmt;
|
|
|
|
bool found = false;
|
2019-02-03 14:06:20 -05:00
|
|
|
int min_cost = INT_MAX;
|
2019-02-03 11:41:04 -05:00
|
|
|
GLXFBConfig ret;
|
|
|
|
for (int i = 0; i < ncfg; i++) {
|
2019-02-03 14:15:30 -05:00
|
|
|
int depthbuf, stencil, doublebuf, bufsize;
|
|
|
|
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BUFFER_SIZE, &bufsize);
|
2019-02-03 14:06:20 -05:00
|
|
|
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_DEPTH_SIZE, &depthbuf);
|
|
|
|
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_STENCIL_SIZE, &stencil);
|
2019-02-03 14:15:30 -05:00
|
|
|
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_DOUBLEBUFFER, &doublebuf);
|
2019-02-03 14:33:28 -05:00
|
|
|
if (depthbuf + stencil + bufsize * (doublebuf + 1) >= min_cost) {
|
2019-02-03 14:06:20 -05:00
|
|
|
continue;
|
|
|
|
}
|
2019-02-03 11:41:04 -05:00
|
|
|
int red, green, blue;
|
|
|
|
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_RED_SIZE, &red);
|
|
|
|
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BLUE_SIZE, &blue);
|
|
|
|
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_GREEN_SIZE, &green);
|
2019-02-06 19:00:39 -05:00
|
|
|
if (red != m.red_size || green != m.green_size || blue != m.blue_size) {
|
2019-02-03 11:41:04 -05:00
|
|
|
// Color size doesn't match, this cannot work
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
int rgb, rgba;
|
|
|
|
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BIND_TO_TEXTURE_RGB_EXT, &rgb);
|
|
|
|
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, &rgba);
|
|
|
|
if (!rgb && !rgba) {
|
|
|
|
log_info("FBConfig is neither RGBA nor RGB, compton cannot "
|
|
|
|
"handle this setup.");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
int visual;
|
|
|
|
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_VISUAL_ID, &visual);
|
2019-02-06 19:00:39 -05:00
|
|
|
if (m.visual_depth != -1 &&
|
|
|
|
x_get_visual_depth(XGetXCBConnection(dpy), visual) != m.visual_depth) {
|
2019-02-03 11:41:04 -05:00
|
|
|
// Some driver might attach fbconfig to a GLX visual with a
|
2019-02-03 13:53:28 -05:00
|
|
|
// different depth.
|
|
|
|
//
|
|
|
|
// (That makes total sense. - NVIDIA developers)
|
2019-02-03 11:41:04 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// All check passed, we are using this one.
|
|
|
|
found = true;
|
|
|
|
ret = cfg[i];
|
2019-02-08 17:34:53 -05:00
|
|
|
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BIND_TO_TEXTURE_TARGETS_EXT,
|
|
|
|
&texture_tgts);
|
2019-02-03 11:41:04 -05:00
|
|
|
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_Y_INVERTED_EXT, &y_inverted);
|
|
|
|
|
|
|
|
// Prefer the texture format with matching alpha, with the other one as
|
|
|
|
// fallback
|
2019-02-06 19:00:39 -05:00
|
|
|
if (m.alpha_size) {
|
2019-02-08 17:34:53 -05:00
|
|
|
texture_fmt = rgba ? GLX_TEXTURE_FORMAT_RGBA_EXT
|
|
|
|
: GLX_TEXTURE_FORMAT_RGB_EXT;
|
2019-02-03 11:41:04 -05:00
|
|
|
} else {
|
2019-02-08 17:34:53 -05:00
|
|
|
texture_fmt =
|
|
|
|
rgb ? GLX_TEXTURE_FORMAT_RGB_EXT : GLX_TEXTURE_FORMAT_RGBA_EXT;
|
2019-02-03 11:41:04 -05:00
|
|
|
}
|
2019-02-03 14:33:28 -05:00
|
|
|
min_cost = depthbuf + stencil + bufsize * (doublebuf + 1);
|
2019-02-03 11:41:04 -05:00
|
|
|
}
|
|
|
|
free(cfg);
|
|
|
|
if (!found) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto info = cmalloc(struct glx_fbconfig_info);
|
|
|
|
info->cfg = ret;
|
|
|
|
info->texture_tgts = texture_tgts;
|
|
|
|
info->texture_fmt = texture_fmt;
|
|
|
|
info->y_inverted = y_inverted;
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2018-10-03 17:46:18 -04:00
|
|
|
/**
|
2019-03-08 20:39:18 -05:00
|
|
|
* Free a glx_texture_t.
|
2018-10-03 17:46:18 -04:00
|
|
|
*/
|
2019-03-08 20:39:18 -05:00
|
|
|
void glx_release_image(backend_t *base, void *image_data) {
|
|
|
|
struct _glx_image_data *wd = image_data;
|
|
|
|
struct _glx_data *gd = (void *)base;
|
2019-03-09 09:12:53 -05:00
|
|
|
(*wd->texture.refcount)--;
|
|
|
|
if (*wd->texture.refcount != 0) {
|
2019-03-08 20:39:18 -05:00
|
|
|
return;
|
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
// Release binding
|
|
|
|
if (wd->glpixmap && wd->texture.texture) {
|
|
|
|
glBindTexture(wd->texture.target, wd->texture.texture);
|
2019-03-08 20:39:18 -05:00
|
|
|
glXReleaseTexImageEXT(gd->display, wd->glpixmap, GLX_FRONT_LEFT_EXT);
|
2018-10-03 17:46:18 -04:00
|
|
|
glBindTexture(wd->texture.target, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free GLX Pixmap
|
|
|
|
if (wd->glpixmap) {
|
2019-03-08 20:39:18 -05:00
|
|
|
glXDestroyPixmap(gd->display, wd->glpixmap);
|
2018-10-03 17:46:18 -04:00
|
|
|
wd->glpixmap = 0;
|
|
|
|
}
|
|
|
|
|
2019-03-09 21:25:41 -05:00
|
|
|
glDeleteTextures(1, &wd->texture.texture);
|
|
|
|
free(wd->texture.refcount);
|
2018-10-03 17:46:18 -04:00
|
|
|
|
|
|
|
// Free structure itself
|
2018-12-30 20:32:31 -05:00
|
|
|
free(wd);
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
gl_check_err();
|
2018-10-03 17:46:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroy GLX related resources.
|
|
|
|
*/
|
2019-03-08 20:39:18 -05:00
|
|
|
void glx_deinit(backend_t *base) {
|
|
|
|
struct _glx_data *gd = (void *)base;
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
gl_deinit(&gd->gl);
|
2018-10-03 17:46:18 -04:00
|
|
|
|
|
|
|
// Destroy GLX context
|
|
|
|
if (gd->ctx) {
|
2019-03-08 20:39:18 -05:00
|
|
|
glXDestroyContext(gd->display, gd->ctx);
|
2018-10-03 17:46:18 -04:00
|
|
|
gd->ctx = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(gd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize OpenGL.
|
|
|
|
*/
|
2019-03-08 20:39:18 -05:00
|
|
|
static backend_t *glx_init(session_t *ps) {
|
2018-10-03 17:46:18 -04:00
|
|
|
bool success = false;
|
2019-02-21 18:25:31 -05:00
|
|
|
glxext_init(ps->dpy, ps->scr);
|
2018-10-03 17:46:18 -04:00
|
|
|
auto gd = ccalloc(1, struct _glx_data);
|
2019-03-08 20:39:18 -05:00
|
|
|
gd->gl.base.c = ps->c;
|
|
|
|
gd->gl.base.root = ps->root;
|
|
|
|
gd->display = ps->dpy;
|
|
|
|
gd->screen = ps->scr;
|
|
|
|
gd->target_win = ps->overlay != XCB_NONE ? ps->overlay : ps->root;
|
2019-02-26 18:52:37 -05:00
|
|
|
|
2018-10-03 17:46:18 -04:00
|
|
|
XVisualInfo *pvis = NULL;
|
|
|
|
|
|
|
|
// Check for GLX extension
|
|
|
|
if (!glXQueryExtension(ps->dpy, &gd->glx_event, &gd->glx_error)) {
|
|
|
|
log_error("No GLX extension.");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get XVisualInfo
|
|
|
|
int nitems = 0;
|
|
|
|
XVisualInfo vreq = {.visualid = ps->vis};
|
|
|
|
pvis = XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems);
|
|
|
|
if (!pvis) {
|
|
|
|
log_error("Failed to acquire XVisualInfo for current visual.");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the visual is double-buffered
|
|
|
|
int value = 0;
|
|
|
|
if (glXGetConfig(ps->dpy, pvis, GLX_USE_GL, &value) || !value) {
|
|
|
|
log_error("Root visual is not a GL visual.");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2019-03-09 20:22:38 -05:00
|
|
|
if (glXGetConfig(ps->dpy, pvis, GLX_STENCIL_SIZE, &value) || !value) {
|
|
|
|
log_error("Root visual lacks stencil buffer.");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2018-10-03 17:46:18 -04:00
|
|
|
if (glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) {
|
|
|
|
log_error("Root visual is not a double buffered GL visual.");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2019-03-09 11:06:57 -05:00
|
|
|
if (glXGetConfig(ps->dpy, pvis, GLX_RGBA, &value) || !value) {
|
|
|
|
log_error("Root visual is a color index visual, not supported");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2019-02-21 18:25:31 -05:00
|
|
|
if (!glxext.has_GLX_EXT_texture_from_pixmap) {
|
|
|
|
log_error("GLX_EXT_texture_from_pixmap is not supported by your driver");
|
2018-10-03 17:46:18 -04:00
|
|
|
goto end;
|
2019-02-21 18:25:31 -05:00
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-03-09 11:06:57 -05:00
|
|
|
if (!glxext.has_GLX_ARB_create_context) {
|
|
|
|
log_error("GLX_ARB_create_context is not supported by your driver");
|
|
|
|
goto end;
|
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-03-09 11:06:57 -05:00
|
|
|
// Find a fbconfig with visualid matching the one from the target win, so we can
|
|
|
|
// be sure that the fbconfig is compatible with our target window.
|
|
|
|
int ncfgs;
|
|
|
|
GLXFBConfig *cfg = glXGetFBConfigs(gd->display, gd->screen, &ncfgs);
|
|
|
|
bool found = false;
|
|
|
|
for (int i = 0; i < ncfgs; i++) {
|
|
|
|
int visualid;
|
|
|
|
glXGetFBConfigAttribChecked(gd->display, cfg[i], GLX_VISUAL_ID, &visualid);
|
|
|
|
if ((VisualID)visualid != pvis->visualid) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
gd->ctx = glXCreateContextAttribsARB(ps->dpy, cfg[i], 0, true,
|
|
|
|
(int[]){
|
|
|
|
GLX_CONTEXT_MAJOR_VERSION_ARB,
|
|
|
|
3,
|
|
|
|
GLX_CONTEXT_MINOR_VERSION_ARB,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
});
|
|
|
|
free(cfg);
|
|
|
|
|
|
|
|
if (!gd->ctx) {
|
|
|
|
log_error("Failed to get GLX context.");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
log_error("Couldn't find a suitable fbconfig for the target window");
|
2018-10-03 17:46:18 -04:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attach GLX context
|
|
|
|
GLXDrawable tgt = ps->overlay;
|
|
|
|
if (!tgt) {
|
|
|
|
tgt = ps->root;
|
|
|
|
}
|
|
|
|
if (!glXMakeCurrent(ps->dpy, tgt, gd->ctx)) {
|
|
|
|
log_error("Failed to attach GLX context.");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
if (!gl_init(&gd->gl, ps)) {
|
|
|
|
log_error("Failed to setup OpenGL");
|
2018-10-03 17:46:18 -04:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
success = true;
|
|
|
|
|
|
|
|
end:
|
2019-03-08 20:39:18 -05:00
|
|
|
if (pvis) {
|
2018-12-31 02:16:35 -05:00
|
|
|
XFree(pvis);
|
2019-03-08 20:39:18 -05:00
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
|
|
|
|
if (!success) {
|
2019-03-08 20:39:18 -05:00
|
|
|
glx_deinit(&gd->gl.base);
|
2018-10-03 17:46:18 -04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
return &gd->gl.base;
|
2018-10-03 17:46:18 -04:00
|
|
|
}
|
|
|
|
|
2019-03-09 09:12:53 -05:00
|
|
|
static void *
|
|
|
|
glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
|
2019-03-08 20:39:18 -05:00
|
|
|
struct _glx_data *gd = (void *)base;
|
2018-12-30 20:32:31 -05:00
|
|
|
// Retrieve pixmap parameters, if they aren't provided
|
2019-03-08 20:39:18 -05:00
|
|
|
if (fmt.visual_depth > OPENGL_MAX_DEPTH) {
|
2018-12-30 20:32:31 -05:00
|
|
|
log_error("Requested depth %d higher than max possible depth %d.",
|
2019-03-08 20:39:18 -05:00
|
|
|
fmt.visual_depth, OPENGL_MAX_DEPTH);
|
2018-10-03 17:46:18 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
auto r = xcb_get_geometry_reply(base->c, xcb_get_geometry(base->c, pixmap), NULL);
|
|
|
|
if (!r) {
|
|
|
|
log_error("Invalid pixmap %#010x", pixmap);
|
|
|
|
return NULL;
|
2019-02-03 11:41:04 -05:00
|
|
|
}
|
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
auto wd = ccalloc(1, struct _glx_image_data);
|
|
|
|
wd->pixmap = pixmap;
|
2019-03-09 13:01:18 -05:00
|
|
|
wd->texture.width = wd->texture.ewidth = r->width;
|
|
|
|
wd->texture.height = wd->texture.eheight = r->height;
|
2019-03-08 20:39:18 -05:00
|
|
|
free(r);
|
|
|
|
|
|
|
|
auto fbcfg = glx_find_fbconfig(gd->display, gd->screen, fmt);
|
2019-02-03 11:41:04 -05:00
|
|
|
if (!fbcfg) {
|
2019-03-08 20:39:18 -05:00
|
|
|
log_error("Couldn't find FBConfig with requested visual %x", fmt.visual);
|
2019-02-03 11:41:04 -05:00
|
|
|
goto err;
|
2018-10-03 17:46:18 -04:00
|
|
|
}
|
|
|
|
|
2018-12-30 20:32:31 -05:00
|
|
|
// Choose a suitable texture target for our pixmap.
|
|
|
|
// Refer to GLX_EXT_texture_om_pixmap spec to see what are the mean
|
|
|
|
// of the bits in texture_tgts
|
|
|
|
GLenum tex_tgt = 0;
|
2019-03-08 20:39:18 -05:00
|
|
|
if (GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts && gd->gl.non_power_of_two_texture)
|
2018-12-30 20:32:31 -05:00
|
|
|
tex_tgt = GLX_TEXTURE_2D_EXT;
|
2019-02-03 11:41:04 -05:00
|
|
|
else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & fbcfg->texture_tgts)
|
2018-12-30 20:32:31 -05:00
|
|
|
tex_tgt = GLX_TEXTURE_RECTANGLE_EXT;
|
2019-02-03 11:41:04 -05:00
|
|
|
else if (!(GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts))
|
2018-12-30 20:32:31 -05:00
|
|
|
tex_tgt = GLX_TEXTURE_RECTANGLE_EXT;
|
|
|
|
else
|
|
|
|
tex_tgt = GLX_TEXTURE_2D_EXT;
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-03-09 11:06:57 -05:00
|
|
|
log_debug("depth %d, tgt %#x, rgba %d", fmt.visual_depth, tex_tgt,
|
2019-02-03 11:41:04 -05:00
|
|
|
(GLX_TEXTURE_FORMAT_RGBA_EXT == fbcfg->texture_fmt));
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2018-12-30 20:32:31 -05:00
|
|
|
GLint attrs[] = {
|
2019-02-03 11:41:04 -05:00
|
|
|
GLX_TEXTURE_FORMAT_EXT,
|
|
|
|
fbcfg->texture_fmt,
|
|
|
|
GLX_TEXTURE_TARGET_EXT,
|
|
|
|
tex_tgt,
|
|
|
|
0,
|
2018-12-30 20:32:31 -05:00
|
|
|
};
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-02-08 17:34:53 -05:00
|
|
|
wd->texture.target =
|
|
|
|
(GLX_TEXTURE_2D_EXT == tex_tgt ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE);
|
2019-02-03 11:41:04 -05:00
|
|
|
wd->texture.y_inverted = fbcfg->y_inverted;
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
wd->glpixmap = glXCreatePixmap(gd->display, fbcfg->cfg, wd->pixmap, attrs);
|
2019-02-03 11:41:04 -05:00
|
|
|
free(fbcfg);
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2018-12-30 20:32:31 -05:00
|
|
|
if (!wd->glpixmap) {
|
2019-03-08 20:39:18 -05:00
|
|
|
log_error("Failed to create glpixmap for pixmap %#010x", pixmap);
|
2018-12-30 20:32:31 -05:00
|
|
|
goto err;
|
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
|
|
|
|
// Create texture
|
2019-03-08 20:39:18 -05:00
|
|
|
wd->texture.texture = gl_new_texture(wd->texture.target);
|
|
|
|
wd->texture.opacity = 1;
|
|
|
|
wd->texture.depth = fmt.visual_depth;
|
|
|
|
wd->texture.color_inverted = false;
|
2019-03-09 21:25:41 -05:00
|
|
|
wd->texture.dim = 0;
|
2019-03-08 20:39:18 -05:00
|
|
|
wd->texture.has_alpha = fmt.alpha_size != 0;
|
2019-03-09 09:12:53 -05:00
|
|
|
wd->texture.refcount = ccalloc(1, int);
|
|
|
|
*wd->texture.refcount = 1;
|
2019-03-08 20:39:18 -05:00
|
|
|
glBindTexture(wd->texture.target, wd->texture.texture);
|
|
|
|
glXBindTexImageEXT(gd->display, wd->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
|
|
|
glBindTexture(wd->texture.target, 0);
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
gl_check_err();
|
2018-12-30 20:32:31 -05:00
|
|
|
return wd;
|
|
|
|
err:
|
|
|
|
if (wd->glpixmap) {
|
2019-03-08 20:39:18 -05:00
|
|
|
glXDestroyPixmap(gd->display, wd->glpixmap);
|
|
|
|
}
|
|
|
|
if (owned) {
|
|
|
|
xcb_free_pixmap(base->c, pixmap);
|
2018-10-03 17:46:18 -04:00
|
|
|
}
|
2018-12-30 20:32:31 -05:00
|
|
|
free(wd);
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-10-03 17:46:18 -04:00
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
static void glx_present(backend_t *base) {
|
|
|
|
struct _glx_data *gd = (void *)base;
|
|
|
|
glXSwapBuffers(gd->display, gd->target_win);
|
2019-01-26 13:00:29 -05:00
|
|
|
}
|
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
static int glx_buffer_age(backend_t *base) {
|
|
|
|
if (!glxext.has_GLX_EXT_buffer_age) {
|
2019-01-27 19:02:54 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
struct _glx_data *gd = (void *)base;
|
|
|
|
unsigned int val;
|
|
|
|
glXQueryDrawable(gd->display, gd->target_win, GLX_BACK_BUFFER_AGE_EXT, &val);
|
|
|
|
return (int)val ?: -1;
|
2019-01-27 19:02:54 -05:00
|
|
|
}
|
|
|
|
|
2019-03-09 09:12:53 -05:00
|
|
|
static void *glx_copy(backend_t *base, const void *image_data, const region_t *reg_visible) {
|
|
|
|
const struct _glx_image_data *img = image_data;
|
|
|
|
auto new_img = ccalloc(1, struct _glx_image_data);
|
|
|
|
*new_img = *img;
|
|
|
|
(*new_img->texture.refcount)++;
|
|
|
|
return new_img;
|
|
|
|
}
|
|
|
|
|
2019-03-08 20:39:18 -05:00
|
|
|
struct backend_operations glx_ops = {
|
|
|
|
.init = glx_init,
|
|
|
|
.deinit = glx_deinit,
|
|
|
|
.bind_pixmap = glx_bind_pixmap,
|
|
|
|
.release_image = glx_release_image,
|
|
|
|
.compose = gl_compose,
|
|
|
|
.image_op = gl_image_op,
|
2019-03-09 09:12:53 -05:00
|
|
|
.copy = glx_copy,
|
2019-03-08 20:39:18 -05:00
|
|
|
.blur = gl_blur,
|
|
|
|
.is_image_transparent = gl_is_image_transparent,
|
|
|
|
.present = glx_present,
|
|
|
|
.buffer_age = glx_buffer_age,
|
|
|
|
.render_shadow = default_backend_render_shadow,
|
2019-03-09 20:51:21 -05:00
|
|
|
.fill = gl_fill,
|
2019-03-09 09:12:53 -05:00
|
|
|
.max_buffer_age = 5, // Why?
|
2019-03-08 20:39:18 -05:00
|
|
|
};
|
2019-02-08 18:57:14 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if a GLX extension exists.
|
|
|
|
*/
|
|
|
|
static inline bool glx_has_extension(Display *dpy, int screen, const char *ext) {
|
|
|
|
const char *glx_exts = glXQueryExtensionsString(dpy, screen);
|
|
|
|
if (!glx_exts) {
|
|
|
|
log_error("Failed get GLX extension list.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
long inlen = strlen(ext);
|
|
|
|
const char *curr = glx_exts;
|
|
|
|
bool match = false;
|
|
|
|
while (curr && !match) {
|
|
|
|
const char *end = strchr(curr, ' ');
|
|
|
|
if (!end) {
|
|
|
|
// Last extension string
|
|
|
|
match = strcmp(ext, curr) == 0;
|
|
|
|
} else if (end - curr == inlen) {
|
|
|
|
// Length match, do match string
|
|
|
|
match = strncmp(ext, curr, end - curr) == 0;
|
|
|
|
}
|
|
|
|
curr = end ? end + 1 : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!match) {
|
|
|
|
log_info("Missing GLX extension %s.", ext);
|
|
|
|
} else {
|
|
|
|
log_info("Found GLX extension %s.", ext);
|
|
|
|
}
|
|
|
|
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct glxext_info glxext = {0};
|
|
|
|
PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI;
|
|
|
|
PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI;
|
|
|
|
PFNGLXGETSYNCVALUESOMLPROC glXGetSyncValuesOML;
|
|
|
|
PFNGLXWAITFORMSCOMLPROC glXWaitForMscOML;
|
|
|
|
PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT;
|
|
|
|
PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI;
|
|
|
|
PFNGLXSWAPINTERVALMESAPROC glXSwapIntervalMESA;
|
|
|
|
PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
|
|
|
|
PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
|
2019-03-09 11:06:57 -05:00
|
|
|
PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
|
2019-02-08 18:57:14 -05:00
|
|
|
|
|
|
|
void glxext_init(Display *dpy, int screen) {
|
|
|
|
if (glxext.initialized) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
glxext.initialized = true;
|
|
|
|
#define check_ext(name) glxext.has_##name = glx_has_extension(dpy, screen, #name)
|
|
|
|
check_ext(GLX_SGI_video_sync);
|
|
|
|
check_ext(GLX_SGI_swap_control);
|
|
|
|
check_ext(GLX_OML_sync_control);
|
|
|
|
check_ext(GLX_MESA_swap_control);
|
|
|
|
check_ext(GLX_EXT_swap_control);
|
|
|
|
check_ext(GLX_EXT_texture_from_pixmap);
|
2019-03-09 11:06:57 -05:00
|
|
|
check_ext(GLX_ARB_create_context);
|
2019-03-08 20:39:18 -05:00
|
|
|
check_ext(GLX_EXT_buffer_age);
|
2019-02-08 18:57:14 -05:00
|
|
|
#undef check_ext
|
|
|
|
|
|
|
|
#define lookup(name) (name = (__typeof__(name))glXGetProcAddress((GLubyte *)#name))
|
|
|
|
// Checking if the returned function pointer is NULL is not really necessary,
|
2019-03-08 20:39:18 -05:00
|
|
|
// or maybe not even useful, since glXGetProcAddress might always return
|
|
|
|
// something. We are doing it just for completeness' sake.
|
2019-02-08 18:57:14 -05:00
|
|
|
if (!lookup(glXGetVideoSyncSGI) || !lookup(glXWaitVideoSyncSGI)) {
|
|
|
|
glxext.has_GLX_SGI_video_sync = false;
|
|
|
|
}
|
|
|
|
if (!lookup(glXSwapIntervalEXT)) {
|
|
|
|
glxext.has_GLX_EXT_swap_control = false;
|
|
|
|
}
|
|
|
|
if (!lookup(glXSwapIntervalMESA)) {
|
|
|
|
glxext.has_GLX_MESA_swap_control = false;
|
|
|
|
}
|
|
|
|
if (!lookup(glXSwapIntervalSGI)) {
|
|
|
|
glxext.has_GLX_SGI_swap_control = false;
|
|
|
|
}
|
|
|
|
if (!lookup(glXWaitForMscOML) || !lookup(glXGetSyncValuesOML)) {
|
|
|
|
glxext.has_GLX_OML_sync_control = false;
|
|
|
|
}
|
|
|
|
if (!lookup(glXBindTexImageEXT) || !lookup(glXReleaseTexImageEXT)) {
|
|
|
|
glxext.has_GLX_EXT_texture_from_pixmap = false;
|
|
|
|
}
|
2019-03-09 11:06:57 -05:00
|
|
|
if (!lookup(glXCreateContextAttribsARB)) {
|
|
|
|
glxext.has_GLX_ARB_create_context = false;
|
|
|
|
}
|
2019-02-08 18:57:14 -05:00
|
|
|
#undef lookup
|
|
|
|
}
|