Feature: OpenGL backend

- Add experimental OpenGL backend (--opengl). --blur-background is
  currently not possible with this backend, because I'm still trying to
  find a proper way to do blur with OpenGL. Flipping backend on-the-fly
  is really hard, so it isn't supported right now. No configuration file
  option exists to enable this, because it isn't stable enough.

- Add `opengl-swc` VSync method that uses SGI_swap_control to control
  buffer swap, with OpenGL backend. (#7)

- Fix a potential read-from-freed-memory issue in paint_all().

- Correctly reattach GLX context after fork.

- Dump error text in error(). Add GLX error code handling.

- Code clean-up.

- Known issues: Region operations take a lot of time in glx_render().
  I'm hesitating about what to do.
This commit is contained in:
Richard Grenville 2013-03-15 23:16:23 +08:00
parent 4bc3de81ab
commit 8ffcf1c1e8
7 changed files with 1183 additions and 424 deletions

5
.gitignore vendored
View File

@ -33,8 +33,8 @@ install_manifest.txt
*~
# Misc files
core
*.core
/core
/*.core
oprofile_data/
compton.plist
callgrind.out.*
@ -42,3 +42,4 @@ man/*.html
man/*.1
doxygen/
.clang_complete
/src/backtrace-symbols.[ch]

View File

@ -47,6 +47,7 @@ ifeq "$(NO_VSYNC_OPENGL)" ""
CFG += -DCONFIG_VSYNC_OPENGL
# -lGL must precede some other libraries, or it segfaults on FreeBSD (#74)
LIBS := -lGL $(LIBS)
OBJS += opengl.o
endif
# ==== D-Bus ====
@ -67,7 +68,16 @@ COMPTON_VERSION ?= git-$(shell git describe --always --dirty)-$(shell git log -1
CFG += -DCOMPTON_VERSION="\"$(COMPTON_VERSION)\""
LDFLAGS ?= -Wl,-O1 -Wl,--as-needed
CFLAGS ?= -DNDEBUG -O2 -D_FORTIFY_SOURCE=2
ifeq "$(DEV)" ""
CFLAGS ?= -DNDEBUG -O2 -D_FORTIFY_SOURCE=2
else
CC = clang
export LD_ALTEXEC = /usr/bin/ld.gold
OBJS += backtrace-symbols.o
LIBS += -lbfd
CFLAGS += -ggdb -Weverything -Wno-disabled-macro-expansion -Wno-padded -Wno-gnu
endif
LIBS += $(shell pkg-config --libs $(PACKAGES))
INCS += $(shell pkg-config --cflags $(PACKAGES))

View File

@ -130,6 +130,9 @@
#error libXcomposite version unsupported
#endif
/// @brief Length of generic buffers.
#define BUF_LEN 80
#define ROUNDED_PERCENT 0.05
#define ROUNDED_PIXELS 10
@ -152,6 +155,9 @@
#define XRFILTER_GUASSIAN "gaussian"
#define XRFILTER_BINOMIAL "binomial"
/// @brief Maximum OpenGL FBConfig depth.
#define OPENGL_MAX_DEPTH 32
// Window flags
// Window size is changed
@ -249,9 +255,18 @@ typedef enum {
VSYNC_DRM,
VSYNC_OPENGL,
VSYNC_OPENGL_OML,
VSYNC_OPENGL_SWC,
NUM_VSYNC,
} vsync_t;
/// @brief Possible backends of compton.
enum backend {
BKEND_XRENDER,
BKEND_GLX,
};
typedef struct _glx_texture glx_texture_t;
#ifdef CONFIG_VSYNC_OPENGL
typedef int (*f_WaitVideoSync) (int, int, unsigned *);
typedef int (*f_GetVideoSync) (unsigned *);
@ -259,15 +274,39 @@ typedef int (*f_GetVideoSync) (unsigned *);
typedef Bool (*f_GetSyncValuesOML) (Display* dpy, GLXDrawable drawable, int64_t* ust, int64_t* msc, int64_t* sbc);
typedef Bool (*f_WaitForMscOML) (Display* dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t* ust, int64_t* msc, int64_t* sbc);
typedef int (*f_SwapIntervalSGI) (int interval);
typedef void (*f_BindTexImageEXT) (Display *display, GLXDrawable drawable, int buffer, const int *attrib_list);
typedef void (*f_ReleaseTexImageEXT) (Display *display, GLXDrawable drawable, int buffer);
struct glx_fbconfig {
/// @brief Wrapper of a GLX FBConfig.
typedef struct {
GLXFBConfig cfg;
GLint texture_fmt;
GLint texture_tgts;
bool y_inverted;
} glx_fbconfig_t;
/// @brief Wrapper of a binded GLX texture.
struct _glx_texture {
GLuint texture;
GLXPixmap glpixmap;
Pixmap pixmap;
int width;
int height;
int depth;
bool y_inverted;
};
#endif
typedef struct {
Pixmap pixmap;
Picture pict;
glx_texture_t *ptex;
} paint_t;
#define PAINT_INIT { .pixmap = None, .pict = None }
typedef struct {
int size;
double *data;
@ -293,6 +332,8 @@ typedef struct {
/// The display name we used. NULL means we are using the value of the
/// <code>DISPLAY</code> environment variable.
char *display;
/// The backend in use.
enum backend backend;
/// Whether to try to detect WM windows and mark them as focused.
bool mark_wmwin_focused;
/// Whether to mark override-redirect windows as focused.
@ -434,8 +475,10 @@ typedef struct {
// Damage root_damage;
/// X Composite overlay window. Used if <code>--paint-on-overlay</code>.
Window overlay;
/// Whether the root tile is filled by compton.
bool root_tile_fill;
/// Picture of the root window background.
Picture root_tile;
paint_t root_tile_paint;
/// A region of the size of the screen.
XserverRegion screen_reg;
/// Picture of root window. Destination of painting in no-DBE painting
@ -488,6 +531,10 @@ typedef struct {
/// Pointer to the <code>next</code> member of tail element of the error
/// ignore linked list.
ignore_t **ignore_tail;
#ifdef CONFIG_VSYNC_OPENGL
/// Current GLX Z value.
int glx_z;
#endif
/// Reset program after next paint.
bool reset;
@ -555,14 +602,14 @@ typedef struct {
f_GetSyncValuesOML glXGetSyncValuesOML;
/// Pointer to glXWaitForMscOML function.
f_WaitForMscOML glXWaitForMscOML;
/// Pointer to glXSwapIntervalSGI function.
f_SwapIntervalSGI glXSwapIntervalProc;
/// Pointer to glXBindTexImageEXT function.
f_BindTexImageEXT glXBindTexImageEXT;
f_BindTexImageEXT glXBindTexImageProc;
/// Pointer to glXReleaseTexImageEXT function.
f_ReleaseTexImageEXT glXReleaseTexImageEXT;
/// FBConfig for RGB GLX pixmap.
struct glx_fbconfig *glx_fbconfig_rgb;
/// FBConfig for RGBA GLX pixmap.
struct glx_fbconfig *glx_fbconfig_rgba;
f_ReleaseTexImageEXT glXReleaseTexImageProc;
/// FBConfig-s for GLX pixmap of different depths.
glx_fbconfig_t *glx_fbconfigs[OPENGL_MAX_DEPTH + 1];
#endif
// === X extension related ===
@ -664,16 +711,16 @@ typedef struct _win {
Window id;
/// Window attributes.
XWindowAttributes a;
/// Window visual pict format;
XRenderPictFormat *pictfmt;
/// Window painting mode.
winmode_t mode;
/// Whether the window has been damaged at least once.
bool damaged;
/// Damage of the window.
Damage damage;
/// NameWindowPixmap of the window.
Pixmap pixmap;
/// Picture of the window.
Picture picture;
/// Paint info of the window.
paint_t paint;
/// Bounding shape of the window.
XserverRegion border_size;
/// Region of the whole window, shadow region included.
@ -750,8 +797,6 @@ typedef struct _win {
/// broken window managers not transferring client window's
/// _NET_WM_OPACITY value
opacity_t opacity_prop_client;
/// Alpha mask Picture to render window with opacity.
Picture alpha_pict;
// Fading-related members
/// Do not fade if it's false. Change on window type change.
@ -763,8 +808,6 @@ typedef struct _win {
// Frame-opacity-related members
/// Current window frame opacity. Affected by window opacity.
double frame_opacity;
/// Alpha mask Picture to render window frame with opacity.
Picture frame_alpha_pict;
/// Frame widths. Determined by client window attributes.
unsigned int left_width, right_width, top_width, bottom_width;
@ -784,9 +827,7 @@ typedef struct _win {
/// Height of shadow. Affected by window size and commandline argument.
int shadow_height;
/// Picture to render shadow. Affected by window size.
Picture shadow_pict;
/// Alpha mask Picture to render shadow. Affected by shadow opacity.
Picture shadow_alpha_pict;
paint_t shadow_paint;
/// The value of _COMPTON_SHADOW attribute of the window. Below 0 for
/// none.
long prop_shadow;
@ -896,6 +937,20 @@ XFixesDestroyRegion_(Display *dpy, XserverRegion reg,
// === Functions ===
/**
* @brief Quit if the passed-in pointer is empty.
*/
static inline void
allocchk_(const char *func_name, void *ptr) {
if (!ptr) {
printf_err("%s(): Failed to allocate memory.", func_name);
exit(1);
}
}
/// @brief Wrapper of allocchk_().
#define allocchk(ptr) allocchk_(__func__, ptr)
/**
* Return whether a struct timeval value is empty.
*/
@ -1304,6 +1359,14 @@ get_atom(session_t *ps, const char *atom_name) {
return XInternAtom(ps->dpy, atom_name, False);
}
/**
* Return the painting target window.
*/
static inline Window
get_tgt_window(session_t *ps) {
return ps->o.paint_on_overlay ? ps->overlay: ps->root;
}
/**
* Find a window from window id in window linked list of the session.
*/
@ -1371,6 +1434,17 @@ copy_region(const session_t *ps, XserverRegion oldregion) {
return region;
}
/**
* Destroy a <code>XserverRegion</code>.
*/
static inline void
free_region(session_t *ps, XserverRegion *p) {
if (*p) {
XFixesDestroyRegion(ps->dpy, *p);
*p = None;
}
}
/**
* Determine if a window has a specific property.
*
@ -1454,6 +1528,66 @@ force_repaint(session_t *ps);
bool
vsync_init(session_t *ps);
#ifdef CONFIG_VSYNC_OPENGL
/** @name GLX
*/
///@{
bool
glx_init(session_t *ps, bool need_render);
void
glx_destroy(session_t *ps);
void
glx_on_root_change(session_t *ps);
bool
glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap,
int width, int height, int depth);
void
glx_release_pixmap(session_t *ps, glx_texture_t *ptex);
static inline bool
glx_tex_binded(const glx_texture_t *ptex) {
return ptex && ptex->glpixmap && ptex->texture;
}
bool
glx_render(session_t *ps, const glx_texture_t *ptex,
int x, int y, int dx, int dy, int width, int height, int z,
double opacity, bool neg, XserverRegion reg_tgt);
#endif
static inline void
free_texture(session_t *ps, glx_texture_t **pptex) {
#ifdef CONFIG_VSYNC_OPENGL
glx_texture_t *ptex = *pptex;
// Quit if there's nothing
if (!ptex)
return;
glx_release_pixmap(ps, ptex);
// Free texture
if (ptex->texture) {
glDeleteTextures(1, &ptex->texture);
ptex->texture = 0;
}
// Free structure itself
free(ptex);
*pptex = NULL;
#endif
}
///@}
/** @name DBus handling
*/
///@{
#ifdef CONFIG_DBUS
/** @name DBus handling
*/

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,6 @@
#include "common.h"
#include <ctype.h>
#include <math.h>
#include <sys/select.h>
#include <limits.h>
@ -52,14 +51,6 @@ set_ignore_next(session_t *ps) {
static int
should_ignore(session_t *ps, unsigned long sequence);
/**
* Return the painting target window.
*/
static inline Window
get_tgt_window(session_t *ps) {
return ps->o.paint_on_overlay ? ps->overlay: ps->root;
}
/**
* Reset filter on a <code>Picture</code>.
*/
@ -119,16 +110,6 @@ array_wid_exists(const Window *arr, int count, Window wid) {
return false;
}
/**
* Destroy a <code>XserverRegion</code>.
*/
inline static void
free_region(session_t *ps, XserverRegion *p) {
if (*p) {
XFixesDestroyRegion(ps->dpy, *p);
*p = None;
}
}
/**
* Destroy a <code>Picture</code>.
@ -176,16 +157,58 @@ free_wincondlst(c2_lptr_t **pcondlst) {
}
#endif
/**
* Check whether a paint_t contains enough data.
*/
static inline bool
paint_isvalid(session_t *ps, const paint_t *ppaint) {
if (!ppaint || !ppaint->pixmap)
return false;
if (BKEND_XRENDER == ps->o.backend && !ppaint->pict)
return false;
#ifdef CONFIG_VSYNC_OPENGL
if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex))
return false;
#endif
return true;
}
/**
* Bind texture in paint_t if we are using OpenGL backend.
*/
static inline bool
paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int hei, int depth) {
#ifdef CONFIG_VSYNC_OPENGL
// TODO: Make sure we have the same Pixmap binded?
if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex)) {
return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei, depth);
}
#endif
return true;
}
/**
* Free paint_t.
*/
static inline void
free_paint(session_t *ps, paint_t *ppaint) {
free_texture(ps, &ppaint->ptex);
free_picture(ps, &ppaint->pict);
free_pixmap(ps, &ppaint->pixmap);
}
/**
* Destroy all resources in a <code>struct _win</code>.
*/
inline static void
static inline void
free_win_res(session_t *ps, win *w) {
free_region(ps, &w->extents);
free_pixmap(ps, &w->pixmap);
free_picture(ps, &w->picture);
free_paint(ps, &w->paint);
free_region(ps, &w->border_size);
free_picture(ps, &w->shadow_pict);
free_paint(ps, &w->shadow_paint);
free_damage(ps, &w->damage);
free_region(ps, &w->reg_ignore);
free(w->name);
@ -194,6 +217,19 @@ free_win_res(session_t *ps, win *w) {
free(w->role);
}
/**
* Free root tile related things.
*/
static inline void
free_root_tile(session_t *ps) {
free_picture(ps, &ps->root_tile_paint.pict);
free_texture(ps, &ps->root_tile_paint.ptex);
if (ps->root_tile_fill)
free_pixmap(ps, &ps->root_tile_paint.pixmap);
ps->root_tile_paint.pixmap = None;
ps->root_tile_fill = false;
}
/**
* Get current system clock in milliseconds.
*/
@ -274,8 +310,8 @@ presum_gaussian(session_t *ps, conv *map);
static XImage *
make_shadow(session_t *ps, double opacity, int width, int height);
static Picture
shadow_picture(session_t *ps, double opacity, int width, int height);
static bool
win_build_shadow(session_t *ps, win *w, double opacity);
static Picture
solid_picture(session_t *ps, bool argb, double a,
@ -463,11 +499,11 @@ group_is_focused(session_t *ps, Window leader) {
static win *
recheck_focus(session_t *ps);
static Picture
root_tile_f(session_t *ps);
static bool
get_root_tile(session_t *ps);
static void
paint_root(session_t *ps, Picture tgt_buffer);
paint_root(session_t *ps, XserverRegion reg_paint);
static XserverRegion
win_get_region(session_t *ps, win *w, bool use_offset);
@ -490,6 +526,22 @@ get_frame_extents(session_t *ps, win *w, Window client);
static win *
paint_preprocess(session_t *ps, win *list);
static void
render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei,
double opacity, bool argb, bool neg,
Picture pict, glx_texture_t *ptex, XserverRegion reg_paint);
static inline void
win_render(session_t *ps, win *w, int x, int y, int wid, int hei, double opacity, XserverRegion reg_paint, Picture pict) {
const int dx = (w ? w->a.x: 0) + x;
const int dy = (w ? w->a.y: 0) + y;
const bool argb = (w && w->mode == WMODE_ARGB);
const bool neg = (w && w->invert_color);
render(ps, x, y, dx, dy, wid, hei, opacity, argb, neg,
pict, (w ? w->paint.ptex: ps->root_tile_paint.ptex), reg_paint);
}
static void
paint_all(session_t *ps, XserverRegion region, win *t);
@ -887,31 +939,7 @@ swopti_init(session_t *ps);
static void
swopti_handle_timeout(session_t *ps, struct timeval *ptv);
static bool
opengl_init(session_t *ps, bool need_render);
static void
opengl_destroy(session_t *ps);
#ifdef CONFIG_VSYNC_OPENGL
/**
* Check if a GLX extension exists.
*/
static inline bool
opengl_hasext(session_t *ps, const char *ext) {
const char *glx_exts = glXQueryExtensionsString(ps->dpy, ps->scr);
const char *pos = strstr(glx_exts, ext);
// Make sure the extension string is matched as a whole word
if (!pos
|| ((pos - glx_exts) && !isspace(*(pos - 1)))
|| (strlen(pos) > strlen(ext) && !isspace(pos[strlen(ext)]))) {
printf_errf("(): Missing OpenGL extension %s.", ext);
return false;
}
return true;
}
/**
* Ensure we have a GLX context.
*/
@ -919,7 +947,7 @@ static inline bool
ensure_glx_context(session_t *ps) {
// Create GLX context
if (!ps->glx_context)
opengl_init(ps, false);
glx_init(ps, false);
return ps->glx_context;
}
@ -939,6 +967,9 @@ vsync_opengl_init(session_t *ps);
static bool
vsync_opengl_oml_init(session_t *ps);
static bool
vsync_opengl_swc_init(session_t *ps);
#ifdef CONFIG_VSYNC_OPENGL
static int
vsync_opengl_wait(session_t *ps);

521
src/opengl.c Normal file
View File

@ -0,0 +1,521 @@
/*
* Compton - a compositor for X11
*
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
*
* Copyright (c) 2011-2013, Christopher Jeffrey
* See LICENSE for more information.
*
*/
#include "opengl.h"
/**
* Initialize OpenGL.
*/
bool
glx_init(session_t *ps, bool need_render) {
bool success = false;
XVisualInfo *pvis = NULL;
// Check for GLX extension
if (!ps->glx_exists) {
if (glXQueryExtension(ps->dpy, &ps->glx_event, &ps->glx_error))
ps->glx_exists = true;
else {
printf_errf("(): No GLX extension.");
goto glx_init_end;
}
}
// Get XVisualInfo
pvis = get_visualinfo_from_visual(ps, ps->vis);
if (!pvis) {
printf_errf("(): Failed to acquire XVisualInfo for current visual.");
goto glx_init_end;
}
// Ensure the visual is double-buffered
if (need_render) {
int value = 0;
if (Success != glXGetConfig(ps->dpy, pvis, GLX_USE_GL, &value) || !value) {
printf_errf("(): Root visual is not a GL visual.");
goto glx_init_end;
}
if (Success != glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value)
|| !value) {
printf_errf("(): Root visual is not a double buffered GL visual.");
goto glx_init_end;
}
}
// Ensure GLX_EXT_texture_from_pixmap exists
if (need_render && !glx_hasext(ps, "GLX_EXT_texture_from_pixmap"))
goto glx_init_end;
// Get GLX context
ps->glx_context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE);
if (!ps->glx_context) {
printf_errf("(): Failed to get GLX context.");
goto glx_init_end;
}
// Attach GLX context
if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), ps->glx_context)) {
printf_errf("(): Failed to attach GLX context.");
goto glx_init_end;
}
// Acquire function addresses
if (need_render) {
ps->glXBindTexImageProc = (f_BindTexImageEXT)
glXGetProcAddress((const GLubyte *) "glXBindTexImageEXT");
ps->glXReleaseTexImageProc = (f_ReleaseTexImageEXT)
glXGetProcAddress((const GLubyte *) "glXReleaseTexImageEXT");
if (!ps->glXBindTexImageProc || !ps->glXReleaseTexImageProc) {
printf_errf("(): Failed to acquire glXBindTexImageEXT() / glXReleaseTexImageEXT().");
goto glx_init_end;
}
}
// Acquire FBConfigs
if (need_render && !glx_update_fbconfig(ps))
goto glx_init_end;
if (need_render) {
// Adjust viewport
glViewport(0, 0, ps->root_width, ps->root_height);
// Initialize settings, copied from dcompmgr
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, ps->root_width, ps->root_height, 0, -100.0, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// glEnable(GL_DEPTH_TEST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_BLEND);
// Clear screen
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glXSwapBuffers(ps->dpy, get_tgt_window(ps));
}
success = true;
glx_init_end:
if (pvis)
XFree(pvis);
if (!success)
glx_destroy(ps);
return success;
}
/**
* Destroy GLX related resources.
*/
void
glx_destroy(session_t *ps) {
// Free FBConfigs
for (int i = 0; i <= OPENGL_MAX_DEPTH; ++i) {
free(ps->glx_fbconfigs[i]);
ps->glx_fbconfigs[i] = NULL;
}
// Destroy GLX context
if (ps->glx_context) {
glXDestroyContext(ps->dpy, ps->glx_context);
ps->glx_context = NULL;
}
}
/**
* Callback to run on root window size change.
*/
void
glx_on_root_change(session_t *ps) {
glViewport(0, 0, ps->root_width, ps->root_height);
}
/**
* @brief Update the FBConfig of given depth.
*/
static inline void
glx_update_fbconfig_bydepth(session_t *ps, int depth, glx_fbconfig_t *pfbcfg) {
// Make sure the depth is sane
if (depth < 0 || depth > OPENGL_MAX_DEPTH)
return;
// Compare new FBConfig with current one
if (glx_cmp_fbconfig(ps, ps->glx_fbconfigs[depth], pfbcfg) < 0) {
#ifdef DEBUG_GLX
printf_dbgf("(%d): %#x overrides %#x, target %#x.\n", depth, (unsigned) pfbcfg->cfg, (ps->glx_fbconfigs[depth] ? (unsigned) ps->glx_fbconfigs[depth]->cfg: 0), pfbcfg->texture_tgts);
#endif
if (!ps->glx_fbconfigs[depth]) {
ps->glx_fbconfigs[depth] = malloc(sizeof(glx_fbconfig_t));
allocchk(ps->glx_fbconfigs[depth]);
}
(*ps->glx_fbconfigs[depth]) = *pfbcfg;
}
}
/**
* Get GLX FBConfigs for all depths.
*/
static bool
glx_update_fbconfig(session_t *ps) {
// Acquire all FBConfigs and loop through them
int nele = 0;
GLXFBConfig* pfbcfgs = glXGetFBConfigs(ps->dpy, ps->scr, &nele);
for (GLXFBConfig *pcur = pfbcfgs; pcur < pfbcfgs + nele; pcur++) {
glx_fbconfig_t fbinfo = {
.cfg = *pcur,
.texture_fmt = 0,
.texture_tgts = 0,
.y_inverted = false,
};
int depth = 0, depth_alpha = 0, val = 0;
if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BUFFER_SIZE, &depth)
|| Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_ALPHA_SIZE, &depth_alpha)) {
printf_errf("(): Failed to retrieve buffer size and alpha size of FBConfig %d.", (int) (pcur - pfbcfgs));
continue;
}
if (Success != glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_TARGETS_EXT, &fbinfo.texture_tgts)) {
printf_errf("(): Failed to retrieve BIND_TO_TEXTURE_TARGETS_EXT of FBConfig %d.", (int) (pcur - pfbcfgs));
continue;
}
bool rgb = false;
bool rgba = false;
if (depth >= 32 && depth_alpha && Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGBA_EXT, &val) && val)
rgba = true;
if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_BIND_TO_TEXTURE_RGB_EXT, &val) && val)
rgb = true;
if (Success == glXGetFBConfigAttrib(ps->dpy, *pcur, GLX_Y_INVERTED_EXT, &val))
fbinfo.y_inverted = val;
if ((depth - depth_alpha) < 32 && rgb) {
fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGB_EXT;
glx_update_fbconfig_bydepth(ps, depth - depth_alpha, &fbinfo);
}
if (rgba) {
fbinfo.texture_fmt = GLX_TEXTURE_FORMAT_RGBA_EXT;
glx_update_fbconfig_bydepth(ps, depth, &fbinfo);
}
}
if (pfbcfgs)
XFree(pfbcfgs);
// Sanity checks
if (!ps->glx_fbconfigs[ps->depth]) {
printf_errf("(): No FBConfig found for default depth %d.", ps->depth);
return false;
}
if (!ps->glx_fbconfigs[32]) {
printf_errf("(): No FBConfig found for depth 32. Expect crazy things.");
}
return true;
}
static inline int
glx_cmp_fbconfig_cmpattr(session_t *ps,
const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b,
int attr) {
int attr_a = 0, attr_b = 0;
// TODO: Error checking
glXGetFBConfigAttrib(ps->dpy, pfbc_a->cfg, attr, &attr_a);
glXGetFBConfigAttrib(ps->dpy, pfbc_b->cfg, attr, &attr_b);
return attr_a - attr_b;
}
/**
* Compare two GLX FBConfig's to find the preferred one.
*/
static int
glx_cmp_fbconfig(session_t *ps,
const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b) {
int result = 0;
if (!pfbc_a)
return -1;
if (!pfbc_b)
return 1;
#define P_CMPATTR_LT(attr) { if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) return -result; }
#define P_CMPATTR_GT(attr) { if ((result = glx_cmp_fbconfig_cmpattr(ps, pfbc_a, pfbc_b, (attr)))) return result; }
P_CMPATTR_LT(GLX_BIND_TO_TEXTURE_RGBA_EXT);
P_CMPATTR_LT(GLX_DOUBLEBUFFER);
P_CMPATTR_LT(GLX_STENCIL_SIZE);
P_CMPATTR_LT(GLX_DEPTH_SIZE);
P_CMPATTR_GT(GLX_BIND_TO_MIPMAP_TEXTURE_EXT);
return 0;
}
/**
* Bind a X pixmap to an OpenGL texture.
*/
bool
glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, Pixmap pixmap,
int width, int height, int depth) {
if (depth > OPENGL_MAX_DEPTH) {
printf_errf("(%d): Requested depth higher than %d.", depth,
OPENGL_MAX_DEPTH);
return false;
}
const glx_fbconfig_t *pcfg = ps->glx_fbconfigs[depth];
if (!pcfg) {
printf_errf("(%d): Couldn't find FBConfig with requested depth.", depth);
return false;
}
const GLenum target = GL_TEXTURE_2D;
glx_texture_t *ptex = *pptex;
bool need_release = true;
// Allocate structure
if (!ptex) {
static const glx_texture_t GLX_TEX_DEF = {
.texture = 0,
.glpixmap = 0,
.pixmap = 0,
.width = 0,
.height = 0,
.depth = 0,
.y_inverted = false,
};
ptex = malloc(sizeof(glx_texture_t));
allocchk(ptex);
memcpy(ptex, &GLX_TEX_DEF, sizeof(glx_texture_t));
*pptex = ptex;
}
glEnable(target);
// Release pixmap if parameters are inconsistent
if (ptex->texture && !(width == ptex->width && height == ptex->height
&& ptex->pixmap == pixmap && depth == ptex->depth)) {
glx_release_pixmap(ps, ptex);
}
// Create texture
if (!ptex->texture) {
need_release = false;
GLuint texture = 0;
glGenTextures(1, &texture);
glBindTexture(target, texture);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(target, 0);
ptex->texture = texture;
ptex->y_inverted = pcfg->y_inverted;
}
if (!ptex->texture) {
printf_errf("(): Failed to allocate texture.");
return false;
}
glBindTexture(target, ptex->texture);
// Create GLX pixmap
if (!ptex->glpixmap) {
need_release = false;
#ifdef DEBUG_GLX
printf_dbgf("(): depth %d rgba %d\n", depth, (GLX_TEXTURE_FORMAT_RGBA_EXT == pcfg->texture_fmt));
#endif
int attrs[] = {
GLX_TEXTURE_FORMAT_EXT,
pcfg->texture_fmt,
// GLX_TEXTURE_TARGET_EXT,
// ,
0,
};
ptex->glpixmap = glXCreatePixmap(ps->dpy, pcfg->cfg, pixmap, attrs);
}
if (!ptex->glpixmap) {
printf_errf("(): Failed to allocate GLX pixmap.");
return false;
}
// The specification requires rebinding whenever the content changes...
// We can't follow this, too slow.
if (need_release)
ps->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
ps->glXBindTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
// Cleanup
glBindTexture(target, 0);
glDisable(target);
ptex->width = width;
ptex->height = height;
ptex->depth = depth;
ptex->pixmap = pixmap;
return true;
}
/**
* @brief Release binding of a texture.
*/
void
glx_release_pixmap(session_t *ps, glx_texture_t *ptex) {
// Release binding
if (ptex->glpixmap && ptex->texture) {
glBindTexture(GL_TEXTURE_2D, ptex->texture);
ps->glXReleaseTexImageProc(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
glBindTexture(GL_TEXTURE_2D, 0);
}
// Free GLX Pixmap
if (ptex->glpixmap) {
glXDestroyPixmap(ps->dpy, ptex->glpixmap);
ptex->glpixmap = 0;
}
}
/**
* @brief Render a region with texture data.
*/
bool
glx_render(session_t *ps, const glx_texture_t *ptex,
int x, int y, int dx, int dy, int width, int height, int z,
double opacity, bool neg, XserverRegion reg_tgt) {
if (!ptex || !ptex->texture) {
printf_errf("(): Missing texture.");
return false;
}
// Enable blending if needed
if (opacity < 1.0 || GLX_TEXTURE_FORMAT_RGBA_EXT ==
ps->glx_fbconfigs[ptex->depth]->texture_fmt) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Needed for handling opacity of ARGB texture
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glColor4f(1.0f, 1.0f, 1.0f, opacity);
}
// Color negation
if (neg) {
// Simple color negation
if (!glIsEnabled(GL_BLEND)) {
glEnable(GL_COLOR_LOGIC_OP);
glLogicOp(GL_COPY_INVERTED);
}
// Blending color negation
else {
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
glTexEnvf(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
}
}
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, ptex->texture);
glBegin(GL_QUADS);
{
XserverRegion reg_new = None;
XRectangle rec_all = {
.x = dx,
.y = dy,
.width = width,
.height = height
};
XRectangle *rects = &rec_all;
int nrects = 1;
#ifdef DEBUG_GLX
printf_dbgf("(): Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d \n", x, y, width, height, dx, dy, ptex->width, ptex->height, z);
#endif
if (reg_tgt) {
reg_new = XFixesCreateRegion(ps->dpy, &rec_all, 1);
XFixesIntersectRegion(ps->dpy, reg_new, reg_new, reg_tgt);
nrects = 0;
rects = XFixesFetchRegion(ps->dpy, reg_new, &nrects);
}
for (int i = 0; i < nrects; ++i) {
GLfloat rx = (double) (rects[i].x - dx + x) / ptex->width;
GLfloat ry = (double) (rects[i].y - dy + y) / ptex->height;
GLfloat rxe = rx + (double) rects[i].width / ptex->width;
GLfloat rye = ry + (double) rects[i].height / ptex->height;
GLint rdx = rects[i].x;
GLint rdy = rects[i].y;
GLint rdxe = rdx + rects[i].width;
GLint rdye = rdy + rects[i].height;
// Invert Y if needed, this may not work as expected, though. I don't
// have such a FBConfig to test with.
if (!ptex->y_inverted) {
ry = 1.0 - ry;
rye = 1.0 - rye;
}
#ifdef DEBUG_GLX
printf_dbgf("(): Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d\n", i, rx, ry, rxe, rye, rdx, rdy, rdxe, rdye);
#endif
glTexCoord2f(rx, ry);
glVertex3i(rdx, rdy, z);
glTexCoord2f(rxe, ry);
glVertex3i(rdxe, rdy, z);
glTexCoord2f(rxe, rye);
glVertex3i(rdxe, rdye, z);
glTexCoord2f(rx, rye);
glVertex3i(rdx, rdye, z);
}
if (rects && rects != &rec_all)
XFree(rects);
free_region(ps, &reg_new);
}
glEnd();
// Cleanup
glBindTexture(GL_TEXTURE_2D, 0);
glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glDisable(GL_BLEND);
glDisable(GL_COLOR_LOGIC_OP);
return true;
}

