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 VisualID visualIDs[MAX_DEPTH + 1];
83 static XVisualInfo *glxPixmapVisuals[MAX_DEPTH + 1];
84 static int loco_screen_num;
85 static gboolean full_redraw_required;
86 static gboolean redraw_required;
88 typedef void (*BindEXTFunc)(Display *, GLXDrawable, int, const int *);
89 typedef void (*ReleaseEXTFunc)(Display *, GLXDrawable, int);
91 BindEXTFunc BindTexImageEXT;
92 ReleaseEXTFunc ReleaseTexImageEXT;
94 static void show_window(LocoWindow *lw);
95 static void remove_window(LocoWindow *lw);
98 static void print_stacking()
102 for (it = stacking_top; it; it = it->next) {
103 printf("0x%lx\n", it->window->id);
108 static glong timecompare(GTimeVal *a, GTimeVal *b)
111 if ((r = a->tv_sec - b->tv_sec)) return r;
112 return a->tv_usec - b->tv_usec;
115 static void timeadd(GTimeVal *t, glong microseconds)
117 t->tv_usec += microseconds;
118 while (t->tv_usec >= G_USEC_PER_SEC) {
119 t->tv_usec -= G_USEC_PER_SEC;
122 while (t->tv_usec < 0) {
123 t->tv_usec += G_USEC_PER_SEC;
128 int create_glxpixmap(LocoWindow *lw)
130 XVisualInfo *visinfo;
132 static const int attrs[] =
134 GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
138 static const int drawable_tfp_attrs[] =
140 GLX_CONFIG_CAVEAT, GLX_NONE,
141 GLX_DOUBLEBUFFER, False,
147 GLX_RENDER_TYPE, GLX_RGBA_BIT,
148 GLX_BIND_TO_TEXTURE_RGBA_EXT, True, // additional for tfp
153 pixmap = XCompositeNameWindowPixmap(obt_display, lw->id);
154 visinfo = glxPixmapVisuals[lw->depth];
156 printf("no visinfo for depth %d\n", lw->depth);
161 GLXFBConfig *fbconfigs = glXChooseFBConfig(obt_display,
163 drawable_tfp_attrs, &count);
167 for(i = 0; i < count; ++i ) {
169 glXGetFBConfigAttrib(obt_display,
170 fbconfigs[i], GLX_VISUAL_ID, &value);
171 if(value == (int)visinfo->visualid)
179 printf("fbconfigless\n");
183 lw->glpixmap = glXCreatePixmap(obt_display, fbc, pixmap, attrs);
184 return !!lw->glpixmap;
187 int bindPixmapToTexture(LocoWindow *lw)
189 if (lw->glpixmap != None)
190 return 1; /* already bound */
192 if (!create_glxpixmap(lw))
193 return 0; /* horrible failure */
196 if (screen->queryDrawable (screen->display->display,
198 GLX_TEXTURE_TARGET_EXT,
201 fprintf (stderr, "%s: glXQueryDrawable failed\n", programName);
203 glXDestroyGLXPixmap (screen->display->display, texture->pixmap);
204 texture->pixmap = None;
210 glBindTexture(GL_TEXTURE_2D, lw->texname);
212 BindTexImageEXT(obt_display, lw->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
214 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
215 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
217 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
218 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
219 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
223 void releasePixmapFromTexture(LocoWindow *lw)
226 glBindTexture(GL_TEXTURE_2D, lw->texname);
228 ReleaseTexImageEXT(obt_display, lw->glpixmap, GLX_FRONT_LEFT_EXT);
230 glBindTexture(GL_TEXTURE_2D, 0);
234 void destroy_glxpixmap(LocoWindow *lw)
237 obt_display_ignore_errors(TRUE);
238 releasePixmapFromTexture(lw);
239 glXDestroyGLXPixmap(obt_display, lw->glpixmap);
240 obt_display_ignore_errors(FALSE);
246 static void full_composite(void)
251 /* XXX if (full_redraw_required) */
252 glClear(GL_COLOR_BUFFER_BIT);
254 for (it = stacking_bottom; it != stacking_top; it = it->prev) {
255 if (it->window->input_only) continue;
256 if (!it->window->visible) continue;
257 /* XXX if (!full_redraw_required && !it->window->damaged) continue; */
259 if (!full_redraw_required) {
260 /* XXX if the window is transparent, then clear the background
264 glBindTexture(GL_TEXTURE_2D, it->window->texname);
265 ret = bindPixmapToTexture(it->window);
267 glColor3f(1.0, 1.0, 1.0);
268 glVertex2i(it->window->x, it->window->y);
270 glVertex2i(it->window->x + it->window->w, it->window->y);
272 glVertex2i(it->window->x + it->window->w,
273 it->window->y + it->window->h);
275 glVertex2i(it->window->x, it->window->y + it->window->h);
279 it->window->damaged = FALSE;
281 glXSwapBuffers(obt_display, loco_overlay);
283 full_redraw_required = redraw_required = FALSE;
286 static LocoList* loco_list_prepend(LocoList **top, LocoList **bottom,
289 LocoList *n = g_new(LocoList, 1);
294 if (n->next) n->next->prev = n;
297 if (!*bottom) *bottom = n;
301 static void loco_list_delete_link(LocoList **top, LocoList **bottom,
304 LocoList *prev = pos->prev;
305 LocoList *next = pos->next;
319 static void loco_list_move_before(LocoList **top, LocoList **bottom,
320 LocoList *move, LocoList *before)
322 LocoList *prev, *next;
324 /* these won't move it anywhere */
325 if (move == before || move->next == before) return;
330 /* remove it from the list */
331 if (next) next->prev = prev;
333 if (prev) prev->next = next;
339 move->prev = before->prev;
340 move->next->prev = move;
341 if (move->prev) move->prev->next = move;
344 /* after the bottom */
345 move->prev = *bottom;
347 if (move->prev) move->prev->next = move;
351 if (!move->prev) *top = move;
354 /* Returns a LocoWindow structure */
355 static LocoWindow* find_window(Window window)
357 return g_hash_table_lookup(window_map, &window);
360 /* Returns a node from the stacking_top/bottom list */
361 static LocoList* find_stacking(Window window)
363 return g_hash_table_lookup(stacking_map, &window);
366 static void add_window(Window window)
370 XWindowAttributes attrib;
372 printf("add window 0x%lx\n", window);
374 if (!XGetWindowAttributes(obt_display, window, &attrib))
377 lw = g_new0(LocoWindow, 1);
379 lw->input_only = attrib.class == InputOnly;
382 lw->w = attrib.width;
383 lw->h = attrib.height;
384 lw->depth = attrib.depth;
386 g_hash_table_insert(window_map, &lw->id, lw);
387 /* new windows are at the top */
388 it = loco_list_prepend(&stacking_top, &stacking_bottom, lw);
389 g_hash_table_insert(stacking_map, &lw->id, it);
391 if (!lw->input_only) {
392 glGenTextures(1, &lw->texname);
393 // glTexImage2D(TARGET, 0, GL_RGB, lw->w, lw->h, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
394 lw->damage = XDamageCreate(obt_display, window, XDamageReportNonEmpty);
399 if (attrib.map_state != IsUnmapped)
403 static void remove_window(LocoWindow *lw)
405 printf("remove window 0x%lx\n", lw->id);
407 LocoList *pos = find_stacking(lw->id);
410 if (!lw->input_only) {
411 glDeleteTextures(1, &lw->texname);
412 obt_display_ignore_errors(TRUE);
413 XDamageDestroy(obt_display, lw->damage);
414 obt_display_ignore_errors(FALSE);
417 loco_list_delete_link(&stacking_top, &stacking_bottom, pos);
418 g_hash_table_remove(stacking_map, &lw->id);
419 g_hash_table_remove(window_map, &lw->id);
426 static void show_window(LocoWindow *lw)
433 /* get the window's semantic type (for different effects!) */
434 lw->type = 0; /* XXX set this to the default type */
435 if (OBT_PROP_GETA32(lw->id, NET_WM_WINDOW_TYPE, ATOM, &type, &ntype)) {
436 /* use the first value that we know about in the array */
437 for (i = 0; i < ntype; ++i) {
438 /* XXX SET THESE TO AN ENUM'S VALUES */
439 if (type[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
441 if (type[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
443 /* XXX there are more TYPES that need to be added to prop.h */
448 full_redraw_required = redraw_required = TRUE;
451 static void hide_window(LocoWindow *lw, gboolean destroyed)
453 /* if destroyed, then the window is no longer available */
455 destroy_glxpixmap(lw);
457 full_redraw_required = redraw_required = TRUE;
460 static void configure_window(LocoWindow *lw, const XConfigureEvent *e)
462 LocoList *above, *pos;
464 pos = find_stacking(e->window);
465 above = find_stacking(e->above);
467 g_assert(pos != NULL && pos->window != NULL);
468 if (e->above && !above)
469 printf("missing windows from the stacking list!!\n");
471 if ((lw->x != e->x) || (lw->y != e->y)) {
475 full_redraw_required = redraw_required = TRUE;
478 if ((lw->w != e->width) || (lw->h != e->height)) {
482 destroy_glxpixmap(lw);
484 full_redraw_required = redraw_required = TRUE;
487 if (pos->next != above) {
488 printf("Window 0x%lx above 0x%lx\n", pos->window->id,
489 above ? above->window->id : 0);
490 loco_list_move_before(&stacking_top, &stacking_bottom, pos, above);
492 full_redraw_required = redraw_required = TRUE;
496 static void damage_window(LocoWindow *lw)
500 pos = find_stacking(lw->id);
502 /* XXX if it is transparent, then damage any windows below it that
505 /* damage any windows above it that intersect */
506 for (it = pos; it; it = it->prev) {
507 const LocoWindow *lwa = it->window;
508 if (lwa->x < lw->x + lw->w && lwa->x + lwa->w > lw->x &&
509 lwa->y < lw->y + lw->h && lwa->y + lwa->h > lw->y)
511 it->window->damaged = TRUE;
516 redraw_required = TRUE;
519 void COMPOSTER_RAWR(const XEvent *e, gpointer data)
521 //g_print("COMPOSTER_RAWR() %d\n", e->type);
523 if (e->type == ConfigureNotify) {
525 printf("Window 0x%lx moved or something\n", e->xconfigure.window);
527 lw = find_window(e->xconfigure.window);
529 configure_window(lw, &e->xconfigure);
532 else if (e->type == CreateNotify) {
533 add_window(e->xmap.window);
535 else if (e->type == DestroyNotify) {
536 LocoWindow *lw = find_window(e->xdestroywindow.window);
538 hide_window(lw, TRUE);
542 printf("destroy notify for unknown window 0x%lx\n",
543 e->xdestroywindow.window);
545 else if (e->type == ReparentNotify) {
546 if (e->xreparent.parent == loco_root)
547 add_window(e->xreparent.window);
549 LocoWindow *lw = find_window(e->xreparent.window);
551 printf("window 0x%lx reparented from root\n", lw->id);
552 hide_window(lw, FALSE);
556 printf("reparent notify away from root for unknown window "
557 "0x%lx\n", e->xreparent.window);
561 else if (e->type == MapNotify) {
562 LocoWindow *lw = find_window(e->xmap.window);
566 printf("map notify for unknown window 0x%lx\n",
569 else if (e->type == UnmapNotify) {
570 LocoWindow *lw = find_window(e->xunmap.window);
572 hide_window(lw, FALSE);
574 printf("unmap notify for unknown window 0x%lx\n",
577 else if (e->type == obt_display_extension_damage_basep + XDamageNotify) {
578 const XDamageNotifyEvent *de;
581 de = (const XDamageNotifyEvent*)e;
582 lw = find_window(de->drawable);
585 /* mark the damage as fixed - we know about it now */
586 XDamageSubtract(obt_display, lw->damage, None, None);
588 else if (de->drawable == loco_root) {
589 XDamageSubtract(obt_display, de->damage, None, None);
590 full_redraw_required = redraw_required = TRUE;
595 static void find_all_windows(gint screen)
600 if (!XQueryTree(obt_display, loco_root, &w, &w, &children, &nchild))
603 for (i = 0; i < nchild; ++i)
604 if (children[i] != None) add_window(children[i]);
606 if (children) XFree(children);
609 static gboolean compositor_timeout(gpointer data)
613 return TRUE; /* repeat */
616 static guint window_hash(Window *w) { return *w; }
617 static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; }
619 void loco_set_mainloop(gint screen_num, ObtMainLoop *loop)
621 int db, stencil, depth;
622 int i, j, value, count;
624 XVisualInfo *vi, tvis, *visinfo;
627 { GLX_DEPTH_SIZE, 1, GLX_DOUBLEBUFFER, GLX_RGBA, None };
629 loco_screen_num = screen_num;
630 loco_root = obt_root(screen_num);
631 loco_overlay = XCompositeGetOverlayWindow(obt_display, loco_root);
632 XserverRegion region = XFixesCreateRegion(obt_display, NULL, 0);
634 XFixesSetWindowShapeRegion (obt_display,
639 XFixesSetWindowShapeRegion (obt_display,
645 XFixesDestroyRegion (obt_display, region);
647 vi = glXChooseVisual(obt_display, screen_num, config);
648 cont = glXCreateContext(obt_display, vi, NULL, GL_TRUE);
650 printf("context creation failed\n");
651 glXMakeCurrent(obt_display, loco_overlay, cont);
653 BindTexImageEXT = (BindEXTFunc)
654 glXGetProcAddress((const guchar*)"glXBindTexImageEXT");
655 ReleaseTexImageEXT = (ReleaseEXTFunc)
656 glXGetProcAddress((const guchar*)"glXReleaseTexImageEXT");
658 w = WidthOfScreen(ScreenOfDisplay(obt_display, screen_num));
659 h = HeightOfScreen(ScreenOfDisplay(obt_display, screen_num));
660 glViewport(0, 0, w, h);
661 glMatrixMode(GL_PROJECTION);
663 printf("Setting up an orthographic projection of %dx%d\n", w, h);
664 glOrtho(0, w, h, 0.0, -1.0, 100.0);
665 glMatrixMode(GL_MODELVIEW);
667 glClear(GL_COLOR_BUFFER_BIT);
668 glEnable(GL_TEXTURE_2D);
670 glXSwapBuffers(obt_display, loco_overlay);
671 glClearColor(1.0, 0.0, 0.0, 1.0);
672 // glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
673 // glEnable(GL_BLEND);
677 for (i = 0; i <= MAX_DEPTH; i++) {
680 visinfo = XGetVisualInfo(obt_display, VisualDepthMask, &tvis, &count);
681 for (j = 0; j < count; j++) {
682 glXGetConfig(obt_display, &visinfo[j], GLX_USE_GL, &value);
686 glXGetConfig(obt_display, &visinfo[j], GLX_DOUBLEBUFFER, &value);
691 glXGetConfig(obt_display, &visinfo[j], GLX_STENCIL_SIZE, &value);
696 glXGetConfig(obt_display, &visinfo[j], GLX_DEPTH_SIZE, &value);
701 visualIDs[i] = visinfo[j].visualid;
705 for (i = 0 ;i <= MAX_DEPTH; i++) {
706 tvis.visualid = visualIDs[i];
707 glxPixmapVisuals[i] = XGetVisualInfo(obt_display, VisualIDMask, &tvis, &count);
708 if (glxPixmapVisuals[i])
709 printf("supporting depth %d\n", i);
711 obt_main_loop_x_add(loop, COMPOSTER_RAWR, NULL, NULL);
712 window_map = g_hash_table_new((GHashFunc)window_hash,
713 (GEqualFunc)window_comp);
714 stacking_map = g_hash_table_new((GHashFunc)window_hash,
715 (GEqualFunc)window_comp);
716 stacking_top = stacking_bottom = NULL;
718 XGrabServer(obt_display);
720 XCompositeRedirectSubwindows(obt_display, loco_root,
721 CompositeRedirectManual);
722 find_all_windows(screen_num);
724 XUngrabServer(obt_display);
726 full_redraw_required = redraw_required = TRUE;
728 obt_main_loop_timeout_add(loop, REFRESH_RATE,
729 (GSourceFunc)compositor_timeout,
733 void loco_shutdown(void)