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