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