]> icculus.org git repositories - dana/openbox.git/blob - loco/loco.c
d636e9af9e75816dbc22a9521f242034e22baf6f
[dana/openbox.git] / loco / loco.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    loco.c for the Openbox window manager
4    Copyright (c) 2008        Derek Foreman
5    Copyright (c) 2008        Dana Jansens
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 <stdio.h>
21 #include "obt/mainloop.h"
22 #include "obt/display.h"
23 #include "obt/mainloop.h"
24 #include "obt/prop.h"
25 #include <glib.h>
26 #include <GL/glx.h>
27 #include <GL/glext.h>
28 #include <GL/glxext.h>
29 #include <GL/glxtokens.h>
30 /*
31 <dana> you want CreateNotify, DestroyNotify, MapNotify, UnmapNotify, and
32           ConfigureNotify
33           <dana> and then xdamage or whatever
34
35 */
36
37 #define glError()                                      \
38 {                                                      \
39     /*const GLchar *err_file = strrchr(err_path, '/');*/ \
40     GLenum        gl_error = glGetError();             \
41                                                        \
42     /*++err_file;*/                                        \
43                                                        \
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)*/ \
48 }
49
50
51 #define MAX_DEPTH 32
52
53 typedef struct {
54     Window id;
55     gint x, y, w, h;
56     gint depth;
57     gboolean input_only;
58     gboolean visible;
59     gint type; /* XXX make this an enum */
60     GLuint texname;
61     GLXPixmap glpixmap;
62 } LocoWindow;
63
64 typedef struct _LocoList {
65     struct _LocoList *next;
66     struct _LocoList *prev;
67     LocoWindow *window;
68 } LocoList;
69
70 static Window      loco_root;
71 static Window      loco_overlay;
72 /* Maps X Window ID -> LocoWindow* */
73 static GHashTable *window_map;
74 /* Maps X Window ID -> LocoList* which is in the stacking_top/bottom list */
75 static GHashTable *stacking_map;
76 /* From top to bottom */
77 static LocoList   *stacking_top;
78 static LocoList   *stacking_bottom;
79 static VisualID visualIDs[MAX_DEPTH + 1];
80 static XVisualInfo *glxPixmapVisuals[MAX_DEPTH + 1];
81 static int loco_screen_num;
82
83 void (*BindTexImageEXT)(Display *, GLXDrawable, int, const int *);
84 void (*ReleaseTexImageEXT)(Display *, GLXDrawable, int);
85
86 /*
87 static void print_stacking()
88 {
89     LocoList *it;
90
91     for (it = stacking_top; it; it = it->next) {
92         printf("0x%lx\n", it->window->id);
93     }
94 }
95 */
96
97 int create_glxpixmap(LocoWindow *lw)
98 {
99     XVisualInfo  *visinfo;
100     Pixmap pixmap;
101     static const int attrs[] =
102         {
103             GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
104             None
105         };
106
107     static const int drawable_tfp_attrs[] =
108         {
109             GLX_CONFIG_CAVEAT, GLX_NONE,
110             GLX_DOUBLEBUFFER, False,
111             GLX_DEPTH_SIZE, 0,
112             GLX_RED_SIZE, 1,
113             GLX_GREEN_SIZE, 1,
114             GLX_BLUE_SIZE, 1,
115             GLX_ALPHA_SIZE, 1,
116             GLX_RENDER_TYPE, GLX_RGBA_BIT,
117             GLX_BIND_TO_TEXTURE_RGBA_EXT, True, // additional for tfp
118             None
119         };
120     int count;
121
122     pixmap = XCompositeNameWindowPixmap(obt_display, lw->id);
123     visinfo = glxPixmapVisuals[lw->depth];
124     if (!visinfo) {
125         printf("no visinfo for depth %d\n", lw->depth);
126         return 0;
127     }
128
129     GLXFBConfig fbc;
130     GLXFBConfig *fbconfigs = glXChooseFBConfig(obt_display,
131                                                loco_screen_num,
132                                                drawable_tfp_attrs, &count);
133
134     int i;
135     fbc = 0;
136     for(i = 0; i < count; ++i ) {
137         int value;
138         glXGetFBConfigAttrib(obt_display,
139                              fbconfigs[i], GLX_VISUAL_ID, &value);
140         if(value == (int)visinfo->visualid)
141         {
142             fbc = fbconfigs[i];
143             XFree(fbconfigs);
144         }
145     }
146
147     if (!fbc) {
148         printf("fbconfigless\n");
149         return 0;
150     }
151
152     lw->glpixmap = glXCreatePixmap(obt_display, fbc, pixmap, attrs);
153     return !!lw->glpixmap;
154 }
155
156 int bindPixmapToTexture(LocoWindow *lw)
157 {
158     if (lw->glpixmap == None) {
159         if (!create_glxpixmap(lw))
160             return 0;
161     }
162
163 /*
164     if (screen->queryDrawable (screen->display->display,
165                                texture->pixmap,
166                                GLX_TEXTURE_TARGET_EXT,
167                                &target))
168     {
169         fprintf (stderr, "%s: glXQueryDrawable failed\n", programName);
170
171         glXDestroyGLXPixmap (screen->display->display, texture->pixmap);
172         texture->pixmap = None;
173
174         return FALSE;
175     }
176 */
177
178     glBindTexture(GL_TEXTURE_2D, lw->texname);
179 glError();
180     BindTexImageEXT(obt_display, lw->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
181
182     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
183     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
184
185     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
186     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
187     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
188     return 1;
189 }
190
191 void destroy_glxpixmap(LocoWindow *lw)
192 {
193     if (lw->glpixmap) {
194         glXDestroyGLXPixmap(obt_display, lw->glpixmap);
195         lw->glpixmap = None;
196     }
197 }
198
199
200 void releasePixmapFromTexture(LocoWindow *lw)
201 {
202     if (lw->glpixmap) {
203         glBindTexture(GL_TEXTURE_2D, lw->texname);
204
205         ReleaseTexImageEXT(obt_display, lw->glpixmap, GLX_FRONT_LEFT_EXT);
206
207         glBindTexture(GL_TEXTURE_2D, 0);
208     }
209 }
210
211 static void full_composite(void)
212 {
213     int ret;
214     LocoList *it;
215
216     glClear(GL_COLOR_BUFFER_BIT);
217     for (it = stacking_bottom; it != stacking_top; it = it->prev) {
218         if (!it->window->visible)
219             continue;
220         if ((it->window->w > 1000) || (it->window->h > 1000)) {
221             printf("I am afraid of window 0x%lx because it is very large\n", it->window->id);
222             continue;
223         }
224         ret = bindPixmapToTexture(it->window);
225         glBegin(GL_QUADS);
226         glColor3f(1.0, 1.0, 1.0);
227         glVertex2i(it->window->x, it->window->y);
228         glTexCoord2f(1, 0);
229         glVertex2i(it->window->x + it->window->w, it->window->y);
230         glTexCoord2f(1, 1);
231         glVertex2i(it->window->x + it->window->w, it->window->y + it->window->h);
232         glTexCoord2f(0, 1);
233         glVertex2i(it->window->x, it->window->y + it->window->h);
234         glTexCoord2f(0, 0);
235         glEnd();
236         releasePixmapFromTexture(it->window);
237     }
238     glXSwapBuffers(obt_display, loco_overlay);
239 }
240
241 static LocoList* loco_list_prepend(LocoList **top, LocoList **bottom,
242                                    LocoWindow *window)
243 {
244     LocoList *n = g_new(LocoList, 1);
245     n->window = window;
246
247     n->prev = NULL;
248     n->next = *top;
249     if (n->next) n->next->prev = n;
250
251     *top = n;
252     if (!*bottom) *bottom = n;
253     return n;
254 }
255
256 static void loco_list_delete_link(LocoList **top, LocoList **bottom,
257                                   LocoList *pos)
258 {
259     LocoList *prev = pos->prev;
260     LocoList *next = pos->next;
261
262     if (next)
263         next->prev = prev;
264     if (prev)
265         prev->next = next;
266     if (!next)
267         *bottom = prev;
268     if (!prev)
269         *top = next;
270
271     g_free(pos);
272 }
273
274 static void loco_list_move_before(LocoList **top, LocoList **bottom,
275                                   LocoList *move, LocoList *before)
276 {
277     LocoList *prev, *next;
278
279     /* these won't move it anywhere */
280     if (move == before || move->next == before) return;
281
282     prev = move->prev;
283     next = move->next;
284
285     /* remove it from the list */
286     if (next) next->prev = prev;
287     else      *bottom = prev;
288     if (prev) prev->next = next;
289     else      *top = next;
290
291     /* reinsert it */
292     if (before) {
293         move->next = before;
294         move->prev = before->prev;
295         move->next->prev = move;
296         if (move->prev) move->prev->next = move;
297     }
298     else {
299         /* after the bottom */
300         move->prev = *bottom;
301         move->next = NULL;
302         if (move->prev) move->prev->next = move;
303         *bottom = move;
304     }
305
306     if (!move->prev) *top = move;
307 }
308
309 /* Returns a LocoWindow structure */
310 static LocoWindow* find_window(Window window)
311 {
312     return g_hash_table_lookup(window_map, &window);
313 }
314
315 /* Returns a node from the stacking_top/bottom list */
316 static LocoList* find_stacking(Window window)
317 {
318     return g_hash_table_lookup(stacking_map, &window);
319 }
320
321 void composite_setup_window(LocoWindow *win)
322 {
323     if (win->input_only) return;
324
325     /*something useful = */XDamageCreate(obt_display, win->id, XDamageReportRawRectangles);
326 }
327
328 static void add_window(Window window)
329 {
330     LocoWindow *lw;
331     LocoList *it;
332     XWindowAttributes attrib;
333
334     printf("add window 0x%lx\n", window);
335
336     if (!XGetWindowAttributes(obt_display, window, &attrib))
337         return;
338
339     lw = g_new0(LocoWindow, 1);
340     lw->id = window;
341     lw->input_only = attrib.class == InputOnly;
342     lw->x = attrib.x;
343     lw->y = attrib.y;
344     lw->w = attrib.width;
345     lw->h = attrib.height;
346     lw->depth = attrib.depth;
347     lw->glpixmap = None;
348     glGenTextures(1, &lw->texname);
349   //  glTexImage2D(TARGET, 0, GL_RGB, lw->w, lw->h, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
350     g_hash_table_insert(window_map, &lw->id, lw);
351     /* new windows are at the top */
352     it = loco_list_prepend(&stacking_top, &stacking_bottom, lw);
353     g_hash_table_insert(stacking_map, &lw->id, it);
354
355     composite_setup_window(lw);
356
357     //print_stacking();
358 }
359
360 static void remove_window(LocoWindow *lw)
361 {
362     printf("remove window 0x%lx\n", lw->id);
363
364     LocoList *pos = find_stacking(lw->id);
365     g_assert(pos);
366
367     glDeleteTextures(1, &lw->texname);
368
369     loco_list_delete_link(&stacking_top, &stacking_bottom, pos);
370     g_hash_table_remove(stacking_map, &lw->id);
371     g_hash_table_remove(window_map, &lw->id);
372
373     g_free(lw);
374
375     //print_stacking();
376 }
377
378 static void show_window(LocoWindow *lw)
379 {
380     guint32 *type;
381     guint i, ntype;
382
383     lw->visible = TRUE;
384
385     /* get the window's semantic type (for different effects!) */
386     lw->type = 0; /* XXX set this to the default type */
387     if (OBT_PROP_GETA32(lw->id, NET_WM_WINDOW_TYPE, ATOM, &type, &ntype)) {
388         /* use the first value that we know about in the array */
389         for (i = 0; i < ntype; ++i) {
390             /* XXX SET THESE TO AN ENUM'S VALUES */
391             if (type[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
392                 lw->type = 1;
393             if (type[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
394                 lw->type = 2;
395             /* XXX there are more TYPES that need to be added to prop.h */
396         }
397         g_free(type);
398     }
399
400 }
401
402 static void hide_window(LocoWindow *lw, gboolean destroyed)
403 {
404     /* if destroyed, then the window is no longer available */
405     lw->visible = FALSE;
406     destroy_glxpixmap(lw);
407 }
408
409 void COMPOSTER_RAWR(const XEvent *e, gpointer data)
410 {
411     int redraw_required = 0;
412
413     //g_print("COMPOSTER_RAWR() %d\n", e->type);
414
415     if (e->type == ConfigureNotify) {
416         LocoWindow *lw;
417 redraw_required = 1;
418         printf("Window 0x%lx moved or something\n", e->xconfigure.window);
419
420         lw = find_window(e->xconfigure.window);
421         if (lw) {
422             LocoList *above, *pos;
423
424             pos = find_stacking(e->xconfigure.window);
425             above = find_stacking(e->xconfigure.above);
426
427             g_assert(pos != NULL && pos->window != NULL);
428             if (e->xconfigure.above && !above)
429                 printf("missing windows from the stacking list!!\n");
430
431             lw->x = e->xconfigure.x;
432             lw->y = e->xconfigure.y;
433             if ((lw->w != e->xconfigure.width) ||
434                 (lw->h != e->xconfigure.height))
435             {
436                 lw->w = e->xconfigure.width;
437                 lw->h = e->xconfigure.height;
438
439                 destroy_glxpixmap(lw);
440             }
441             printf("Window 0x%lx above 0x%lx\n", pos->window->id,
442                    above ? above->window->id : 0);
443             loco_list_move_before(&stacking_top, &stacking_bottom, pos, above);
444         }
445         //print_stacking();
446     }
447     else if (e->type == CreateNotify) {
448         add_window(e->xmap.window);
449     }
450     else if (e->type == DestroyNotify) {
451         LocoWindow *lw = find_window(e->xdestroywindow.window);
452         if (lw) {
453             hide_window(lw, TRUE);
454             if (lw->glpixmap != None) {
455                 destroy_glxpixmap(lw);
456             }
457             remove_window(lw);
458         }
459         else
460             printf("destroy notify for unknown window 0x%lx\n",
461                    e->xdestroywindow.window);
462     }
463     else if (e->type == ReparentNotify) {
464         if (e->xreparent.parent == loco_root)
465             add_window(e->xreparent.window);
466         else {
467             LocoWindow *lw = find_window(e->xreparent.window);
468             if (lw) {
469                 hide_window(lw, FALSE);
470                 remove_window(lw);
471             }
472             else
473                 printf("reparent notify away from root for unknown window "
474                        "0x%lx\n", e->xreparent.window);
475         }
476     }
477
478     else if (e->type == MapNotify) {
479         LocoWindow *lw = find_window(e->xmap.window);
480         if (lw) {
481             show_window(lw);
482             redraw_required = 1;
483         }
484         else
485             printf("map notify for unknown window 0x%lx\n",
486                    e->xmap.window);
487     }
488     else if (e->type == UnmapNotify) {
489         LocoWindow *lw = find_window(e->xunmap.window);
490         if (lw) {
491             hide_window(lw, FALSE);
492             redraw_required = 1;
493         }
494         else
495             printf("unmap notify for unknown window 0x%lx\n",
496                    e->xunmap.window);
497     }
498     else if (e->type == obt_display_extension_damage_basep + XDamageNotify) {
499 //        LocoWindow *lw = find_window(e->xdamage.window);
500   //      if (lw->visible)
501             redraw_required = 1;
502     }
503     if (redraw_required)
504         full_composite();
505 }
506
507 static void find_all_windows(gint screen)
508 {
509     guint i, nchild;
510     Window w, *children;
511
512     if (!XQueryTree(obt_display, loco_root, &w, &w, &children, &nchild))
513         nchild = 0;
514
515     for (i = 0; i < nchild; ++i)
516         if (children[i] != None) add_window(children[i]);
517
518     if (children) XFree(children);
519 }
520
521 static guint window_hash(Window *w) { return *w; }
522 static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; }
523
524 void loco_set_mainloop(gint screen_num, ObtMainLoop *loop)
525 {
526     int db, stencil, depth;
527     int i, j, value, count;
528     int w, h;
529     XVisualInfo *vi, tvis, *visinfo;
530     GLXContext cont;
531     int config[] =
532         { GLX_DEPTH_SIZE, 1, GLX_DOUBLEBUFFER, GLX_RGBA, None };
533
534     loco_screen_num = screen_num;
535     loco_root = obt_root(screen_num);
536     loco_overlay = XCompositeGetOverlayWindow(obt_display, loco_root);
537 XserverRegion region = XFixesCreateRegion(obt_display, NULL, 0);
538
539     XFixesSetWindowShapeRegion (obt_display,
540                                 loco_overlay,
541                                 ShapeBounding,
542                                 0, 0,
543                                 0);
544     XFixesSetWindowShapeRegion (obt_display,
545                                 loco_overlay,
546                                 ShapeInput,
547                                 0, 0,
548                                 region);
549
550     XFixesDestroyRegion (obt_display, region);
551
552     vi = glXChooseVisual(obt_display, screen_num, config);
553     cont = glXCreateContext(obt_display, vi, NULL, GL_TRUE);
554     if (cont == NULL)
555         printf("context creation failed\n");
556     glXMakeCurrent(obt_display, loco_overlay, cont);
557
558     BindTexImageEXT =
559         glXGetProcAddress((const guchar*)"glXBindTexImageEXT");
560     ReleaseTexImageEXT =
561         glXGetProcAddress((const guchar*)"glXReleaseTexImageEXT");
562
563     w = WidthOfScreen(ScreenOfDisplay(obt_display, screen_num));
564     h = HeightOfScreen(ScreenOfDisplay(obt_display, screen_num));
565     glViewport(0, 0, w, h);
566     glMatrixMode(GL_PROJECTION);
567     glLoadIdentity();
568 printf("Setting up an orthographic projection of %dx%d\n", w, h);
569     glOrtho(0, w, h, 0.0, -1.0, 100.0);
570     glMatrixMode(GL_MODELVIEW);
571     glLoadIdentity();
572     glClear(GL_COLOR_BUFFER_BIT);
573     glEnable(GL_TEXTURE_2D);
574 glError();
575     glXSwapBuffers(obt_display, loco_overlay);
576     glClearColor(1.0, 0.0, 0.0, 1.0);
577 //    glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
578 //    glEnable(GL_BLEND);
579     db = 32767;
580     stencil = 32767;
581     depth = 32767;
582     for (i = 0; i <= MAX_DEPTH; i++) {
583         tvis.depth = i;
584         visualIDs[i] = 0;
585         visinfo = XGetVisualInfo(obt_display, VisualDepthMask, &tvis, &count);
586         for (j = 0; j < count; j++) {
587             glXGetConfig(obt_display, &visinfo[j], GLX_USE_GL, &value);
588             if (!value)
589                 continue;
590
591             glXGetConfig(obt_display, &visinfo[j], GLX_DOUBLEBUFFER, &value);
592             if (value > db)
593                 continue;
594             db = value;
595
596             glXGetConfig(obt_display, &visinfo[j], GLX_STENCIL_SIZE, &value);
597             if (value > stencil)
598                 continue;
599             stencil = value;
600
601             glXGetConfig(obt_display, &visinfo[j], GLX_DEPTH_SIZE, &value);
602             if (value > depth)
603                 continue;
604             depth = value;
605
606             visualIDs[i] = visinfo[j].visualid;
607         }
608     }
609
610     for (i = 0 ;i <= MAX_DEPTH; i++) {
611         tvis.visualid = visualIDs[i];
612         glxPixmapVisuals[i] = XGetVisualInfo(obt_display, VisualIDMask, &tvis, &count);
613         if (glxPixmapVisuals[i])
614             printf("supporting depth %d\n", i);
615     }
616     obt_main_loop_x_add(loop, COMPOSTER_RAWR, NULL, NULL);
617     window_map = g_hash_table_new((GHashFunc)window_hash,
618                                   (GEqualFunc)window_comp);
619     stacking_map = g_hash_table_new((GHashFunc)window_hash,
620                                     (GEqualFunc)window_comp);
621     stacking_top = stacking_bottom = NULL;
622
623     XCompositeRedirectSubwindows(obt_display, loco_root, CompositeRedirectManual);
624     find_all_windows(screen_num);
625 }
626
627 void loco_shutdown(void)
628 {
629 }