track when windows are visible or not, and get a window's TYPE hint when it becomes...
[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 typedef struct {
38     Window id;
39     gint x, y, w, h;
40     gboolean input_only;
41     gboolean visible;
42     gint type; /* XXX make this an enum */
43 } LocoWindow;
44
45 typedef struct _LocoList {
46     struct _LocoList *next;
47     struct _LocoList *prev;
48     LocoWindow *window;
49 } LocoList;
50
51 static Window      loco_root;
52 /* Maps X Window ID -> LocoWindow* */
53 static GHashTable *window_map;
54 /* Maps X Window ID -> LocoList* which is in the stacking_top/bottom list */
55 static GHashTable *stacking_map;
56 /* From top to bottom */
57 static LocoList   *stacking_top;
58 static LocoList   *stacking_bottom;
59
60 /*
61 static void print_stacking()
62 {
63     LocoList *it;
64
65     for (it = stacking_top; it; it = it->next) {
66         printf("0x%lx\n", it->window->id);
67     }
68 }
69 */
70
71 static LocoList* loco_list_prepend(LocoList **top, LocoList **bottom,
72                                    LocoWindow *window)
73 {
74     LocoList *n = g_new(LocoList, 1);
75     n->window = window;
76
77     n->prev = NULL;
78     n->next = *top;
79     if (n->next) n->next->prev = n;
80
81     *top = n;
82     if (!*bottom) *bottom = n;
83     return n;
84 }
85
86 static void loco_list_delete_link(LocoList **top, LocoList **bottom,
87                                   LocoList *pos)
88 {
89     LocoList *prev = pos->prev;
90     LocoList *next = pos->next;
91
92     if (next)
93         next->prev = prev;
94     if (prev)
95         prev->next = next;
96     if (!next)
97         *bottom = prev;
98     if (!prev)
99         *top = next;
100
101     g_free(pos);
102 }
103
104 static void loco_list_move_before(LocoList **top, LocoList **bottom,
105                                   LocoList *move, LocoList *before)
106 {
107     LocoList *prev, *next;
108
109     /* these won't move it anywhere */
110     if (move == before || move->next == before) return;
111
112     prev = move->prev;
113     next = move->next;
114
115     /* remove it from the list */
116     if (next) next->prev = prev;
117     else      *bottom = prev;
118     if (prev) prev->next = next;
119     else      *top = next;
120
121     /* reinsert it */
122     if (before) {
123         move->next = before;
124         move->prev = before->prev;
125         move->next->prev = move;
126         if (move->prev) move->prev->next = move;
127     }
128     else {
129         /* after the bottom */
130         move->prev = *bottom;
131         move->next = NULL;
132         if (move->prev) move->prev->next = move;
133         *bottom = move;
134     }
135
136     if (!move->prev) *top = move;
137 }
138
139 /* Returns a LocoWindow structure */
140 static LocoWindow* find_window(Window window)
141 {
142     return g_hash_table_lookup(window_map, &window);
143 }
144
145 /* Returns a node from the stacking_top/bottom list */
146 static LocoList* find_stacking(Window window)
147 {
148     return g_hash_table_lookup(stacking_map, &window);
149 }
150
151 void composite_setup_window(LocoWindow *win)
152 {
153     if (win->input_only) return;
154
155     //XCompositeRedirectWindow(obt_display, win->id, CompositeRedirectAutomatic);
156     /*something useful = */XDamageCreate(obt_display, win->id, XDamageReportRawRectangles);
157 }
158
159 static void add_window(Window window)
160 {
161     LocoWindow *lw;
162     LocoList *it;
163     XWindowAttributes attrib;
164
165     printf("add window 0x%lx\n", window);
166
167     if (!XGetWindowAttributes(obt_display, window, &attrib))
168         return;
169
170     lw = g_new0(LocoWindow, 1);
171     lw->id = window;
172     lw->input_only = attrib.class == InputOnly;
173     lw->x = attrib.x;
174     lw->y = attrib.y;
175     lw->w = attrib.width;
176     lw->h = attrib.height;
177     g_hash_table_insert(window_map, &lw->id, lw);
178     /* new windows are at the top */
179     it = loco_list_prepend(&stacking_top, &stacking_bottom, lw);
180     g_hash_table_insert(stacking_map, &lw->id, it);
181
182     composite_setup_window(lw);
183
184     //print_stacking();
185 }
186
187 static void remove_window(LocoWindow *lw)
188 {
189     printf("remove window 0x%lx\n", lw->id);
190
191     LocoList *pos = find_stacking(lw->id);
192     g_assert(pos);
193
194     loco_list_delete_link(&stacking_top, &stacking_bottom, pos);
195     g_hash_table_remove(stacking_map, &lw->id);
196     g_hash_table_remove(window_map, &lw->id);
197
198     g_free(lw);
199
200     //print_stacking();
201 }
202
203 static void show_window(LocoWindow *lw)
204 {
205     guint32 *type;
206     guint i, ntype;
207
208     lw->visible = TRUE;
209
210     /* get the window's semantic type (for different effects!) */
211     lw->type = 0; /* XXX set this to the default type */
212     if (OBT_PROP_GETA32(lw->id, NET_WM_WINDOW_TYPE, ATOM, &type, &ntype)) {
213         /* use the first value that we know about in the array */
214         for (i = 0; i < ntype; ++i) {
215             /* XXX SET THESE TO AN ENUM'S VALUES */
216             if (type[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
217                 lw->type = 1;
218             if (type[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
219                 lw->type = 2;
220             /* XXX there are more TYPES that need to be added to prop.h */
221         }
222         g_free(type);
223     }
224
225 }
226
227 static void hide_window(LocoWindow *lw, gboolean destroyed)
228 {
229     /* if destroyed, then the window is no longer available */
230     lw->visible = FALSE;
231 }
232
233 void COMPOSTER_RAWR(const XEvent *e, gpointer data)
234 {
235     if (e->type == ConfigureNotify) {
236         LocoWindow *lw;
237         printf("Window 0x%lx moved or something\n", e->xconfigure.window);
238
239         lw = find_window(e->xconfigure.window);
240         if (lw) {
241             LocoList *above, *pos;
242
243             pos = find_stacking(e->xconfigure.window);
244             above = find_stacking(e->xconfigure.above);
245
246             g_assert(pos != NULL && pos->window != NULL);
247             if (e->xconfigure.above && !above)
248                 printf("missing windows from the stacking list!!\n");
249
250             lw->x = e->xconfigure.x;
251             lw->y = e->xconfigure.y;
252             lw->w = e->xconfigure.width;
253             lw->h = e->xconfigure.height;
254             printf("Window 0x%lx above 0x%lx\n", pos->window->id,
255                    above ? above->window->id : 0);
256             loco_list_move_before(&stacking_top, &stacking_bottom, pos, above);
257         }
258         //print_stacking();
259     }
260     else if (e->type == CreateNotify) {
261         add_window(e->xmap.window);
262     }
263     else if (e->type == DestroyNotify) {
264         LocoWindow *lw = find_window(e->xdestroywindow.window);
265         if (lw) {
266             hide_window(lw, TRUE);
267             remove_window(lw);
268         }
269         else
270             printf("destroy notify for unknown window 0x%lx\n",
271                    e->xdestroywindow.window);
272     }
273     else if (e->type == ReparentNotify) {
274         if (e->xreparent.parent == loco_root)
275             add_window(e->xreparent.window);
276         else {
277             LocoWindow *lw = find_window(e->xreparent.window);
278             if (lw) {
279                 hide_window(lw, FALSE);
280                 remove_window(lw);
281             }
282             else
283                 printf("reparent notify away from root for unknown window "
284                        "0x%lx\n", e->xreparent.window);
285         }
286     }
287     else if (e->type == MapNotify) {
288         LocoWindow *lw = find_window(e->xmap.window);
289         if (lw) show_window(lw);
290         else
291             printf("map notify for unknown window 0x%lx\n",
292                    e->xmap.window);
293     }
294     else if (e->type == UnmapNotify) {
295         LocoWindow *lw = find_window(e->xunmap.window);
296         if (lw) hide_window(lw, FALSE);
297         else
298             printf("unmap notify for unknown window 0x%lx\n",
299                    e->xunmap.window);
300     }
301     else if (e->type == obt_display_extension_damage_basep + XDamageNotify) {
302     }
303 }
304
305 static void find_all_windows(gint screen)
306 {
307     guint i, nchild;
308     Window w, *children;
309
310     if (!XQueryTree(obt_display, loco_root, &w, &w, &children, &nchild))
311         nchild = 0;
312
313     for (i = 0; i < nchild; ++i)
314         if (children[i] != None) add_window(children[i]);
315
316     if (children) XFree(children);
317 }
318
319 static guint window_hash(Window *w) { return *w; }
320 static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; }
321
322 void loco_set_mainloop(gint screen_num, ObtMainLoop *loop)
323 {
324     int w, h;
325     XVisualInfo *vi;
326     GLXContext cont;
327     int config[] =
328         { GLX_DEPTH_SIZE, 1, GLX_DOUBLEBUFFER, GLX_RGBA, None };
329
330     loco_root = obt_root(screen_num);
331
332     vi = glXChooseVisual(obt_display, screen_num, config);
333     cont = glXCreateContext(obt_display, vi, NULL, GL_TRUE);
334     if (cont == NULL)
335         printf("context creation failed\n");
336     glXMakeCurrent(obt_display, loco_root, cont);
337
338     w = WidthOfScreen(ScreenOfDisplay(obt_display, screen_num));
339     h = HeightOfScreen(ScreenOfDisplay(obt_display, screen_num));
340     glViewport(0, 0, w, h);
341     glMatrixMode(GL_PROJECTION);
342     glLoadIdentity();
343 printf("Setting up an orthographic projection of %dx%d\n", w, h);
344     glOrtho(0, w, h, 0.0, -1.0, 100.0);
345     glMatrixMode(GL_MODELVIEW);
346     glLoadIdentity();
347     glClear(GL_COLOR_BUFFER_BIT);
348     glEnable(GL_TEXTURE_RECTANGLE_ARB);
349     glXSwapBuffers(obt_display, loco_root);
350     obt_main_loop_x_add(loop, COMPOSTER_RAWR, NULL, NULL);
351     window_map = g_hash_table_new((GHashFunc)window_hash,
352                                   (GEqualFunc)window_comp);
353     stacking_map = g_hash_table_new((GHashFunc)window_hash,
354                                     (GEqualFunc)window_comp);
355     stacking_top = stacking_bottom = NULL;
356
357     find_all_windows(screen_num);
358 }
359
360 void loco_shutdown(void)
361 {
362 }