better handling of maximizing, wrt changing decorations on the windows, and showing...
[dana/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         self->max_horz = self->client->max_horz;
217
218         if (self->decorations & OB_FRAME_DECOR_BORDER) {
219             self->bwidth = ob_rr_theme->bwidth;
220             self->cbwidth_x = self->cbwidth_y = ob_rr_theme->cbwidth;
221         } else {
222             self->bwidth = self->cbwidth_x = self->cbwidth_y = 0;
223         }
224         self->rbwidth = self->bwidth;
225
226         if (self->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->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("desktop", name))
605         return OB_FRAME_CONTEXT_DESKTOP;
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))
642         return OB_FRAME_CONTEXT_DESKTOP;
643     if (client == NULL) return OB_FRAME_CONTEXT_NONE;
644     if (win == client->window) {
645         /* conceptually, this is the desktop, as far as users are
646            concerned */
647         if (client->type == OB_CLIENT_TYPE_DESKTOP)
648             return OB_FRAME_CONTEXT_DESKTOP;
649         return OB_FRAME_CONTEXT_CLIENT;
650     }
651
652     self = client->frame;
653     if (win == self->plate) {
654         /* conceptually, this is the desktop, as far as users are
655            concerned */
656         if (client->type == OB_CLIENT_TYPE_DESKTOP)
657             return OB_FRAME_CONTEXT_DESKTOP;
658         return OB_FRAME_CONTEXT_CLIENT;
659     }
660
661     if (win == self->window)   return OB_FRAME_CONTEXT_FRAME;
662     if (win == self->title)    return OB_FRAME_CONTEXT_TITLEBAR;
663     if (win == self->label)    return OB_FRAME_CONTEXT_TITLEBAR;
664     if (win == self->handle)   return OB_FRAME_CONTEXT_HANDLE;
665     if (win == self->lgrip)    return OB_FRAME_CONTEXT_BLCORNER;
666     if (win == self->rgrip)    return OB_FRAME_CONTEXT_BRCORNER;
667     if (win == self->tlresize) return OB_FRAME_CONTEXT_TLCORNER;
668     if (win == self->trresize) return OB_FRAME_CONTEXT_TRCORNER;
669     if (win == self->max)      return OB_FRAME_CONTEXT_MAXIMIZE;
670     if (win == self->iconify)  return OB_FRAME_CONTEXT_ICONIFY;
671     if (win == self->close)    return OB_FRAME_CONTEXT_CLOSE;
672     if (win == self->icon)     return OB_FRAME_CONTEXT_ICON;
673     if (win == self->desk)     return OB_FRAME_CONTEXT_ALLDESKTOPS;
674     if (win == self->shade)    return OB_FRAME_CONTEXT_SHADE;
675
676     return OB_FRAME_CONTEXT_NONE;
677 }
678
679 void frame_client_gravity(ObFrame *self, int *x, int *y)
680 {
681     /* horizontal */
682     switch (self->client->gravity) {
683     default:
684     case NorthWestGravity:
685     case SouthWestGravity:
686     case WestGravity:
687         break;
688
689     case NorthGravity:
690     case SouthGravity:
691     case CenterGravity:
692         *x -= (self->size.left + self->size.right) / 2;
693         break;
694
695     case NorthEastGravity:
696     case SouthEastGravity:
697     case EastGravity:
698         *x -= self->size.left + self->size.right;
699         break;
700
701     case ForgetGravity:
702     case StaticGravity:
703         *x -= self->size.left;
704         break;
705     }
706
707     /* vertical */
708     switch (self->client->gravity) {
709     default:
710     case NorthWestGravity:
711     case NorthEastGravity:
712     case NorthGravity:
713         break;
714
715     case CenterGravity:
716     case EastGravity:
717     case WestGravity:
718         *y -= (self->size.top + self->size.bottom) / 2;
719         break;
720
721     case SouthWestGravity:
722     case SouthEastGravity:
723     case SouthGravity:
724         *y -= self->size.top + self->size.bottom;
725         break;
726
727     case ForgetGravity:
728     case StaticGravity:
729         *y -= self->size.top;
730         break;
731     }
732 }
733
734 void frame_frame_gravity(ObFrame *self, int *x, int *y)
735 {
736     /* horizontal */
737     switch (self->client->gravity) {
738     default:
739     case NorthWestGravity:
740     case WestGravity:
741     case SouthWestGravity:
742         break;
743     case NorthGravity:
744     case CenterGravity:
745     case SouthGravity:
746         *x += (self->size.left + self->size.right) / 2;
747         break;
748     case NorthEastGravity:
749     case EastGravity:
750     case SouthEastGravity:
751         *x += self->size.left + self->size.right;
752         break;
753     case StaticGravity:
754     case ForgetGravity:
755         *x += self->size.left;
756         break;
757     }
758
759     /* vertical */
760     switch (self->client->gravity) {
761     default:
762     case NorthWestGravity:
763     case NorthGravity:
764     case NorthEastGravity:
765         break;
766     case WestGravity:
767     case CenterGravity:
768     case EastGravity:
769         *y += (self->size.top + self->size.bottom) / 2;
770         break;
771     case SouthWestGravity:
772     case SouthGravity:
773     case SouthEastGravity:
774         *y += self->size.top + self->size.bottom;
775         break;
776     case StaticGravity:
777     case ForgetGravity:
778         *y += self->size.top;
779         break;
780     }
781 }