]> icculus.org git repositories - dana/openbox.git/blob - openbox/frame.c
position the grips
[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 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
9                          ButtonPressMask | ButtonReleaseMask | ExposureMask)
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                          RrInstanceDepth(ob_render_inst), InputOutput,
28                          RrInstanceVisual(ob_render_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     self->s_frame = RrSurfaceNew(ob_render_inst, 0, self->window, 0);
52     self->s_title = RrSurfaceNewChild(0, self->s_frame, 0);
53     self->s_label = RrSurfaceNewChild(0, self->s_title, 0);
54     self->s_max = RrSurfaceNewChild(0, self->s_title, 0);
55     self->s_close = RrSurfaceNewChild(0, self->s_title, 0);
56     self->s_desk = RrSurfaceNewChild(0, self->s_title, 0);
57     self->s_shade = RrSurfaceNewChild(0, self->s_title, 0);
58     self->s_iconify = RrSurfaceNewChild(0, self->s_title, 0);
59     self->s_icon = RrSurfaceNewChild(0, self->s_title, 0);
60     self->s_handle = RrSurfaceNewChild(0, self->s_frame, 0);
61     self->s_lgrip = RrSurfaceNewChild(0, self->s_handle, 0);
62     self->s_rgrip = RrSurfaceNewChild(0, self->s_handle, 0);
63
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     XMapWindow(ob_display, self->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_frame);
119
120     XDestroyWindow(ob_display, self->plate);
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         XMoveResizeWindow(ob_display, self->plate,
246                           self->size.left - self->cbwidth,
247                           self->size.top - self->cbwidth,
248                           self->client->area.width,
249                           self->client->area.height);
250         /* when the client has StaticGravity, it likes to move around. */
251         XMoveWindow(ob_display, self->client->window, 0, 0);
252     }
253
254     /* shading can change without being moved or resized */
255     RECT_SET_SIZE(self->area,
256                   self->client->area.width +
257                   self->size.left + self->size.right,
258                   (self->client->shaded ? RrThemeTitleHeight(ob_theme) :
259                    self->client->area.height +
260                    self->size.top + self->size.bottom));
261
262     if (moved) {
263         /* find the new coordinates, done after setting the frame.size, for
264            frame_client_gravity. */
265         self->area.x = self->client->area.x;
266         self->area.y = self->client->area.y;
267         frame_client_gravity((Frame*)self,
268                              &self->area.x, &self->area.y);
269     }
270
271     /* move and resize the top level frame.
272        shading can change without being moved or resized */
273     RrSurfaceSetArea(self->s_frame, self->area.x, self->area.y,
274                      self->area.width, self->area.height);
275
276     if (resized) {
277         framerender_frame(self);
278
279         frame_adjust_shape(self);
280     }
281 }
282
283 void frame_adjust_state(Frame *self)
284 {
285     framerender_frame(self);
286 }
287
288 void frame_adjust_focus(Frame *self, gboolean hilite)
289 {
290     self->focused = hilite;
291     framerender_frame(self);
292 }
293
294 void frame_adjust_title(Frame *self)
295 {
296     framerender_frame(self);
297 }
298
299 void frame_adjust_icon(Frame *self)
300 {
301     framerender_frame(self);
302 }
303
304 void frame_grab_client(Frame *self, Client *client)
305 {
306     self->client = client;
307
308     /* reparent the client to the frame */
309     XReparentWindow(ob_display, client->window, self->plate, 0, 0);
310     /*
311       When reparenting the client window, it is usually not mapped yet, since
312       this occurs from a MapRequest. However, in the case where Openbox is
313       starting up, the window is already mapped, so we'll see unmap events for
314       it. There are 2 unmap events generated that we see, one with the 'event'
315       member set the root window, and one set to the client, but both get
316       handled and need to be ignored.
317     */
318     if (ob_state == State_Starting)
319         client->ignore_unmaps += 2;
320
321     /* select the event mask on the client's parent (to receive config/map
322        req's) the ButtonPress is to catch clicks on the client border */
323     XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
324
325     /* map the client so it maps when the frame does */
326     XMapWindow(ob_display, client->window);
327
328     frame_adjust_area(self, TRUE, TRUE);
329
330     /* set all the windows for the frame in the window_map */
331     g_hash_table_insert(window_map, &self->window, client);
332     g_hash_table_insert(window_map, &self->plate, client);
333     g_hash_table_insert(window_map, &self->w_title, client);
334     g_hash_table_insert(window_map, &self->w_label, client);
335     g_hash_table_insert(window_map, &self->w_max, client);
336     g_hash_table_insert(window_map, &self->w_close, client);
337     g_hash_table_insert(window_map, &self->w_desk, client);
338     g_hash_table_insert(window_map, &self->w_shade, client);
339     g_hash_table_insert(window_map, &self->w_iconify, client);
340     g_hash_table_insert(window_map, &self->w_icon, client);
341     g_hash_table_insert(window_map, &self->w_handle, client);
342     g_hash_table_insert(window_map, &self->w_lgrip, client);
343     g_hash_table_insert(window_map, &self->w_rgrip, client);
344 }
345
346 void frame_release_client(Frame *self, Client *client)
347 {
348     XEvent ev;
349
350     g_assert(self->client == client);
351
352     /* check if the app has already reparented its window away */
353     if (XCheckTypedWindowEvent(ob_display, client->window,
354                                ReparentNotify, &ev)) {
355         XPutBackEvent(ob_display, &ev);
356
357         /* re-map the window since the unmanaging process unmaps it */
358
359         /* XXX ... um no it doesnt it unmaps its parent, the window itself
360            retains its mapped state, no?! XXX
361            XMapWindow(ob_display, client->window); */
362     } else {
363         /* according to the ICCCM - if the client doesn't reparent itself,
364            then we will reparent the window to root for them */
365         XReparentWindow(ob_display, client->window, ob_root,
366                         client->area.x,
367                         client->area.y);
368     }
369
370     /* remove all the windows for the frame from the window_map */
371     g_hash_table_remove(window_map, &self->window);
372     g_hash_table_remove(window_map, &self->plate);
373     g_hash_table_remove(window_map, &self->w_title);
374     g_hash_table_remove(window_map, &self->w_label);
375     g_hash_table_remove(window_map, &self->w_max);
376     g_hash_table_remove(window_map, &self->w_close);
377     g_hash_table_remove(window_map, &self->w_desk);
378     g_hash_table_remove(window_map, &self->w_shade);
379     g_hash_table_remove(window_map, &self->w_icon);
380     g_hash_table_remove(window_map, &self->w_iconify);
381     g_hash_table_remove(window_map, &self->w_handle);
382     g_hash_table_remove(window_map, &self->w_lgrip);
383     g_hash_table_remove(window_map, &self->w_rgrip);
384
385     frame_free(self);
386 }
387
388 static void layout_title(Frame *self)
389 {
390     char *lc;
391     int x;
392     gboolean n, d, i, l, m, c, s;
393
394     n = d = i = l = m = c = s = FALSE;
395
396     /* figure out whats being shown, and the width of the label */
397     self->label_width = self->width - (ob_theme->bevel + 1) * 2;
398     for (lc = ob_theme->title_layout; *lc != '\0'; ++lc) {
399         switch (*lc) {
400         case 'N':
401             if (!(self->client->decorations & Decor_Icon)) break;
402             if (n) { *lc = ' '; break; } /* rm duplicates */
403             n = TRUE;
404             self->label_width -= RrThemeButtonSize(ob_theme) + 2 +
405                 ob_theme->bevel + 1;
406             break;
407         case 'D':
408             if (!(self->client->decorations & Decor_AllDesktops)) break;
409             if (d) { *lc = ' '; break; } /* rm duplicates */
410             d = TRUE;
411             self->label_width -= RrThemeButtonSize(ob_theme) +
412                 ob_theme->bevel + 1;
413             break;
414         case 'S':
415             if (!(self->client->decorations & Decor_Shade)) break;
416             if (s) { *lc = ' '; break; } /* rm duplicates */
417             s = TRUE;
418             self->label_width -= RrThemeButtonSize(ob_theme) +
419                 ob_theme->bevel + 1;
420             break;
421         case 'I':
422             if (!(self->client->decorations & Decor_Iconify)) break;
423             if (i) { *lc = ' '; break; } /* rm duplicates */
424             i = TRUE;
425             self->label_width -= RrThemeButtonSize(ob_theme) +
426                 ob_theme->bevel + 1;
427             break;
428         case 'L':
429             if (l) { *lc = ' '; break; } /* rm duplicates */
430             l = TRUE;
431             break;
432         case 'M':
433             if (!(self->client->decorations & Decor_Maximize)) break;
434             if (m) { *lc = ' '; break; } /* rm duplicates */
435             m = TRUE;
436             self->label_width -= RrThemeButtonSize(ob_theme) +
437                 ob_theme->bevel + 1;
438             break;
439         case 'C':
440             if (!(self->client->decorations & Decor_Close)) break;
441             if (c) { *lc = ' '; break; } /* rm duplicates */
442             c = TRUE;
443             self->label_width -= RrThemeButtonSize(ob_theme) +
444                 ob_theme->bevel + 1;
445             break;
446         }
447     }
448     if (self->label_width < 1) self->label_width = 1;
449
450     if (!n) RrSurfaceHide(self->s_icon);
451     if (!d) RrSurfaceHide(self->s_desk);
452     if (!s) RrSurfaceHide(self->s_shade);
453     if (!i) RrSurfaceHide(self->s_iconify);
454     if (!l) RrSurfaceHide(self->s_label);
455     if (!m) RrSurfaceHide(self->s_max);
456     if (!c) RrSurfaceHide(self->s_close);
457
458
459     x = ob_theme->bevel + 1;
460     for (lc = ob_theme->title_layout; *lc != '\0'; ++lc) {
461         switch (*lc) {
462         case 'N':
463             if (!n) break;
464             self->icon_x = x;
465             RrSurfaceSetArea(self->s_icon, x, ob_theme->bevel,
466                              RrThemeButtonSize(ob_theme) + 2,
467                              RrThemeButtonSize(ob_theme) + 2);
468             RrSurfaceShow(self->s_icon);
469             x += RrThemeButtonSize(ob_theme) + 2 + ob_theme->bevel + 1;
470             break;
471         case 'D':
472             if (!d) break;
473             self->desk_x = x;
474             RrSurfaceSetArea(self->s_desk, x, ob_theme->bevel + 1,
475                              RrThemeButtonSize(ob_theme),
476                              RrThemeButtonSize(ob_theme));
477             RrSurfaceShow(self->s_desk);
478             x += RrThemeButtonSize(ob_theme) + ob_theme->bevel + 1;
479             break;
480         case 'S':
481             if (!s) break;
482             self->shade_x = x;
483             RrSurfaceSetArea(self->s_shade, x, ob_theme->bevel + 1,
484                              RrThemeButtonSize(ob_theme),
485                              RrThemeButtonSize(ob_theme));
486             RrSurfaceShow(self->s_shade);
487             x += RrThemeButtonSize(ob_theme) + ob_theme->bevel + 1;
488             break;
489         case 'I':
490             if (!i) break;
491             self->iconify_x = x;
492             RrSurfaceSetArea(self->s_iconify, x, ob_theme->bevel + 1,
493                              RrThemeButtonSize(ob_theme),
494                              RrThemeButtonSize(ob_theme));
495             RrSurfaceShow(self->s_iconify);
496             x += RrThemeButtonSize(ob_theme) + ob_theme->bevel + 1;
497             break;
498         case 'L':
499             if (!l) break;
500             self->label_x = x;
501             RrSurfaceSetArea(self->s_label, x, ob_theme->bevel,
502                              self->label_width, RrThemeLabelHeight(ob_theme));
503             RrSurfaceShow(self->s_label);
504             x += self->label_width + ob_theme->bevel + 1;
505             break;
506         case 'M':
507             if (!m) break;
508             self->max_x = x;
509             RrSurfaceSetArea(self->s_max, x, ob_theme->bevel + 1,
510                              RrThemeButtonSize(ob_theme),
511                              RrThemeButtonSize(ob_theme));
512             RrSurfaceShow(self->s_max);
513             x += RrThemeButtonSize(ob_theme) + ob_theme->bevel + 1;
514             break;
515         case 'C':
516             if (!c) break;
517             self->close_x = x;
518             RrSurfaceSetArea(self->s_close, x, ob_theme->bevel + 1,
519                              RrThemeButtonSize(ob_theme),
520                              RrThemeButtonSize(ob_theme));
521             RrSurfaceShow(self->s_close);
522             x += RrThemeButtonSize(ob_theme) + ob_theme->bevel + 1;
523             break;
524         }
525     }
526 }
527
528 Context frame_context_from_string(char *name)
529 {
530     if (!g_ascii_strcasecmp("root", name))
531         return Context_Root;
532     else if (!g_ascii_strcasecmp("client", name))
533         return Context_Client;
534     else if (!g_ascii_strcasecmp("titlebar", name))
535         return Context_Titlebar;
536     else if (!g_ascii_strcasecmp("handle", name))
537         return Context_Handle;
538     else if (!g_ascii_strcasecmp("frame", name))
539         return Context_Frame;
540     else if (!g_ascii_strcasecmp("blcorner", name))
541         return Context_BLCorner;
542     else if (!g_ascii_strcasecmp("tlcorner", name))
543         return Context_TLCorner;
544     else if (!g_ascii_strcasecmp("brcorner", name))
545         return Context_BRCorner;
546     else if (!g_ascii_strcasecmp("trcorner", name))
547         return Context_TRCorner;
548     else if (!g_ascii_strcasecmp("maximize", name))
549         return Context_Maximize;
550     else if (!g_ascii_strcasecmp("alldesktops", name))
551         return Context_AllDesktops;
552     else if (!g_ascii_strcasecmp("shade", name))
553         return Context_Shade;
554     else if (!g_ascii_strcasecmp("iconify", name))
555         return Context_Iconify;
556     else if (!g_ascii_strcasecmp("icon", name))
557         return Context_Icon;
558     else if (!g_ascii_strcasecmp("close", name))
559         return Context_Close;
560     return Context_None;
561 }
562
563 Context frame_context(Client *client, Window win)
564 {
565     Frame *self;
566
567     if (win == ob_root) return Context_Root;
568     if (client == NULL) return Context_None;
569     if (win == client->window) return Context_Client;
570
571     self = client->frame;
572     if (win == self->window)   return Context_Frame;
573     if (win == self->plate)    return Context_Client;
574     if (win == self->w_title)  return Context_Titlebar;
575     if (win == self->w_label)  return Context_Titlebar;
576     if (win == self->w_handle) return Context_Handle;
577     if (win == self->w_lgrip)  return Context_BLCorner;
578     if (win == self->w_rgrip)  return Context_BRCorner;
579     if (win == self->w_max)    return Context_Maximize;
580     if (win == self->w_iconify)return Context_Iconify;
581     if (win == self->w_close)  return Context_Close;
582     if (win == self->w_icon)   return Context_Icon;
583     if (win == self->w_desk)   return Context_AllDesktops;
584     if (win == self->w_shade)  return Context_Shade;
585
586     return Context_None;
587 }
588
589 void frame_client_gravity(Frame *self, int *x, int *y)
590 {
591     /* horizontal */
592     switch (self->client->gravity) {
593     default:
594     case NorthWestGravity:
595     case SouthWestGravity:
596     case WestGravity:
597         break;
598
599     case NorthGravity:
600     case SouthGravity:
601     case CenterGravity:
602         *x -= (self->size.left + self->size.right) / 2;
603         break;
604
605     case NorthEastGravity:
606     case SouthEastGravity:
607     case EastGravity:
608         *x -= self->size.left + self->size.right;
609         break;
610
611     case ForgetGravity:
612     case StaticGravity:
613         *x -= self->size.left;
614         break;
615     }
616
617     /* vertical */
618     switch (self->client->gravity) {
619     default:
620     case NorthWestGravity:
621     case NorthEastGravity:
622     case NorthGravity:
623         break;
624
625     case CenterGravity:
626     case EastGravity:
627     case WestGravity:
628         *y -= (self->size.top + self->size.bottom) / 2;
629         break;
630
631     case SouthWestGravity:
632     case SouthEastGravity:
633     case SouthGravity:
634         *y -= self->size.top + self->size.bottom;
635         break;
636
637     case ForgetGravity:
638     case StaticGravity:
639         *y -= self->size.top;
640         break;
641     }
642 }
643
644 void frame_frame_gravity(Frame *self, int *x, int *y)
645 {
646     /* horizontal */
647     switch (self->client->gravity) {
648     default:
649     case NorthWestGravity:
650     case WestGravity:
651     case SouthWestGravity:
652         break;
653     case NorthGravity:
654     case CenterGravity:
655     case SouthGravity:
656         *x += (self->size.left + self->size.right) / 2;
657         break;
658     case NorthEastGravity:
659     case EastGravity:
660     case SouthEastGravity:
661         *x += self->size.left + self->size.right;
662         break;
663     case StaticGravity:
664     case ForgetGravity:
665         *x += self->size.left;
666         break;
667     }
668
669     /* vertical */
670     switch (self->client->gravity) {
671     default:
672     case NorthWestGravity:
673     case WestGravity:
674     case SouthWestGravity:
675         break;
676     case NorthGravity:
677     case CenterGravity:
678     case SouthGravity:
679         *y += (self->size.top + self->size.bottom) / 2;
680         break;
681     case NorthEastGravity:
682     case EastGravity:
683     case SouthEastGravity:
684         *y += self->size.top + self->size.bottom;
685         break;
686     case StaticGravity:
687     case ForgetGravity:
688         *y += self->size.top;
689         break;
690     }
691 }