Basic compositing with GL
authorDerek Foreman <foremande@gmail.com>
Wed, 9 Jun 2010 20:04:10 +0000 (16:04 -0400)
committerDana Jansens <danakj@orodu.net>
Sat, 26 Jun 2010 23:30:46 +0000 (01:30 +0200)
openbox/composite.c
openbox/composite.h
openbox/event.c
openbox/window.c
openbox/window.h

index d33dc3ace56377a2b3d468b55e5b491b10b04791..db355346e0619d91b8b5d149e3864a80d51987f0 100644 (file)
 #include "composite.h"
 #include "config.h"
 #include "obt/display.h"
+#include "openbox.h"
+#include "screen.h"
+#include "client.h"
+#include "frame.h"
+#include "geom.h"
 
 #include <X11/Xlib.h>
 #include <glib.h>
 
+#ifdef USE_COMPOSITING
+
+static gboolean composite(gpointer data);
+
+static struct ObCompositor obcomp;
+
+static inline void
+time_fix(struct timeval *tv)
+{
+    while (tv->tv_usec >= 1000000) {
+        tv->tv_usec -= 1000000;
+        ++tv->tv_sec;
+    }
+    while (tv->tv_usec < 0) {
+        tv->tv_usec += 1000000;
+        --tv->tv_sec;
+    }
+}
+
+static gboolean composite_need_redraw(void)
+{
+    return TRUE;
+}
+
+static void get_best_fbcon(GLXFBConfig *in, int count, int depth, GLXFBConfig *out)
+{
+    GLXFBConfig best = 0;
+    XVisualInfo *vi;
+    int i, value, has_alpha;
+
+    for (i = 0; i < count; i++) {
+
+        vi = glXGetVisualFromFBConfig(obt_display, in[i]);
+        if (vi == NULL)
+            continue;
+
+        value = vi->depth;
+        XFree(vi);
+
+        if (value != depth)
+            continue;
+
+        value = 0;
+        if (depth == 32) {
+            obcomp.GetFBConfigAttrib(obt_display, in[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, &value);
+        }
+        if (!value) {
+            obcomp.GetFBConfigAttrib(obt_display, in[i], GLX_BIND_TO_TEXTURE_RGB_EXT, &value);
+        }
+        if (!value) // neither bind to texture?  no dice
+            continue;
+        best = in[i];
+    }
+    *out = best;
+}
+#endif
+
 void composite_startup(gboolean reconfig)
 {
     /* This function will try enable composite if config_comp is TRUE.  At the
        end of this process, config_comp will be set to TRUE only if composite
        is enabled, and FALSE otherwise. */
 #ifdef USE_COMPOSITING
-    gboolean try;
+    int count, val;
+    Time timestamp;
+    XWindowAttributes xa;
+    XserverRegion xr;
+    XVisualInfo tmp;
+    XVisualInfo *vi;
+    GLXFBConfig *fbcs;
+    const char *glstring;
+    gchar *astr;
+    Atom cm_atom;
+    Window cm_owner;
+    Window root;
+    int i;
 
     if (reconfig) return;
-    if (!(try = config_comp)) return;
+    if (!config_comp) return;
 
     config_comp = FALSE;
 
-    /* XXX test GL things we need also */
-    if (!obt_display_extension_composite ||
-        !obt_display_extension_damage ||
-        !obt_display_extension_fixes)
-    {
-        try = FALSE;
+    root = RootWindow(obt_display, ob_screen);
+
+    astr = g_strdup_printf("_NET_WM_CM_S%d", ob_screen);
+    cm_atom = XInternAtom(obt_display, astr, 0);
+    g_free(astr);
+
+    cm_owner = XGetSelectionOwner(obt_display, cm_atom);
+    if (cm_owner != None) {
+        g_message("Failed to enable composite.  There is already a compositor running.");
+        return;
+    }
+
+    timestamp = event_time();
+    XSetSelectionOwner(obt_display, cm_atom, screen_support_win, timestamp);
+
+    if (XGetSelectionOwner(obt_display, cm_atom) != screen_support_win) {
+        g_message("Failed to enable composite.  Could not acquire the composite manager selection on screen %d", ob_screen);
+        return;
+    }
+
+    if (!obt_display_extension_composite) {
+        g_message("Failed to enable composite.  The XComposite extension is missing.");
+        return;
+    }
+
+    if (!obt_display_extension_damage) {
+        g_message("Failed to enable composite.  The XDamage extension is missing.");
+        return;
+    }
+
+    if (!obt_display_extension_fixes) {
+        g_message("Failed to enable composite.  The XFixes extension is missing.");
+        return;
+    }
+
+    glstring = glXQueryExtensionsString(obt_display, ob_screen);
+    if (!strstr(glstring, "GLX_EXT_texture_from_pixmap")) {
+        g_message("Failed to enable composite.  GLX_EXT_texture_from_pixmap is not present.");
+        return;
+    }
+
+    obcomp.CreatePixmap = (CreatePixmapT)glXGetProcAddress("glXCreatePixmap");
+    if (!obcomp.CreatePixmap) {
+        g_message("Failed to enable composite.  glXCreatePixmap unavailable.");
+        return;
+    }
+
+    obcomp.BindTexImage = (BindTexImageT)glXGetProcAddress("glXBindTexImageEXT");
+    if (!obcomp.BindTexImage) {
+        g_message("Failed to enable composite.  glXBindTexImage unavailable.");
+        return;
+    }
+
+    obcomp.ReleaseTexImage = (ReleaseTexImageT)glXGetProcAddress("glXReleaseTexImageEXT");
+    if (!obcomp.ReleaseTexImage) {
+        g_message("Failed to enable composite.  glXReleaseTexImage unavailable.");
+        return;
     }
 
-    config_comp = try;
-    if (!config_comp)
-        g_message("Failed to enable composite.  A better explanation why would be nice");
+    obcomp.GetFBConfigs = (GetFBConfigsT)glXGetProcAddress("glXGetFBConfigs");
+    if (!obcomp.GetFBConfigs) {
+        g_message("Failed to enable composite.  glXGetFBConfigs unavailable.");
+        return;
+    }
+
+    obcomp.GetFBConfigAttrib = (GetFBConfigAttribT)glXGetProcAddress("glXGetFBConfigAttrib");
+    if (!obcomp.GetFBConfigAttrib) {
+        g_message("Failed to enable composite.  glXGetFBConfigAttrib unavailable.");
+        return;
+    }
+
+    obcomp.overlay = XCompositeGetOverlayWindow(obt_display, root);
+//now you've done it.  better release this if we fail later!
+//or move this get to the end?
+
+    xr = XFixesCreateRegion(obt_display, NULL, 0);
+    XFixesSetWindowShapeRegion(obt_display, obcomp.overlay, ShapeBounding, 0, 0, 0);
+    XFixesSetWindowShapeRegion(obt_display, obcomp.overlay, ShapeInput, 0, 0, xr);
+    XFixesDestroyRegion(obt_display, xr);
+
+    if (!XGetWindowAttributes(obt_display, root, &xa)) {
+        g_message("Failed to enable composite.  XGetWindowAttributes failed.");
+        return;
+    }
+
+    tmp.visualid = XVisualIDFromVisual(xa.visual);
+    vi = XGetVisualInfo(obt_display, VisualIDMask, &tmp, &count);
+
+    if (!count) {
+        g_message("Failed to enable composite.  Couldn't get visual info.");
+        return;
+    }
+
+
+    glXGetConfig(obt_display, vi, GLX_USE_GL, &val);
+    if (!val) {
+        g_message("Failed to enable composite.  Visual is not GL capable");
+        return;
+    }
+
+    glXGetConfig(obt_display, vi, GLX_DOUBLEBUFFER, &val);
+    if (!val) {
+        g_message("Failed to enable composite.  Visual is not double buffered");
+        return;
+    }
+
+    obcomp.ctx = glXCreateContext(obt_display, vi, NULL, True);
+    XFree(vi);
+
+    fbcs = obcomp.GetFBConfigs(obt_display, ob_screen, &count);
+
+    if (!count) {
+        g_message("Failed to enable composite.  No valid FBConfigs.");
+        return;
+    }
+
+    memset(&obcomp.PixmapConfig, 0, sizeof(obcomp.PixmapConfig));
+
+    for (i = 0; i < MAX_DEPTH + 1; i++) {
+        get_best_fbcon(fbcs, count, i, &obcomp.PixmapConfig[i]);
+    }
+
+    if (count)
+        XFree(fbcs);
+
+    printf("Best visual for 24bpp was 0x%x\n", obcomp.PixmapConfig[24]);
+    printf("Best visual for 32bpp was 0x%x\n", obcomp.PixmapConfig[32]);
+
+    g_idle_add(composite, NULL);
+
+    glXMakeCurrent(obt_display, obcomp.overlay, obcomp.ctx);
+    config_comp = TRUE;
+    obcomp.screendims = screen_physical_area_all_monitors();
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+    glXSwapBuffers(obt_display, obcomp.overlay);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+
+    glOrtho(obcomp.screendims->x,
+            obcomp.screendims->x + obcomp.screendims->width,
+            obcomp.screendims->y + obcomp.screendims->height,
+            obcomp.screendims->y,
+            -100, 100);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    glDisable(GL_DEPTH_TEST);
+    glEnable(GL_TEXTURE_2D);
+    glEnable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 #endif
 }
 
@@ -57,3 +269,71 @@ void composite_shutdown(gboolean reconfig)
     if (reconfig) return;
 #endif
 }
+
+static gboolean composite(gpointer data)
+{
+#ifdef USE_COMPOSITING
+    int attribs[] = {
+        GLX_TEXTURE_FORMAT_EXT,
+        None,
+        None
+    };
+    struct timeval start, end, dif;
+    GList *it;
+    ObWindow *win;
+    ObClient *client;
+    static int i;
+
+//    if (!obcomp.need_redraw)
+//        return;
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+//    for (it = stacking_list; it; it = g_list_next(it)) {
+    for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
+        win = it->data;
+        if (win->type != OB_WINDOW_CLASS_CLIENT)
+            continue;
+
+        client = WINDOW_AS_CLIENT(win);
+        if (client->desktop != screen_desktop)
+            continue;
+
+        if (win->depth == 32)
+            attribs[1] = GLX_TEXTURE_FORMAT_RGBA_EXT;
+        else
+            attribs[1] = GLX_TEXTURE_FORMAT_RGB_EXT;
+
+        if (win->gpixmap == None)
+            win->gpixmap = obcomp.CreatePixmap(obt_display, obcomp.PixmapConfig[win->depth], win->pixmap, attribs);
+
+        glBindTexture(GL_TEXTURE_2D, win->texture);
+gettimeofday(&start, NULL);
+        obcomp.BindTexImage(obt_display, win->gpixmap, GLX_FRONT_LEFT_EXT, NULL);
+gettimeofday(&end, NULL);
+dif.tv_sec = end.tv_sec - start.tv_sec;
+dif.tv_usec = end.tv_usec - start.tv_usec;
+time_fix(&dif);
+//printf("took %f ms\n", dif.tv_sec * 1000.0 + dif.tv_usec / 1000.0);
+        glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+        glBegin(GL_QUADS);
+        glTexCoord2f(0, 0);
+        glVertex3f(client->frame->area.x, client->frame->area.y, 0.0);
+        glTexCoord2f(0, 1);
+        glVertex3f(client->frame->area.x, client->frame->area.y + client->frame->area.height, 0.0);
+        glTexCoord2f(1, 1);
+        glVertex3f(client->frame->area.x + client->frame->area.width, client->frame->area.y + client->frame->area.height, 0.0);
+        glTexCoord2f(1, 0);
+        glVertex3f(client->frame->area.x + client->frame->area.width, client->frame->area.y, 0.0);
+        glEnd();
+    }
+    glXSwapBuffers(obt_display, obcomp.overlay);
+    glFinish();
+    obcomp.need_redraw = 0;
+#endif
+    return TRUE;
+}
index 6b03cadc48848f7dc6fdbf1ebf1e3f99aace31e9..01b02d86da9b87a37c6adba0776656c5190b75fe 100644 (file)
 #ifndef __composite_h
 #define __composite_h
 
