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