1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 loco.c for the Openbox window manager
4 Copyright (c) 2008 Derek Foreman
5 Copyright (c) 2008 Dana Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
21 #include "obt/mainloop.h"
22 #include "obt/display.h"
23 #include "obt/mainloop.h"
28 #include <GL/glxext.h>
29 #include <GL/glxtokens.h>
31 <dana> you want CreateNotify, DestroyNotify, MapNotify, UnmapNotify, and
33 <dana> and then xdamage or whatever
39 /*const GLchar *err_file = strrchr(err_path, '/');*/ \
40 GLenum gl_error = glGetError(); \
44 for (; (gl_error); gl_error = glGetError()) \
45 fprintf(stderr, "%s: %d caught at line %u\n", \
46 __FUNCTION__, gl_error, __LINE__); \
47 /*(const GLchar*)gluErrorString(gl_error)*/ \
52 #define REFRESH_RATE (G_USEC_PER_SEC/60)
60 gint type; /* XXX make this an enum */
67 typedef struct _LocoList {
68 struct _LocoList *next;
69 struct _LocoList *prev;
73 static Window loco_root;
74 static Window loco_overlay;
75 /* Maps X Window ID -> LocoWindow* */
76 static GHashTable *window_map;
77 /* Maps X Window ID -> LocoList* which is in the stacking_top/bottom list */
78 static GHashTable *stacking_map;
79 /* From top to bottom */
80 static LocoList *stacking_top;
81 static LocoList *stacking_bottom;
82 static GLXFBConfig glxFBConfig[MAX_DEPTH + 1];
83 static int loco_screen_num;
84 static gboolean full_redraw_required;
85 static gboolean redraw_required;
87 typedef void (*BindEXTFunc)(Display *, GLXDrawable, int, const int *);
88 typedef void (*ReleaseEXTFunc)(Display *, GLXDrawable, int);
90 BindEXTFunc BindTexImageEXT;
91 ReleaseEXTFunc ReleaseTexImageEXT;
93 static void show_window(LocoWindow *lw);
94 static void remove_window(LocoWindow *lw);
97 static void print_stacking()
101 for (it = stacking_top; it; it = it->next) {
102 printf("0x%lx\n", it->window->id);
107 static glong timecompare(GTimeVal *a, GTimeVal *b)
110 if ((r = a->tv_sec - b->tv_sec)) return r;
111 return a->tv_usec - b->tv_usec;
114 static void timeadd(GTimeVal *t, glong microseconds)
116 t->tv_usec += microseconds;
117 while (t->tv_usec >= G_USEC_PER_SEC) {
118 t->tv_usec -= G_USEC_PER_SEC;
121 while (t->tv_usec < 0) {
122 t->tv_usec += G_USEC_PER_SEC;
127 gboolean create_glxpixmap(LocoWindow *lw)
130 static const int attrs[] =
131 { GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT, None };
133 if (!glxFBConfig[lw->depth]) {
134 printf("no glxFBConfig for depth %d for window 0x%lx\n",
139 pixmap = XCompositeNameWindowPixmap(obt_display, lw->id);
140 lw->glpixmap = glXCreatePixmap(obt_display, glxFBConfig[lw->depth],
142 return !!lw->glpixmap;
145 int bindPixmapToTexture(LocoWindow *lw)
147 if (lw->glpixmap != None)
148 return 1; /* already bound */
150 if (!create_glxpixmap(lw))
151 return 0; /* horrible failure */
154 if (screen->queryDrawable (screen->display->display,
156 GLX_TEXTURE_TARGET_EXT,
159 fprintf (stderr, "%s: glXQueryDrawable failed\n", programName);
161 glXDestroyGLXPixmap (screen->display->display, texture->pixmap);
162 texture->pixmap = None;
168 glBindTexture(GL_TEXTURE_2D, lw->texname);
170 BindTexImageEXT(obt_display, lw->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
172 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
173 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
175 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
176 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
177 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
181 void releasePixmapFromTexture(LocoWindow *lw)
184 glBindTexture(GL_TEXTURE_2D, lw->texname);
186 ReleaseTexImageEXT(obt_display, lw->glpixmap, GLX_FRONT_LEFT_EXT);
188 glBindTexture(GL_TEXTURE_2D, 0);
192 void destroy_glxpixmap(LocoWindow *lw)
195 obt_display_ignore_errors(TRUE);
196 releasePixmapFromTexture(lw);
197 glXDestroyGLXPixmap(obt_display, lw->glpixmap);
198 obt_display_ignore_errors(FALSE);
204 static void full_composite(void)
209 /* XXX if (full_redraw_required) */
210 glClear(GL_COLOR_BUFFER_BIT);
212 for (it = stacking_bottom; it != stacking_top; it = it->prev) {
213 if (it->window->input_only) continue;
214 if (!it->window->visible) continue;
215 /* XXX if (!full_redraw_required && !it->window->damaged) continue; */
217 if (!full_redraw_required) {
218 /* XXX if the window is transparent, then clear the background
222 glBindTexture(GL_TEXTURE_2D, it->window->texname);
223 ret = bindPixmapToTexture(it->window);
226 glColor3f(1.0, 1.0, 1.0);
227 glVertex2i(it->window->x, it->window->y);
229 glVertex2i(it->window->x + it->window->w, it->window->y);
231 glVertex2i(it->window->x + it->window->w,
232 it->window->y + it->window->h);
234 glVertex2i(it->window->x, it->window->y + it->window->h);
239 it->window->damaged = FALSE;
241 glXSwapBuffers(obt_display, loco_overlay);
243 full_redraw_required = redraw_required = FALSE;
246 static LocoList* loco_list_prepend(LocoList **top, LocoList **bottom,
249 LocoList *n = g_new(LocoList, 1);
254 if (n->next) n->next->prev = n;
257 if (!*bottom) *bottom = n;
261 static void loco_list_delete_link(LocoList **top, LocoList **bottom,
264 LocoList *prev = pos->prev;
265 LocoList *next = pos->next;
279 static void loco_list_move_before(LocoList **top, LocoList **bottom,
280 LocoList *move, LocoList *before)
282 LocoList *prev, *next;
284 /* these won't move it anywhere */
285 if (move == before || move->next == before) return;
290 /* remove it from the list */
291 if (next) next->prev = prev;
293 if (prev) prev->next = next;
299 move->prev = before->prev;
300 move->next->prev = move;
301 if (move->prev) move->prev->next = move;
304 /* after the bottom */
305 move->prev = *bottom;
307 if (move->prev) move->prev->next = move;
311 if (!move->prev) *top = move;
314 /* Returns a LocoWindow structure */
315 static LocoWindow* find_window(Window window)
317 return g_hash_table_lookup(window_map, &window);
320 /* Returns a node from the stacking_top/bottom list */
321 static LocoList* find_stacking(Window window)
323 return g_hash_table_lookup(stacking_map, &window);
326 static void add_window(Window window)
330 XWindowAttributes attrib;
332 printf("add window 0x%lx\n", window);
334 if (!XGetWindowAttributes(obt_display, window, &attrib))
337 lw = g_new0(LocoWindow, 1);
339 lw->input_only = attrib.class == InputOnly;
342 lw->w = attrib.width;
343 lw->h = attrib.height;
344 lw->depth = attrib.depth;
346 g_hash_table_insert(window_map, &lw->id, lw);
347 /* new windows are at the top */
348 it = loco_list_prepend(&stacking_top, &stacking_bottom, lw);
349 g_hash_table_insert(stacking_map, &lw->id, it);
351 if (!lw->input_only) {
352 glGenTextures(1, &lw->texname);
353 // glTexImage2D(TARGET, 0, GL_RGB, lw->w, lw->h, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
354 lw->damage = XDamageCreate(obt_display, window, XDamageReportNonEmpty);
359 if (attrib.map_state != IsUnmapped)
363 static void remove_window(LocoWindow *lw)
365 printf("remove window 0x%lx\n", lw->id);
367 LocoList *pos = find_stacking(lw->id);
370 if (!lw->input_only) {
371 glDeleteTextures(1, &lw->texname);
372 obt_display_ignore_errors(TRUE);
373 XDamageDestroy(obt_display, lw->damage);
374 obt_display_ignore_errors(FALSE);
377 loco_list_delete_link(&stacking_top, &stacking_bottom, pos);
378 g_hash_table_remove(stacking_map, &lw->id);
379 g_hash_table_remove(window_map, &lw->id);
386 static void show_window(LocoWindow *lw)
393 /* get the window's semantic type (for different effects!) */
394 lw->type = 0; /* XXX set this to the default type */
395 if (OBT_PROP_GETA32(lw->id, NET_WM_WINDOW_TYPE, ATOM, &type, &ntype)) {
396 /* use the first value that we know about in the array */
397 for (i = 0; i < ntype; ++i) {
398 /* XXX SET THESE TO AN ENUM'S VALUES */
399 if (type[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
401 if (type[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
403 /* XXX there are more TYPES that need to be added to prop.h */
408 full_redraw_required = redraw_required = TRUE;
411 static void hide_window(LocoWindow *lw, gboolean destroyed)
413 /* if destroyed, then the window is no longer available */
415 destroy_glxpixmap(lw);
417 full_redraw_required = redraw_required = TRUE;
420 static void configure_window(LocoWindow *lw, const XConfigureEvent *e)
422 LocoList *above, *pos;
424 pos = find_stacking(e->window);
425 above = find_stacking(e->above);
427 g_assert(pos != NULL && pos->window != NULL);
428 if (e->above && !above)
429 printf("missing windows from the stacking list!!\n");
431 if ((lw->x != e->x) || (lw->y != e->y)) {
435 full_redraw_required = redraw_required = TRUE;
438 if ((lw->w != e->width) || (lw->h != e->height)) {
442 destroy_glxpixmap(lw);
444 full_redraw_required = redraw_required = TRUE;
447 if (pos->next != above) {
448 //printf("Window 0x%lx above 0x%lx\n", pos->window->id,
449 // above ? above->window->id : 0);
450 loco_list_move_before(&stacking_top, &stacking_bottom, pos, above);
452 full_redraw_required = redraw_required = TRUE;
456 static void damage_window(LocoWindow *lw)
460 pos = find_stacking(lw->id);
462 /* XXX if it is transparent, then damage any windows below it that
465 /* damage any windows above it that intersect */
466 for (it = pos; it; it = it->prev) {
467 const LocoWindow *lwa = it->window;
468 if (lwa->x < lw->x + lw->w && lwa->x + lwa->w > lw->x &&
469 lwa->y < lw->y + lw->h && lwa->y + lwa->h > lw->y)
471 it->window->damaged = TRUE;
475 /* mark the damage as fixed - we know about it now */
476 obt_display_ignore_errors(TRUE);
477 XDamageSubtract(obt_display, lw->damage, None, None);
478 obt_display_ignore_errors(FALSE);
481 redraw_required = TRUE;
484 void COMPOSTER_RAWR(const XEvent *e, gpointer data)
486 //g_print("COMPOSTER_RAWR() %d\n", e->type);
488 if (e->type == ConfigureNotify) {
491 lw = find_window(e->xconfigure.window);
493 configure_window(lw, &e->xconfigure);
496 else if (e->type == CreateNotify) {
497 add_window(e->xmap.window);
499 else if (e->type == DestroyNotify) {
500 LocoWindow *lw = find_window(e->xdestroywindow.window);
502 hide_window(lw, TRUE);
506 printf("destroy notify for unknown window 0x%lx\n",
507 e->xdestroywindow.window);
509 else if (e->type == ReparentNotify) {
510 if (e->xreparent.parent == loco_root)
511 add_window(e->xreparent.window);
513 LocoWindow *lw = find_window(e->xreparent.window);
515 printf("window 0x%lx reparented from root\n", lw->id);
516 hide_window(lw, FALSE);
520 printf("reparent notify away from root for unknown window "
521 "0x%lx\n", e->xreparent.window);
525 else if (e->type == MapNotify) {
526 LocoWindow *lw = find_window(e->xmap.window);
530 printf("map notify for unknown window 0x%lx\n",
533 else if (e->type == UnmapNotify) {
534 LocoWindow *lw = find_window(e->xunmap.window);
536 hide_window(lw, FALSE);
538 printf("unmap notify for unknown window 0x%lx\n",
541 else if (e->type == obt_display_extension_damage_basep + XDamageNotify) {
542 const XDamageNotifyEvent *de;
545 de = (const XDamageNotifyEvent*)e;
546 lw = find_window(de->drawable);
550 else if (de->drawable == loco_root) {
551 XDamageSubtract(obt_display, de->damage, None, None);
552 full_redraw_required = redraw_required = TRUE;
557 static void find_all_windows(gint screen)
562 if (!XQueryTree(obt_display, loco_root, &w, &w, &children, &nchild))
565 for (i = 0; i < nchild; ++i)
566 if (children[i] != None) add_window(children[i]);
568 if (children) XFree(children);
571 static gboolean compositor_timeout(gpointer data)
575 return TRUE; /* repeat */
578 static guint window_hash(Window *w) { return *w; }
579 static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; }
581 void loco_set_mainloop(gint screen_num, ObtMainLoop *loop)
583 int db, stencil, depth;
584 int i, j, value, count;
586 XVisualInfo *vi, tvis, *visinfo;
587 GLXFBConfig *allfbconfigs;
590 static int config[] =
591 { GLX_DEPTH_SIZE, 1, GLX_DOUBLEBUFFER, GLX_RGBA, None };
592 static const int drawable_tfp_attrs[] =
594 GLX_CONFIG_CAVEAT, GLX_NONE,
595 GLX_DOUBLEBUFFER, False,
601 GLX_RENDER_TYPE, GLX_RGBA_BIT,
602 GLX_BIND_TO_TEXTURE_RGBA_EXT, True, // additional for tfp
606 loco_screen_num = screen_num;
607 loco_root = obt_root(screen_num);
608 loco_overlay = XCompositeGetOverlayWindow(obt_display, loco_root);
609 XserverRegion region = XFixesCreateRegion(obt_display, NULL, 0);
611 XFixesSetWindowShapeRegion (obt_display,
616 XFixesSetWindowShapeRegion (obt_display,
622 XFixesDestroyRegion (obt_display, region);
624 vi = glXChooseVisual(obt_display, screen_num, config);
625 cont = glXCreateContext(obt_display, vi, NULL, GL_TRUE);
627 printf("context creation failed\n");
628 glXMakeCurrent(obt_display, loco_overlay, cont);
630 BindTexImageEXT = (BindEXTFunc)
631 glXGetProcAddress((const guchar*)"glXBindTexImageEXT");
632 ReleaseTexImageEXT = (ReleaseEXTFunc)
633 glXGetProcAddress((const guchar*)"glXReleaseTexImageEXT");
635 w = WidthOfScreen(ScreenOfDisplay(obt_display, screen_num));
636 h = HeightOfScreen(ScreenOfDisplay(obt_display, screen_num));
637 glViewport(0, 0, w, h);
638 glMatrixMode(GL_PROJECTION);
640 printf("Setting up an orthographic projection of %dx%d\n", w, h);
641 glOrtho(0, w, h, 0.0, -1.0, 100.0);
642 glMatrixMode(GL_MODELVIEW);
644 glClear(GL_COLOR_BUFFER_BIT);
645 glEnable(GL_TEXTURE_2D);
647 glXSwapBuffers(obt_display, loco_overlay);
648 glClearColor(1.0, 0.0, 0.0, 1.0);
649 // glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
650 // glEnable(GL_BLEND);
652 allfbconfigs = glXChooseFBConfig(obt_display,
660 for (i = 0; i <= MAX_DEPTH; i++) {
667 visinfo = XGetVisualInfo(obt_display, VisualDepthMask, &tvis, &count);
668 /* pick the nicest visual for the depth */
669 for (j = 0; j < count; j++) {
670 glXGetConfig(obt_display, &visinfo[j], GLX_USE_GL, &value);
674 glXGetConfig(obt_display, &visinfo[j], GLX_DOUBLEBUFFER, &value);
679 glXGetConfig(obt_display, &visinfo[j], GLX_STENCIL_SIZE, &value);
684 glXGetConfig(obt_display, &visinfo[j], GLX_DEPTH_SIZE, &value);
689 /* use this visual */
690 vid = visinfo[j].visualid;
696 printf("found visual %d\n", i);
698 /* find the fbconfig for this depth/visual */
699 for(j = 0; j < numfbconfigs; ++j) {
700 glXGetFBConfigAttrib(obt_display, allfbconfigs[j],
701 GLX_VISUAL_ID, &value);
702 if (value == (int)vid) {
703 glxFBConfig[i] = allfbconfigs[j]; /* save it */
704 printf("supporting depth %d\n", i);
705 break; /* next depth */
712 obt_main_loop_x_add(loop, COMPOSTER_RAWR, NULL, NULL);
713 window_map = g_hash_table_new((GHashFunc)window_hash,
714 (GEqualFunc)window_comp);
715 stacking_map = g_hash_table_new((GHashFunc)window_hash,
716 (GEqualFunc)window_comp);
717 stacking_top = stacking_bottom = NULL;
719 XGrabServer(obt_display);
721 XCompositeRedirectSubwindows(obt_display, loco_root,
722 CompositeRedirectManual);
723 find_all_windows(screen_num);
725 XUngrabServer(obt_display);
727 full_redraw_required = redraw_required = TRUE;
729 obt_main_loop_timeout_add(loop, REFRESH_RATE,
730 (GSourceFunc)compositor_timeout,
734 void loco_shutdown(void)