+#ifdef USE_COMPOSITING
 #include <glib.h>
+#include <GL/gl.h>
+#include <GL/glx.h>
+
+#include "geom.h"
+
+#define MAX_DEPTH 32
+
+typedef GLXPixmap (*CreatePixmapT)(Display *display,
+                                   GLXFBConfig config,
+                                   int attribute,
+                                   int *value);
+
+typedef void (*BindTexImageT)(Display *display,
+                              GLXDrawable drawable,
+                              int buffer,
+                              int *attriblist);
+
+typedef void (*ReleaseTexImageT)(Display *display,
+                                 GLXDrawable drawable,
+                                 int buffer);
+
+typedef GLXFBConfig *(*GetFBConfigsT) (Display *display,
+                                       int screen,
+                                       int *nElements);
+
+typedef int (*GetFBConfigAttribT) (Display *display,
+                                   GLXFBConfig config,
+                                   int attribute,
+                                   int *value);
+
+struct ObCompositor {
+    CreatePixmapT CreatePixmap;
+    BindTexImageT BindTexImage;
+    ReleaseTexImageT ReleaseTexImage;
+    GetFBConfigsT GetFBConfigs;
+    GetFBConfigAttribT GetFBConfigAttrib;
+
+    Window overlay;
+
+    GLXContext ctx;
+    GLXFBConfig PixmapConfig[MAX_DEPTH + 1]; // need a [32]
+
+    gboolean need_redraw;
+    const Rect *screendims;
+};
+#endif
 
 void composite_startup(gboolean reconfig);
 void composite_shutdown(gboolean reconfig);
