]> icculus.org git repositories - dana/openbox.git/blob - openbox/slit.c
some slit fixes, support resizing slitapps
[dana/openbox.git] / openbox / slit.c
1 #include "slit.h"
2 #include "screen.h"
3 #include "openbox.h"
4 #include "render/theme.h"
5 #include "render/render.h"
6
7 #define SLIT_EVENT_MASK (EnterNotifyMask | LeaveNotifyMask)
8 #define SLITAPP_EVENT_MASK (StructureNotifyMask)
9
10 struct Slit {
11     Window frame;
12
13     /* user-requested position stuff */
14     SlitPosition pos;
15     int gravity;
16     int user_x, user_y;
17
18     /* actual position (when not auto-hidden) */
19     int x, y;
20     int w, h;
21
22     gboolean horz;
23     gboolean hide;
24     gboolean hidden;
25
26     Appearance *a_frame;
27
28     GList *slit_apps;
29 };
30
31 GHashTable *slit_map = NULL;
32 GHashTable *slit_app_map = NULL;
33
34 static Slit *slit;
35 static int nslits;
36
37 static void slit_configure(Slit *self);
38
39 void slit_startup()
40 {
41     XSetWindowAttributes attrib;
42     int i;
43
44     slit_map = g_hash_table_new(g_int_hash, g_int_equal);
45     slit_app_map = g_hash_table_new(g_int_hash, g_int_equal);
46
47     nslits = 1;
48     slit = g_new0(struct Slit, nslits);
49
50     for (i = 0; i < nslits; ++i) {
51         slit[i].horz = FALSE;
52         slit[i].hide = TRUE;
53         slit[i].hidden = FALSE;
54         slit[i].pos = SlitPos_TopRight;
55
56         attrib.override_redirect = True;
57         slit[i].frame = XCreateWindow(ob_display, ob_root, 0, 0, 1, 1, 0,
58                                       render_depth, InputOutput, render_visual,
59                                       CWOverrideRedirect, &attrib);
60         slit[i].a_frame = appearance_copy(theme_a_unfocused_title);
61         XSetWindowBorder(ob_display, slit[i].frame, theme_b_color->pixel);
62         XSetWindowBorderWidth(ob_display, slit[i].frame, theme_bwidth);
63
64         g_hash_table_insert(slit_map, &slit[i].frame, &slit[i]);
65     }
66 }
67
68 void slit_shutdown()
69 {
70     int i;
71
72     for (i = 0; i < nslits; ++i) {
73         XDestroyWindow(ob_display, slit[i].frame);
74         appearance_free(slit[i].a_frame);
75         g_hash_table_remove(slit_map, &slit[i].frame);
76     }
77     g_hash_table_destroy(slit_app_map);
78     g_hash_table_destroy(slit_map);
79 }
80
81 void slit_add(Window win, XWMHints *wmhints, XWindowAttributes *attrib)
82 {
83     Slit *s;
84     SlitApp *app;
85
86     /* XXX pick a slit */
87     s = &slit[0];
88
89     app = g_new0(SlitApp, 1);
90     app->slit = s;
91     app->win = win;
92     app->icon_win = (wmhints->flags & IconWindowHint) ?
93         wmhints->icon_window : win;
94     
95     app->w = attrib->width;
96     app->h = attrib->height;
97
98     s->slit_apps = g_list_append(s->slit_apps, app);
99     slit_configure(s);
100
101     XReparentWindow(ob_display, app->icon_win, s->frame, app->x, app->y);
102     /*
103       This is the same case as in frame.c for client windows. When Openbox is
104       starting, the window is already mapped so we see unmap events occur for
105       it. There are 2 unmap events generated that we see, one with the 'event'
106       member set the root window, and one set to the client, but both get
107       handled and need to be ignored.
108     */
109     if (ob_state == State_Starting)
110         app->ignore_unmaps += 2;
111
112     if (app->win != app->icon_win) {
113         /* have to map it so that it can be re-managed on a restart */
114         XMoveWindow(ob_display, app->win, -1000, -1000);
115         XMapWindow(ob_display, app->win);
116     }
117     g_message("   Slitting 0x%lx 0x%lx", app->icon_win, app->win);
118     XMapWindow(ob_display, app->icon_win);
119     XSync(ob_display, False);
120
121     /* specify that if we exit, the window should not be destroyed and should
122        be reparented back to root automatically */
123     XChangeSaveSet(ob_display, app->icon_win, SetModeInsert);
124     XSelectInput(ob_display, app->icon_win, SLITAPP_EVENT_MASK);
125
126     g_hash_table_insert(slit_app_map, &app->icon_win, app);
127
128     g_message("Managed Slit App: 0x%lx", app->icon_win);
129 }
130
131 void slit_remove_all()
132 {
133     int i;
134
135     for (i = 0; i < nslits; ++i)
136         while (slit[i].slit_apps)
137             slit_remove(slit[i].slit_apps->data, TRUE);
138 }
139
140 void slit_remove(SlitApp *app, gboolean reparent)
141 {
142     XSelectInput(ob_display, app->icon_win, NoEventMask);
143     /* remove the window from our save set */
144     XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
145     XSync(ob_display, False);
146
147     g_hash_table_remove(slit_app_map, &app->icon_win);
148
149     if (reparent)
150         XReparentWindow(ob_display, app->icon_win, ob_root, app->x, app->y);
151
152     app->slit->slit_apps = g_list_remove(app->slit->slit_apps, app);
153     slit_configure(app->slit);
154
155     g_message("Unmanaged Slit App: 0x%lx", app->icon_win);
156
157     g_free(app);
158 }
159
160 void slit_configure_all()
161 {
162     int i; for (i = 0; i < nslits; ++i) slit_configure(&slit[i]);
163 }
164
165 static void slit_configure(Slit *self)
166 {
167     GList *it;
168     int spot;
169
170     self->w = self->h = spot = 0;
171
172     for (it = self->slit_apps; it; it = it->next) {
173         struct SlitApp *app = it->data;
174         if (self->horz) {
175             app->x = spot;
176             app->y = 0;
177             self->w += app->w;
178             self->h = MAX(self->h, app->h);
179             spot += app->w;
180         } else {
181             app->x = 0;
182             app->y = spot;
183             self->w = MAX(self->h, app->w);
184             self->h += app->h;
185             spot += app->h;
186         }
187
188         XMoveWindow(ob_display, app->icon_win, app->x, app->y);
189     }
190
191     /* used for calculating offsets */
192     self->w += theme_bwidth * 2;
193     self->h += theme_bwidth * 2;
194
195     /* calculate position */
196     switch (self->pos) {
197     case SlitPos_Floating:
198         self->x = self->user_x;
199         self->y = self->user_y;
200         break;
201     case SlitPos_TopLeft:
202         self->x = 0;
203         self->y = 0;
204         self->gravity = NorthWestGravity;
205         break;
206     case SlitPos_Top:
207         self->x = screen_physical_size.width / 2;
208         self->y = 0;
209         self->gravity = NorthGravity;
210         break;
211     case SlitPos_TopRight:
212         self->x = screen_physical_size.width;
213         self->y = 0;
214         self->gravity = NorthEastGravity;
215         break;
216     case SlitPos_Left:
217         self->x = 0;
218         self->y = screen_physical_size.height / 2;
219         self->gravity = WestGravity;
220         break;
221     case SlitPos_Right:
222         self->x = screen_physical_size.width;
223         self->y = screen_physical_size.height / 2;
224         self->gravity = EastGravity;
225         break;
226     case SlitPos_BottomLeft:
227         self->x = 0;
228         self->y = screen_physical_size.height;
229         self->gravity = SouthWestGravity;
230         break;
231     case SlitPos_Bottom:
232         self->x = screen_physical_size.width / 2;
233         self->y = screen_physical_size.height;
234         self->gravity = SouthGravity;
235         break;
236     case SlitPos_BottomRight:
237         self->x = screen_physical_size.width;
238         self->y = screen_physical_size.height;
239         self->gravity = SouthEastGravity;
240         break;
241     }
242
243     switch(self->gravity) {
244     case NorthGravity:
245     case CenterGravity:
246     case SouthGravity:
247         self->x -= self->w / 2;
248         break;
249     case NorthEastGravity:
250     case EastGravity:
251     case SouthEastGravity:
252         self->x -= self->w;
253         break;
254     }
255     switch(self->gravity) {
256     case WestGravity:
257     case CenterGravity:
258     case EastGravity:
259         self->y -= self->h / 2;
260         break;
261     case SouthWestGravity:
262     case SouthGravity:
263     case SouthEastGravity:
264         self->y -= self->h;
265         break;
266     }
267
268     if (self->hide && self->hidden) {
269         g_message("hidden");
270         switch (self->pos) {
271         case SlitPos_Floating:
272             break;
273         case SlitPos_TopLeft:
274             if (self->horz)
275                 self->y -= self->h - theme_bwidth;
276             else
277                 self->x -= self->w - theme_bwidth;
278             break;
279         case SlitPos_Top:
280             self->y -= self->h - theme_bwidth;
281             break;
282         case SlitPos_TopRight:
283             if (self->horz)
284                 self->y -= self->h - theme_bwidth;
285             else
286                 self->x += self->w - theme_bwidth;
287             break;
288         case SlitPos_Left:
289             self->x -= self->w - theme_bwidth;
290             break;
291         case SlitPos_Right:
292             self->x += self->w - theme_bwidth;
293             break;
294         case SlitPos_BottomLeft:
295             if (self->horz)
296                 self->y += self->h - theme_bwidth;
297             else
298                 self->x -= self->w - theme_bwidth;
299             break;
300         case SlitPos_Bottom:
301             self->y += self->h - theme_bwidth;
302             break;
303         case SlitPos_BottomRight:
304             if (self->horz)
305                 self->y += self->h - theme_bwidth;
306             else
307                 self->x += self->w - theme_bwidth;
308             break;
309         }    
310     }
311
312     /* not used for actually sizing shit */
313     self->w -= theme_bwidth * 2;
314     self->h -= theme_bwidth * 2;
315
316     if (self->w > 0 && self->h > 0) {
317         RECT_SET(self->a_frame->area, 0, 0, self->w, self->h);
318         XMoveResizeWindow(ob_display, self->frame,
319                           self->x, self->y, self->w, self->h);
320
321         paint(self->frame, self->a_frame);
322         XMapWindow(ob_display, self->frame);
323     } else
324         XUnmapWindow(ob_display, self->frame);
325 }
326
327 void slit_app_configure(SlitApp *app, int w, int h)
328 {
329     app->w = w;
330     app->h = h;
331     slit_configure(app->slit);
332 }