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