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