index 46e569bfbfccd23a815da59c81e2be02d7d3590b..3fba27f63a9e4b7f06753a973c49a299ef62b9f7 100644 (file)
@@ -621,6 +621,21 @@ static void event_process(const XEvent *ec, gpointer data)
         if (client && client != focus_client)
             frame_adjust_focus(client->frame, FALSE);
     }
+#ifdef USE_COMPOSITING
+    else if ((e->type == ConfigureNotify || e->type == MapNotify)
+             && obwin && obwin->type != OB_WINDOW_CLASS_PROMPT) {
+        if (obwin->pixmap != None)
+            XFreePixmap(obt_display, obwin->pixmap);
+        obwin->pixmap = XCompositeNameWindowPixmap(obt_display, window_top(obwin));
+        if (obwin->gpixmap != None) {
+            XFreePixmap(obt_display, obwin->gpixmap);
+            obwin->gpixmap = None;
+        }
+
+//XXX actually need to check if this was just a move and not re-make the pmap!
+//printf("win %d has pixmap %d\n", window_top(obwin), obwin->pixmap);
+    }
+#endif
     else if (client)
         event_handle_client(client, e);
     else if (dockapp)
index ad61294d3ef734343904cdaab162e19a95c9673f..f28bd43e2c054dcd074c577f956420ac545796c3 100644 (file)
@@ -94,12 +94,48 @@ void window_add(Window *xwin, ObWindow *win)
 {
     g_assert(xwin != NULL);
     g_assert(win != NULL);
+#ifdef USE_COMPOSITING
+    XWindowAttributes wattrib;
+    Status ret;
+
+    if (win->type != OB_WINDOW_CLASS_PROMPT) {
+        win->damage = XDamageCreate(obt_display, window_top(win), XDamageReportNonEmpty);
+
+        XCompositeRedirectWindow(obt_display, window_top(win), CompositeRedirectManual);
+
+        win->pixmap = None;
+        glGenTextures(1, &win->texture);
+        ret = XGetWindowAttributes(obt_display, window_top(win), &wattrib);
+        g_assert(ret != BadDrawable);
+        g_assert(ret != BadWindow);
+
+        win->depth = wattrib.depth;
+    }
+#endif
     g_hash_table_insert(window_map, xwin, win);
 }
 
 void window_remove(Window xwin)
 {
     g_assert(xwin != None);
+#ifdef USE_COMPOSITING
+    ObWindow *win;
+    win = window_find(xwin);
+    if (!win) {
+        printf("Compositor tried to clean up a window, but it was not there.\n");
+        return;
+    }
+    if (win->type != OB_WINDOW_CLASS_PROMPT) {
+        if (win->damage)
+            XDamageDestroy(obt_display, win->damage);
+        if (win->gpixmap)
+            XFreePixmap(obt_display, win->gpixmap);
+        if (win->pixmap)
+            XFreePixmap(obt_display, win->pixmap);
+        if (win->texture)
+            glDeleteTextures(1, &win->texture);
+    }
+#endif
     g_hash_table_remove(window_map, &xwin);
 }
 
index 24a7d07bc7d7163024941c5d9471d7417b05e770..9883554dfef2202867361b8b785661fad6aa13ec 100644 (file)
 #include <X11/Xlib.h>
 #include <glib.h>
 
+#ifdef USE_COMPOSITING
+#include <X11/extensions/Xdamage.h>
+#include <GL/gl.h>
+#include <GL/glx.h>
+#endif
+
 typedef struct _ObWindow         ObWindow;
 typedef struct _ObInternalWindow ObInternalWindow;
 
@@ -39,6 +45,13 @@ typedef enum {
    struct */
 struct _ObWindow {
     ObWindowClass type;
+#ifdef USE_COMPOSITING
+    GLuint texture;
+    Pixmap pixmap;
+    GLXPixmap gpixmap;
+    Damage damage;
+    int depth;
+#endif
 };
 
 #define WINDOW_IS_MENUFRAME(win) \