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