]> Dogcows Code - chaz/openbox/blob - openbox/frame.c
Merge branch 'master' into chaz
[chaz/openbox] / 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 "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
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
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
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
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
225 static void free_theme_statics(ObFrame *self)
226 {
227 }
228
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
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
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
268 void frame_adjust_theme(ObFrame *self)
269 {
270 free_theme_statics(self);
271 set_theme_statics(self);
272 }
273
274 #ifdef SHAPE
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
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
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
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
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
963 void frame_adjust_state(ObFrame *self)
964 {
965 self->need_render = TRUE;
966 framerender_frame(self);
967 }
968
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
980 void frame_adjust_title(ObFrame *self)
981 {
982 self->need_render = TRUE;
983 framerender_frame(self);
984 }
985
986 void frame_adjust_icon(ObFrame *self)
987 {
988 self->need_render = TRUE;
989 framerender_frame(self);
990 }
991
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
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
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? */
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
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
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
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
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
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
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
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
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
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
1663 static void flash_done(gpointer data)
1664 {
1665 ObFrame *self = data;
1666
1667 if (self->focused != self->flash_on)
1668 frame_adjust_focus(self, self->focused);
1669 }
1670
1671 static gboolean flash_timeout(gpointer data)
1672 {
1673 ObFrame *self = data;
1674 GTimeVal now;
1675
1676 if (config_frame_flash_duration != 0) {
1677 g_get_current_time(&now);
1678 if (now.tv_sec > self->flash_end.tv_sec ||
1679 (now.tv_sec == self->flash_end.tv_sec &&
1680 now.tv_usec >= self->flash_end.tv_usec))
1681 self->flashing = FALSE;
1682 }
1683
1684 if (!self->flashing)
1685 return FALSE; /* we are done */
1686
1687 self->flash_on = !self->flash_on;
1688 if (!self->focused) {
1689 frame_adjust_focus(self, self->flash_on);
1690 self->focused = FALSE;
1691 }
1692
1693 return TRUE; /* go again */
1694 }
1695
1696 void frame_flash_start(ObFrame *self)
1697 {
1698 if (config_frame_flash_delay == 0) return;
1699
1700 self->flash_on = self->focused;
1701
1702 if (!self->flashing)
1703 self->flash_timer = g_timeout_add_full(G_PRIORITY_DEFAULT,
1704 config_frame_flash_delay, flash_timeout, self,
1705 flash_done);
1706
1707 if (config_frame_flash_duration != 0) {
1708 g_get_current_time(&self->flash_end);
1709 g_time_val_add(&self->flash_end, 1000 * config_frame_flash_duration);
1710 }
1711
1712 self->flashing = TRUE;
1713 }
1714
1715 void frame_flash_stop(ObFrame *self)
1716 {
1717 self->flashing = FALSE;
1718 }
1719
1720 static gulong frame_animate_iconify_time_left(ObFrame *self,
1721 const GTimeVal *now)
1722 {
1723 glong sec, usec;
1724 sec = self->iconify_animation_end.tv_sec - now->tv_sec;
1725 usec = self->iconify_animation_end.tv_usec - now->tv_usec;
1726 if (usec < 0) {
1727 usec += G_USEC_PER_SEC;
1728 sec--;
1729 }
1730 /* no negative values */
1731 return MAX(sec * G_USEC_PER_SEC + usec, 0);
1732 }
1733
1734 static gboolean frame_animate_iconify(gpointer p)
1735 {
1736 ObFrame *self = p;
1737 gint x, y, w, h;
1738 gint iconx, icony, iconw;
1739 GTimeVal now;
1740 gulong time;
1741 gboolean iconifying;
1742
1743 if (self->client->icon_geometry.width == 0) {
1744 /* there is no icon geometry set so just go straight down */
1745 const Rect *a;
1746
1747 a = screen_physical_area_monitor(screen_find_monitor(&self->area));
1748 iconx = self->area.x + self->area.width / 2 + 32;
1749 icony = a->y + a->width;
1750 iconw = 64;
1751 } else {
1752 iconx = self->client->icon_geometry.x;
1753 icony = self->client->icon_geometry.y;
1754 iconw = self->client->icon_geometry.width;
1755 }
1756
1757 iconifying = self->iconify_animation_going > 0;
1758
1759 /* how far do we have left to go ? */
1760 g_get_current_time(&now);
1761 time = frame_animate_iconify_time_left(self, &now);
1762
1763 if ((time > 0 && iconifying) || (time == 0 && !iconifying)) {
1764 /* start where the frame is supposed to be */
1765 x = self->area.x;
1766 y = self->area.y;
1767 w = self->area.width;
1768 h = self->area.height;
1769 } else {
1770 /* start at the icon */
1771 x = iconx;
1772 y = icony;
1773 w = iconw;
1774 h = self->size.top; /* just the titlebar */
1775 }
1776
1777 if (time > 0) {
1778 glong dx, dy, dw;
1779 glong elapsed;
1780
1781 dx = self->area.x - iconx;
1782 dy = self->area.y - icony;
1783 dw = self->area.width - self->bwidth * 2 - iconw;
1784 /* if restoring, we move in the opposite direction */
1785 if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; }
1786
1787 elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
1788 x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1789 y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1790 w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1791 h = self->size.top; /* just the titlebar */
1792 }
1793
1794 XMoveResizeWindow(obt_display, self->window, x, y, w, h);
1795 XFlush(obt_display);
1796
1797 if (time == 0)
1798 frame_end_iconify_animation(self);
1799
1800 return time > 0; /* repeat until we're out of time */
1801 }
1802
1803 void frame_end_iconify_animation(ObFrame *self)
1804 {
1805 /* see if there is an animation going */
1806 if (self->iconify_animation_going == 0) return;
1807
1808 if (!self->visible)
1809 XUnmapWindow(obt_display, self->window);
1810 else {
1811 /* Send a ConfigureNotify when the animation is done, this fixes
1812 KDE's pager showing the window in the wrong place. since the
1813 window is mapped at a different location and is then moved, we
1814 need to send the synthetic configurenotify, since apps may have
1815 read the position when the client mapped, apparently. */
1816 client_reconfigure(self->client, TRUE);
1817 }
1818
1819 /* we're not animating any more ! */
1820 self->iconify_animation_going = 0;
1821
1822 XMoveResizeWindow(obt_display, self->window,
1823 self->area.x, self->area.y,
1824 self->area.width, self->area.height);
1825 /* we delay re-rendering until after we're done animating */
1826 framerender_frame(self);
1827 XFlush(obt_display);
1828 }
1829
1830 void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying)
1831 {
1832 gulong time;
1833 gboolean new_anim = FALSE;
1834 gboolean set_end = TRUE;
1835 GTimeVal now;
1836
1837 /* if there is no titlebar, just don't animate for now
1838 XXX it would be nice tho.. */
1839 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1840 return;
1841
1842 /* get the current time */
1843 g_get_current_time(&now);
1844
1845 /* get how long until the end */
1846 time = FRAME_ANIMATE_ICONIFY_TIME;
1847 if (self->iconify_animation_going) {
1848 if (!!iconifying != (self->iconify_animation_going > 0)) {
1849 /* animation was already going on in the opposite direction */
1850 time = time - frame_animate_iconify_time_left(self, &now);
1851 } else
1852 /* animation was already going in the same direction */
1853 set_end = FALSE;
1854 } else
1855 new_anim = TRUE;
1856 self->iconify_animation_going = iconifying ? 1 : -1;
1857
1858 /* set the ending time */
1859 if (set_end) {
1860 self->iconify_animation_end.tv_sec = now.tv_sec;
1861 self->iconify_animation_end.tv_usec = now.tv_usec;
1862 g_time_val_add(&self->iconify_animation_end, time);
1863 }
1864
1865 if (new_anim) {
1866 if (self->iconify_animation_timer)
1867 g_source_remove(self->iconify_animation_timer);
1868 self->iconify_animation_timer =
1869 g_timeout_add_full(G_PRIORITY_DEFAULT,
1870 FRAME_ANIMATE_ICONIFY_STEP_TIME,
1871 frame_animate_iconify, self, NULL);
1872
1873
1874 /* do the first step */
1875 frame_animate_iconify(self);
1876
1877 /* show it during the animation even if it is not "visible" */
1878 if (!self->visible)
1879 XMapWindow(obt_display, self->window);
1880 }
1881 }
This page took 0.126292 seconds and 4 git commands to generate.