]> icculus.org git repositories - dana/openbox.git/blob - openbox/window.c
when managing a window, make sure its area is within its min/max bounds
[dana/openbox.git] / openbox / window.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    window.c for the Openbox window manager
4    Copyright (c) 2003-2007   Dana Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "window.h"
20 #include "menuframe.h"
21 #include "config.h"
22 #include "dock.h"
23 #include "client.h"
24 #include "unmanaged.h"
25 #include "composite.h"
26 #include "frame.h"
27 #include "openbox.h"
28 #include "prompt.h"
29 #include "debug.h"
30 #include "grab.h"
31 #include "obt/xqueue.h"
32
33 static GHashTable *window_map;
34
35 static guint window_hash(Window *w) { return *w; }
36 static gboolean window_comp(Window *w1, Window *w2) { return *w1 == *w2; }
37
38 void window_startup(gboolean reconfig)
39 {
40     if (reconfig) return;
41
42     window_map = g_hash_table_new((GHashFunc)window_hash,
43                                   (GEqualFunc)window_comp);
44 }
45
46 void window_shutdown(gboolean reconfig)
47 {
48     if (reconfig) return;
49
50     g_hash_table_destroy(window_map);
51 }
52
53 ObWindow* window_new_size(ObWindowClass type, gsize size)
54 {
55     ObWindow *self;
56
57     g_assert(size >= sizeof(ObWindow));
58     self = g_slice_alloc0(size);
59     self->size = size;
60     self->type = type;
61     return self;
62 }
63
64 void window_set_abstract(ObWindow *self,
65                          const Window *top,
66                          const ObStackingLayer *layer,
67                          const int *depth,
68                          const guint32 *alpha)
69 {
70     g_assert(!self->top && !self->layer && !self->depth && !self->alpha);
71
72     self->top = top;
73     self->layer = layer;
74     self->depth = depth;
75     self->alpha = alpha;
76
77     /* set up any things in ObWindow that require use of the abstract pointers
78        now */
79
80     composite_window_setup(self);
81 }
82
83 void window_cleanup(ObWindow *self)
84 {
85     composite_window_cleanup(self);
86 }
87
88 void window_free(ObWindow *self)
89 {
90     /* The abstract pointers must not be used here, they are likely invalid
91        by now ! */
92
93     g_slice_free1(self->size, self);
94 }
95
96 ObWindow* window_find(Window xwin)
97 {
98     return g_hash_table_lookup(window_map, &xwin);
99 }
100
101 void wfe(gpointer k, gpointer v, gpointer data)
102 {
103     ((ObWindowForeachFunc)data)(v);
104 }
105
106 void window_foreach(ObWindowForeachFunc func)
107 {
108     g_hash_table_foreach(window_map, wfe, func);
109 }
110
111 void window_add(Window *xwin, ObWindow *win)
112 {
113     g_assert(xwin != NULL);
114     g_assert(win != NULL);
115     g_hash_table_insert(window_map, xwin, win);
116 }
117
118 void window_remove(Window xwin)
119 {
120     g_assert(xwin != None);
121     g_hash_table_remove(window_map, &xwin);
122 }
123
124 ObInternalWindow* window_internal_new(Window window, int depth)
125 {
126     ObInternalWindow *self;
127
128     self = window_new(OB_WINDOW_CLASS_INTERNAL, ObInternalWindow);
129     self->window = window;
130     self->layer = OB_STACKING_LAYER_INTERNAL;
131     self->depth = depth;
132     window_set_abstract(INTERNAL_AS_WINDOW(self),
133                         &self->window, /* top-most window */
134                         &self->layer,  /* stacking layer */
135                         &self->depth,  /* window depth */
136                         NULL);         /* opacity */
137     return self;
138 }
139
140 void window_manage_all(void)
141 {
142     guint i, j, nchild;
143     Window w, *children;
144     XWMHints *wmhints;
145     XWindowAttributes attrib;
146
147     if (!XQueryTree(obt_display, RootWindow(obt_display, ob_screen),
148                     &w, &w, &children, &nchild)) {
149         ob_debug("XQueryTree failed in window_manage_all");
150         nchild = 0;
151     }
152
153     /* remove all icon windows from the list */
154     for (i = 0; i < nchild; i++) {
155         if (children[i] == None) continue;
156         wmhints = XGetWMHints(obt_display, children[i]);
157         if (wmhints) {
158             if ((wmhints->flags & IconWindowHint) &&
159                 (wmhints->icon_window != children[i]))
160                 for (j = 0; j < nchild; j++)
161                     if (children[j] == wmhints->icon_window) {
162                         /* XXX watch the window though */
163                         children[j] = None;
164                         break;
165                     }
166             XFree(wmhints);
167         }
168     }
169
170     for (i = 0; i < nchild; ++i) {
171         if (children[i] == None) continue;
172         if (window_find(children[i])) continue; /* skip our own windows */
173         if (XGetWindowAttributes(obt_display, children[i], &attrib)) {
174             if (attrib.map_state == IsUnmapped)
175                 ;
176             else
177                 window_manage(children[i]);
178         }
179     }
180
181     if (children) XFree(children);
182 }
183
184 static gboolean check_unmap(XEvent *e, gpointer data)
185 {
186     const Window win = *(Window*)data;
187     return ((e->type == DestroyNotify && e->xdestroywindow.window == win) ||
188             (e->type == UnmapNotify && e->xunmap.window == win));
189 }
190
191 void window_manage(Window win)
192 {
193     XWindowAttributes attrib;
194     gboolean no_manage = FALSE;
195     gboolean is_dockapp = FALSE;
196     Window icon_win = None;
197
198     grab_server(TRUE);
199
200     /* check if it has already been unmapped by the time we started
201        mapping. the grab does a sync so we don't have to here */
202     if (xqueue_exists_local(check_unmap, &win)) {
203         ob_debug("Trying to manage unmapped window. Aborting that.");
204         no_manage = TRUE;
205     }
206     else if (!XGetWindowAttributes(obt_display, win, &attrib))
207         no_manage = TRUE;
208     else {
209         XWMHints *wmhints;
210
211         /* is the window a docking app */
212         is_dockapp = FALSE;
213         if ((wmhints = XGetWMHints(obt_display, win))) {
214             if ((wmhints->flags & StateHint) &&
215                 wmhints->initial_state == WithdrawnState)
216             {
217                 if (wmhints->flags & IconWindowHint)
218                     icon_win = wmhints->icon_window;
219                 is_dockapp = TRUE;
220             }
221             XFree(wmhints);
222         }
223     }
224
225     if (!no_manage) {
226         if (attrib.override_redirect) {
227             ob_debug("not managing override redirect window 0x%x", win);
228             grab_server(FALSE);
229         }
230         else if (is_dockapp) {
231             if (!icon_win)
232                 icon_win = win;
233             dock_manage(icon_win, win);
234         }
235         else
236             client_manage(win, NULL);
237     }
238     else {
239         grab_server(FALSE);
240         ob_debug("FAILED to manage window 0x%x", win);
241     }
242 }
243
244 void window_unmanage_all(void)
245 {
246     dock_unmanage_all();
247     client_unmanage_all();
248     unmanaged_destroy_all();
249 }