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