merge r6815 from 3.4-working (!) 'fix positioning of tlt, top and trt resize elements...
[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) 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_cycle.h"
29 #include "focus_cycle_indicator.h"
30 #include "moveresize.h"
31 #include "screen.h"
32 #include "render/theme.h"
33
34 #define PLATE_EVENTMASK (SubstructureRedirectMask | FocusChangeMask)
35 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
36                          ButtonPressMask | ButtonReleaseMask)
37 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
38                            ButtonMotionMask | PointerMotionMask | \
39                            EnterWindowMask | LeaveWindowMask)
40 /* The inner window does not need enter/leave events.
41    If it does get them, then it needs its own context for enter events
42    because sloppy focus will focus the window when you enter the inner window
43    from the frame. */
44 #define INNER_EVENTMASK (ButtonPressMask)
45
46 #define FRAME_ANIMATE_ICONIFY_TIME 150000 /* .15 seconds */
47 #define FRAME_ANIMATE_ICONIFY_STEP_TIME (G_USEC_PER_SEC / 60) /* 60 Hz */
48
49 #define FRAME_HANDLE_Y(f) (f->size.top + f->client->area.height + f->cbwidth_y)
50
51 static void flash_done(gpointer data);
52 static gboolean flash_timeout(gpointer data);
53
54 static void layout_title(ObFrame *self);
55 static void set_theme_statics(ObFrame *self);
56 static void free_theme_statics(ObFrame *self);
57 static gboolean frame_animate_iconify(gpointer self);
58 static void frame_adjust_cursors(ObFrame *self);
59
60 static Window createWindow(Window parent, Visual *visual,
61                            gulong mask, XSetWindowAttributes *attrib)
62 {
63     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
64                          (visual ? 32 : RrDepth(ob_rr_inst)), InputOutput,
65                          (visual ? visual : RrVisual(ob_rr_inst)),
66                          mask, attrib);
67                        
68 }
69
70 static Visual *check_32bit_client(ObClient *c)
71 {
72     XWindowAttributes wattrib;
73     Status ret;
74
75     /* we're already running at 32 bit depth, yay. we don't need to use their
76        visual */
77     if (RrDepth(ob_rr_inst) == 32)
78         return NULL;
79
80     ret = XGetWindowAttributes(ob_display, c->window, &wattrib);
81     g_assert(ret != BadDrawable);
82     g_assert(ret != BadWindow);
83
84     if (wattrib.depth == 32)
85         return wattrib.visual;
86     return NULL;
87 }
88
89 ObFrame *frame_new(ObClient *client)
90 {
91     XSetWindowAttributes attrib;
92     gulong mask;
93     ObFrame *self;
94     Visual *visual;
95
96     self = g_new0(ObFrame, 1);
97     self->client = client;
98
99     visual = check_32bit_client(client);
100
101     /* create the non-visible decor windows */
102
103     mask = CWEventMask;
104     if (visual) {
105         /* client has a 32-bit visual */
106         mask |= CWColormap | CWBackPixel | CWBorderPixel;
107         /* create a colormap with the visual */
108         self->colormap = attrib.colormap =
109             XCreateColormap(ob_display,
110                             RootWindow(ob_display, ob_screen),
111                             visual, AllocNone);
112         attrib.background_pixel = BlackPixel(ob_display, ob_screen);
113         attrib.border_pixel = BlackPixel(ob_display, ob_screen);
114     }
115     attrib.event_mask = FRAME_EVENTMASK;
116     self->window = createWindow(RootWindow(ob_display, ob_screen), visual,
117                                 mask, &attrib);
118
119     attrib.event_mask = INNER_EVENTMASK;
120     self->inner = createWindow(self->window, visual, mask, &attrib);
121
122     mask &= ~CWEventMask;
123     self->plate = createWindow(self->inner, visual, mask, &attrib);
124
125     /* create the visible decor windows */
126
127     mask = CWEventMask;
128     if (visual) {
129         /* client has a 32-bit visual */
130         mask |= CWColormap | CWBackPixel | CWBorderPixel;
131         attrib.colormap = RrColormap(ob_rr_inst);
132     }
133     attrib.event_mask = ELEMENT_EVENTMASK;
134     self->title = createWindow(self->window, NULL, mask, &attrib);
135     self->titleleft = createWindow(self->window, NULL, mask, &attrib);
136     self->titletop = createWindow(self->window, NULL, mask, &attrib);
137     self->titletopleft = createWindow(self->window, NULL, mask, &attrib);
138     self->titletopright = createWindow(self->window, NULL, mask, &attrib);
139     self->titleright = createWindow(self->window, NULL, mask, &attrib);
140     self->titlebottom = createWindow(self->window, NULL, mask, &attrib);
141
142     self->topresize = createWindow(self->title, NULL, mask, &attrib);
143     self->tltresize = createWindow(self->title, NULL, mask, &attrib);
144     self->tllresize = createWindow(self->title, NULL, mask, &attrib);
145     self->trtresize = createWindow(self->title, NULL, mask, &attrib);
146     self->trrresize = createWindow(self->title, NULL, mask, &attrib);
147
148     self->left = createWindow(self->window, NULL, mask, &attrib);
149     self->right = createWindow(self->window, NULL, mask, &attrib);
150
151     self->label = createWindow(self->title, NULL, mask, &attrib);
152     self->max = createWindow(self->title, NULL, mask, &attrib);
153     self->close = createWindow(self->title, NULL, mask, &attrib);
154     self->desk = createWindow(self->title, NULL, mask, &attrib);
155     self->shade = createWindow(self->title, NULL, mask, &attrib);
156     self->icon = createWindow(self->title, NULL, mask, &attrib);
157     self->iconify = createWindow(self->title, NULL, mask, &attrib);
158
159     self->handle = createWindow(self->window, NULL, mask, &attrib);
160     self->lgrip = createWindow(self->handle, NULL, mask, &attrib);
161     self->rgrip = createWindow(self->handle, NULL, mask, &attrib); 
162
163     self->handleleft = createWindow(self->handle, NULL, mask, &attrib);
164     self->handleright = createWindow(self->handle, NULL, mask, &attrib);
165
166     self->handletop = createWindow(self->window, NULL, mask, &attrib);
167     self->handlebottom = createWindow(self->window, NULL, mask, &attrib);
168     self->lgripleft = createWindow(self->window, NULL, mask, &attrib);
169     self->lgriptop = createWindow(self->window, NULL, mask, &attrib);
170     self->lgripbottom = createWindow(self->window, NULL, mask, &attrib);
171     self->rgripright = createWindow(self->window, NULL, mask, &attrib);
172     self->rgriptop = createWindow(self->window, NULL, mask, &attrib);
173     self->rgripbottom = createWindow(self->window, NULL, mask, &attrib);
174
175     self->focused = FALSE;
176
177     /* the other stuff is shown based on decor settings */
178     XMapWindow(ob_display, self->plate);
179     XMapWindow(ob_display, self->inner);
180     XMapWindow(ob_display, self->label);
181
182     self->max_press = self->close_press = self->desk_press = 
183         self->iconify_press = self->shade_press = FALSE;
184     self->max_hover = self->close_hover = self->desk_hover = 
185         self->iconify_hover = self->shade_hover = FALSE;
186
187     set_theme_statics(self);
188
189     return (ObFrame*)self;
190 }
191
192 static void set_theme_statics(ObFrame *self)
193 {
194     /* set colors/appearance/sizes for stuff that doesn't change */
195     XResizeWindow(ob_display, self->max,
196                   ob_rr_theme->button_size, ob_rr_theme->button_size);
197     XResizeWindow(ob_display, self->iconify,
198                   ob_rr_theme->button_size, ob_rr_theme->button_size);
199     XResizeWindow(ob_display, self->icon,
200                   ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
201     XResizeWindow(ob_display, self->close,
202                   ob_rr_theme->button_size, ob_rr_theme->button_size);
203     XResizeWindow(ob_display, self->desk,
204                   ob_rr_theme->button_size, ob_rr_theme->button_size);
205     XResizeWindow(ob_display, self->shade,
206                   ob_rr_theme->button_size, ob_rr_theme->button_size);
207     XResizeWindow(ob_display, self->tltresize,
208                   ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
209     XResizeWindow(ob_display, self->trtresize,
210                   ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
211     XResizeWindow(ob_display, self->tllresize,
212                   ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
213     XResizeWindow(ob_display, self->trrresize,
214                   ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
215
216     /* set up the dynamic appearances */
217     self->a_unfocused_title = RrAppearanceCopy(ob_rr_theme->a_unfocused_title);
218     self->a_focused_title = RrAppearanceCopy(ob_rr_theme->a_focused_title);
219     self->a_unfocused_label = RrAppearanceCopy(ob_rr_theme->a_unfocused_label);
220     self->a_focused_label = RrAppearanceCopy(ob_rr_theme->a_focused_label);
221     self->a_unfocused_handle =
222         RrAppearanceCopy(ob_rr_theme->a_unfocused_handle);
223     self->a_focused_handle = RrAppearanceCopy(ob_rr_theme->a_focused_handle);
224     self->a_icon = RrAppearanceCopy(ob_rr_theme->a_icon);
225 }
226
227 static void free_theme_statics(ObFrame *self)
228 {
229     RrAppearanceFree(self->a_unfocused_title); 
230     RrAppearanceFree(self->a_focused_title);
231     RrAppearanceFree(self->a_unfocused_label);
232     RrAppearanceFree(self->a_focused_label);
233     RrAppearanceFree(self->a_unfocused_handle);
234     RrAppearanceFree(self->a_focused_handle);
235     RrAppearanceFree(self->a_icon);
236 }
237
238 void frame_free(ObFrame *self)
239 {
240     free_theme_statics(self);
241
242     XDestroyWindow(ob_display, self->window);
243     if (self->colormap)
244         XFreeColormap(ob_display, self->colormap);
245
246     g_free(self);
247 }
248
249 void frame_show(ObFrame *self)
250 {
251     if (!self->visible) {
252         self->visible = TRUE;
253         XMapWindow(ob_display, self->client->window);
254         XMapWindow(ob_display, self->plate);
255         XMapWindow(ob_display, self->window);
256     }
257 }
258
259 void frame_hide(ObFrame *self)
260 {
261     if (self->visible) {
262         self->visible = FALSE;
263         if (!frame_iconify_animating(self))
264             XUnmapWindow(ob_display, self->window);
265         /* unmap the plate along with the client. some people (libwnck) look
266            to see if it is unmapped when the client is iconified, for whatever
267            reason. so let's play along... */
268         XUnmapWindow(ob_display, self->plate);
269         /* we unmap the client itself so that we can get MapRequest
270            events, and because the ICCCM tells us to! */
271         XUnmapWindow(ob_display, self->client->window);
272         self->client->ignore_unmaps += 1;
273     }
274 }
275
276 void frame_adjust_theme(ObFrame *self)
277 {
278     free_theme_statics(self);
279     set_theme_statics(self);
280 }
281
282 void frame_adjust_shape(ObFrame *self)
283 {
284 #ifdef SHAPE
285     gint num;
286     XRectangle xrect[2];
287
288     if (!self->client->shaped) {
289         /* clear the shape on the frame window */
290         XShapeCombineMask(ob_display, self->window, ShapeBounding,
291                           self->size.left,
292                           self->size.top,
293                           None, ShapeSet);
294     } else {
295         /* make the frame's shape match the clients */
296         XShapeCombineShape(ob_display, self->window, ShapeBounding,
297                            self->size.left,
298                            self->size.top,
299                            self->client->window,
300                            ShapeBounding, ShapeSet);
301
302         num = 0;
303         if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
304             xrect[0].x = 0;
305             xrect[0].y = 0;
306             xrect[0].width = self->area.width;
307             xrect[0].height = ob_rr_theme->title_height +
308                 self->bwidth + self->rbwidth;
309             ++num;
310         }
311
312         if (self->decorations & OB_FRAME_DECOR_HANDLE &&
313             ob_rr_theme->handle_height > 0)
314         {
315             xrect[1].x = 0;
316             xrect[1].y = FRAME_HANDLE_Y(self);
317             xrect[1].width = self->area.width;
318             xrect[1].height = ob_rr_theme->handle_height +
319                 self->bwidth * 2;
320             ++num;
321         }
322
323         XShapeCombineRectangles(ob_display, self->window,
324                                 ShapeBounding, 0, 0, xrect, num,
325                                 ShapeUnion, Unsorted);
326     }
327 #endif
328 }
329
330 void frame_adjust_area(ObFrame *self, gboolean moved,
331                        gboolean resized, gboolean fake)
332 {
333     Strut oldsize;
334
335     oldsize = self->size;
336
337     if (resized) {
338         /* do this before changing the frame's status like max_horz max_vert */
339         frame_adjust_cursors(self);
340
341         self->functions = self->client->functions;
342         self->decorations = self->client->decorations;
343         self->max_horz = self->client->max_horz;
344         self->max_vert = self->client->max_vert;
345
346         if (self->decorations & OB_FRAME_DECOR_BORDER) {
347             self->bwidth = ob_rr_theme->fbwidth;
348             self->cbwidth_x = ob_rr_theme->cbwidthx;
349             self->cbwidth_y = ob_rr_theme->cbwidthy;
350         } else {
351             self->bwidth = self->cbwidth_x = self->cbwidth_y = 0;
352         }
353         self->rbwidth = self->bwidth;
354
355         if (self->max_horz) {
356             self->cbwidth_x = 0;
357             self->width = self->client->area.width - self->bwidth * 2;
358         } else
359             self->width = self->client->area.width + self->cbwidth_x * 2;
360
361         STRUT_SET(self->size,
362                   self->cbwidth_x + (!self->max_horz ? self->bwidth : 0),
363                   self->cbwidth_y + self->bwidth,
364                   self->cbwidth_x + (!self->max_horz ? self->bwidth : 0),
365                   self->cbwidth_y + self->bwidth);
366
367         if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
368             self->size.top += ob_rr_theme->title_height + self->rbwidth;
369         if (self->decorations & OB_FRAME_DECOR_HANDLE &&
370             ob_rr_theme->handle_height > 0)
371         {
372             self->size.bottom += ob_rr_theme->handle_height + self->bwidth;
373         }
374   
375         /* position/size and map/unmap all the windows */
376
377         if (!fake) {
378             if (self->bwidth) {
379                 XMoveResizeWindow(ob_display, self->titletop,
380                                   ob_rr_theme->grip_width + self->bwidth, 0,
381                                   /* width + bwidth*2 - bwidth*2 - grips*2 */
382                                   self->width - ob_rr_theme->grip_width * 2,
383                                   self->bwidth);
384                 XMoveResizeWindow(ob_display, self->titletopleft,
385                                   0, 0,
386                                   ob_rr_theme->grip_width + self->bwidth,
387                                   self->bwidth);
388                 XMoveResizeWindow(ob_display, self->titletopright,
389                                   self->client->area.width +
390                                   self->size.left + self->size.right -
391                                   ob_rr_theme->grip_width - self->bwidth,
392                                   0,
393                                   ob_rr_theme->grip_width + self->bwidth,
394                                   self->bwidth);
395
396                 XMoveResizeWindow(ob_display, self->titleleft,
397                                   0, self->bwidth,
398                                   self->bwidth,
399                                   (!self->max_horz ?
400                                    ob_rr_theme->grip_width :
401                                    self->size.top - self->bwidth));
402                 XMoveResizeWindow(ob_display, self->titleright,
403                                   self->client->area.width +
404                                   self->size.left + self->size.right -
405                                   self->bwidth,
406                                   self->bwidth,
407                                   self->bwidth,
408                                   (!self->max_horz ?
409                                    ob_rr_theme->grip_width :
410                                    self->size.top - self->bwidth));
411
412                 XMapWindow(ob_display, self->titletop);
413                 XMapWindow(ob_display, self->titletopleft);
414                 XMapWindow(ob_display, self->titletopright);
415                 XMapWindow(ob_display, self->titleleft);
416                 XMapWindow(ob_display, self->titleright);
417
418                 if (self->decorations & OB_FRAME_DECOR_TITLEBAR &&
419                     self->rbwidth)
420                 {
421                     XMoveResizeWindow(ob_display, self->titlebottom,
422                                       self->bwidth,
423                                       ob_rr_theme->title_height + self->bwidth,
424                                       self->width,
425                                       self->rbwidth);
426
427                     XMapWindow(ob_display, self->titlebottom);
428                 } else
429                     XUnmapWindow(ob_display, self->titlebottom);
430             } else {
431                 XUnmapWindow(ob_display, self->titlebottom);
432
433                 XUnmapWindow(ob_display, self->titletop);
434                 XUnmapWindow(ob_display, self->titletopleft);
435                 XUnmapWindow(ob_display, self->titletopright);
436                 XUnmapWindow(ob_display, self->titleleft);
437                 XUnmapWindow(ob_display, self->titleright);
438             }
439
440             if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
441                 XMoveResizeWindow(ob_display, self->title,
442                                   self->bwidth, self->bwidth,
443                                   self->width, ob_rr_theme->title_height);
444
445                 XMapWindow(ob_display, self->title);
446
447                 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
448                     XMoveResizeWindow(ob_display, self->topresize,
449                                       ob_rr_theme->grip_width,
450                                       0,
451                                       self->width - ob_rr_theme->grip_width *2,
452                                       ob_rr_theme->paddingy + 1);
453
454                     XMoveWindow(ob_display, self->tltresize, 0, 0);
455                     XMoveWindow(ob_display, self->tllresize, 0, 0);
456                     XMoveWindow(ob_display, self->trtresize,
457                                 self->width - ob_rr_theme->grip_width, 0);
458                     XMoveWindow(ob_display, self->trrresize,
459                                 self->width - ob_rr_theme->paddingx - 1, 0);
460
461                     XMapWindow(ob_display, self->topresize);
462                     XMapWindow(ob_display, self->tltresize);
463                     XMapWindow(ob_display, self->tllresize);
464                     XMapWindow(ob_display, self->trtresize);
465                     XMapWindow(ob_display, self->trrresize);
466                 } else {
467                     XUnmapWindow(ob_display, self->topresize);
468                     XUnmapWindow(ob_display, self->tltresize);
469                     XUnmapWindow(ob_display, self->tllresize);
470                     XUnmapWindow(ob_display, self->trtresize);
471                     XUnmapWindow(ob_display, self->trrresize);
472                 }
473             } else
474                 XUnmapWindow(ob_display, self->title);
475         }
476
477         if ((self->decorations & OB_FRAME_DECOR_TITLEBAR))
478             /* layout the title bar elements */
479             layout_title(self);
480
481         if (!fake) {
482             if (self->bwidth) {
483                 XMoveResizeWindow(ob_display, self->handlebottom,
484                                   ob_rr_theme->grip_width +
485                                   self->bwidth * 2,
486                                   self->size.top + self->client->area.height +
487                                   self->size.bottom - self->bwidth,
488                                   self->width - (ob_rr_theme->grip_width +
489                                                  self->bwidth) * 2,
490                                   self->bwidth);
491
492                 XMoveResizeWindow(ob_display, self->lgripleft,
493                                   0,
494                                   self->size.top + self->client->area.height +
495                                   self->size.bottom -
496                                   (!self->max_horz ?
497                                    ob_rr_theme->grip_width :
498                                    self->size.bottom),
499                                   self->bwidth,
500                                   (!self->max_horz ?
501                                    ob_rr_theme->grip_width :
502                                    self->size.bottom));
503                 XMoveResizeWindow(ob_display, self->rgripright,
504                                   self->size.left + self->client->area.width +
505                                   self->size.right - self->bwidth,
506                                   self->size.top + self->client->area.height +
507                                   self->size.bottom -
508                                   (!self->max_horz ?
509                                    ob_rr_theme->grip_width :
510                                    self->size.bottom),
511                                   self->bwidth,
512                                   (!self->max_horz ?
513                                    ob_rr_theme->grip_width :
514                                    self->size.bottom));
515
516                 XMoveResizeWindow(ob_display, self->lgripbottom,
517                                   self->bwidth,
518                                   self->size.top + self->client->area.height +
519                                   self->size.bottom - self->bwidth,
520                                   ob_rr_theme->grip_width + self->bwidth,
521                                   self->bwidth);
522                 XMoveResizeWindow(ob_display, self->rgripbottom,
523                                   self->size.left + self->client->area.width +
524                                   self->size.right - self->bwidth * 2 -
525                                   ob_rr_theme->grip_width,
526                                   self->size.top + self->client->area.height +
527                                   self->size.bottom - self->bwidth,
528                                   ob_rr_theme->grip_width + self->bwidth,
529                                   self->bwidth);
530
531                 XMapWindow(ob_display, self->handlebottom);
532                 XMapWindow(ob_display, self->lgripleft);
533                 XMapWindow(ob_display, self->rgripright);
534                 XMapWindow(ob_display, self->lgripbottom);
535                 XMapWindow(ob_display, self->rgripbottom);
536
537                 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
538                     ob_rr_theme->handle_height > 0)
539                 {
540                     XMoveResizeWindow(ob_display, self->handletop,
541                                       ob_rr_theme->grip_width +
542                                       self->bwidth * 2,
543                                       FRAME_HANDLE_Y(self),
544                                       self->width - (ob_rr_theme->grip_width +
545                                                      self->bwidth) * 2,
546                                       self->bwidth);
547                     XMapWindow(ob_display, self->handletop);
548
549                     if (self->decorations & OB_FRAME_DECOR_GRIPS) {
550                         XMoveResizeWindow(ob_display, self->handleleft,
551                                           ob_rr_theme->grip_width,
552                                           0,
553                                           self->bwidth,
554                                           ob_rr_theme->handle_height);
555                         XMoveResizeWindow(ob_display, self->handleright,
556                                           self->width -
557                                           ob_rr_theme->grip_width -
558                                           self->bwidth,
559                                           0,
560                                           self->bwidth,
561                                           ob_rr_theme->handle_height);
562
563                         XMoveResizeWindow(ob_display, self->lgriptop,
564                                           self->bwidth,
565                                           FRAME_HANDLE_Y(self),
566                                           ob_rr_theme->grip_width +
567                                           self->bwidth,
568                                           self->bwidth);
569                         XMoveResizeWindow(ob_display, self->rgriptop,
570                                           self->size.left +
571                                           self->client->area.width +
572                                           self->size.right - self->bwidth * 2 -
573                                           ob_rr_theme->grip_width,
574                                           FRAME_HANDLE_Y(self),
575                                           ob_rr_theme->grip_width +
576                                           self->bwidth,
577                                           self->bwidth);
578
579                         XMapWindow(ob_display, self->handleleft);
580                         XMapWindow(ob_display, self->handleright);
581                         XMapWindow(ob_display, self->lgriptop);
582                         XMapWindow(ob_display, self->rgriptop);
583                     } else {
584                         XUnmapWindow(ob_display, self->handleleft);
585                         XUnmapWindow(ob_display, self->handleright);
586                         XUnmapWindow(ob_display, self->lgriptop);
587                         XUnmapWindow(ob_display, self->rgriptop);
588                     }
589                 } else {
590                     XUnmapWindow(ob_display, self->handleleft);
591                     XUnmapWindow(ob_display, self->handleright);
592                     XUnmapWindow(ob_display, self->lgriptop);
593                     XUnmapWindow(ob_display, self->rgriptop);
594
595                     XUnmapWindow(ob_display, self->handletop);
596                 }
597             } else {
598                 XUnmapWindow(ob_display, self->handleleft);
599                 XUnmapWindow(ob_display, self->handleright);
600                 XUnmapWindow(ob_display, self->lgriptop);
601                 XUnmapWindow(ob_display, self->rgriptop);
602
603                 XUnmapWindow(ob_display, self->handletop);
604
605                 XUnmapWindow(ob_display, self->handlebottom);
606                 XUnmapWindow(ob_display, self->lgripleft);
607                 XUnmapWindow(ob_display, self->rgripright);
608                 XUnmapWindow(ob_display, self->lgripbottom);
609                 XUnmapWindow(ob_display, self->rgripbottom);
610             }
611
612             if (self->decorations & OB_FRAME_DECOR_HANDLE &&
613                 ob_rr_theme->handle_height > 0)
614             {
615                 XMoveResizeWindow(ob_display, self->handle,
616                                   self->bwidth,
617                                   FRAME_HANDLE_Y(self) + self->bwidth,
618                                   self->width, ob_rr_theme->handle_height);
619                 XMapWindow(ob_display, self->handle);
620
621                 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
622                     XMoveResizeWindow(ob_display, self->lgrip,
623                                       0, 0,
624                                       ob_rr_theme->grip_width,
625                                       ob_rr_theme->handle_height);
626                     XMoveResizeWindow(ob_display, self->rgrip,
627                                       self->width - ob_rr_theme->grip_width,
628                                       0,
629                                       ob_rr_theme->grip_width,
630                                       ob_rr_theme->handle_height);
631
632                     XMapWindow(ob_display, self->lgrip);
633                     XMapWindow(ob_display, self->rgrip);
634                 } else {
635                     XUnmapWindow(ob_display, self->lgrip);
636                     XUnmapWindow(ob_display, self->rgrip);
637                 }
638             } else {
639                 XUnmapWindow(ob_display, self->lgrip);
640                 XUnmapWindow(ob_display, self->rgrip);
641
642                 XUnmapWindow(ob_display, self->handle);
643             }
644
645             if (self->bwidth && !self->max_horz) {
646                 XMoveResizeWindow(ob_display, self->left,
647                                   0,
648                                   self->bwidth + ob_rr_theme->grip_width,
649                                   self->bwidth,
650                                   self->client->area.height +
651                                   self->size.top + self->size.bottom -
652                                   ob_rr_theme->grip_width * 2);
653
654                 XMapWindow(ob_display, self->left);
655             } else
656                 XUnmapWindow(ob_display, self->left);
657
658             if (self->bwidth && !self->max_horz) {
659                 XMoveResizeWindow(ob_display, self->right,
660                                   self->client->area.width +
661                                   self->cbwidth_x * 2 + self->bwidth,
662                                   self->bwidth + ob_rr_theme->grip_width,
663                                   self->bwidth,
664                                   self->client->area.height +
665                                   self->size.top + self->size.bottom -
666                                   ob_rr_theme->grip_width * 2);
667
668                 XMapWindow(ob_display, self->right);
669             } else
670                 XUnmapWindow(ob_display, self->right);
671
672             /* move and resize the inner border window which contains the plate
673              */
674             XMoveResizeWindow(ob_display, self->inner,
675                               0,
676                               self->size.top - self->cbwidth_y,
677                               self->client->area.width +
678                               self->cbwidth_x * 2 +
679                               (!self->max_horz ? self->bwidth * 2 : 0),
680                               self->client->area.height +
681                               self->cbwidth_y * 2);
682
683             /* move the plate */
684             XMoveWindow(ob_display, self->plate,
685                         (!self->max_horz ? self->bwidth : 0) + self->cbwidth_x,
686                         self->cbwidth_y);
687
688             /* when the client has StaticGravity, it likes to move around. */
689             XMoveWindow(ob_display, self->client->window, 0, 0);
690         }
691     }
692
693     /* shading can change without being moved or resized */
694     RECT_SET_SIZE(self->area,
695                   self->client->area.width +
696                   self->size.left + self->size.right,
697                   (self->client->shaded ?
698                    ob_rr_theme->title_height + self->bwidth * 2:
699                    self->client->area.height +
700                    self->size.top + self->size.bottom));
701
702     if ((moved || resized) && !fake) {
703         /* find the new coordinates, done after setting the frame.size, for
704            frame_client_gravity. */
705         self->area.x = self->client->area.x;
706         self->area.y = self->client->area.y;
707         frame_client_gravity(self, &self->area.x, &self->area.y,
708                              self->client->area.width,
709                              self->client->area.height);
710     }
711
712     if (!fake) {
713         if (!frame_iconify_animating(self))
714             /* move and resize the top level frame.
715                shading can change without being moved or resized.
716                
717                but don't do this during an iconify animation. it will be
718                reflected afterwards.
719             */
720             XMoveResizeWindow(ob_display, self->window,
721                               self->area.x,
722                               self->area.y,
723                               self->area.width,
724                               self->area.height);
725
726         if (resized) {
727             framerender_frame(self);
728             frame_adjust_shape(self);
729         }
730
731         if (!STRUT_EQUAL(self->size, oldsize)) {
732             gulong vals[4];
733             vals[0] = self->size.left;
734             vals[1] = self->size.right;
735             vals[2] = self->size.top;
736             vals[3] = self->size.bottom;
737             PROP_SETA32(self->client->window, net_frame_extents,
738                         cardinal, vals, 4);
739             PROP_SETA32(self->client->window, kde_net_wm_frame_strut,
740                         cardinal, vals, 4);
741         }
742
743         /* if this occurs while we are focus cycling, the indicator needs to
744            match the changes */
745         if (focus_cycle_target == self->client)
746             focus_cycle_draw_indicator(self->client);
747     }
748     if (resized && (self->decorations & OB_FRAME_DECOR_TITLEBAR))
749         XResizeWindow(ob_display, self->label, self->label_width,
750                       ob_rr_theme->label_height);
751 }
752
753 static void frame_adjust_cursors(ObFrame *self)
754 {
755     if ((self->functions & OB_CLIENT_FUNC_RESIZE) !=
756         (self->client->functions & OB_CLIENT_FUNC_RESIZE) ||
757         self->max_horz != self->client->max_horz ||
758         self->max_vert != self->client->max_vert)
759     {
760         gboolean r = (self->client->functions & OB_CLIENT_FUNC_RESIZE) &&
761             !(self->client->max_horz && self->client->max_vert);
762         gboolean topbot = !self->client->max_vert;
763         XSetWindowAttributes a;
764
765         /* these ones turn off when max vert */
766         a.cursor = ob_cursor(r && topbot ? OB_CURSOR_NORTH : OB_CURSOR_NONE);
767         XChangeWindowAttributes(ob_display, self->topresize, CWCursor, &a);
768         XChangeWindowAttributes(ob_display, self->titletop, CWCursor, &a);
769         a.cursor = ob_cursor(r && topbot ? OB_CURSOR_SOUTH : OB_CURSOR_NONE);
770         XChangeWindowAttributes(ob_display, self->handle, CWCursor, &a);
771         XChangeWindowAttributes(ob_display, self->handletop, CWCursor, &a);
772         XChangeWindowAttributes(ob_display, self->handlebottom, CWCursor, &a);
773
774         /* these ones don't */
775         a.cursor = ob_cursor(r ? OB_CURSOR_NORTHWEST : OB_CURSOR_NONE);
776         XChangeWindowAttributes(ob_display, self->tltresize, CWCursor, &a);
777         XChangeWindowAttributes(ob_display, self->tllresize, CWCursor, &a);
778         XChangeWindowAttributes(ob_display, self->titletopleft, CWCursor, &a);
779         XChangeWindowAttributes(ob_display, self->titleleft, CWCursor, &a);
780         a.cursor = ob_cursor(r ? OB_CURSOR_NORTHEAST : OB_CURSOR_NONE);
781         XChangeWindowAttributes(ob_display, self->trtresize, CWCursor, &a);
782         XChangeWindowAttributes(ob_display, self->trrresize, CWCursor, &a);
783         XChangeWindowAttributes(ob_display, self->titletopright, CWCursor, &a);
784         XChangeWindowAttributes(ob_display, self->titleright, CWCursor, &a);
785         a.cursor = ob_cursor(r ? OB_CURSOR_WEST : OB_CURSOR_NONE);
786         XChangeWindowAttributes(ob_display, self->left, CWCursor, &a);
787         a.cursor = ob_cursor(r ? OB_CURSOR_EAST : OB_CURSOR_NONE);
788         XChangeWindowAttributes(ob_display, self->right, CWCursor, &a);
789         a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHWEST : OB_CURSOR_NONE);
790         XChangeWindowAttributes(ob_display, self->lgrip, CWCursor, &a);
791         XChangeWindowAttributes(ob_display, self->handleleft, CWCursor, &a);
792         XChangeWindowAttributes(ob_display, self->lgripleft, CWCursor, &a);
793         XChangeWindowAttributes(ob_display, self->lgriptop, CWCursor, &a);
794         XChangeWindowAttributes(ob_display, self->lgripbottom, CWCursor, &a);
795         a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHEAST : OB_CURSOR_NONE);
796         XChangeWindowAttributes(ob_display, self->rgrip, CWCursor, &a);
797         XChangeWindowAttributes(ob_display, self->handleright, CWCursor, &a);
798         XChangeWindowAttributes(ob_display, self->rgripright, CWCursor, &a);
799         XChangeWindowAttributes(ob_display, self->rgriptop, CWCursor, &a);
800         XChangeWindowAttributes(ob_display, self->rgripbottom, CWCursor, &a);
801     }
802 }
803
804 void frame_adjust_client_area(ObFrame *self)
805 {
806     /* resize the plate */
807     XResizeWindow(ob_display, self->plate,
808                   self->client->area.width, self->client->area.height);
809 }
810
811 void frame_adjust_state(ObFrame *self)
812 {
813     framerender_frame(self);
814 }
815
816 void frame_adjust_focus(ObFrame *self, gboolean hilite)
817 {
818     self->focused = hilite;
819     framerender_frame(self);
820     XFlush(ob_display);
821 }
822
823 void frame_adjust_title(ObFrame *self)
824 {
825     framerender_frame(self);
826 }
827
828 void frame_adjust_icon(ObFrame *self)
829 {
830     framerender_frame(self);
831 }
832
833 void frame_grab_client(ObFrame *self)
834 {
835     /* reparent the client to the frame */
836     XReparentWindow(ob_display, self->client->window, self->plate, 0, 0);
837
838     /*
839       When reparenting the client window, it is usually not mapped yet, since
840       this occurs from a MapRequest. However, in the case where Openbox is
841       starting up, the window is already mapped, so we'll see unmap events for
842       it. There are 2 unmap events generated that we see, one with the 'event'
843       member set the root window, and one set to the client, but both get
844       handled and need to be ignored.
845     */
846     if (ob_state() == OB_STATE_STARTING)
847         self->client->ignore_unmaps += 2;
848
849     /* select the event mask on the client's parent (to receive config/map
850        req's) the ButtonPress is to catch clicks on the client border */
851     XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
852
853     /* map the client so it maps when the frame does */
854     XMapWindow(ob_display, self->client->window);
855
856     /* set all the windows for the frame in the window_map */
857     g_hash_table_insert(window_map, &self->window, self->client);
858     g_hash_table_insert(window_map, &self->plate, self->client);
859     g_hash_table_insert(window_map, &self->inner, self->client);
860     g_hash_table_insert(window_map, &self->title, self->client);
861     g_hash_table_insert(window_map, &self->label, self->client);
862     g_hash_table_insert(window_map, &self->max, self->client);
863     g_hash_table_insert(window_map, &self->close, self->client);
864     g_hash_table_insert(window_map, &self->desk, self->client);
865     g_hash_table_insert(window_map, &self->shade, self->client);
866     g_hash_table_insert(window_map, &self->icon, self->client);
867     g_hash_table_insert(window_map, &self->iconify, self->client);
868     g_hash_table_insert(window_map, &self->handle, self->client);
869     g_hash_table_insert(window_map, &self->lgrip, self->client);
870     g_hash_table_insert(window_map, &self->rgrip, self->client);
871     g_hash_table_insert(window_map, &self->topresize, self->client);
872     g_hash_table_insert(window_map, &self->tltresize, self->client);
873     g_hash_table_insert(window_map, &self->tllresize, self->client);
874     g_hash_table_insert(window_map, &self->trtresize, self->client);
875     g_hash_table_insert(window_map, &self->trrresize, self->client);
876     g_hash_table_insert(window_map, &self->left, self->client);
877     g_hash_table_insert(window_map, &self->right, self->client);
878     g_hash_table_insert(window_map, &self->titleleft, self->client);
879     g_hash_table_insert(window_map, &self->titletop, self->client);
880     g_hash_table_insert(window_map, &self->titletopleft, self->client);
881     g_hash_table_insert(window_map, &self->titletopright, self->client);
882     g_hash_table_insert(window_map, &self->titleright, self->client);
883     g_hash_table_insert(window_map, &self->titlebottom, self->client);
884     g_hash_table_insert(window_map, &self->handleleft, self->client);
885     g_hash_table_insert(window_map, &self->handletop, self->client);
886     g_hash_table_insert(window_map, &self->handleright, self->client);
887     g_hash_table_insert(window_map, &self->handlebottom, self->client);
888     g_hash_table_insert(window_map, &self->lgripleft, self->client);
889     g_hash_table_insert(window_map, &self->lgriptop, self->client);
890     g_hash_table_insert(window_map, &self->lgripbottom, self->client);
891     g_hash_table_insert(window_map, &self->rgripright, self->client);
892     g_hash_table_insert(window_map, &self->rgriptop, self->client);
893     g_hash_table_insert(window_map, &self->rgripbottom, self->client);
894 }
895
896 void frame_release_client(ObFrame *self)
897 {
898     XEvent ev;
899     gboolean reparent = TRUE;
900
901     /* if there was any animation going on, kill it */
902     ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
903                                      self, FALSE);
904
905     /* check if the app has already reparented its window away */
906     while (XCheckTypedWindowEvent(ob_display, self->client->window,
907                                   ReparentNotify, &ev))
908     {
909         /* This check makes sure we don't catch our own reparent action to
910            our frame window. This doesn't count as the app reparenting itself
911            away of course.
912
913            Reparent events that are generated by us are just discarded here.
914            They are of no consequence to us anyhow.
915         */
916         if (ev.xreparent.parent != self->plate) {
917             reparent = FALSE;
918             XPutBackEvent(ob_display, &ev);
919             break;
920         }
921     }
922
923     if (reparent) {
924         /* according to the ICCCM - if the client doesn't reparent itself,
925            then we will reparent the window to root for them */
926         XReparentWindow(ob_display, self->client->window,
927                         RootWindow(ob_display, ob_screen),
928                         self->client->area.x,
929                         self->client->area.y);
930     }
931
932     /* remove all the windows for the frame from the window_map */
933     g_hash_table_remove(window_map, &self->window);
934     g_hash_table_remove(window_map, &self->plate);
935     g_hash_table_remove(window_map, &self->inner);
936     g_hash_table_remove(window_map, &self->title);
937     g_hash_table_remove(window_map, &self->label);
938     g_hash_table_remove(window_map, &self->max);
939     g_hash_table_remove(window_map, &self->close);
940     g_hash_table_remove(window_map, &self->desk);
941     g_hash_table_remove(window_map, &self->shade);
942     g_hash_table_remove(window_map, &self->icon);
943     g_hash_table_remove(window_map, &self->iconify);
944     g_hash_table_remove(window_map, &self->handle);
945     g_hash_table_remove(window_map, &self->lgrip);
946     g_hash_table_remove(window_map, &self->rgrip);
947     g_hash_table_remove(window_map, &self->topresize);
948     g_hash_table_remove(window_map, &self->tltresize);
949     g_hash_table_remove(window_map, &self->tllresize);
950     g_hash_table_remove(window_map, &self->trtresize);
951     g_hash_table_remove(window_map, &self->trrresize);
952     g_hash_table_remove(window_map, &self->left);
953     g_hash_table_remove(window_map, &self->right);
954     g_hash_table_remove(window_map, &self->titleleft);
955     g_hash_table_remove(window_map, &self->titletop);
956     g_hash_table_remove(window_map, &self->titletopleft);
957     g_hash_table_remove(window_map, &self->titletopright);
958     g_hash_table_remove(window_map, &self->titleright);
959     g_hash_table_remove(window_map, &self->titlebottom);
960     g_hash_table_remove(window_map, &self->handleleft);
961     g_hash_table_remove(window_map, &self->handletop);
962     g_hash_table_remove(window_map, &self->handleright);
963     g_hash_table_remove(window_map, &self->handlebottom);
964     g_hash_table_remove(window_map, &self->lgripleft);
965     g_hash_table_remove(window_map, &self->lgriptop);
966     g_hash_table_remove(window_map, &self->lgripbottom);
967     g_hash_table_remove(window_map, &self->rgripright);
968     g_hash_table_remove(window_map, &self->rgriptop);
969     g_hash_table_remove(window_map, &self->rgripbottom);
970
971     ob_main_loop_timeout_remove_data(ob_main_loop, flash_timeout, self, TRUE);
972 }
973
974 /* is there anything present between us and the label? */
975 static gboolean is_button_present(ObFrame *self, const gchar *lc, gint dir) {
976     for (; *lc != '\0' && lc >= config_title_layout; lc += dir) {
977         if (*lc == ' ') continue; /* it was invalid */
978         if (*lc == 'N' && self->decorations & OB_FRAME_DECOR_ICON)
979             return TRUE;
980         if (*lc == 'D' && self->decorations & OB_FRAME_DECOR_ALLDESKTOPS)
981             return TRUE;
982         if (*lc == 'S' && self->decorations & OB_FRAME_DECOR_SHADE)
983             return TRUE;
984         if (*lc == 'I' && self->decorations & OB_FRAME_DECOR_ICONIFY)
985             return TRUE;
986         if (*lc == 'M' && self->decorations & OB_FRAME_DECOR_MAXIMIZE)
987             return TRUE;
988         if (*lc == 'C' && self->decorations & OB_FRAME_DECOR_CLOSE)
989             return TRUE;
990         if (*lc == 'L') return FALSE;
991     }
992     return FALSE;
993 }
994
995 static void layout_title(ObFrame *self)
996 {
997     gchar *lc;
998     gint i;
999
1000     const gint bwidth = ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
1001     /* position of the left most button */
1002     const gint left = ob_rr_theme->paddingx + 1;
1003     /* position of the right most button */
1004     const gint right = self->width - bwidth;
1005
1006     /* turn them all off */
1007     self->icon_on = self->desk_on = self->shade_on = self->iconify_on =
1008         self->max_on = self->close_on = self->label_on = FALSE;
1009     self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2;
1010     self->leftmost = self->rightmost = OB_FRAME_CONTEXT_NONE;
1011
1012     /* figure out what's being show, find each element's position, and the
1013        width of the label
1014
1015        do the ones before the label, then after the label,
1016        i will be +1 the first time through when working to the left,
1017        and -1 the second time through when working to the right */
1018     for (i = 1; i >= -1; i-=2) {
1019         gint x;
1020         ObFrameContext *firstcon;
1021
1022         if (i > 0) {
1023             x = left;
1024             lc = config_title_layout;
1025             firstcon = &self->leftmost;
1026         } else {
1027             x = right;
1028             lc = config_title_layout + strlen(config_title_layout)-1;
1029             firstcon = &self->rightmost;
1030         }
1031
1032         /* stop at the end of the string (or the label, which calls break) */
1033         for (; *lc != '\0' && lc >= config_title_layout; lc+=i) {
1034             if (*lc == 'L') {
1035                 if (i > 0) {
1036                     self->label_on = TRUE;
1037                     self->label_x = x;
1038                 }
1039                 break; /* break the for loop, do other side of label */
1040             } else if (*lc == 'N') {
1041                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICON;
1042                 if ((self->icon_on = is_button_present(self, lc, i))) {
1043                     /* icon is bigger than buttons */
1044                     self->label_width -= bwidth + 2;
1045                     self->icon_x = x;
1046                     x += i * (bwidth + 2);
1047                 }
1048             } else if (*lc == 'D') {
1049                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ALLDESKTOPS;
1050                 if ((self->desk_on = is_button_present(self, lc, i))) {
1051                     self->label_width -= bwidth;
1052                     self->desk_x = x;
1053                     x += i * bwidth;
1054                 }
1055             } else if (*lc == 'S') {
1056                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_SHADE;
1057                 if ((self->shade_on = is_button_present(self, lc, i))) {
1058                     self->label_width -= bwidth;
1059                     self->shade_x = x;
1060                     x += i * bwidth;
1061                 }
1062             } else if (*lc == 'I') {
1063                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICONIFY;
1064                 if ((self->iconify_on = is_button_present(self, lc, i))) {
1065                     self->label_width -= bwidth;
1066                     self->iconify_x = x;
1067                     x += i * bwidth;
1068                 }
1069             } else if (*lc == 'M') {
1070                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_MAXIMIZE;
1071                 if ((self->max_on = is_button_present(self, lc, i))) {
1072                     self->label_width -= bwidth;
1073                     self->max_x = x;
1074                     x += i * bwidth;
1075                 }
1076             } else if (*lc == 'C') {
1077                 if (firstcon) *firstcon = OB_FRAME_CONTEXT_CLOSE;
1078                 if ((self->close_on = is_button_present(self, lc, i))) {
1079                     self->label_width -= bwidth;
1080                     self->close_x = x;
1081                     x += i * bwidth;
1082                 }
1083             } else
1084                 continue; /* don't set firstcon */
1085             firstcon = NULL;
1086         }
1087     }
1088
1089     /* position and map the elements */
1090     if (self->icon_on) {
1091         XMapWindow(ob_display, self->icon);
1092         XMoveWindow(ob_display, self->icon, self->icon_x,
1093                     ob_rr_theme->paddingy);
1094     } else
1095         XUnmapWindow(ob_display, self->icon);
1096
1097     if (self->desk_on) {
1098         XMapWindow(ob_display, self->desk);
1099         XMoveWindow(ob_display, self->desk, self->desk_x,
1100                     ob_rr_theme->paddingy + 1);
1101     } else
1102         XUnmapWindow(ob_display, self->desk);
1103
1104     if (self->shade_on) {
1105         XMapWindow(ob_display, self->shade);
1106         XMoveWindow(ob_display, self->shade, self->shade_x,
1107                     ob_rr_theme->paddingy + 1);
1108     } else
1109         XUnmapWindow(ob_display, self->shade);
1110
1111     if (self->iconify_on) {
1112         XMapWindow(ob_display, self->iconify);
1113         XMoveWindow(ob_display, self->iconify, self->iconify_x,
1114                     ob_rr_theme->paddingy + 1);
1115     } else
1116         XUnmapWindow(ob_display, self->iconify);
1117
1118     if (self->max_on) {
1119         XMapWindow(ob_display, self->max);
1120         XMoveWindow(ob_display, self->max, self->max_x,
1121                     ob_rr_theme->paddingy + 1);
1122     } else
1123         XUnmapWindow(ob_display, self->max);
1124
1125     if (self->close_on) {
1126         XMapWindow(ob_display, self->close);
1127         XMoveWindow(ob_display, self->close, self->close_x,
1128                     ob_rr_theme->paddingy + 1);
1129     } else
1130         XUnmapWindow(ob_display, self->close);
1131
1132     if (self->label_on) {
1133         self->label_width = MAX(1, self->label_width); /* no lower than 1 */
1134         XMapWindow(ob_display, self->label);
1135         XMoveWindow(ob_display, self->label, self->label_x,
1136                     ob_rr_theme->paddingy);
1137     } else
1138         XUnmapWindow(ob_display, self->label);
1139 }
1140
1141 ObFrameContext frame_context_from_string(const gchar *name)
1142 {
1143     if (!g_ascii_strcasecmp("Desktop", name))
1144         return OB_FRAME_CONTEXT_DESKTOP;
1145     else if (!g_ascii_strcasecmp("Root", name))
1146         return OB_FRAME_CONTEXT_ROOT;
1147     else if (!g_ascii_strcasecmp("Client", name))
1148         return OB_FRAME_CONTEXT_CLIENT;
1149     else if (!g_ascii_strcasecmp("Titlebar", name))
1150         return OB_FRAME_CONTEXT_TITLEBAR;
1151     else if (!g_ascii_strcasecmp("Frame", name))
1152         return OB_FRAME_CONTEXT_FRAME;
1153     else if (!g_ascii_strcasecmp("TLCorner", name))
1154         return OB_FRAME_CONTEXT_TLCORNER;
1155     else if (!g_ascii_strcasecmp("TRCorner", name))
1156         return OB_FRAME_CONTEXT_TRCORNER;
1157     else if (!g_ascii_strcasecmp("BLCorner", name))
1158         return OB_FRAME_CONTEXT_BLCORNER;
1159     else if (!g_ascii_strcasecmp("BRCorner", name))
1160         return OB_FRAME_CONTEXT_BRCORNER;
1161     else if (!g_ascii_strcasecmp("Top", name))
1162         return OB_FRAME_CONTEXT_TOP;
1163     else if (!g_ascii_strcasecmp("Bottom", name))
1164         return OB_FRAME_CONTEXT_BOTTOM;
1165     else if (!g_ascii_strcasecmp("Left", name))
1166         return OB_FRAME_CONTEXT_LEFT;
1167     else if (!g_ascii_strcasecmp("Right", name))
1168         return OB_FRAME_CONTEXT_RIGHT;
1169     else if (!g_ascii_strcasecmp("Maximize", name))
1170         return OB_FRAME_CONTEXT_MAXIMIZE;
1171     else if (!g_ascii_strcasecmp("AllDesktops", name))
1172         return OB_FRAME_CONTEXT_ALLDESKTOPS;
1173     else if (!g_ascii_strcasecmp("Shade", name))
1174         return OB_FRAME_CONTEXT_SHADE;
1175     else if (!g_ascii_strcasecmp("Iconify", name))
1176         return OB_FRAME_CONTEXT_ICONIFY;
1177     else if (!g_ascii_strcasecmp("Icon", name))
1178         return OB_FRAME_CONTEXT_ICON;
1179     else if (!g_ascii_strcasecmp("Close", name))
1180         return OB_FRAME_CONTEXT_CLOSE;
1181     else if (!g_ascii_strcasecmp("MoveResize", name))
1182         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1183     return OB_FRAME_CONTEXT_NONE;
1184 }
1185
1186 ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y)
1187 {
1188     ObFrame *self;
1189
1190     if (moveresize_in_progress)
1191         return OB_FRAME_CONTEXT_MOVE_RESIZE;
1192
1193     if (win == RootWindow(ob_display, ob_screen))
1194         return OB_FRAME_CONTEXT_ROOT ;
1195     if (client == NULL) return OB_FRAME_CONTEXT_NONE;
1196     if (win == client->window) {
1197         /* conceptually, this is the desktop, as far as users are
1198            concerned */
1199         if (client->type == OB_CLIENT_TYPE_DESKTOP)
1200             return OB_FRAME_CONTEXT_DESKTOP;
1201         return OB_FRAME_CONTEXT_CLIENT;
1202     }
1203
1204     self = client->frame;
1205     if (win == self->inner || win == self->plate) {
1206         /* conceptually, this is the desktop, as far as users are
1207            concerned */
1208         if (client->type == OB_CLIENT_TYPE_DESKTOP)
1209             return OB_FRAME_CONTEXT_DESKTOP;
1210         return OB_FRAME_CONTEXT_CLIENT;
1211     }
1212
1213     /* when the user clicks in the corners of the titlebar and the client
1214        is fully maximized, then treat it like they clicked in the
1215        button that is there */
1216     if (self->max_horz && self->max_vert &&
1217         (win == self->title || win == self->titletop ||
1218          win == self->titleleft || win == self->titletopleft ||
1219          win == self->titleright || win == self->titletopright))
1220     {
1221         /* get the mouse coords in reference to the whole frame */
1222         gint fx = x;
1223         gint fy = y;
1224
1225         /* these windows are down a border width from the top of the frame */
1226         if (win == self->title ||
1227             win == self->titleleft || win == self->titleright)
1228             fy += self->bwidth;
1229
1230         /* title is a border width in from the edge */
1231         if (win == self->title)
1232             fx += self->bwidth;
1233         /* titletop is a bit to the right */
1234         else if (win == self->titletop)
1235             fx += ob_rr_theme->grip_width + self->bwidth;
1236         /* titletopright is way to the right edge */
1237         else if (win == self->titletopright)
1238             fx += self->area.width - (ob_rr_theme->grip_width + self->bwidth);
1239         /* titleright is even more way to the right edge */
1240         else if (win == self->titleright)
1241             fx += self->area.width - self->bwidth;
1242
1243         /* figure out if we're over the area that should be considered a
1244            button */
1245         if (fy < self->bwidth + ob_rr_theme->paddingy + 1 +
1246             ob_rr_theme->button_size)
1247         {
1248             if (fx < (self->bwidth + ob_rr_theme->paddingx + 1 +
1249                       ob_rr_theme->button_size))
1250             {
1251                 if (self->leftmost != OB_FRAME_CONTEXT_NONE)
1252                     return self->leftmost;
1253             }
1254             else if (fx >= (self->area.width -
1255                             (self->bwidth + ob_rr_theme->paddingx + 1 +
1256                              ob_rr_theme->button_size)))
1257             {
1258                 if (self->rightmost != OB_FRAME_CONTEXT_NONE)
1259                     return self->rightmost;
1260             }
1261         }
1262
1263         /* there is no resizing maximized windows so make them the titlebar
1264            context */
1265         return OB_FRAME_CONTEXT_TITLEBAR;
1266     }
1267     else if (self->max_vert &&
1268              (win == self->titletop || win == self->topresize))
1269         /* can't resize vertically when max vert */
1270         return OB_FRAME_CONTEXT_TITLEBAR;
1271
1272     if (win == self->window)            return OB_FRAME_CONTEXT_FRAME;
1273     if (win == self->label)             return OB_FRAME_CONTEXT_TITLEBAR;
1274     if (win == self->handle)            return OB_FRAME_CONTEXT_BOTTOM;
1275     if (win == self->handletop)         return OB_FRAME_CONTEXT_BOTTOM;
1276     if (win == self->handlebottom)      return OB_FRAME_CONTEXT_BOTTOM;
1277     if (win == self->handleleft)        return OB_FRAME_CONTEXT_BLCORNER;
1278     if (win == self->lgrip)             return OB_FRAME_CONTEXT_BLCORNER;
1279     if (win == self->lgripleft)         return OB_FRAME_CONTEXT_BLCORNER;
1280     if (win == self->lgriptop)          return OB_FRAME_CONTEXT_BLCORNER;
1281     if (win == self->lgripbottom)       return OB_FRAME_CONTEXT_BLCORNER;
1282     if (win == self->handleright)       return OB_FRAME_CONTEXT_BRCORNER;
1283     if (win == self->rgrip)             return OB_FRAME_CONTEXT_BRCORNER;
1284     if (win == self->rgripright)        return OB_FRAME_CONTEXT_BLCORNER;
1285     if (win == self->rgriptop)          return OB_FRAME_CONTEXT_BLCORNER;
1286     if (win == self->rgripbottom)       return OB_FRAME_CONTEXT_BLCORNER;
1287     if (win == self->title)             return OB_FRAME_CONTEXT_TITLEBAR;
1288     if (win == self->titleleft)         return OB_FRAME_CONTEXT_TLCORNER;
1289     if (win == self->titletopleft)      return OB_FRAME_CONTEXT_TLCORNER;
1290     if (win == self->titleright)        return OB_FRAME_CONTEXT_TRCORNER;
1291     if (win == self->titletopright)     return OB_FRAME_CONTEXT_TRCORNER;
1292     if (win == self->titletop)          return OB_FRAME_CONTEXT_TOP;
1293     if (win == self->topresize)         return OB_FRAME_CONTEXT_TOP;
1294     if (win == self->tltresize)         return OB_FRAME_CONTEXT_TLCORNER;
1295     if (win == self->tllresize)         return OB_FRAME_CONTEXT_TLCORNER;
1296     if (win == self->trtresize)         return OB_FRAME_CONTEXT_TRCORNER;
1297     if (win == self->trrresize)         return OB_FRAME_CONTEXT_TRCORNER;
1298     if (win == self->left)              return OB_FRAME_CONTEXT_LEFT;
1299     if (win == self->right)             return OB_FRAME_CONTEXT_RIGHT;
1300     if (win == self->max)               return OB_FRAME_CONTEXT_MAXIMIZE;
1301     if (win == self->iconify)           return OB_FRAME_CONTEXT_ICONIFY;
1302     if (win == self->close)             return OB_FRAME_CONTEXT_CLOSE;
1303     if (win == self->icon)              return OB_FRAME_CONTEXT_ICON;
1304     if (win == self->desk)              return OB_FRAME_CONTEXT_ALLDESKTOPS;
1305     if (win == self->shade)             return OB_FRAME_CONTEXT_SHADE;
1306
1307     return OB_FRAME_CONTEXT_NONE;
1308 }
1309
1310 void frame_client_gravity(ObFrame *self, gint *x, gint *y, gint w, gint h)
1311 {
1312     /* horizontal */
1313     switch (self->client->gravity) {
1314     default:
1315     case NorthWestGravity:
1316     case SouthWestGravity:
1317     case WestGravity:
1318         break;
1319
1320     case NorthGravity:
1321     case SouthGravity:
1322     case CenterGravity:
1323         /* the middle of the client will be the middle of the frame */
1324         *x -= (self->size.right - self->size.left) / 2;
1325         break;
1326
1327     case NorthEastGravity:
1328     case SouthEastGravity:
1329     case EastGravity:
1330         /* the right side of the client will be the right side of the frame */
1331         *x -= self->size.right + self->size.left;
1332         break;
1333
1334     case ForgetGravity:
1335     case StaticGravity:
1336         /* the client's position won't move */
1337         *x -= self->size.left;
1338         break;
1339     }
1340
1341     /* vertical */
1342     switch (self->client->gravity) {
1343     default:
1344     case NorthWestGravity:
1345     case NorthEastGravity:
1346     case NorthGravity:
1347         break;
1348
1349     case CenterGravity:
1350     case EastGravity:
1351     case WestGravity:
1352         /* the middle of the client will be the middle of the frame */
1353         *y -= (self->size.bottom - self->size.top) / 2;
1354         break;
1355
1356     case SouthWestGravity:
1357     case SouthEastGravity:
1358     case SouthGravity:
1359         /* the bottom of the client will be the bottom of the frame */
1360         *y -= self->size.bottom + self->size.top;
1361         break;
1362
1363     case ForgetGravity:
1364     case StaticGravity:
1365         /* the client's position won't move */
1366         *y -= self->size.top;
1367         break;
1368     }
1369 }
1370
1371 void frame_frame_gravity(ObFrame *self, gint *x, gint *y, gint w, gint h)
1372 {
1373     /* horizontal */
1374     switch (self->client->gravity) {
1375     default:
1376     case NorthWestGravity:
1377     case WestGravity:
1378     case SouthWestGravity:
1379         break;
1380     case NorthGravity:
1381     case CenterGravity:
1382     case SouthGravity:
1383         /* the middle of the client will be the middle of the frame */
1384         *x += (self->size.right - self->size.left) / 2;
1385         break;
1386     case NorthEastGravity:
1387     case EastGravity:
1388     case SouthEastGravity:
1389         /* the right side of the client will be the right side of the frame */
1390         *x += self->size.right + self->size.left;
1391         break;
1392     case StaticGravity:
1393     case ForgetGravity:
1394         /* the client's position won't move */
1395         *x += self->size.left;
1396         break;
1397     }
1398
1399     /* vertical */
1400     switch (self->client->gravity) {
1401     default:
1402     case NorthWestGravity:
1403     case NorthGravity:
1404     case NorthEastGravity:
1405         break;
1406     case WestGravity:
1407     case CenterGravity:
1408     case EastGravity:
1409         /* the middle of the client will be the middle of the frame */
1410         *y += (self->size.bottom - self->size.top) / 2;
1411         break;
1412     case SouthWestGravity:
1413     case SouthGravity:
1414     case SouthEastGravity:
1415         /* the bottom of the client will be the bottom of the frame */
1416         *y += self->size.bottom + self->size.top;
1417         break;
1418     case StaticGravity:
1419     case ForgetGravity:
1420         /* the client's position won't move */
1421         *y += self->size.top;
1422         break;
1423     }
1424 }
1425
1426 static void flash_done(gpointer data)
1427 {
1428     ObFrame *self = data;
1429
1430     if (self->focused != self->flash_on)
1431         frame_adjust_focus(self, self->focused);
1432 }
1433
1434 static gboolean flash_timeout(gpointer data)
1435 {
1436     ObFrame *self = data;
1437     GTimeVal now;
1438
1439     g_get_current_time(&now);
1440     if (now.tv_sec > self->flash_end.tv_sec ||
1441         (now.tv_sec == self->flash_end.tv_sec &&
1442          now.tv_usec >= self->flash_end.tv_usec))
1443         self->flashing = FALSE;
1444
1445     if (!self->flashing)
1446         return FALSE; /* we are done */
1447
1448     self->flash_on = !self->flash_on;
1449     if (!self->focused) {
1450         frame_adjust_focus(self, self->flash_on);
1451         self->focused = FALSE;
1452     }
1453
1454     return TRUE; /* go again */
1455 }
1456
1457 void frame_flash_start(ObFrame *self)
1458 {
1459     self->flash_on = self->focused;
1460
1461     if (!self->flashing)
1462         ob_main_loop_timeout_add(ob_main_loop,
1463                                  G_USEC_PER_SEC * 0.6,
1464                                  flash_timeout,
1465                                  self,
1466                                  g_direct_equal,
1467                                  flash_done);
1468     g_get_current_time(&self->flash_end);
1469     g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
1470     
1471     self->flashing = TRUE;
1472 }
1473
1474 void frame_flash_stop(ObFrame *self)
1475 {
1476     self->flashing = FALSE;
1477 }
1478
1479 static gulong frame_animate_iconify_time_left(ObFrame *self,
1480                                               const GTimeVal *now)
1481 {
1482     glong sec, usec;
1483     sec = self->iconify_animation_end.tv_sec - now->tv_sec;
1484     usec = self->iconify_animation_end.tv_usec - now->tv_usec;
1485     if (usec < 0) {
1486         usec += G_USEC_PER_SEC;
1487         sec--;
1488     }
1489     /* no negative values */
1490     return MAX(sec * G_USEC_PER_SEC + usec, 0);
1491 }
1492
1493 static gboolean frame_animate_iconify(gpointer p)
1494 {
1495     ObFrame *self = p;
1496     gint x, y, w, h;
1497     gint iconx, icony, iconw;
1498     GTimeVal now;
1499     gulong time;
1500     gboolean iconifying;
1501
1502     if (self->client->icon_geometry.width == 0) {
1503         /* there is no icon geometry set so just go straight down */
1504         Rect *a = screen_physical_area();
1505         iconx = self->area.x + self->area.width / 2 + 32;
1506         icony = a->y + a->width;
1507         iconw = 64;
1508     } else {
1509         iconx = self->client->icon_geometry.x;
1510         icony = self->client->icon_geometry.y;
1511         iconw = self->client->icon_geometry.width;
1512     }
1513
1514     iconifying = self->iconify_animation_going > 0;
1515
1516     /* how far do we have left to go ? */
1517     g_get_current_time(&now);
1518     time = frame_animate_iconify_time_left(self, &now);
1519     
1520     if (time == 0 || iconifying) {
1521         /* start where the frame is supposed to be */
1522         x = self->area.x;
1523         y = self->area.y;
1524         w = self->area.width;
1525         h = self->area.height;
1526     } else {
1527         /* start at the icon */
1528         x = iconx;
1529         y = icony;
1530         w = iconw;
1531         h = self->size.top; /* just the titlebar */
1532     }
1533
1534     if (time > 0) {
1535         glong dx, dy, dw;
1536         glong elapsed;
1537
1538         dx = self->area.x - iconx;
1539         dy = self->area.y - icony;
1540         dw = self->area.width - self->bwidth * 2 - iconw;
1541          /* if restoring, we move in the opposite direction */
1542         if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; }
1543
1544         elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
1545         x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1546         y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1547         w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1548         h = self->size.top; /* just the titlebar */
1549     }
1550
1551     if (time == 0)
1552         frame_end_iconify_animation(self);
1553     else {
1554         XMoveResizeWindow(ob_display, self->window, x, y, w, h);
1555         XFlush(ob_display);
1556     }
1557
1558     return time > 0; /* repeat until we're out of time */
1559 }
1560
1561 void frame_end_iconify_animation(ObFrame *self)
1562 {
1563     /* see if there is an animation going */
1564     if (self->iconify_animation_going == 0) return;
1565
1566     if (!self->visible)
1567         XUnmapWindow(ob_display, self->window);
1568     else
1569         /* Send a ConfigureNotify when the animation is done, this fixes
1570            KDE's pager showing the window in the wrong place. */
1571         client_reconfigure(self->client);
1572
1573     /* we're not animating any more ! */
1574     self->iconify_animation_going = 0;
1575
1576     XMoveResizeWindow(ob_display, self->window,
1577                       self->area.x, self->area.y,
1578                       self->area.width, self->area.height);
1579     XFlush(ob_display);
1580 }
1581
1582 void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying)
1583 {
1584     gulong time;
1585     gboolean new_anim = FALSE;
1586     gboolean set_end = TRUE;
1587     GTimeVal now;
1588
1589     /* if there is no titlebar, just don't animate for now
1590        XXX it would be nice tho.. */
1591     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1592         return;
1593
1594     /* get the current time */
1595     g_get_current_time(&now);
1596
1597     /* get how long until the end */
1598     time = FRAME_ANIMATE_ICONIFY_TIME;
1599     if (self->iconify_animation_going) {
1600         if (!!iconifying != (self->iconify_animation_going > 0)) {
1601             /* animation was already going on in the opposite direction */
1602             time = time - frame_animate_iconify_time_left(self, &now);
1603         } else
1604             /* animation was already going in the same direction */
1605             set_end = FALSE;
1606     } else
1607         new_anim = TRUE;
1608     self->iconify_animation_going = iconifying ? 1 : -1;
1609
1610     /* set the ending time */
1611     if (set_end) {
1612         self->iconify_animation_end.tv_sec = now.tv_sec;
1613         self->iconify_animation_end.tv_usec = now.tv_usec;
1614         g_time_val_add(&self->iconify_animation_end, time);
1615     }
1616
1617     if (new_anim) {
1618         ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
1619                                          self, FALSE);
1620         ob_main_loop_timeout_add(ob_main_loop,
1621                                  FRAME_ANIMATE_ICONIFY_STEP_TIME,
1622                                  frame_animate_iconify, self,
1623                                  g_direct_equal, NULL);
1624
1625         /* do the first step */
1626         frame_animate_iconify(self);
1627
1628         /* show it during the animation even if it is not "visible" */
1629         if (!self->visible)
1630             XMapWindow(ob_display, self->window);
1631     }
1632 }