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