a couple frame layout bugfixes hidden inside all this.. wee
[mikachu/openbox.git] / openbox / frame.c
1 #include "frame.h"
2 #include "client.h"
3 #include "openbox.h"
4 #include "extensions.h"
5 #include "config.h"
6 #include "framerender.h"
7 #include "render/theme.h"
8
9 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask)
10 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
11                          ButtonPressMask | ButtonReleaseMask)
12 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
13                            ButtonMotionMask | ExposureMask | \
14                            EnterWindowMask | LeaveWindowMask)
15
16 #define FRAME_HANDLE_Y(f) (f->innersize.top + f->client->area.height + \
17                            f->cbwidth_y)
18
19 static void layout_title(ObFrame *self);
20
21 static Window createWindow(Window parent, unsigned long mask,
22                            XSetWindowAttributes *attrib)
23 {
24     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
25                          RrDepth(ob_rr_inst), InputOutput,
26                          RrVisual(ob_rr_inst), mask, attrib);
27                        
28 }
29
30 ObFrame *frame_new()
31 {
32     XSetWindowAttributes attrib;
33     unsigned long mask;
34     ObFrame *self;
35
36     self = g_new(ObFrame, 1);
37
38     self->visible = FALSE;
39     self->decorations = 0;
40
41     /* create all of the decor windows */
42     mask = CWOverrideRedirect | CWEventMask;
43     attrib.event_mask = FRAME_EVENTMASK;
44     attrib.override_redirect = TRUE;
45     self->window = createWindow(RootWindow(ob_display, ob_screen),
46                                 mask, &attrib);
47
48     mask = 0;
49     self->plate = createWindow(self->window, mask, &attrib);
50
51     mask = CWEventMask;
52     attrib.event_mask = ELEMENT_EVENTMASK;
53     self->title = createWindow(self->window, mask, &attrib);
54
55     mask |= CWCursor;
56     attrib.cursor = ob_cursor(OB_CURSOR_NORTHWEST);
57     self->tlresize = createWindow(self->title, mask, &attrib);
58     attrib.cursor = ob_cursor(OB_CURSOR_NORTHEAST);
59     self->trresize = createWindow(self->title, mask, &attrib);
60
61     mask &= ~CWCursor;
62     self->label = createWindow(self->title, mask, &attrib);
63     self->max = createWindow(self->title, mask, &attrib);
64     self->close = createWindow(self->title, mask, &attrib);
65     self->desk = createWindow(self->title, mask, &attrib);
66     self->shade = createWindow(self->title, mask, &attrib);
67     self->icon = createWindow(self->title, mask, &attrib);
68     self->iconify = createWindow(self->title, mask, &attrib);
69     self->handle = createWindow(self->window, mask, &attrib);
70
71     mask |= CWCursor;
72     attrib.cursor = ob_cursor(OB_CURSOR_SOUTHWEST);
73     self->lgrip = createWindow(self->handle, mask, &attrib);
74     attrib.cursor = ob_cursor(OB_CURSOR_SOUTHEAST);
75     self->rgrip = createWindow(self->handle, mask, &attrib); 
76
77     self->focused = FALSE;
78
79     /* the other stuff is shown based on decor settings */
80     XMapWindow(ob_display, self->plate);
81     XMapWindow(ob_display, self->lgrip);
82     XMapWindow(ob_display, self->rgrip);
83     XMapWindow(ob_display, self->label);
84     XMapWindow(ob_display, self->tlresize);
85     XMapWindow(ob_display, self->trresize);
86
87     /* set colors/appearance/sizes for stuff that doesn't change */
88     XSetWindowBorder(ob_display, self->window, ob_rr_theme->b_color->pixel);
89     XSetWindowBorder(ob_display, self->title, ob_rr_theme->b_color->pixel);
90     XSetWindowBorder(ob_display, self->handle, ob_rr_theme->b_color->pixel);
91     XSetWindowBorder(ob_display, self->rgrip, ob_rr_theme->b_color->pixel);
92     XSetWindowBorder(ob_display, self->lgrip, ob_rr_theme->b_color->pixel);
93
94     XResizeWindow(ob_display, self->max,
95                   ob_rr_theme->button_size, ob_rr_theme->button_size);
96     XResizeWindow(ob_display, self->iconify,
97                   ob_rr_theme->button_size, ob_rr_theme->button_size);
98     XResizeWindow(ob_display, self->icon,
99                   ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
100     XResizeWindow(ob_display, self->close,
101                   ob_rr_theme->button_size, ob_rr_theme->button_size);
102     XResizeWindow(ob_display, self->desk,
103                   ob_rr_theme->button_size, ob_rr_theme->button_size);
104     XResizeWindow(ob_display, self->shade,
105                   ob_rr_theme->button_size, ob_rr_theme->button_size);
106     XResizeWindow(ob_display, self->lgrip,
107                   ob_rr_theme->grip_width, ob_rr_theme->handle_height);
108     XResizeWindow(ob_display, self->rgrip,
109                   ob_rr_theme->grip_width, ob_rr_theme->handle_height);
110     XResizeWindow(ob_display, self->tlresize,
111                   ob_rr_theme->grip_width, ob_rr_theme->handle_height);
112     XResizeWindow(ob_display, self->trresize,
113                   ob_rr_theme->grip_width, ob_rr_theme->handle_height);
114
115     /* set up the dynamic appearances */
116     self->a_unfocused_title = RrAppearanceCopy(ob_rr_theme->a_unfocused_title);
117     self->a_focused_title = RrAppearanceCopy(ob_rr_theme->a_focused_title);
118     self->a_unfocused_label = RrAppearanceCopy(ob_rr_theme->a_unfocused_label);
119     self->a_focused_label = RrAppearanceCopy(ob_rr_theme->a_focused_label);
120     self->a_unfocused_handle =
121         RrAppearanceCopy(ob_rr_theme->a_unfocused_handle);
122     self->a_focused_handle = RrAppearanceCopy(ob_rr_theme->a_focused_handle);
123     self->a_icon = RrAppearanceCopy(ob_rr_theme->a_icon);
124
125     self->max_press = self->close_press = self->desk_press = 
126         self->iconify_press = self->shade_press = FALSE;
127     self->max_hover = self->close_hover = self->desk_hover = 
128         self->iconify_hover = self->shade_hover = FALSE;
129
130     return (ObFrame*)self;
131 }
132
133 static void frame_free(ObFrame *self)
134 {
135     RrAppearanceFree(self->a_unfocused_title); 
136     RrAppearanceFree(self->a_focused_title);
137     RrAppearanceFree(self->a_unfocused_label);
138     RrAppearanceFree(self->a_focused_label);
139     RrAppearanceFree(self->a_unfocused_handle);
140     RrAppearanceFree(self->a_focused_handle);
141     RrAppearanceFree(self->a_icon);
142
143     XDestroyWindow(ob_display, self->window);
144
145     g_free(self);
146 }
147
148 void frame_show(ObFrame *self)
149 {
150     if (!self->visible) {
151         self->visible = TRUE;
152         XMapWindow(ob_display, self->window);
153     }
154 }
155
156 void frame_hide(ObFrame *self)
157 {
158     if (self->visible) {
159         self->visible = FALSE;
160         self->client->ignore_unmaps++;
161         XUnmapWindow(ob_display, self->window);
162     }
163 }
164
165 void frame_adjust_shape(ObFrame *self)
166 {
167 #ifdef SHAPE
168     int num;
169     XRectangle xrect[2];
170
171     if (!self->client->shaped) {
172         /* clear the shape on the frame window */
173         XShapeCombineMask(ob_display, self->window, ShapeBounding,
174                           self->innersize.left,
175                           self->innersize.top,
176                           None, ShapeSet);
177     } else {
178         /* make the frame's shape match the clients */
179         XShapeCombineShape(ob_display, self->window, ShapeBounding,
180                            self->innersize.left,
181                            self->innersize.top,
182                            self->client->window,
183                            ShapeBounding, ShapeSet);
184
185         num = 0;
186         if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
187             xrect[0].x = -ob_rr_theme->bwidth;
188             xrect[0].y = -ob_rr_theme->bwidth;
189             xrect[0].width = self->width + self->rbwidth * 2;
190             xrect[0].height = ob_rr_theme->title_height +
191                 self->bwidth * 2;
192             ++num;
193         }
194
195         if (self->decorations & OB_FRAME_DECOR_HANDLE) {
196             xrect[1].x = -ob_rr_theme->bwidth;
197             xrect[1].y = FRAME_HANDLE_Y(self);
198             xrect[1].width = self->width + self->rbwidth * 2;
199             xrect[1].height = ob_rr_theme->handle_height +
200                 self->bwidth * 2;
201             ++num;
202         }
203
204         XShapeCombineRectangles(ob_display, self->window,
205                                 ShapeBounding, 0, 0, xrect, num,
206                                 ShapeUnion, Unsorted);
207     }
208 #endif
209 }
210
211 void frame_adjust_area(ObFrame *self, gboolean moved,
212                        gboolean resized, gboolean fake)
213 {
214     if (resized) {
215         self->decorations = self->client->decorations;
216
217         if (self->decorations & OB_FRAME_DECOR_BORDER) {
218             self->bwidth = ob_rr_theme->bwidth;
219             self->cbwidth_x = self->cbwidth_y = ob_rr_theme->cbwidth;
220         } else {
221             self->bwidth = self->cbwidth_x = self->cbwidth_y = 0;
222         }
223         self->rbwidth = self->bwidth;
224
225         if (self->client->max_vert && self->client->max_horz)
226             self->decorations &= ~OB_FRAME_DECOR_HANDLE;
227
228         if (self->client->max_horz)
229             self->bwidth = self->cbwidth_x = 0;
230
231         STRUT_SET(self->innersize,
232                   self->cbwidth_x,
233                   self->cbwidth_y,
234                   self->cbwidth_x,
235                   self->cbwidth_y);
236         self->width = self->client->area.width + self->cbwidth_x * 2 -
237             (self->client->max_horz ? self->rbwidth * 2 : 0);
238         self->width = MAX(self->width, 1); /* no lower than 1 */
239
240         /* set border widths */
241         if (!fake) {
242             XSetWindowBorderWidth(ob_display, self->window, self->bwidth);
243             XSetWindowBorderWidth(ob_display, self->title,  self->rbwidth);
244             XSetWindowBorderWidth(ob_display, self->handle, self->rbwidth);
245             XSetWindowBorderWidth(ob_display, self->lgrip,  self->rbwidth);
246             XSetWindowBorderWidth(ob_display, self->rgrip,  self->rbwidth);
247         }
248
249         if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
250             self->innersize.top += ob_rr_theme->title_height + self->rbwidth +
251                 (self->rbwidth - self->bwidth);
252         if (self->decorations & OB_FRAME_DECOR_HANDLE)
253             self->innersize.bottom += ob_rr_theme->handle_height +
254                 self->rbwidth + (self->rbwidth - self->bwidth);
255   
256         /* they all default off, they're turned on in layout_title */
257         self->icon_x = -1;
258         self->desk_x = -1;
259         self->shade_x = -1;
260         self->iconify_x = -1;
261         self->label_x = -1;
262         self->max_x = -1;
263         self->close_x = -1;
264
265         /* position/size and map/unmap all the windows */
266
267         if (!fake) {
268             if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
269                 XMoveResizeWindow(ob_display, self->title,
270                                   -self->bwidth, -self->bwidth,
271                                   self->width, ob_rr_theme->title_height);
272                 XMapWindow(ob_display, self->title);
273
274                 XMoveWindow(ob_display, self->tlresize, 0, 0);
275                 XMoveWindow(ob_display, self->trresize,
276                             self->width - ob_rr_theme->grip_width, 0);
277
278             } else
279                 XUnmapWindow(ob_display, self->title);
280         }
281
282         if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
283             /* layout the title bar elements */
284             layout_title(self);
285
286         if (!fake) {
287             if (self->decorations & OB_FRAME_DECOR_HANDLE) {
288                 XMoveResizeWindow(ob_display, self->handle,
289                                   -self->bwidth, FRAME_HANDLE_Y(self),
290                                   self->width, ob_rr_theme->handle_height);
291                 XMapWindow(ob_display, self->handle);
292
293                 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
294                     XMoveWindow(ob_display, self->lgrip,
295                                 -self->rbwidth, -self->rbwidth);
296                     XMoveWindow(ob_display, self->rgrip,
297                                 -self->rbwidth + self->width -
298                                 ob_rr_theme->grip_width, -self->rbwidth);
299                     XMapWindow(ob_display, self->lgrip);
300                     XMapWindow(ob_display, self->rgrip);
301                 } else {
302                     XUnmapWindow(ob_display, self->lgrip);
303                     XUnmapWindow(ob_display, self->rgrip);
304                 }
305
306                 /* XXX make a subwindow with these dimentions?
307                    ob_rr_theme->grip_width + self->bwidth, 0,
308                    self->width - (ob_rr_theme->grip_width + self->bwidth) * 2,
309                    ob_rr_theme->handle_height);
310                 */
311             } else
312                 XUnmapWindow(ob_display, self->handle);
313
314             /* move and resize the plate */
315             XMoveResizeWindow(ob_display, self->plate,
316                               self->innersize.left - self->cbwidth_x,
317                               self->innersize.top - self->cbwidth_y,
318                               self->client->area.width + self->cbwidth_x * 2,
319                               self->client->area.height + self->cbwidth_y * 2);
320             /* when the client has StaticGravity, it likes to move around. */
321             XMoveWindow(ob_display, self->client->window,
322                         self->cbwidth_x, self->cbwidth_y);
323         }
324
325         STRUT_SET(self->size,
326                   self->innersize.left + self->bwidth,
327                   self->innersize.top + self->bwidth,
328                   self->innersize.right + self->bwidth,
329                   self->innersize.bottom + self->bwidth);
330     }
331
332     /* shading can change without being moved or resized */
333     RECT_SET_SIZE(self->area,
334                   self->client->area.width +
335                   self->size.left + self->size.right,
336                   (self->client->shaded ?
337                    ob_rr_theme->title_height + self->bwidth*2:
338                    self->client->area.height +
339                    self->size.top + self->size.bottom));
340
341     if (moved) {
342         /* find the new coordinates, done after setting the frame.size, for
343            frame_client_gravity. */
344         self->area.x = self->client->area.x;
345         self->area.y = self->client->area.y;
346         frame_client_gravity(self, &self->area.x, &self->area.y);
347     }
348
349     if (!fake) {
350         /* move and resize the top level frame.
351            shading can change without being moved or resized */
352         XMoveResizeWindow(ob_display, self->window,
353                           self->area.x, self->area.y,
354                           self->area.width - self->bwidth * 2,
355                           self->area.height - self->bwidth * 2);
356
357         if (resized) {
358             framerender_frame(self);
359
360             frame_adjust_shape(self);
361         }
362     }
363 }
364
365 void frame_adjust_state(ObFrame *self)
366 {
367     framerender_frame(self);
368 }
369
370 void frame_adjust_focus(ObFrame *self, gboolean hilite)
371 {
372     self->focused = hilite;
373     framerender_frame(self);
374 }
375
376 void frame_adjust_title(ObFrame *self)
377 {
378     framerender_frame(self);
379 }
380
381 void frame_adjust_icon(ObFrame *self)
382 {
383     framerender_frame(self);
384 }
385
386 void frame_grab_client(ObFrame *self, ObClient *client)
387 {
388     self->client = client;
389
390     /* reparent the client to the frame */
391     XReparentWindow(ob_display, client->window, self->plate, 0, 0);
392     /*
393       When reparenting the client window, it is usually not mapped yet, since
394       this occurs from a MapRequest. However, in the case where Openbox is
395       starting up, the window is already mapped, so we'll see unmap events for
396       it. There are 2 unmap events generated that we see, one with the 'event'
397       member set the root window, and one set to the client, but both get
398       handled and need to be ignored.
399     */
400     if (ob_state() == OB_STATE_STARTING)
401         client->ignore_unmaps += 2;
402
403     /* select the event mask on the client's parent (to receive config/map
404        req's) the ButtonPress is to catch clicks on the client border */
405     XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
406
407     /* map the client so it maps when the frame does */
408     XMapWindow(ob_display, client->window);
409
410     frame_adjust_area(self, TRUE, TRUE, FALSE);
411
412     /* set all the windows for the frame in the window_map */
413     g_hash_table_insert(window_map, &self->window, client);
414     g_hash_table_insert(window_map, &self->plate, client);
415     g_hash_table_insert(window_map, &self->title, client);
416     g_hash_table_insert(window_map, &self->label, client);
417     g_hash_table_insert(window_map, &self->max, client);
418     g_hash_table_insert(window_map, &self->close, client);
419     g_hash_table_insert(window_map, &self->desk, client);
420     g_hash_table_insert(window_map, &self->shade, client);
421     g_hash_table_insert(window_map, &self->icon, client);
422     g_hash_table_insert(window_map, &self->iconify, client);
423     g_hash_table_insert(window_map, &self->handle, client);
424     g_hash_table_insert(window_map, &self->lgrip, client);
425     g_hash_table_insert(window_map, &self->rgrip, client);
426     g_hash_table_insert(window_map, &self->tlresize, client);
427     g_hash_table_insert(window_map, &self->trresize, client);
428 }
429
430 void frame_release_client(ObFrame *self, ObClient *client)
431 {
432     XEvent ev;
433
434     g_assert(self->client == client);
435
436     /* check if the app has already reparented its window away */
437     if (XCheckTypedWindowEvent(ob_display, client->window,
438                                ReparentNotify, &ev)) {
439         XPutBackEvent(ob_display, &ev);
440
441         /* re-map the window since the unmanaging process unmaps it */
442
443         /* XXX ... um no it doesnt it unmaps its parent, the window itself
444            retains its mapped state, no?! XXX
445            XMapWindow(ob_display, client->window); */
446     } else {
447         /* according to the ICCCM - if the client doesn't reparent itself,
448            then we will reparent the window to root for them */
449         XReparentWindow(ob_display, client->window,
450                         RootWindow(ob_display, ob_screen),
451                         client->area.x,
452                         client->area.y);
453     }
454
455     /* remove all the windows for the frame from the window_map */
456     g_hash_table_remove(window_map, &self->window);
457     g_hash_table_remove(window_map, &self->plate);
458     g_hash_table_remove(window_map, &self->title);
459     g_hash_table_remove(window_map, &self->label);
460     g_hash_table_remove(window_map, &self->max);
461     g_hash_table_remove(window_map, &self->close);
462     g_hash_table_remove(window_map, &self->desk);
463     g_hash_table_remove(window_map, &self->shade);
464     g_hash_table_remove(window_map, &self->icon);
465     g_hash_table_remove(window_map, &self->iconify);
466     g_hash_table_remove(window_map, &self->handle);
467     g_hash_table_remove(window_map, &self->lgrip);
468     g_hash_table_remove(window_map, &self->rgrip);
469     g_hash_table_remove(window_map, &self->tlresize);
470     g_hash_table_remove(window_map, &self->trresize);
471
472     frame_free(self);
473 }
474
475 static void layout_title(ObFrame *self)
476 {
477     char *lc;
478     int x;
479     gboolean n, d, i, l, m, c, s;
480
481     n = d = i = l = m = c = s = FALSE;
482
483     /* figure out whats being shown, and the width of the label */
484     self->label_width = self->width - (ob_rr_theme->bevel + 1) * 2;
485     for (lc = config_title_layout; *lc != '\0'; ++lc) {
486         switch (*lc) {
487         case 'N':
488             if (n) { *lc = ' '; break; } /* rm duplicates */
489             n = TRUE;
490             self->label_width -= (ob_rr_theme->button_size + 2 +
491                                   ob_rr_theme->bevel + 1);
492             break;
493         case 'D':
494             if (d) { *lc = ' '; break; } /* rm duplicates */
495             d = TRUE;
496             self->label_width -= (ob_rr_theme->button_size +
497                                   ob_rr_theme->bevel + 1);
498             break;
499         case 'S':
500             if (s) { *lc = ' '; break; } /* rm duplicates */
501             s = TRUE;
502             self->label_width -= (ob_rr_theme->button_size +
503                                   ob_rr_theme->bevel + 1);
504             break;
505         case 'I':
506             if (i) { *lc = ' '; break; } /* rm duplicates */
507             i = TRUE;
508             self->label_width -= (ob_rr_theme->button_size +
509                                   ob_rr_theme->bevel + 1);
510             break;
511         case 'L':
512             if (l) { *lc = ' '; break; } /* rm duplicates */
513             l = TRUE;
514             break;
515         case 'M':
516             if (m) { *lc = ' '; break; } /* rm duplicates */
517             m = TRUE;
518             self->label_width -= (ob_rr_theme->button_size +
519                                   ob_rr_theme->bevel + 1);
520             break;
521         case 'C':
522             if (c) { *lc = ' '; break; } /* rm duplicates */
523             c = TRUE;
524             self->label_width -= (ob_rr_theme->button_size +
525                                   ob_rr_theme->bevel + 1);
526             break;
527         }
528     }
529     if (self->label_width < 1) self->label_width = 1;
530
531     XResizeWindow(ob_display, self->label, self->label_width,
532                   ob_rr_theme->label_height);
533   
534     if (!n) XUnmapWindow(ob_display, self->icon);
535     if (!d) XUnmapWindow(ob_display, self->desk);
536     if (!s) XUnmapWindow(ob_display, self->shade);
537     if (!i) XUnmapWindow(ob_display, self->iconify);
538     if (!l) XUnmapWindow(ob_display, self->label);
539     if (!m) XUnmapWindow(ob_display, self->max);
540     if (!c) XUnmapWindow(ob_display, self->close);
541
542     x = ob_rr_theme->bevel + 1;
543     for (lc = config_title_layout; *lc != '\0'; ++lc) {
544         switch (*lc) {
545         case 'N':
546             if (!n) break;
547             self->icon_x = x;
548             XMapWindow(ob_display, self->icon);
549             XMoveWindow(ob_display, self->icon, x, ob_rr_theme->bevel);
550             x += ob_rr_theme->button_size + 2 + ob_rr_theme->bevel + 1;
551             break;
552         case 'D':
553             if (!d) break;
554             self->desk_x = x;
555             XMapWindow(ob_display, self->desk);
556             XMoveWindow(ob_display, self->desk, x, ob_rr_theme->bevel + 1);
557             x += ob_rr_theme->button_size + ob_rr_theme->bevel + 1;
558             break;
559         case 'S':
560             if (!s) break;
561             self->shade_x = x;
562             XMapWindow(ob_display, self->shade);
563             XMoveWindow(ob_display, self->shade, x, ob_rr_theme->bevel + 1);
564             x += ob_rr_theme->button_size + ob_rr_theme->bevel + 1;
565             break;
566         case 'I':
567             if (!i) break;
568             self->iconify_x = x;
569             XMapWindow(ob_display, self->iconify);
570             XMoveWindow(ob_display, self->iconify, x, ob_rr_theme->bevel + 1);
571             x += ob_rr_theme->button_size + ob_rr_theme->bevel + 1;
572             break;
573         case 'L':
574             if (!l) break;
575             self->label_x = x;
576             XMapWindow(ob_display, self->label);
577             XMoveWindow(ob_display, self->label, x, ob_rr_theme->bevel);
578             x += self->label_width + ob_rr_theme->bevel + 1;
579             break;
580         case 'M':
581             if (!m) break;
582             self->max_x = x;
583             XMapWindow(ob_display, self->max);
584             XMoveWindow(ob_display, self->max, x, ob_rr_theme->bevel + 1);
585             x += ob_rr_theme->button_size + ob_rr_theme->bevel + 1;
586             break;
587         case 'C':
588             if (!c) break;
589             self->close_x = x;
590             XMapWindow(ob_display, self->close);
591             XMoveWindow(ob_display, self->close, x, ob_rr_theme->bevel + 1);
592             x += ob_rr_theme->button_size + ob_rr_theme->bevel + 1;
593             break;
594         }
595     }
596 }
597
598 ObFrameContext frame_context_from_string(char *name)
599 {
600     if (!g_ascii_strcasecmp("root", name))
601         return OB_FRAME_CONTEXT_ROOT;
602     else if (!g_ascii_strcasecmp("client", name))
603         return OB_FRAME_CONTEXT_CLIENT;
604     else if (!g_ascii_strcasecmp("titlebar", name))
605         return OB_FRAME_CONTEXT_TITLEBAR;
606     else if (!g_ascii_strcasecmp("handle", name))
607         return OB_FRAME_CONTEXT_HANDLE;
608     else if (!g_ascii_strcasecmp("frame", name))
609         return OB_FRAME_CONTEXT_FRAME;
610     else if (!g_ascii_strcasecmp("tlcorner", name))
611         return OB_FRAME_CONTEXT_TLCORNER;
612     else if (!g_ascii_strcasecmp("trcorner", name))
613         return OB_FRAME_CONTEXT_TRCORNER;
614     else if (!g_ascii_strcasecmp("blcorner", name))
615         return OB_FRAME_CONTEXT_BLCORNER;
616     else if (!g_ascii_strcasecmp("brcorner", name))
617         return OB_FRAME_CONTEXT_BRCORNER;
618     else if (!g_ascii_strcasecmp("maximize", name))
619         return OB_FRAME_CONTEXT_MAXIMIZE;
620     else if (!g_ascii_strcasecmp("alldesktops", name))
621         return OB_FRAME_CONTEXT_ALLDESKTOPS;
622     else if (!g_ascii_strcasecmp("shade", name))
623         return OB_FRAME_CONTEXT_SHADE;
624     else if (!g_ascii_strcasecmp("iconify", name))
625         return OB_FRAME_CONTEXT_ICONIFY;
626     else if (!g_ascii_strcasecmp("icon", name))
627         return OB_FRAME_CONTEXT_ICON;
628     else if (!g_ascii_strcasecmp("close", name))
629         return OB_FRAME_CONTEXT_CLOSE;
630     return OB_FRAME_CONTEXT_NONE;
631 }
632
633 ObFrameContext frame_context(ObClient *client, Window win)
634 {
635     ObFrame *self;
636
637     if (win == RootWindow(ob_display, ob_screen)) return OB_FRAME_CONTEXT_ROOT;
638     if (client == NULL) return OB_FRAME_CONTEXT_NONE;
639     if (win == client->window) return OB_FRAME_CONTEXT_CLIENT;
640
641     self = client->frame;
642     if (win == self->window)   return OB_FRAME_CONTEXT_FRAME;
643     if (win == self->plate)    return OB_FRAME_CONTEXT_CLIENT;
644     if (win == self->title)    return OB_FRAME_CONTEXT_TITLEBAR;
645     if (win == self->label)    return OB_FRAME_CONTEXT_TITLEBAR;
646     if (win == self->handle)   return OB_FRAME_CONTEXT_HANDLE;
647     if (win == self->lgrip)    return OB_FRAME_CONTEXT_BLCORNER;
648     if (win == self->rgrip)    return OB_FRAME_CONTEXT_BRCORNER;
649     if (win == self->tlresize) return OB_FRAME_CONTEXT_TLCORNER;
650     if (win == self->trresize) return OB_FRAME_CONTEXT_TRCORNER;
651     if (win == self->max)      return OB_FRAME_CONTEXT_MAXIMIZE;
652     if (win == self->iconify)  return OB_FRAME_CONTEXT_ICONIFY;
653     if (win == self->close)    return OB_FRAME_CONTEXT_CLOSE;
654     if (win == self->icon)     return OB_FRAME_CONTEXT_ICON;
655     if (win == self->desk)     return OB_FRAME_CONTEXT_ALLDESKTOPS;
656     if (win == self->shade)    return OB_FRAME_CONTEXT_SHADE;
657
658     return OB_FRAME_CONTEXT_NONE;
659 }
660
661 void frame_client_gravity(ObFrame *self, int *x, int *y)
662 {
663     /* horizontal */
664     switch (self->client->gravity) {
665     default:
666     case NorthWestGravity:
667     case SouthWestGravity:
668     case WestGravity:
669         break;
670
671     case NorthGravity:
672     case SouthGravity:
673     case CenterGravity:
674         *x -= (self->size.left + self->size.right) / 2;
675         break;
676
677     case NorthEastGravity:
678     case SouthEastGravity:
679     case EastGravity:
680         *x -= self->size.left + self->size.right;
681         break;
682
683     case ForgetGravity:
684     case StaticGravity:
685         *x -= self->size.left;
686         break;
687     }
688
689     /* vertical */
690     switch (self->client->gravity) {
691     default:
692     case NorthWestGravity:
693     case NorthEastGravity:
694     case NorthGravity:
695         break;
696
697     case CenterGravity:
698     case EastGravity:
699     case WestGravity:
700         *y -= (self->size.top + self->size.bottom) / 2;
701         break;
702
703     case SouthWestGravity:
704     case SouthEastGravity:
705     case SouthGravity:
706         *y -= self->size.top + self->size.bottom;
707         break;
708
709     case ForgetGravity:
710     case StaticGravity:
711         *y -= self->size.top;
712         break;
713     }
714 }
715
716 void frame_frame_gravity(ObFrame *self, int *x, int *y)
717 {
718     /* horizontal */
719     switch (self->client->gravity) {
720     default:
721     case NorthWestGravity:
722     case WestGravity:
723     case SouthWestGravity:
724         break;
725     case NorthGravity:
726     case CenterGravity:
727     case SouthGravity:
728         *x += (self->size.left + self->size.right) / 2;
729         break;
730     case NorthEastGravity:
731     case EastGravity:
732     case SouthEastGravity:
733         *x += self->size.left + self->size.right;
734         break;
735     case StaticGravity:
736     case ForgetGravity:
737         *x += self->size.left;
738         break;
739     }
740
741     /* vertical */
742     switch (self->client->gravity) {
743     default:
744     case NorthWestGravity:
745     case NorthGravity:
746     case NorthEastGravity:
747         break;
748     case WestGravity:
749     case CenterGravity:
750     case EastGravity:
751         *y += (self->size.top + self->size.bottom) / 2;
752         break;
753     case SouthWestGravity:
754     case SouthGravity:
755     case SouthEastGravity:
756         *y += self->size.top + self->size.bottom;
757         break;
758     case StaticGravity:
759     case ForgetGravity:
760         *y += self->size.top;
761         break;
762     }
763 }