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