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