]> icculus.org git repositories - dana/openbox.git/blob - openbox/composite.c
Basic compositing with GL
[dana/openbox.git] / openbox / composite.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    composite.c for the Openbox window manager
4    Copyright (c) 2010        Dana Jansens
5    Copyright (c) 2010        Derek Foreman
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "composite.h"
21 #include "config.h"
22 #include "obt/display.h"
23 #include "openbox.h"
24 #include "screen.h"
25 #include "client.h"
26 #include "frame.h"
27 #include "geom.h"
28
29 #include <X11/Xlib.h>
30 #include <glib.h>
31
32 #ifdef USE_COMPOSITING
33
34 static gboolean composite(gpointer data);
35
36 static struct ObCompositor obcomp;
37
38 static inline void
39 time_fix(struct timeval *tv)
40 {
41     while (tv->tv_usec >= 1000000) {
42         tv->tv_usec -= 1000000;
43         ++tv->tv_sec;
44     }
45     while (tv->tv_usec < 0) {
46         tv->tv_usec += 1000000;
47         --tv->tv_sec;
48     }
49 }
50
51 static gboolean composite_need_redraw(void)
52 {
53     return TRUE;
54 }
55
56 static void get_best_fbcon(GLXFBConfig *in, int count, int depth, GLXFBConfig *out)
57 {
58     GLXFBConfig best = 0;
59     XVisualInfo *vi;
60     int i, value, has_alpha;
61
62     for (i = 0; i < count; i++) {
63
64         vi = glXGetVisualFromFBConfig(obt_display, in[i]);
65         if (vi == NULL)
66             continue;
67
68         value = vi->depth;
69         XFree(vi);
70
71         if (value != depth)
72             continue;
73
74         value = 0;
75         if (depth == 32) {
76             obcomp.GetFBConfigAttrib(obt_display, in[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, &value);
77         }
78         if (!value) {
79             obcomp.GetFBConfigAttrib(obt_display, in[i], GLX_BIND_TO_TEXTURE_RGB_EXT, &value);
80         }
81         if (!value) // neither bind to texture?  no dice
82             continue;
83         best = in[i];
84     }
85     *out = best;
86 }
87 #endif
88
89 void composite_startup(gboolean reconfig)
90 {
91     /* This function will try enable composite if config_comp is TRUE.  At the
92        end of this process, config_comp will be set to TRUE only if composite
93        is enabled, and FALSE otherwise. */
94 #ifdef USE_COMPOSITING
95     int count, val;
96     Time timestamp;
97     XWindowAttributes xa;
98     XserverRegion xr;
99     XVisualInfo tmp;
100     XVisualInfo *vi;
101     GLXFBConfig *fbcs;
102     const char *glstring;
103     gchar *astr;
104     Atom cm_atom;
105     Window cm_owner;
106     Window root;
107     int i;
108
109     if (reconfig) return;
110     if (!config_comp) return;
111
112     config_comp = FALSE;
113
114     root = RootWindow(obt_display, ob_screen);
115
116     astr = g_strdup_printf("_NET_WM_CM_S%d", ob_screen);
117     cm_atom = XInternAtom(obt_display, astr, 0);
118     g_free(astr);
119
120     cm_owner = XGetSelectionOwner(obt_display, cm_atom);
121     if (cm_owner != None) {
122         g_message("Failed to enable composite.  There is already a compositor running.");
123         return;
124     }
125
126     timestamp = event_time();
127     XSetSelectionOwner(obt_display, cm_atom, screen_support_win, timestamp);
128
129     if (XGetSelectionOwner(obt_display, cm_atom) != screen_support_win) {
130         g_message("Failed to enable composite.  Could not acquire the composite manager selection on screen %d", ob_screen);
131         return;
132     }
133
134     if (!obt_display_extension_composite) {
135         g_message("Failed to enable composite.  The XComposite extension is missing.");
136         return;
137     }
138
139     if (!obt_display_extension_damage) {
140         g_message("Failed to enable composite.  The XDamage extension is missing.");
141         return;
142     }
143
144     if (!obt_display_extension_fixes) {
145         g_message("Failed to enable composite.  The XFixes extension is missing.");
146         return;
147     }
148
149     glstring = glXQueryExtensionsString(obt_display, ob_screen);
150     if (!strstr(glstring, "GLX_EXT_texture_from_pixmap")) {
151         g_message("Failed to enable composite.  GLX_EXT_texture_from_pixmap is not present.");
152         return;
153     }
154
155     obcomp.CreatePixmap = (CreatePixmapT)glXGetProcAddress("glXCreatePixmap");
156     if (!obcomp.CreatePixmap) {
157         g_message("Failed to enable composite.  glXCreatePixmap unavailable.");
158         return;
159     }
160
161     obcomp.BindTexImage = (BindTexImageT)glXGetProcAddress("glXBindTexImageEXT");
162     if (!obcomp.BindTexImage) {
163         g_message("Failed to enable composite.  glXBindTexImage unavailable.");
164         return;
165     }
166
167     obcomp.ReleaseTexImage = (ReleaseTexImageT)glXGetProcAddress("glXReleaseTexImageEXT");
168     if (!obcomp.ReleaseTexImage) {
169         g_message("Failed to enable composite.  glXReleaseTexImage unavailable.");
170         return;
171     }
172
173     obcomp.GetFBConfigs = (GetFBConfigsT)glXGetProcAddress("glXGetFBConfigs");
174     if (!obcomp.GetFBConfigs) {
175         g_message("Failed to enable composite.  glXGetFBConfigs unavailable.");
176         return;
177     }
178
179     obcomp.GetFBConfigAttrib = (GetFBConfigAttribT)glXGetProcAddress("glXGetFBConfigAttrib");
180     if (!obcomp.GetFBConfigAttrib) {
181         g_message("Failed to enable composite.  glXGetFBConfigAttrib unavailable.");
182         return;
183     }
184
185     obcomp.overlay = XCompositeGetOverlayWindow(obt_display, root);
186 //now you've done it.  better release this if we fail later!
187 //or move this get to the end?
188
189     xr = XFixesCreateRegion(obt_display, NULL, 0);
190     XFixesSetWindowShapeRegion(obt_display, obcomp.overlay, ShapeBounding, 0, 0, 0);
191     XFixesSetWindowShapeRegion(obt_display, obcomp.overlay, ShapeInput, 0, 0, xr);
192     XFixesDestroyRegion(obt_display, xr);
193
194     if (!XGetWindowAttributes(obt_display, root, &xa)) {
195         g_message("Failed to enable composite.  XGetWindowAttributes failed.");
196         return;
197     }
198
199     tmp.visualid = XVisualIDFromVisual(xa.visual);
200     vi = XGetVisualInfo(obt_display, VisualIDMask, &tmp, &count);
201
202     if (!count) {
203         g_message("Failed to enable composite.  Couldn't get visual info.");
204         return;
205     }
206
207
208     glXGetConfig(obt_display, vi, GLX_USE_GL, &val);
209     if (!val) {
210         g_message("Failed to enable composite.  Visual is not GL capable");
211         return;
212     }
213
214     glXGetConfig(obt_display, vi, GLX_DOUBLEBUFFER, &val);
215     if (!val) {
216         g_message("Failed to enable composite.  Visual is not double buffered");
217         return;
218     }
219
220     obcomp.ctx = glXCreateContext(obt_display, vi, NULL, True);
221     XFree(vi);
222
223     fbcs = obcomp.GetFBConfigs(obt_display, ob_screen, &count);
224
225     if (!count) {
226         g_message("Failed to enable composite.  No valid FBConfigs.");
227         return;
228     }
229
230     memset(&obcomp.PixmapConfig, 0, sizeof(obcomp.PixmapConfig));
231
232     for (i = 0; i < MAX_DEPTH + 1; i++) {
233         get_best_fbcon(fbcs, count, i, &obcomp.PixmapConfig[i]);
234     }
235
236     if (count)
237         XFree(fbcs);
238
239     printf("Best visual for 24bpp was 0x%x\n", obcomp.PixmapConfig[24]);
240     printf("Best visual for 32bpp was 0x%x\n", obcomp.PixmapConfig[32]);
241
242     g_idle_add(composite, NULL);
243
244     glXMakeCurrent(obt_display, obcomp.overlay, obcomp.ctx);
245     config_comp = TRUE;
246     obcomp.screendims = screen_physical_area_all_monitors();
247     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
248     glXSwapBuffers(obt_display, obcomp.overlay);
249     glMatrixMode(GL_PROJECTION);
250     glLoadIdentity();
251
252     glOrtho(obcomp.screendims->x,
253             obcomp.screendims->x + obcomp.screendims->width,
254             obcomp.screendims->y + obcomp.screendims->height,
255             obcomp.screendims->y,
256             -100, 100);
257     glMatrixMode(GL_MODELVIEW);
258     glLoadIdentity();
259     glDisable(GL_DEPTH_TEST);
260     glEnable(GL_TEXTURE_2D);
261     glEnable(GL_BLEND);
262     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
263 #endif
264 }
265
266 void composite_shutdown(gboolean reconfig)
267 {
268 #ifdef USE_COMPOSITING
269     if (reconfig) return;
270 #endif
271 }
272
273 static gboolean composite(gpointer data)
274 {
275 #ifdef USE_COMPOSITING
276     int attribs[] = {
277         GLX_TEXTURE_FORMAT_EXT,
278         None,
279         None
280     };
281     struct timeval start, end, dif;
282     GList *it;
283     ObWindow *win;
284     ObClient *client;
285     static int i;
286
287 //    if (!obcomp.need_redraw)
288 //        return;
289     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
290
291 //    for (it = stacking_list; it; it = g_list_next(it)) {
292     for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
293         win = it->data;
294         if (win->type != OB_WINDOW_CLASS_CLIENT)
295             continue;
296
297         client = WINDOW_AS_CLIENT(win);
298         if (client->desktop != screen_desktop)
299             continue;
300
301         if (win->depth == 32)
302             attribs[1] = GLX_TEXTURE_FORMAT_RGBA_EXT;
303         else
304             attribs[1] = GLX_TEXTURE_FORMAT_RGB_EXT;
305
306         if (win->gpixmap == None)
307             win->gpixmap = obcomp.CreatePixmap(obt_display, obcomp.PixmapConfig[win->depth], win->pixmap, attribs);
308
309         glBindTexture(GL_TEXTURE_2D, win->texture);
310 gettimeofday(&start, NULL);
311         obcomp.BindTexImage(obt_display, win->gpixmap, GLX_FRONT_LEFT_EXT, NULL);
312 gettimeofday(&end, NULL);
313 dif.tv_sec = end.tv_sec - start.tv_sec;
314 dif.tv_usec = end.tv_usec - start.tv_usec;
315 time_fix(&dif);
316 //printf("took %f ms\n", dif.tv_sec * 1000.0 + dif.tv_usec / 1000.0);
317         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
318         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
319         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
320         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
321         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
322
323         glBegin(GL_QUADS);
324         glTexCoord2f(0, 0);
325         glVertex3f(client->frame->area.x, client->frame->area.y, 0.0);
326         glTexCoord2f(0, 1);
327         glVertex3f(client->frame->area.x, client->frame->area.y + client->frame->area.height, 0.0);
328         glTexCoord2f(1, 1);
329         glVertex3f(client->frame->area.x + client->frame->area.width, client->frame->area.y + client->frame->area.height, 0.0);
330         glTexCoord2f(1, 0);
331         glVertex3f(client->frame->area.x + client->frame->area.width, client->frame->area.y, 0.0);
332         glEnd();
333     }
334     glXSwapBuffers(obt_display, obcomp.overlay);
335     glFinish();
336     obcomp.need_redraw = 0;
337 #endif
338     return TRUE;
339 }