]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/frame.c
add the --replace command line option, and support for the WM_Sn selection
[mikachu/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
14 #define FRAME_HANDLE_Y(f) (f->innersize.top + f->client->area.height + \
15                            f->cbwidth)
16
17 static void layout_title(ObFrame *self);
18
19 static Window createWindow(Window parent, unsigned long mask,
20                            XSetWindowAttributes *attrib)
21 {
22     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
23                          RrDepth(ob_rr_inst), InputOutput,
24                          RrVisual(ob_rr_inst), mask, attrib);
25                        
26 }
27
28 ObFrame *frame_new()
29 {
30     XSetWindowAttributes attrib;
31     unsigned long mask;
32     ObFrame *self;
33
34     self = g_new(ObFrame, 1);
35
36     self->visible = FALSE;
37     self->decorations = 0;
38
39     /* create all of the decor windows */
40     mask = CWOverrideRedirect | CWEventMask;
41     attrib.event_mask = FRAME_EVENTMASK;
42     attrib.override_redirect = TRUE;
43     self->window = createWindow(RootWindow(ob_display, ob_screen),
44                                 mask, &attrib);
45
46     mask = 0;
47     self->plate = createWindow(self->window, mask, &attrib);
48
49     mask = CWEventMask;
50     attrib.event_mask = ELEMENT_EVENTMASK;
51     self->title = createWindow(self->window, mask, &attrib);
52     self->label = createWindow(self->title, mask, &attrib);
53     self->max = createWindow(self->title, mask, &attrib);
54     self->close = createWindow(self->title, mask, &attrib);
55     self->desk = createWindow(self->title, mask, &attrib);
56     self->shade = createWindow(self->title, mask, &attrib);
57     self->icon = createWindow(self->title, mask, &attrib);
58     self->iconify = createWindow(self->title, mask, &attrib);
59     self->handle = createWindow(self->window, mask, &attrib);
60     mask |= CWCursor;
61     attrib.cursor = ob_cursor(OB_CURSOR_SOUTHWEST);
62     self->lgrip = createWindow(self->handle, mask, &attrib);
63     attrib.cursor = ob_cursor(OB_CURSOR_SOUTHEAST);
64     self->rgrip = createWindow(self->handle, mask, &attrib);
65
66     self->focused = FALSE;
67
68     /* the other stuff is shown based on decor settings */
69     XMapWindow(ob_display, self->plate);
70     XMapWindow(ob_display, self->lgrip);
71     XMapWindow(ob_display, self->rgrip);
72     XMapWindow(ob_display, self->label);
73
74     /* set colors/appearance/sizes for stuff that doesn't change */
75     XSetWindowBorder(ob_display, self->window, ob_rr_theme->b_color->pixel);
76     XSetWindowBorder(ob_display, self->label, ob_rr_theme->b_color->pixel);
77     XSetWindowBorder(ob_display, self->rgrip, ob_rr_theme->b_color->pixel);
78     XSetWindowBorder(ob_display, self->lgrip, ob_rr_theme->b_color->pixel);
79
80     XResizeWindow(ob_display, self->max,
81                   ob_rr_theme->button_size, ob_rr_theme->button_size);
82     XResizeWindow(ob_display, self->iconify,
83                   ob_rr_theme->button_size, ob_rr_theme->button_size);
84     XResizeWindow(ob_display, self->icon,
85                   ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
86     XResizeWindow(ob_display, self->close,
87                   ob_rr_theme->button_size, ob_rr_theme->button_size);
88     XResizeWindow(ob_display, self->desk,
89                   ob_rr_theme->button_size, ob_rr_theme->button_size);
90     XResizeWindow(ob_display, self->shade,
91                   ob_rr_theme->button_size, ob_rr_theme->button_size);
92     XResizeWindow(ob_display, self->lgrip,
93                   ob_rr_theme->grip_width, ob_rr_theme->handle_height);
94     XResizeWindow(ob_display, self->rgrip,
95                   ob_rr_theme->grip_width, ob_rr_theme->handle_height);
96
97     /* set up the dynamic appearances */
98     self->a_unfocused_title = RrAppearanceCopy(ob_rr_theme->a_unfocused_title);
99     self->a_focused_title = RrAppearanceCopy(ob_rr_theme->a_focused_title);
100     self->a_unfocused_label = RrAppearanceCopy(ob_rr_theme->a_unfocused_label);
101     self->a_focused_label = RrAppearanceCopy(ob_rr_theme->a_focused_label);
102     self->a_unfocused_handle =
103         RrAppearanceCopy(ob_rr_theme->a_unfocused_handle);
104     self->a_focused_handle = RrAppearanceCopy(ob_rr_theme->a_focused_handle);
105     self->a_icon = RrAppearanceCopy(ob_rr_theme->a_icon);
106
107     self->max_press = self->close_press = self->desk_press = 
108         self->iconify_press = self->shade_press = FALSE;
109
110     return (ObFrame*)self;
111 }
112
113 static void frame_free(ObFrame *self)
114 {
115     RrAppearanceFree(self->a_unfocused_title); 
116     RrAppearanceFree(self->a_focused_title);
117     RrAppearanceFree(self->a_unfocused_label);
118     RrAppearanceFree(self->a_focused_label);
119     RrAppearanceFree(self->a_unfocused_handle);
120     RrAppearanceFree(self->a_focused_handle);
121     RrAppearanceFree(self->a_icon);
122
123     XDestroyWindow(ob_display, self->window);
124
125     g_free(self);
126 }
127
128 void frame_show(ObFrame *self)
129 {
130     if (!self->visible) {
131         self->visible = TRUE;
132         XMapWindow(ob_display, self->window);
133     }
134 }
135
136 void frame_hide(ObFrame *self)
137 {
138     if (self->visible) {
139         self->visible = FALSE;
140         self->client->ignore_unmaps++;
141         XUnmapWindow(ob_display, self->window);
142     }
143 }
144
145 void frame_adjust_shape(ObFrame *self)
146 {
147 #ifdef SHAPE
148     int num;
149     XRectangle xrect[2];
150
151     if (!self->client->shaped) {
152         /* clear the shape on the frame window */
153         XShapeCombineMask(ob_display, self->window, ShapeBounding,
154                           self->innersize.left,
155                           self->innersize.top,
156                           None, ShapeSet);
157     } else {
158         /* make the frame's shape match the clients */
159         XShapeCombineShape(ob_display, self->window, ShapeBounding,
160                            self->innersize.left,
161                            self->innersize.top,
162                            self->client->window,
163                            ShapeBounding, ShapeSet);
164
165         num = 0;
166         if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
167             xrect[0].x = -ob_rr_theme->bevel;
168             xrect[0].y = -ob_rr_theme->bevel;
169             xrect[0].width = self->width + self->bwidth * 2;
170             xrect[0].height = ob_rr_theme->title_height +
171                 self->bwidth * 2;
172             ++num;
173         }
174
175         if (self->decorations & OB_FRAME_DECOR_HANDLE) {
176             xrect[1].x = -ob_rr_theme->bevel;
177             xrect[1].y = FRAME_HANDLE_Y(self);
178             xrect[1].width = self->width + self->bwidth * 2;
179             xrect[1].height = ob_rr_theme->handle_height +
180                 self->bwidth * 2;
181             ++num;
182         }
183
184         XShapeCombineRectangles(ob_display, self->window,
185                                 ShapeBounding, 0, 0, xrect, num,
186                                 ShapeUnion, Unsorted);
187     }
188 #endif
189 }
190
191 void frame_adjust_area(ObFrame *self, gboolean moved, gboolean resized)
192 {
193     if (resized) {
194         self->decorations = self->client->decorations;
195         if (self->decorations & OB_FRAME_DECOR_BORDER) {
196             self->bwidth = ob_rr_theme->bwidth;
197             self->cbwidth = ob_rr_theme->cbwidth;
198         } else {
199             self->bwidth = self->cbwidth = 0;
200         }
201         STRUT_SET(self->innersize, self->cbwidth, self->cbwidth,
202                   self->cbwidth, self->cbwidth);
203         self->width = self->client->area.width + self->cbwidth * 2;
204         g_assert(self->width > 0);
205
206         /* set border widths */
207         XSetWindowBorderWidth(ob_display, self->plate,  self->cbwidth);
208         XSetWindowBorderWidth(ob_display, self->window, self->bwidth);
209         XSetWindowBorderWidth(ob_display, self->title,  self->bwidth);
210         XSetWindowBorderWidth(ob_display, self->handle, self->bwidth);
211         XSetWindowBorderWidth(ob_display, self->lgrip,  self->bwidth);
212         XSetWindowBorderWidth(ob_display, self->rgrip,  self->bwidth);
213   
214         /* position/size and map/unmap all the windows */
215
216         /* they all default off, they're turned on in layout_title */
217         self->icon_x = -1;
218         self->desk_x = -1;
219         self->shade_x = -1;
220         self->iconify_x = -1;
221         self->label_x = -1;
222         self->max_x = -1;
223         self->close_x = -1;
224
225         if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
226             XMoveResizeWindow(ob_display, self->title,
227                               -self->bwidth, -self->bwidth,
228                               self->width, ob_rr_theme->title_height);
229             self->innersize.top += ob_rr_theme->title_height + self->bwidth;
230             XMapWindow(ob_display, self->title);
231
232             /* layout the title bar elements */
233             layout_title(self);
234         } else
235             XUnmapWindow(ob_display, self->title);
236
237         if (self->decorations & OB_FRAME_DECOR_HANDLE) {
238             XMoveResizeWindow(ob_display, self->handle,
239                               -self->bwidth, FRAME_HANDLE_Y(self),
240                               self->width, ob_rr_theme->handle_height);
241             self->innersize.bottom += ob_rr_theme->handle_height +
242                 self->bwidth;
243             XMapWindow(ob_display, self->handle);
244
245             if (self->decorations & OB_FRAME_DECOR_GRIPS) {
246                 XMoveWindow(ob_display, self->lgrip,
247                             -self->bwidth, -self->bwidth);
248                 XMoveWindow(ob_display, self->rgrip,
249                             -self->bwidth + self->width -
250                             ob_rr_theme->grip_width, -self->bwidth);
251                 XMapWindow(ob_display, self->lgrip);
252                 XMapWindow(ob_display, self->rgrip);
253             } else {
254                 XUnmapWindow(ob_display, self->lgrip);
255                 XUnmapWindow(ob_display, self->rgrip);
256             }
257
258             /* XXX make a subwindow with these dimentions?
259                ob_rr_theme->grip_width + self->bwidth, 0,
260                self->width - (ob_rr_theme->grip_width + self->bwidth) * 2,
261                ob_rr_theme->handle_height);
262             */
263         } else
264             XUnmapWindow(ob_display, self->handle);
265     }
266
267     if (resized) {
268         /* move and resize the plate */
269         XMoveResizeWindow(ob_display, self->plate,
270                           self->innersize.left - self->cbwidth,
271                           self->innersize.top - self->cbwidth,
272                           self->client->area.width,
273                           self->client->area.height);
274         /* when the client has StaticGravity, it likes to move around. */
275         XMoveWindow(ob_display, self->client->window, 0, 0);
276     }
277
278     if (resized) {
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 ? ob_rr_theme->title_height + self->bwidth*2:
291                    self->client->area.height +
292                    self->size.top + self->size.bottom));
293
294     if (moved) {
295         /* find the new coordinates, done after setting the frame.size, for
296            frame_client_gravity. */
297         self->area.x = self->client->area.x;
298         self->area.y = self->client->area.y;
299         frame_client_gravity((ObFrame*)self,
300                              &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 = ob_rr_theme->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 = ob_rr_theme->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 WestGravity:
688     case SouthWestGravity:
689         break;
690     case NorthGravity:
691     case CenterGravity:
692     case SouthGravity:
693         *y += (self->size.top + self->size.bottom) / 2;
694         break;
695     case NorthEastGravity:
696     case EastGravity:
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 }