move ob_pointer_pos to screen_pointer_pos
[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 void frame_startup()
20 {
21 }
22
23 void frame_shutdown()
24 {
25 }
26
27 static Window createWindow(Window parent, unsigned long mask,
28                            XSetWindowAttributes *attrib)
29 {
30     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
31                          RrDepth(ob_rr_inst), InputOutput,
32                          RrVisual(ob_rr_inst), mask, attrib);
33                        
34 }
35
36 ObFrame *frame_new()
37 {
38     XSetWindowAttributes attrib;
39     unsigned long mask;
40     ObFrame *self;
41
42     self = g_new(ObFrame, 1);
43
44     self->visible = FALSE;
45     self->decorations = 0;
46
47     /* create all of the decor windows */
48     mask = CWOverrideRedirect | CWEventMask;
49     attrib.event_mask = FRAME_EVENTMASK;
50     attrib.override_redirect = TRUE;
51     self->window = createWindow(RootWindow(ob_display, ob_screen),
52                                 mask, &attrib);
53
54     mask = 0;
55     self->plate = createWindow(self->window, mask, &attrib);
56
57     mask = CWEventMask;
58     attrib.event_mask = ELEMENT_EVENTMASK;
59     self->title = createWindow(self->window, mask, &attrib);
60     self->label = createWindow(self->title, mask, &attrib);
61     self->max = createWindow(self->title, mask, &attrib);
62     self->close = createWindow(self->title, mask, &attrib);
63     self->desk = createWindow(self->title, mask, &attrib);
64     self->shade = createWindow(self->title, mask, &attrib);
65     self->icon = createWindow(self->title, mask, &attrib);
66     self->iconify = createWindow(self->title, mask, &attrib);
67     self->handle = createWindow(self->window, mask, &attrib);
68     mask |= CWCursor;
69     attrib.cursor = ob_cursor(OB_CURSOR_SOUTHWEST);
70     self->lgrip = createWindow(self->handle, mask, &attrib);
71     attrib.cursor = ob_cursor(OB_CURSOR_SOUTHEAST);
72     self->rgrip = createWindow(self->handle, mask, &attrib);
73
74     self->focused = FALSE;
75
76     /* the other stuff is shown based on decor settings */
77     XMapWindow(ob_display, self->plate);
78     XMapWindow(ob_display, self->lgrip);
79     XMapWindow(ob_display, self->rgrip);
80     XMapWindow(ob_display, self->label);
81
82     /* set colors/appearance/sizes for stuff that doesn't change */
83     XSetWindowBorder(ob_display, self->window, ob_rr_theme->b_color->pixel);
84     XSetWindowBorder(ob_display, self->label, ob_rr_theme->b_color->pixel);
85     XSetWindowBorder(ob_display, self->rgrip, ob_rr_theme->b_color->pixel);
86     XSetWindowBorder(ob_display, self->lgrip, ob_rr_theme->b_color->pixel);
87
88     XResizeWindow(ob_display, self->max,
89                   ob_rr_theme->button_size, ob_rr_theme->button_size);
90     XResizeWindow(ob_display, self->iconify,
91                   ob_rr_theme->button_size, ob_rr_theme->button_size);
92     XResizeWindow(ob_display, self->icon,
93                   ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
94     XResizeWindow(ob_display, self->close,
95                   ob_rr_theme->button_size, ob_rr_theme->button_size);
96     XResizeWindow(ob_display, self->desk,
97                   ob_rr_theme->button_size, ob_rr_theme->button_size);
98     XResizeWindow(ob_display, self->shade,
99                   ob_rr_theme->button_size, ob_rr_theme->button_size);
100     XResizeWindow(ob_display, self->lgrip,
101                   ob_rr_theme->grip_width, ob_rr_theme->handle_height);
102     XResizeWindow(ob_display, self->rgrip,
103                   ob_rr_theme->grip_width, ob_rr_theme->handle_height);
104
105     /* set up the dynamic appearances */
106     self->a_unfocused_title = RrAppearanceCopy(ob_rr_theme->a_unfocused_title);
107     self->a_focused_title = RrAppearanceCopy(ob_rr_theme->a_focused_title);
108     self->a_unfocused_label = RrAppearanceCopy(ob_rr_theme->a_unfocused_label);
109     self->a_focused_label = RrAppearanceCopy(ob_rr_theme->a_focused_label);
110     self->a_unfocused_handle =
111         RrAppearanceCopy(ob_rr_theme->a_unfocused_handle);
112     self->a_focused_handle = RrAppearanceCopy(ob_rr_theme->a_focused_handle);
113     self->a_icon = RrAppearanceCopy(ob_rr_theme->a_icon);
114
115     self->max_press = self->close_press = self->desk_press = 
116         self->iconify_press = self->shade_press = FALSE;
117
118     return (ObFrame*)self;
119 }
120
121 static void frame_free(ObFrame *self)
122 {
123     RrAppearanceFree(self->a_unfocused_title); 
124     RrAppearanceFree(self->a_focused_title);
125     RrAppearanceFree(self->a_unfocused_label);
126     RrAppearanceFree(self->a_focused_label);
127     RrAppearanceFree(self->a_unfocused_handle);
128     RrAppearanceFree(self->a_focused_handle);
129     RrAppearanceFree(self->a_icon);
130
131     XDestroyWindow(ob_display, self->window);
132
133     g_free(self);
134 }
135
136 void frame_show(ObFrame *self)
137 {
138     if (!self->visible) {
139         self->visible = TRUE;
140         XMapWindow(ob_display, self->window);
141     }
142 }
143
144 void frame_hide(ObFrame *self)
145 {
146     if (self->visible) {
147         self->visible = FALSE;
148         self->client->ignore_unmaps++;
149         XUnmapWindow(ob_display, self->window);
150     }
151 }
152
153 void frame_adjust_shape(ObFrame *self)
154 {
155 #ifdef SHAPE
156     int num;
157     XRectangle xrect[2];
158
159     if (!self->client->shaped) {
160         /* clear the shape on the frame window */
161         XShapeCombineMask(ob_display, self->window, ShapeBounding,
162                           self->innersize.left,
163                           self->innersize.top,
164                           None, ShapeSet);
165     } else {
166         /* make the frame's shape match the clients */
167         XShapeCombineShape(ob_display, self->window, ShapeBounding,
168                            self->innersize.left,
169                            self->innersize.top,
170                            self->client->window,
171                            ShapeBounding, ShapeSet);
172
173         num = 0;
174         if (self->decorations & Decor_Titlebar) {
175             xrect[0].x = -ob_rr_theme->bevel;
176             xrect[0].y = -ob_rr_theme->bevel;
177             xrect[0].width = self->width + self->bwidth * 2;
178             xrect[0].height = ob_rr_theme->title_height +
179                 self->bwidth * 2;
180             ++num;
181         }
182
183         if (self->decorations & Decor_Handle) {
184             xrect[1].x = -ob_rr_theme->bevel;
185             xrect[1].y = FRAME_HANDLE_Y(self);
186             xrect[1].width = self->width + self->bwidth * 2;
187             xrect[1].height = ob_rr_theme->handle_height +
188                 self->bwidth * 2;
189             ++num;
190         }
191
192         XShapeCombineRectangles(ob_display, self->window,
193                                 ShapeBounding, 0, 0, xrect, num,
194                                 ShapeUnion, Unsorted);
195     }
196 #endif
197 }
198
199 void frame_adjust_area(ObFrame *self, gboolean moved, gboolean resized)
200 {
201     if (resized) {
202         self->decorations = self->client->decorations;
203         if (self->decorations & Decor_Border) {
204             self->bwidth = ob_rr_theme->bwidth;
205             self->cbwidth = ob_rr_theme->cbwidth;
206         } else {
207             self->bwidth = self->cbwidth = 0;
208         }
209         STRUT_SET(self->innersize, self->cbwidth, self->cbwidth,
210                   self->cbwidth, self->cbwidth);
211         self->width = self->client->area.width + self->cbwidth * 2;
212         g_assert(self->width > 0);
213
214         /* set border widths */
215         XSetWindowBorderWidth(ob_display, self->plate,  self->cbwidth);
216         XSetWindowBorderWidth(ob_display, self->window, self->bwidth);
217         XSetWindowBorderWidth(ob_display, self->title,  self->bwidth);
218         XSetWindowBorderWidth(ob_display, self->handle, self->bwidth);
219         XSetWindowBorderWidth(ob_display, self->lgrip,  self->bwidth);
220         XSetWindowBorderWidth(ob_display, self->rgrip,  self->bwidth);
221   
222         /* position/size and map/unmap all the windows */
223
224         /* they all default off, they're turned on in layout_title */
225         self->icon_x = -1;
226         self->desk_x = -1;
227         self->shade_x = -1;
228         self->iconify_x = -1;
229         self->label_x = -1;
230         self->max_x = -1;
231         self->close_x = -1;
232
233         if (self->decorations & Decor_Titlebar) {
234             XMoveResizeWindow(ob_display, self->title,
235                               -self->bwidth, -self->bwidth,
236                               self->width, ob_rr_theme->title_height);
237             self->innersize.top += ob_rr_theme->title_height + self->bwidth;
238             XMapWindow(ob_display, self->title);
239
240             /* layout the title bar elements */
241             layout_title(self);
242         } else
243             XUnmapWindow(ob_display, self->title);
244
245         if (self->decorations & Decor_Handle) {
246             XMoveResizeWindow(ob_display, self->handle,
247                               -self->bwidth, FRAME_HANDLE_Y(self),
248                               self->width, ob_rr_theme->handle_height);
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             self->innersize.bottom += ob_rr_theme->handle_height +
255                 self->bwidth;
256             XMapWindow(ob_display, self->handle);
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 (!(self->decorations & Decor_Icon)) break;
437             if (n) { *lc = ' '; break; } /* rm duplicates */
438             n = TRUE;
439             self->label_width -= (ob_rr_theme->button_size + 2 +
440                                   ob_rr_theme->bevel + 1);
441             break;
442         case 'D':
443             if (!(self->decorations & Decor_AllDesktops)) break;
444             if (d) { *lc = ' '; break; } /* rm duplicates */
445             d = TRUE;
446             self->label_width -= (ob_rr_theme->button_size +
447                                   ob_rr_theme->bevel + 1);
448             break;
449         case 'S':
450             if (!(self->decorations & Decor_Shade)) break;
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 (!(self->decorations & Decor_Iconify)) break;
458             if (i) { *lc = ' '; break; } /* rm duplicates */
459             i = TRUE;
460             self->label_width -= (ob_rr_theme->button_size +
461                                   ob_rr_theme->bevel + 1);
462             break;
463         case 'L':
464             if (l) { *lc = ' '; break; } /* rm duplicates */
465             l = TRUE;
466             break;
467         case 'M':
468             if (!(self->decorations & Decor_Maximize)) break;
469             if (m) { *lc = ' '; break; } /* rm duplicates */
470             m = TRUE;
471             self->label_width -= (ob_rr_theme->button_size +
472                                   ob_rr_theme->bevel + 1);
473             break;
474         case 'C':
475             if (!(self->decorations & Decor_Close)) break;
476             if (c) { *lc = ' '; break; } /* rm duplicates */
477             c = TRUE;
478             self->label_width -= (ob_rr_theme->button_size +
479                                   ob_rr_theme->bevel + 1);
480             break;
481         }
482     }
483     if (self->label_width < 1) self->label_width = 1;
484
485     XResizeWindow(ob_display, self->label, self->label_width,
486                   ob_rr_theme->label_height);
487   
488     if (!n) XUnmapWindow(ob_display, self->icon);
489     if (!d) XUnmapWindow(ob_display, self->desk);
490     if (!s) XUnmapWindow(ob_display, self->shade);
491     if (!i) XUnmapWindow(ob_display, self->iconify);
492     if (!l) XUnmapWindow(ob_display, self->label);
493     if (!m) XUnmapWindow(ob_display, self->max);
494     if (!c) XUnmapWindow(ob_display, self->close);
495
496     x = ob_rr_theme->bevel + 1;
497     for (lc = ob_rr_theme->title_layout; *lc != '\0'; ++lc) {
498         switch (*lc) {
499         case 'N':
500             if (!n) break;
501             self->icon_x = x;
502             XMapWindow(ob_display, self->icon);
503             XMoveWindow(ob_display, self->icon, x, ob_rr_theme->bevel);
504             x += ob_rr_theme->button_size + 2 + ob_rr_theme->bevel + 1;
505             break;
506         case 'D':
507             if (!d) break;
508             self->desk_x = x;
509             XMapWindow(ob_display, self->desk);
510             XMoveWindow(ob_display, self->desk, x, ob_rr_theme->bevel + 1);
511             x += ob_rr_theme->button_size + ob_rr_theme->bevel + 1;
512             break;
513         case 'S':
514             if (!s) break;
515             self->shade_x = x;
516             XMapWindow(ob_display, self->shade);
517             XMoveWindow(ob_display, self->shade, x, ob_rr_theme->bevel + 1);
518             x += ob_rr_theme->button_size + ob_rr_theme->bevel + 1;
519             break;
520         case 'I':
521             if (!i) break;
522             self->iconify_x = x;
523             XMapWindow(ob_display, self->iconify);
524             XMoveWindow(ob_display, self->iconify, x, ob_rr_theme->bevel + 1);
525             x += ob_rr_theme->button_size + ob_rr_theme->bevel + 1;
526             break;
527         case 'L':
528             if (!l) break;
529             self->label_x = x;
530             XMapWindow(ob_display, self->label);
531             XMoveWindow(ob_display, self->label, x, ob_rr_theme->bevel);
532             x += self->label_width + ob_rr_theme->bevel + 1;
533             break;
534         case 'M':
535             if (!m) break;
536             self->max_x = x;
537             XMapWindow(ob_display, self->max);
538             XMoveWindow(ob_display, self->max, x, ob_rr_theme->bevel + 1);
539             x += ob_rr_theme->button_size + ob_rr_theme->bevel + 1;
540             break;
541         case 'C':
542             if (!c) break;
543             self->close_x = x;
544             XMapWindow(ob_display, self->close);
545             XMoveWindow(ob_display, self->close, x, ob_rr_theme->bevel + 1);
546             x += ob_rr_theme->button_size + ob_rr_theme->bevel + 1;
547             break;
548         }
549     }
550 }
551
552 ObFrameContext frame_context_from_string(char *name)
553 {
554     if (!g_ascii_strcasecmp("root", name))
555         return OB_FRAME_CONTEXT_ROOT;
556     else if (!g_ascii_strcasecmp("client", name))
557         return OB_FRAME_CONTEXT_CLIENT;
558     else if (!g_ascii_strcasecmp("titlebar", name))
559         return OB_FRAME_CONTEXT_TITLEBAR;
560     else if (!g_ascii_strcasecmp("handle", name))
561         return OB_FRAME_CONTEXT_HANDLE;
562     else if (!g_ascii_strcasecmp("frame", name))
563         return OB_FRAME_CONTEXT_FRAME;
564     else if (!g_ascii_strcasecmp("blcorner", name))
565         return OB_FRAME_CONTEXT_BLCORNER;
566     else if (!g_ascii_strcasecmp("brcorner", name))
567         return OB_FRAME_CONTEXT_BRCORNER;
568     else if (!g_ascii_strcasecmp("maximize", name))
569         return OB_FRAME_CONTEXT_MAXIMIZE;
570     else if (!g_ascii_strcasecmp("alldesktops", name))
571         return OB_FRAME_CONTEXT_ALLDESKTOPS;
572     else if (!g_ascii_strcasecmp("shade", name))
573         return OB_FRAME_CONTEXT_SHADE;
574     else if (!g_ascii_strcasecmp("iconify", name))
575         return OB_FRAME_CONTEXT_ICONIFY;
576     else if (!g_ascii_strcasecmp("icon", name))
577         return OB_FRAME_CONTEXT_ICON;
578     else if (!g_ascii_strcasecmp("close", name))
579         return OB_FRAME_CONTEXT_CLOSE;
580     return OB_FRAME_CONTEXT_NONE;
581 }
582
583 ObFrameContext frame_context(ObClient *client, Window win)
584 {
585     ObFrame *self;
586
587     if (win == RootWindow(ob_display, ob_screen)) return OB_FRAME_CONTEXT_ROOT;
588     if (client == NULL) return OB_FRAME_CONTEXT_NONE;
589     if (win == client->window) return OB_FRAME_CONTEXT_CLIENT;
590
591     self = client->frame;
592     if (win == self->window) return OB_FRAME_CONTEXT_FRAME;
593     if (win == self->plate)  return OB_FRAME_CONTEXT_CLIENT;
594     if (win == self->title)  return OB_FRAME_CONTEXT_TITLEBAR;
595     if (win == self->label)  return OB_FRAME_CONTEXT_TITLEBAR;
596     if (win == self->handle) return OB_FRAME_CONTEXT_HANDLE;
597     if (win == self->lgrip)  return OB_FRAME_CONTEXT_BLCORNER;
598     if (win == self->rgrip)  return OB_FRAME_CONTEXT_BRCORNER;
599     if (win == self->max)    return OB_FRAME_CONTEXT_MAXIMIZE;
600     if (win == self->iconify)return OB_FRAME_CONTEXT_ICONIFY;
601     if (win == self->close)  return OB_FRAME_CONTEXT_CLOSE;
602     if (win == self->icon)   return OB_FRAME_CONTEXT_ICON;
603     if (win == self->desk)   return OB_FRAME_CONTEXT_ALLDESKTOPS;
604     if (win == self->shade)  return OB_FRAME_CONTEXT_SHADE;
605
606     return OB_FRAME_CONTEXT_NONE;
607 }
608
609 void frame_client_gravity(ObFrame *self, int *x, int *y)
610 {
611     /* horizontal */
612     switch (self->client->gravity) {
613     default:
614     case NorthWestGravity:
615     case SouthWestGravity:
616     case WestGravity:
617         break;
618
619     case NorthGravity:
620     case SouthGravity:
621     case CenterGravity:
622         *x -= (self->size.left + self->size.right) / 2;
623         break;
624
625     case NorthEastGravity:
626     case SouthEastGravity:
627     case EastGravity:
628         *x -= self->size.left + self->size.right;
629         break;
630
631     case ForgetGravity:
632     case StaticGravity:
633         *x -= self->size.left;
634         break;
635     }
636
637     /* vertical */
638     switch (self->client->gravity) {
639     default:
640     case NorthWestGravity:
641     case NorthEastGravity:
642     case NorthGravity:
643         break;
644
645     case CenterGravity:
646     case EastGravity:
647     case WestGravity:
648         *y -= (self->size.top + self->size.bottom) / 2;
649         break;
650
651     case SouthWestGravity:
652     case SouthEastGravity:
653     case SouthGravity:
654         *y -= self->size.top + self->size.bottom;
655         break;
656
657     case ForgetGravity:
658     case StaticGravity:
659         *y -= self->size.top;
660         break;
661     }
662 }
663
664 void frame_frame_gravity(ObFrame *self, int *x, int *y)
665 {
666     /* horizontal */
667     switch (self->client->gravity) {
668     default:
669     case NorthWestGravity:
670     case WestGravity:
671     case SouthWestGravity:
672         break;
673     case NorthGravity:
674     case CenterGravity:
675     case SouthGravity:
676         *x += (self->size.left + self->size.right) / 2;
677         break;
678     case NorthEastGravity:
679     case EastGravity:
680     case SouthEastGravity:
681         *x += self->size.left + self->size.right;
682         break;
683     case StaticGravity:
684     case ForgetGravity:
685         *x += self->size.left;
686         break;
687     }
688
689     /* vertical */
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         *y += (self->size.top + self->size.bottom) / 2;
700         break;
701     case NorthEastGravity:
702     case EastGravity:
703     case SouthEastGravity:
704         *y += self->size.top + self->size.bottom;
705         break;
706     case StaticGravity:
707     case ForgetGravity:
708         *y += self->size.top;
709         break;
710     }
711 }