46
src/opengl.h Normal file
View File

@ -0,0 +1,46 @@
/*
* Compton - a compositor for X11
*
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
*
* Copyright (c) 2011-2013, Christopher Jeffrey
* See LICENSE for more information.
*
*/
#include "common.h"
#include <ctype.h>
/**
* Check if a GLX extension exists.
*/
static inline bool
glx_hasext(session_t *ps, const char *ext) {
const char *glx_exts = glXQueryExtensionsString(ps->dpy, ps->scr);
const char *pos = strstr(glx_exts, ext);
// Make sure the extension string is matched as a whole word
if (!pos
|| ((pos - glx_exts) && !isspace(*(pos - 1)))
|| (strlen(pos) > strlen(ext) && !isspace(pos[strlen(ext)]))) {
printf_errf("(): Missing OpenGL extension %s.", ext);
return false;
}
return true;
}
static inline XVisualInfo *
get_visualinfo_from_visual(session_t *ps, Visual *visual) {
XVisualInfo vreq = { .visualid = XVisualIDFromVisual(visual) };
int nitems = 0;
return XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems);
}
static bool
glx_update_fbconfig(session_t *ps);
static int
glx_cmp_fbconfig(session_t *ps,
const glx_fbconfig_t *pfbc_a, const glx_fbconfig_t *pfbc_b);