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