add decoration state to the frame struct. make client_configure adjust the frame...
[dana/openbox.git] / openbox / frame.c
1 #include "frame.h"
2 #include "openbox.h"
3 #include "extensions.h"
4 #include "framerender.h"
5 #include "render/theme.h"
6
7 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask)
8 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
9                          ButtonPressMask | ButtonReleaseMask)
10 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
11                            ButtonMotionMask | ExposureMask)
12
13 static void layout_title(Frame *self);
14
15 void frame_startup()
16 {
17 }
18
19 void frame_shutdown()
20 {
21 }
22
23 static Window createWindow(Window parent, unsigned long mask,
24                            XSetWindowAttributes *attrib)
25 {
26     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
27                          RrDepth(ob_rr_inst), InputOutput,
28                          RrVisual(ob_rr_inst), mask, attrib);
29                        
30 }
31
32 Frame *frame_new()
33 {
34     XSetWindowAttributes attrib;
35     unsigned long mask;
36     Frame *self;
37
38     self = g_new(Frame, 1);
39
40     self->visible = FALSE;
41     self->decorations = 0;
42
43     /* create all of the decor windows */
44     mask = CWOverrideRedirect | CWEventMask;
45     attrib.event_mask = FRAME_EVENTMASK;
46     attrib.override_redirect = TRUE;
47     self->window = createWindow(ob_root, mask, &attrib);
48
49     mask = 0;
50     self->plate = createWindow(self->window, mask, &attrib);
51
52     mask = CWEventMask;
53     attrib.event_mask = ELEMENT_EVENTMASK;
54     self->title = createWindow(self->window, mask, &attrib);
55     self->label = createWindow(self->title, mask, &attrib);
56     self->max = createWindow(self->title, mask, &attrib);
57     self->close = createWindow(self->title, mask, &attrib);
58     self->desk = createWindow(self->title, mask, &attrib);
59     self->shade = createWindow(self->title, mask, &attrib);
60     self->icon = createWindow(self->title, mask, &attrib);
61     self->iconify = createWindow(self->title, mask, &attrib);
62     self->handle = createWindow(self->window, mask, &attrib);
63     mask |= CWCursor;
64     attrib.cursor = ob_cursors.bl;
65     self->lgrip = createWindow(self->handle, mask, &attrib);
66     attrib.cursor = ob_cursors.br;
67     self->rgrip = createWindow(self->handle, mask, &attrib);
68
69     self->focused = FALSE;
70
71     /* the other stuff is shown based on decor settings */
72     XMapWindow(ob_display, self->plate);
73     XMapWindow(ob_display, self->lgrip);
74     XMapWindow(ob_display, self->rgrip);
75     XMapWindow(ob_display, self->label);
76
77     /* set colors/appearance/sizes for stuff that doesn't change */
78     XSetWindowBorder(ob_display, self->window, ob_rr_theme->b_color->pixel);
79     XSetWindowBorder(ob_display, self->label, ob_rr_theme->b_color->pixel);
80     XSetWindowBorder(ob_display, self->rgrip, ob_rr_theme->b_color->pixel);
81     XSetWindowBorder(ob_display, self->lgrip, ob_rr_theme->b_color->pixel);
82
83     XResizeWindow(ob_display, self->max,
84                   ob_rr_theme->button_size, ob_rr_theme->button_size);
85     XResizeWindow(ob_display, self->iconify,
86                   ob_rr_theme->button_size, ob_rr_theme->button_size);
87     XResizeWindow(ob_display, self->icon,
88                   ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
89     XResizeWindow(ob_display, self->close,
90                   ob_rr_theme->button_size, ob_rr_theme->button_size);
91     XResizeWindow(ob_display, self->desk,
92                   ob_rr_theme->button_size, ob_rr_theme->button_size);
93     XResizeWindow(ob_display, self->shade,
94                   ob_rr_theme->button_size, ob_rr_theme->button_size);
95     XResizeWindow(ob_display, self->lgrip,
96                   ob_rr_theme->grip_width, ob_rr_theme->handle_height);
97     XResizeWindow(ob_display, self->rgrip,
98                   ob_rr_theme->grip_width, ob_rr_theme->handle_height);
99
100     /* set up the dynamic appearances */
101     self->a_unfocused_title = RrAppearanceCopy(ob_rr_theme->a_unfocused_title);
102     self->a_focused_title = RrAppearanceCopy(ob_rr_theme->a_focused_title);
103     self->a_unfocused_label = RrAppearanceCopy(ob_rr_theme->a_unfocused_label);
104     self->a_focused_label = RrAppearanceCopy(ob_rr_theme->a_focused_label);
105     self->a_unfocused_handle =
106         RrAppearanceCopy(ob_rr_theme->a_unfocused_handle);
107     self->a_focused_handle = RrAppearanceCopy(ob_rr_theme->a_focused_handle);
108     self->a_icon = RrAppearanceCopy(ob_rr_theme->a_icon);
109
110     self->max_press = self->close_press = self->desk_press = 
111         self->iconify_press = self->shade_press = FALSE;
112
113     return (Frame*)self;
114 }
115
116 static void frame_free(Frame *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(Frame *self)
132 {
133     if (!self->visible) {
134         self->visible = TRUE;
135         XMapWindow(ob_display, self->window);
136     }
137 }
138
139 void frame_hide(Frame *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(Frame *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 & 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 & 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(Frame *self, gboolean moved, gboolean resized)
195 {
196     if (resized) {
197         self->decorations = self->client->decorations;
198         if (self->decorations & 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 & 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 & Decor_Handle) {
241             XMoveResizeWindow(ob_display, self->handle,
242                               -self->bwidth, FRAME_HANDLE_Y(self),
243                               self->width, ob_rr_theme->handle_height);
244             XMoveWindow(ob_display, self->lgrip,
245                         -self->bwidth, -self->bwidth);
246             XMoveWindow(ob_display, self->rgrip,
247                         -self->bwidth + self->width -
248                         ob_rr_theme->grip_width, -self->bwidth);
249             self->innersize.bottom += ob_rr_theme->handle_height +
250                 self->bwidth;
251             XMapWindow(ob_display, self->handle);
252
253             /* XXX make a subwindow with these dimentions?
254                ob_rr_theme->grip_width + self->bwidth, 0,
255                self->width - (ob_rr_theme->grip_width + self->bwidth) * 2,
256                ob_rr_theme->handle_height);
257             */
258         } else
259             XUnmapWindow(ob_display, self->handle);
260     }
261
262     if (resized) {
263         /* move and resize the plate */
264         XMoveResizeWindow(ob_display, self->plate,
265                           self->innersize.left - self->cbwidth,
266                           self->innersize.top - self->cbwidth,
267                           self->client->area.width,
268                           self->client->area.height);
269         /* when the client has StaticGravity, it likes to move around. */
270         XMoveWindow(ob_display, self->client->window, 0, 0);
271     }
272
273     if (resized) {
274         STRUT_SET(self->size,
275                   self->innersize.left + self->bwidth,
276                   self->innersize.top + self->bwidth,
277                   self->innersize.right + self->bwidth,
278                   self->innersize.bottom + self->bwidth);
279     }
280
281     /* shading can change without being moved or resized */
282     RECT_SET_SIZE(self->area,
283                   self->client->area.width +
284                   self->size.left + self->size.right,
285                   (self->client->shaded ? ob_rr_theme->title_height + self->bwidth*2:
286                    self->client->area.height +
287                    self->size.top + self->size.bottom));
288
289     if (moved) {
290         /* find the new coordinates, done after setting the frame.size, for
291            frame_client_gravity. */
292         self->area.x = self->client->area.x;
293         self->area.y = self->client->area.y;
294         frame_client_gravity((Frame*)self,
295                              &self->area.x, &self->area.y);
296     }
297
298     /* move and resize the top level frame.
299        shading can change without being moved or resized */
300     XMoveResizeWindow(ob_display, self->window,
301                       self->area.x, self->area.y,
302                       self->width,
303                       self->area.height - self->bwidth * 2);
304
305     if (resized) {
306         framerender_frame(self);
307
308         frame_adjust_shape(self);
309     }
310 }
311
312 void frame_adjust_state(Frame *self)
313 {
314     framerender_frame(self);
315 }
316
317 void frame_adjust_focus(Frame *self, gboolean hilite)
318 {
319     self->focused = hilite;
320     framerender_frame(self);
321 }
322
323 void frame_adjust_title(Frame *self)
324 {
325     framerender_frame(self);
326 }
327
328 void frame_adjust_icon(Frame *self)
329 {
330     framerender_frame(self);
331 }
332
333 void frame_grab_client(Frame *self, Client *client)
334 {
335     self->client = client;
336
337     /* reparent the client to the frame */
338     XReparentWindow(ob_display, client->window, self->plate, 0, 0);
339     /*
340       When reparenting the client window, it is usually not mapped yet, since
341       this occurs from a MapRequest. However, in the case where Openbox is
342       starting up, the window is already mapped, so we'll see unmap events for
343       it. There are 2 unmap events generated that we see, one with the 'event'
344       member set the root window, and one set to the client, but both get
345       handled and need to be ignored.
346     */
347     if (ob_state == State_Starting)
348         client->ignore_unmaps += 2;
349
350     /* select the event mask on the client's parent (to receive config/map
351        req's) the ButtonPress is to catch clicks on the client border */
352     XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
353
354     /* map the client so it maps when the frame does */
355     XMapWindow(ob_display, client->window);
356
357     frame_adjust_area(self, TRUE, TRUE);
358
359     /* set all the windows for the frame in the window_map */
360     g_hash_table_insert(window_map, &self->window, client);
361     g_hash_table_insert(window_map, &self->plate, client);
362     g_hash_table_insert(window_map, &self->title, client);
363     g_hash_table_insert(window_map, &self->label, client);
364     g_hash_table_insert(window_map, &self->max, client);
365     g_hash_table_insert(window_map, &self->close, client);
366     g_hash_table_insert(window_map, &self->desk, client);
367     g_hash_table_insert(window_map, &self->shade, client);
368     g_hash_table_insert(window_map, &self->icon, client);
369     g_hash_table_insert(window_map, &self->iconify, client);
370     g_hash_table_insert(window_map, &self->handle, client);
371     g_hash_table_insert(window_map, &self->lgrip, client);
372     g_hash_table_insert(window_map, &self->rgrip, client);
373 }
374
375 void frame_release_client(Frame *self, Client *client)
376 {
377     XEvent ev;
378
379     g_assert(self->client == client);
380
381     /* check if the app has already reparented its window away */
382     if (XCheckTypedWindowEvent(ob_display, client->window,
383                                ReparentNotify, &ev)) {
384         XPutBackEvent(ob_display, &ev);
385
386         /* re-map the window since the unmanaging process unmaps it */
387
388         /* XXX ... um no it doesnt it unmaps its parent, the window itself
389            retains its mapped state, no?! XXX
390            XMapWindow(ob_display, client->window); */
391     } else {
392         /* according to the ICCCM - if the client doesn't reparent itself,
393            then we will reparent the window to root for them */
394         XReparentWindow(ob_display, client->window, ob_root,
395                         client->area.x,
396                         client->area.y);
397     }
398
399     /* remove all the windows for the frame from the window_map */
400     g_hash_table_remove(window_map, &self->window);
401     g_hash_table_remove(window_map, &self->plate);
402     g_hash_table_remove(window_map, &self->title);
403     g_hash_table_remove(window_map, &self->label);
404     g_hash_table_remove(window_map, &self->max);
405     g_hash_table_remove(window_map, &self->close);
406     g_hash_table_remove(window_map, &self->desk);
407     g_hash_table_remove(window_map, &self->shade);
408     g_hash_table_remove(window_map, &self->icon);
409     g_hash_table_remove(window_map, &self->iconify);
410     g_hash_table_remove(window_map, &self->handle);
411     g_hash_table_remove(window_map, &self->lgrip);
412     g_hash_table_remove(window_map, &self->rgrip);
413
414     frame_free(self);
415 }
416
417 static void layout_title(Frame *self)
418 {
419     char *lc;
420     int x;
421     gboolean n, d, i, l, m, c, s;
422
423     n = d = i = l = m = c = s = FALSE;
424
425     /* figure out whats being shown, and the width of the label */
426     self->label_width = self->width - (ob_rr_theme->bevel + 1) * 2;
427     for (lc = ob_rr_theme->title_layout; *lc != '\0'; ++lc) {
428         switch (*lc) {
429         case 'N':
430             if (!(self->decorations & Decor_Icon)) break;
431             if (n) { *lc = ' '; break; } /* rm duplicates */
432             n = TRUE;
433             self->label_width -= (ob_rr_theme->button_size + 2 +
434                                   ob_rr_theme->bevel + 1);
435             break;
436         case 'D':
437             if (!(self->decorations & Decor_AllDesktops)) break;
438             if (d) { *lc = ' '; break; } /* rm duplicates */
439             d = TRUE;
440             self->label_width -= (ob_rr_theme->button_size +
441                                   ob_rr_theme->bevel + 1);
442             break;
443         case 'S':
444             if (!(self->decorations & Decor_Shade)) break;
445             if (s) { *lc = ' '; break; } /* rm duplicates */
446             s = TRUE;
447             self->label_width -= (ob_rr_theme->button_size +
448                                   ob_rr_theme->bevel + 1);
449             break;
450         case 'I':
451             if (!(self->decorations & Decor_Iconify)) break;
452             if (i) { *lc = ' '; break; } /* rm duplicates */
453             i = TRUE;
454             self->label_width -= (ob_rr_theme->button_size +
455                                   ob_rr_theme->bevel + 1);
456             break;
457         case 'L':
458             if (l) { *lc = ' '; break; } /* rm duplicates */
459             l = TRUE;
460             break;
461         case 'M':
462             if (!(self->decorations & Decor_Maximize)) break;
463             if (m) { *lc = ' '; break; } /* rm duplicates */
464             m = TRUE;
465             self->label_width -= (ob_rr_theme->button_size +
466                                   ob_rr_theme->bevel + 1);
467             break;
468         case 'C':
469             if (!(self->decorations & Decor_Close)) break;
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 Context frame_context_from_string(char *name)
547 {
548     if (!g_ascii_strcasecmp("root", name))
549         return Context_Root;
550     else if (!g_ascii_strcasecmp("client", name))
551         return Context_Client;
552     else if (!g_ascii_strcasecmp("titlebar", name))
553         return Context_Titlebar;
554     else if (!g_ascii_strcasecmp("handle", name))
555         return Context_Handle;
556     else if (!g_ascii_strcasecmp("frame", name))
557         return Context_Frame;
558     else if (!g_ascii_strcasecmp("blcorner", name))
559         return Context_BLCorner;
560     else if (!g_ascii_strcasecmp("tlcorner", name))
561         return Context_TLCorner;
562     else if (!g_ascii_strcasecmp("brcorner", name))
563         return Context_BRCorner;
564     else if (!g_ascii_strcasecmp("trcorner", name))
565         return Context_TRCorner;
566     else if (!g_ascii_strcasecmp("maximize", name))
567         return Context_Maximize;
568     else if (!g_ascii_strcasecmp("alldesktops", name))
569         return Context_AllDesktops;
570     else if (!g_ascii_strcasecmp("shade", name))
571         return Context_Shade;
572     else if (!g_ascii_strcasecmp("iconify", name))
573         return Context_Iconify;
574     else if (!g_ascii_strcasecmp("icon", name))
575         return Context_Icon;
576     else if (!g_ascii_strcasecmp("close", name))
577         return Context_Close;
578     return Context_None;
579 }
580
581 Context frame_context(Client *client, Window win)
582 {
583     Frame *self;
584
585     if (win == ob_root) return Context_Root;
586     if (client == NULL) return Context_None;
587     if (win == client->window) return Context_Client;
588
589     self = client->frame;
590     if (win == self->window) return Context_Frame;
591     if (win == self->plate)  return Context_Client;
592     if (win == self->title)  return Context_Titlebar;
593     if (win == self->label)  return Context_Titlebar;
594     if (win == self->handle) return Context_Handle;
595     if (win == self->lgrip)  return Context_BLCorner;
596     if (win == self->rgrip)  return Context_BRCorner;
597     if (win == self->max)    return Context_Maximize;
598     if (win == self->iconify)return Context_Iconify;
599     if (win == self->close)  return Context_Close;
600     if (win == self->icon)   return Context_Icon;
601     if (win == self->desk)   return Context_AllDesktops;
602     if (win == self->shade)  return Context_Shade;
603
604     return Context_None;
605 }
606
607 void frame_client_gravity(Frame *self, int *x, int *y)
608 {
609     /* horizontal */
610     switch (self->client->gravity) {
611     default:
612     case NorthWestGravity:
613     case SouthWestGravity:
614     case WestGravity:
615         break;
616
617     case NorthGravity:
618     case SouthGravity:
619     case CenterGravity:
620         *x -= (self->size.left + self->size.right) / 2;
621         break;
622
623     case NorthEastGravity:
624     case SouthEastGravity:
625     case EastGravity:
626         *x -= self->size.left + self->size.right;
627         break;
628
629     case ForgetGravity:
630     case StaticGravity:
631         *x -= self->size.left;
632         break;
633     }
634
635     /* vertical */
636     switch (self->client->gravity) {
637     default:
638     case NorthWestGravity:
639     case NorthEastGravity:
640     case NorthGravity:
641         break;
642
643     case CenterGravity:
644     case EastGravity:
645     case WestGravity:
646         *y -= (self->size.top + self->size.bottom) / 2;
647         break;
648
649     case SouthWestGravity:
650     case SouthEastGravity:
651     case SouthGravity:
652         *y -= self->size.top + self->size.bottom;
653         break;
654
655     case ForgetGravity:
656     case StaticGravity:
657         *y -= self->size.top;
658         break;
659     }
660 }
661
662 void frame_frame_gravity(Frame *self, int *x, int *y)
663 {
664     /* horizontal */
665     switch (self->client->gravity) {
666     default:
667     case NorthWestGravity:
668     case WestGravity:
669     case SouthWestGravity:
670         break;
671     case NorthGravity:
672     case CenterGravity:
673     case SouthGravity:
674         *x += (self->size.left + self->size.right) / 2;
675         break;
676     case NorthEastGravity:
677     case EastGravity:
678     case SouthEastGravity:
679         *x += self->size.left + self->size.right;
680         break;
681     case StaticGravity:
682     case ForgetGravity:
683         *x += self->size.left;
684         break;
685     }
686
687     /* vertical */
688     switch (self->client->gravity) {
689     default:
690     case NorthWestGravity:
691     case WestGravity:
692     case SouthWestGravity:
693         break;
694     case NorthGravity:
695     case CenterGravity:
696     case SouthGravity:
697         *y += (self->size.top + self->size.bottom) / 2;
698         break;
699     case NorthEastGravity:
700     case EastGravity:
701     case SouthEastGravity:
702         *y += self->size.top + self->size.bottom;
703         break;
704     case StaticGravity:
705     case ForgetGravity:
706         *y += self->size.top;
707         break;
708     }
709 }