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