]> Dogcows Code - chaz/openbox/blob - openbox/frame.c
Add options for controlling frame flashing
[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 + self->bwidth,
382 self->cbwidth_r + (!self->max_horz ? self->bwidth : 0),
383 self->cbwidth_b +
384 (!self->max_horz || !self->max_vert ? self->bwidth : 0));
385
386 if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
387 self->size.top += ob_rr_theme->title_height + self->bwidth;
388 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
389 ob_rr_theme->handle_height > 0)
390 {
391 self->size.bottom += ob_rr_theme->handle_height + self->bwidth;
392 }
393
394 /* position/size and map/unmap all the windows */
395
396 if (!fake) {
397 gint innercornerheight =
398 ob_rr_theme->grip_width - self->size.bottom;
399
400 if (self->cbwidth_l) {
401 XMoveResizeWindow(obt_display, self->innerleft,
402 self->size.left - self->cbwidth_l,
403 self->size.top,
404 self->cbwidth_l, self->client->area.height);
405
406 XMapWindow(obt_display, self->innerleft);
407 } else
408 XUnmapWindow(obt_display, self->innerleft);
409
410 if (self->cbwidth_l && innercornerheight > 0) {
411 XMoveResizeWindow(obt_display, self->innerbll,
412 0,
413 self->client->area.height -
414 (ob_rr_theme->grip_width -
415 self->size.bottom),
416 self->cbwidth_l,
417 ob_rr_theme->grip_width - self->size.bottom);
418
419 XMapWindow(obt_display, self->innerbll);
420 } else
421 XUnmapWindow(obt_display, self->innerbll);
422
423 if (self->cbwidth_r) {
424 XMoveResizeWindow(obt_display, self->innerright,
425 self->size.left + self->client->area.width,
426 self->size.top,
427 self->cbwidth_r, self->client->area.height);
428
429 XMapWindow(obt_display, self->innerright);
430 } else
431 XUnmapWindow(obt_display, self->innerright);
432
433 if (self->cbwidth_r && innercornerheight > 0) {
434 XMoveResizeWindow(obt_display, self->innerbrr,
435 0,
436 self->client->area.height -
437 (ob_rr_theme->grip_width -
438 self->size.bottom),
439 self->cbwidth_r,
440 ob_rr_theme->grip_width - self->size.bottom);
441
442 XMapWindow(obt_display, self->innerbrr);
443 } else
444 XUnmapWindow(obt_display, self->innerbrr);
445
446 if (self->cbwidth_t) {
447 XMoveResizeWindow(obt_display, self->innertop,
448 self->size.left - self->cbwidth_l,
449 self->size.top - self->cbwidth_t,
450 self->client->area.width +
451 self->cbwidth_l + self->cbwidth_r,
452 self->cbwidth_t);
453
454 XMapWindow(obt_display, self->innertop);
455 } else
456 XUnmapWindow(obt_display, self->innertop);
457
458 if (self->cbwidth_b) {
459 XMoveResizeWindow(obt_display, self->innerbottom,
460 self->size.left - self->cbwidth_l,
461 self->size.top + self->client->area.height,
462 self->client->area.width +
463 self->cbwidth_l + self->cbwidth_r,
464 self->cbwidth_b);
465
466 XMoveResizeWindow(obt_display, self->innerblb,
467 0, 0,
468 ob_rr_theme->grip_width + self->bwidth,
469 self->cbwidth_b);
470 XMoveResizeWindow(obt_display, self->innerbrb,
471 self->client->area.width +
472 self->cbwidth_l + self->cbwidth_r -
473 (ob_rr_theme->grip_width + self->bwidth),
474 0,
475 ob_rr_theme->grip_width + self->bwidth,
476 self->cbwidth_b);
477
478 XMapWindow(obt_display, self->innerbottom);
479 XMapWindow(obt_display, self->innerblb);
480 XMapWindow(obt_display, self->innerbrb);
481 } else {
482 XUnmapWindow(obt_display, self->innerbottom);
483 XUnmapWindow(obt_display, self->innerblb);
484 XUnmapWindow(obt_display, self->innerbrb);
485 }
486
487 if (self->bwidth) {
488 gint titlesides;
489
490 /* height of titleleft and titleright */
491 titlesides = (!self->max_horz ? ob_rr_theme->grip_width : 0);
492
493 XMoveResizeWindow(obt_display, self->titletop,
494 ob_rr_theme->grip_width + self->bwidth, 0,
495 /* width + bwidth*2 - bwidth*2 - grips*2 */
496 self->width - ob_rr_theme->grip_width * 2,
497 self->bwidth);
498 XMoveResizeWindow(obt_display, self->titletopleft,
499 0, 0,
500 ob_rr_theme->grip_width + self->bwidth,
501 self->bwidth);
502 XMoveResizeWindow(obt_display, self->titletopright,
503 self->client->area.width +
504 self->size.left + self->size.right -
505 ob_rr_theme->grip_width - self->bwidth,
506 0,
507 ob_rr_theme->grip_width + self->bwidth,
508 self->bwidth);
509
510 if (titlesides > 0) {
511 XMoveResizeWindow(obt_display, self->titleleft,
512 0, self->bwidth,
513 self->bwidth,
514 titlesides);
515 XMoveResizeWindow(obt_display, self->titleright,
516 self->client->area.width +
517 self->size.left + self->size.right -
518 self->bwidth,
519 self->bwidth,
520 self->bwidth,
521 titlesides);
522
523 XMapWindow(obt_display, self->titleleft);
524 XMapWindow(obt_display, self->titleright);
525 } else {
526 XUnmapWindow(obt_display, self->titleleft);
527 XUnmapWindow(obt_display, self->titleright);
528 }
529
530 XMapWindow(obt_display, self->titletop);
531 XMapWindow(obt_display, self->titletopleft);
532 XMapWindow(obt_display, self->titletopright);
533
534 if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
535 XMoveResizeWindow(obt_display, self->titlebottom,
536 (self->max_horz ? 0 : self->bwidth),
537 ob_rr_theme->title_height + self->bwidth,
538 self->width,
539 self->bwidth);
540
541 XMapWindow(obt_display, self->titlebottom);
542 } else
543 XUnmapWindow(obt_display, self->titlebottom);
544 } else {
545 XUnmapWindow(obt_display, self->titlebottom);
546
547 XUnmapWindow(obt_display, self->titletop);
548 XUnmapWindow(obt_display, self->titletopleft);
549 XUnmapWindow(obt_display, self->titletopright);
550 XUnmapWindow(obt_display, self->titleleft);
551 XUnmapWindow(obt_display, self->titleright);
552 }
553
554 if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
555 XMoveResizeWindow(obt_display, self->title,
556 (self->max_horz ? 0 : self->bwidth),
557 self->bwidth,
558 self->width, ob_rr_theme->title_height);
559
560 XMapWindow(obt_display, self->title);
561
562 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
563 XMoveResizeWindow(obt_display, self->topresize,
564 ob_rr_theme->grip_width,
565 0,
566 self->width - ob_rr_theme->grip_width *2,
567 ob_rr_theme->paddingy + 1);
568
569 XMoveWindow(obt_display, self->tltresize, 0, 0);
570 XMoveWindow(obt_display, self->tllresize, 0, 0);
571 XMoveWindow(obt_display, self->trtresize,
572 self->width - ob_rr_theme->grip_width, 0);
573 XMoveWindow(obt_display, self->trrresize,
574 self->width - ob_rr_theme->paddingx - 1, 0);
575
576 XMapWindow(obt_display, self->topresize);
577 XMapWindow(obt_display, self->tltresize);
578 XMapWindow(obt_display, self->tllresize);
579 XMapWindow(obt_display, self->trtresize);
580 XMapWindow(obt_display, self->trrresize);
581 } else {
582 XUnmapWindow(obt_display, self->topresize);
583 XUnmapWindow(obt_display, self->tltresize);
584 XUnmapWindow(obt_display, self->tllresize);
585 XUnmapWindow(obt_display, self->trtresize);
586 XUnmapWindow(obt_display, self->trrresize);
587 }
588 } else
589 XUnmapWindow(obt_display, self->title);
590 }
591
592 if ((self->decorations & OB_FRAME_DECOR_TITLEBAR))
593 /* layout the title bar elements */
594 layout_title(self);
595
596 if (!fake) {
597 gint sidebwidth = self->max_horz ? 0 : self->bwidth;
598
599 if (self->bwidth && self->size.bottom) {
600 XMoveResizeWindow(obt_display, self->handlebottom,
601 ob_rr_theme->grip_width +
602 self->bwidth + sidebwidth,
603 self->size.top + self->client->area.height +
604 self->size.bottom - self->bwidth,
605 self->width - (ob_rr_theme->grip_width +
606 sidebwidth) * 2,
607 self->bwidth);
608
609
610 if (sidebwidth) {
611 XMoveResizeWindow(obt_display, self->lgripleft,
612 0,
613 self->size.top +
614 self->client->area.height +
615 self->size.bottom -
616 (!self->max_horz ?
617 ob_rr_theme->grip_width :
618 self->size.bottom - self->cbwidth_b),
619 self->bwidth,
620 (!self->max_horz ?
621 ob_rr_theme->grip_width :
622 self->size.bottom - self->cbwidth_b));
623 XMoveResizeWindow(obt_display, self->rgripright,
624 self->size.left +
625 self->client->area.width +
626 self->size.right - self->bwidth,
627 self->size.top +
628 self->client->area.height +
629 self->size.bottom -
630 (!self->max_horz ?
631 ob_rr_theme->grip_width :
632 self->size.bottom - self->cbwidth_b),
633 self->bwidth,
634 (!self->max_horz ?
635 ob_rr_theme->grip_width :
636 self->size.bottom - self->cbwidth_b));
637
638 XMapWindow(obt_display, self->lgripleft);
639 XMapWindow(obt_display, self->rgripright);
640 } else {
641 XUnmapWindow(obt_display, self->lgripleft);
642 XUnmapWindow(obt_display, self->rgripright);
643 }
644
645 XMoveResizeWindow(obt_display, self->lgripbottom,
646 sidebwidth,
647 self->size.top + self->client->area.height +
648 self->size.bottom - self->bwidth,
649 ob_rr_theme->grip_width + self->bwidth,
650 self->bwidth);
651 XMoveResizeWindow(obt_display, self->rgripbottom,
652 self->size.left + self->client->area.width +
653 self->size.right - self->bwidth - sidebwidth-
654 ob_rr_theme->grip_width,
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
660 XMapWindow(obt_display, self->handlebottom);
661 XMapWindow(obt_display, self->lgripbottom);
662 XMapWindow(obt_display, self->rgripbottom);
663
664 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
665 ob_rr_theme->handle_height > 0)
666 {
667 XMoveResizeWindow(obt_display, self->handletop,
668 ob_rr_theme->grip_width +
669 self->bwidth + sidebwidth,
670 FRAME_HANDLE_Y(self),
671 self->width - (ob_rr_theme->grip_width +
672 sidebwidth) * 2,
673 self->bwidth);
674 XMapWindow(obt_display, self->handletop);
675
676 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
677 XMoveResizeWindow(obt_display, self->handleleft,
678 ob_rr_theme->grip_width,
679 0,
680 self->bwidth,
681 ob_rr_theme->handle_height);
682 XMoveResizeWindow(obt_display, self->handleright,
683 self->width -
684 ob_rr_theme->grip_width -
685 self->bwidth,
686 0,
687 self->bwidth,
688 ob_rr_theme->handle_height);
689
690 XMoveResizeWindow(obt_display, self->lgriptop,
691 sidebwidth,
692 FRAME_HANDLE_Y(self),
693 ob_rr_theme->grip_width +
694 self->bwidth,
695 self->bwidth);
696 XMoveResizeWindow(obt_display, self->rgriptop,
697 self->size.left +
698 self->client->area.width +
699 self->size.right - self->bwidth -
700 sidebwidth - ob_rr_theme->grip_width,
701 FRAME_HANDLE_Y(self),
702 ob_rr_theme->grip_width +
703 self->bwidth,
704 self->bwidth);
705
706 XMapWindow(obt_display, self->handleleft);
707 XMapWindow(obt_display, self->handleright);
708 XMapWindow(obt_display, self->lgriptop);
709 XMapWindow(obt_display, self->rgriptop);
710 } else {
711 XUnmapWindow(obt_display, self->handleleft);
712 XUnmapWindow(obt_display, self->handleright);
713 XUnmapWindow(obt_display, self->lgriptop);
714 XUnmapWindow(obt_display, self->rgriptop);
715 }
716 } else {
717 XUnmapWindow(obt_display, self->handleleft);
718 XUnmapWindow(obt_display, self->handleright);
719 XUnmapWindow(obt_display, self->lgriptop);
720 XUnmapWindow(obt_display, self->rgriptop);
721
722 XUnmapWindow(obt_display, self->handletop);
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 XUnmapWindow(obt_display, self->handlebottom);
733 XUnmapWindow(obt_display, self->lgripleft);
734 XUnmapWindow(obt_display, self->rgripright);
735 XUnmapWindow(obt_display, self->lgripbottom);
736 XUnmapWindow(obt_display, self->rgripbottom);
737 }
738
739 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
740 ob_rr_theme->handle_height > 0)
741 {
742 XMoveResizeWindow(obt_display, self->handle,
743 sidebwidth,
744 FRAME_HANDLE_Y(self) + self->bwidth,
745 self->width, ob_rr_theme->handle_height);
746 XMapWindow(obt_display, self->handle);
747
748 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
749 XMoveResizeWindow(obt_display, self->lgrip,
750 0, 0,
751 ob_rr_theme->grip_width,
752 ob_rr_theme->handle_height);
753 XMoveResizeWindow(obt_display, self->rgrip,
754 self->width - ob_rr_theme->grip_width,
755 0,
756 ob_rr_theme->grip_width,
757 ob_rr_theme->handle_height);
758
759 XMapWindow(obt_display, self->lgrip);
760 XMapWindow(obt_display, self->rgrip);
761 } else {
762 XUnmapWindow(obt_display, self->lgrip);
763 XUnmapWindow(obt_display, self->rgrip);
764 }
765 } else {
766 XUnmapWindow(obt_display, self->lgrip);
767 XUnmapWindow(obt_display, self->rgrip);
768
769 XUnmapWindow(obt_display, self->handle);
770 }
771
772 if (self->bwidth && !self->max_horz &&
773 (self->client->area.height + self->size.top +
774 self->size.bottom) > ob_rr_theme->grip_width * 2)
775 {
776 XMoveResizeWindow(obt_display, self->left,
777 0,
778 self->bwidth + ob_rr_theme->grip_width,
779 self->bwidth,
780 self->client->area.height +
781 self->size.top + self->size.bottom -
782 ob_rr_theme->grip_width * 2);
783
784 XMapWindow(obt_display, self->left);
785 } else
786 XUnmapWindow(obt_display, self->left);
787
788 if (self->bwidth && !self->max_horz &&
789 (self->client->area.height + self->size.top +
790 self->size.bottom) > ob_rr_theme->grip_width * 2)
791 {
792 XMoveResizeWindow(obt_display, self->right,
793 self->client->area.width + self->cbwidth_l +
794 self->cbwidth_r + self->bwidth,
795 self->bwidth + ob_rr_theme->grip_width,
796 self->bwidth,
797 self->client->area.height +
798 self->size.top + self->size.bottom -
799 ob_rr_theme->grip_width * 2);
800
801 XMapWindow(obt_display, self->right);
802 } else
803 XUnmapWindow(obt_display, self->right);
804
805 XMoveResizeWindow(obt_display, self->backback,
806 self->size.left, self->size.top,
807 self->client->area.width,
808 self->client->area.height);
809 }
810 }
811
812 /* shading can change without being moved or resized */
813 RECT_SET_SIZE(self->area,
814 self->client->area.width +
815 self->size.left + self->size.right,
816 (self->client->shaded ?
817 ob_rr_theme->title_height + self->bwidth * 2:
818 self->client->area.height +
819 self->size.top + self->size.bottom));
820
821 if ((moved || resized) && !fake) {
822 /* find the new coordinates, done after setting the frame.size, for
823 frame_client_gravity. */
824 self->area.x = self->client->area.x;
825 self->area.y = self->client->area.y;
826 frame_client_gravity(self, &self->area.x, &self->area.y);
827 }
828
829 if (!fake) {
830 if (!frame_iconify_animating(self))
831 /* move and resize the top level frame.
832 shading can change without being moved or resized.
833
834 but don't do this during an iconify animation. it will be
835 reflected afterwards.
836 */
837 XMoveResizeWindow(obt_display, self->window,
838 self->area.x,
839 self->area.y,
840 self->area.width,
841 self->area.height);
842
843 /* when the client has StaticGravity, it likes to move around.
844 also this correctly positions the client when it maps.
845 this also needs to be run when the frame's decorations sizes change!
846 */
847 XMoveWindow(obt_display, self->client->window,
848 self->size.left, self->size.top);
849
850 if (resized) {
851 self->need_render = TRUE;
852 framerender_frame(self);
853 frame_adjust_shape(self);
854 }
855
856 if (!STRUT_EQUAL(self->size, self->oldsize)) {
857 gulong vals[4];
858 vals[0] = self->size.left;
859 vals[1] = self->size.right;
860 vals[2] = self->size.top;
861 vals[3] = self->size.bottom;
862 OBT_PROP_SETA32(self->client->window, NET_FRAME_EXTENTS,
863 CARDINAL, vals, 4);
864 OBT_PROP_SETA32(self->client->window, KDE_NET_WM_FRAME_STRUT,
865 CARDINAL, vals, 4);
866 self->oldsize = self->size;
867 }
868
869 /* if this occurs while we are focus cycling, the indicator needs to
870 match the changes */
871 if (focus_cycle_target == self->client)
872 focus_cycle_update_indicator(self->client);
873 }
874 if (resized && (self->decorations & OB_FRAME_DECOR_TITLEBAR) &&
875 self->label_width)
876 {
877 XResizeWindow(obt_display, self->label, self->label_width,
878 ob_rr_theme->label_height);
879 }
880 }
881
882 static void frame_adjust_cursors(ObFrame *self)
883 {
884 if ((self->functions & OB_CLIENT_FUNC_RESIZE) !=
885 (self->client->functions & OB_CLIENT_FUNC_RESIZE) ||
886 self->max_horz != self->client->max_horz ||
887 self->max_vert != self->client->max_vert ||
888 self->shaded != self->client->shaded)
889 {
890 gboolean r = (self->client->functions & OB_CLIENT_FUNC_RESIZE) &&
891 !(self->client->max_horz && self->client->max_vert);
892 gboolean topbot = !self->client->max_vert;
893 gboolean sh = self->client->shaded;
894 XSetWindowAttributes a;
895
896 /* these ones turn off when max vert, and some when shaded */
897 a.cursor = ob_cursor(r && topbot && !sh ?
898 OB_CURSOR_NORTH : OB_CURSOR_NONE);
899 XChangeWindowAttributes(obt_display, self->topresize, CWCursor, &a);
900 XChangeWindowAttributes(obt_display, self->titletop, CWCursor, &a);
901 a.cursor = ob_cursor(r && topbot ? OB_CURSOR_SOUTH : OB_CURSOR_NONE);
902 XChangeWindowAttributes(obt_display, self->handle, CWCursor, &a);
903 XChangeWindowAttributes(obt_display, self->handletop, CWCursor, &a);
904 XChangeWindowAttributes(obt_display, self->handlebottom, CWCursor, &a);
905 XChangeWindowAttributes(obt_display, self->innerbottom, CWCursor, &a);
906
907 /* these ones change when shaded */
908 a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_WEST : OB_CURSOR_NORTHWEST) :
909 OB_CURSOR_NONE);
910 XChangeWindowAttributes(obt_display, self->titleleft, CWCursor, &a);
911 XChangeWindowAttributes(obt_display, self->tltresize, CWCursor, &a);
912 XChangeWindowAttributes(obt_display, self->tllresize, CWCursor, &a);
913 XChangeWindowAttributes(obt_display, self->titletopleft, CWCursor, &a);
914 a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_EAST : OB_CURSOR_NORTHEAST) :
915 OB_CURSOR_NONE);
916 XChangeWindowAttributes(obt_display, self->titleright, CWCursor, &a);
917 XChangeWindowAttributes(obt_display, self->trtresize, CWCursor, &a);
918 XChangeWindowAttributes(obt_display, self->trrresize, CWCursor, &a);
919 XChangeWindowAttributes(obt_display, self->titletopright, CWCursor,&a);
920
921 /* these ones are pretty static */
922 a.cursor = ob_cursor(r ? OB_CURSOR_WEST : OB_CURSOR_NONE);
923 XChangeWindowAttributes(obt_display, self->left, CWCursor, &a);
924 XChangeWindowAttributes(obt_display, self->innerleft, CWCursor, &a);
925 a.cursor = ob_cursor(r ? OB_CURSOR_EAST : OB_CURSOR_NONE);
926 XChangeWindowAttributes(obt_display, self->right, CWCursor, &a);
927 XChangeWindowAttributes(obt_display, self->innerright, CWCursor, &a);
928 a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHWEST : OB_CURSOR_NONE);
929 XChangeWindowAttributes(obt_display, self->lgrip, CWCursor, &a);
930 XChangeWindowAttributes(obt_display, self->handleleft, CWCursor, &a);
931 XChangeWindowAttributes(obt_display, self->lgripleft, CWCursor, &a);
932 XChangeWindowAttributes(obt_display, self->lgriptop, CWCursor, &a);
933 XChangeWindowAttributes(obt_display, self->lgripbottom, CWCursor, &a);
934 XChangeWindowAttributes(obt_display, self->innerbll, CWCursor, &a);
935 XChangeWindowAttributes(obt_display, self->innerblb, CWCursor, &a);
936 a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHEAST : OB_CURSOR_NONE);
937 XChangeWindowAttributes(obt_display, self->rgrip, CWCursor, &a);
938 XChangeWindowAttributes(obt_display, self->handleright, CWCursor, &a);
939 XChangeWindowAttributes(obt_display, self->rgripright, CWCursor, &a);
940 XChangeWindowAttributes(obt_display, self->rgriptop, CWCursor, &a);
941 XChangeWindowAttributes(obt_display, self->rgripbottom, CWCursor, &a);
942 XChangeWindowAttributes(obt_display, self->innerbrr, CWCursor, &a);
943 XChangeWindowAttributes(obt_display, self->innerbrb, CWCursor, &a);
944 }
945 }
946
947 void frame_adjust_client_area(ObFrame *self)
948 {
949 /* adjust the window which is there to prevent flashing on unmap */
950 XMoveResizeWindow(obt_display, self->backfront, 0, 0,
951 self->client->area.width,
952 self->client->area.height);
953 }
954
955 void frame_adjust_state(ObFrame *self)
956 {
957 self->need_render = TRUE;
958 framerender_frame(self);
959 }
960
961 void frame_adjust_focus(ObFrame *self, gboolean hilite)
962 {
963 ob_debug_type(OB_DEBUG_FOCUS,
964 "Frame for 0x%x has focus: %d",
965 self->client->window, hilite);
966 self->focused = hilite;
967 self->need_render = TRUE;
968 framerender_frame(self);
969 XFlush(obt_display);
970 }
971
972 void frame_adjust_title(ObFrame *self)
973 {
974 self->need_render = TRUE;
975 framerender_frame(self);
976 }
977
978 void frame_adjust_icon(ObFrame *self)
979 {
980 self->need_render = TRUE;
981 framerender_frame(self);
982 }
983
984 void frame_grab_client(ObFrame *self)
985 {
986 /* DO NOT map the client window here. we used to do that, but it is bogus.
987 we need to set up the client's dimensions and everything before we
988 send a mapnotify or we create race conditions.
989 */
990
991 /* reparent the client to the frame */
992 XReparentWindow(obt_display, self->client->window, self->window, 0, 0);
993
994 /*
995 When reparenting the client window, it is usually not mapped yet, since
996 this occurs from a MapRequest. However, in the case where Openbox is
997 starting up, the window is already mapped, so we'll see an unmap event
998 for it.
999 */
1000 if (ob_state() == OB_STATE_STARTING)
1001 ++self->client->ignore_unmaps;
1002
1003 /* select the event mask on the client's parent (to receive config/map
1004 req's) the ButtonPress is to catch clicks on the client border */
1005 XSelectInput(obt_display, self->window, FRAME_EVENTMASK);
1006
1007 /* set all the windows for the frame in the window_map */
1008 window_add(&self->window, CLIENT_AS_WINDOW(self->client));
1009 window_add(&self->backback, CLIENT_AS_WINDOW(self->client));
1010 window_add(&self->backfront, CLIENT_AS_WINDOW(self->client));
1011 window_add(&self->innerleft, CLIENT_AS_WINDOW(self->client));
1012 window_add(&self->innertop, CLIENT_AS_WINDOW(self->client));
1013 window_add(&self->innerright, CLIENT_AS_WINDOW(self->client));
1014 window_add(&self->innerbottom, CLIENT_AS_WINDOW(self->client));
1015 window_add(&self->innerblb, CLIENT_AS_WINDOW(self->client));
1016 window_add(&self->innerbll, CLIENT_AS_WINDOW(self->client));
1017 window_add(&self->innerbrb, CLIENT_AS_WINDOW(self->client));
1018 window_add(&self->innerbrr, CLIENT_AS_WINDOW(self->client));
1019 window_add(&self->title, CLIENT_AS_WINDOW(self->client));
1020 window_add(&self->label, CLIENT_AS_WINDOW(self->client));
1021 window_add(&self->max, CLIENT_AS_WINDOW(self->client));
1022 window_add(&self->close, CLIENT_AS_WINDOW(self->client));
1023 window_add(&self->desk, CLIENT_AS_WINDOW(self->client));
1024 window_add(&self->shade, CLIENT_AS_WINDOW(self->client));
1025 window_add(&self->icon, CLIENT_AS_WINDOW(self->client));
1026 window_add(&self->iconify, CLIENT_AS_WINDOW(self->client));
1027 window_add(&self->handle, CLIENT_AS_WINDOW(self->client));
1028 window_add(&self->lgrip, CLIENT_AS_WINDOW(self->client));
1029 window_add(&self->rgrip, CLIENT_AS_WINDOW(self->client));
1030 window_add(&self->topresize, CLIENT_AS_WINDOW(self->client));
1031 window_add(&self->tltresize, CLIENT_AS_WINDOW(self->client));
1032 window_add(&self->tllresize, CLIENT_AS_WINDOW(self->client));
1033 window_add(&self->trtresize, CLIENT_AS_WINDOW(self->client));
1034 window_add(&self->trrresize, CLIENT_AS_WINDOW(self->client));
1035 window_add(&self->left, CLIENT_AS_WINDOW(self->client));
1036 window_add(&self->right, CLIENT_AS_WINDOW(self->client));
1037 window_add(&self->titleleft, CLIENT_AS_WINDOW(self->client));
1038 window_add(&self->titletop, CLIENT_AS_WINDOW(self->client));
1039 window_add(&self->titletopleft, CLIENT_AS_WINDOW(self->client));
1040 window_add(&self->titletopright, CLIENT_AS_WINDOW(self->client));
1041 window_add(&self->titleright, CLIENT_AS_WINDOW(self->client));
1042 window_add(&self->titlebottom, CLIENT_AS_WINDOW(self->client));
1043 window_add(&self->handleleft, CLIENT_AS_WINDOW(self->client));
1044 window_add(&self->handletop, CLIENT_AS_WINDOW(self->client));
1045 window_add(&self->handleright, CLIENT_AS_WINDOW(self->client));
1046 window_add(&self->handlebottom, CLIENT_AS_WINDOW(self->client));
1047 window_add(&self->lgripleft, CLIENT_AS_WINDOW(self->client));
1048 window_add(&self->lgriptop, CLIENT_AS_WINDOW(self->client));
1049 window_add(&self->lgripbottom, CLIENT_AS_WINDOW(self->client));
1050 window_add(&self->rgripright, CLIENT_AS_WINDOW(self->client));
1051 window_add(&self->rgriptop, CLIENT_AS_WINDOW(self->client));
1052 window_add(&self->rgripbottom, CLIENT_AS_WINDOW(self->client));
1053 }
1054
1055 static gboolean find_reparent(XEvent *e, gpointer data)
1056 {
1057 const ObFrame *self = data;
1058
1059 /* Find ReparentNotify events for the window that aren't being reparented into the
1060 frame, thus the client reparenting itself off the frame. */
1061 return e->type == ReparentNotify && e->xreparent.window == self->client->window &&
1062 e->xreparent.parent != self->window;
1063 }
1064
1065 void frame_release_client(ObFrame *self)
1066 {
1067 /* if there was any animation going on, kill it */
1068 if (self->iconify_animation_timer)
1069 g_source_remove(self->iconify_animation_timer);
1070
1071 /* check if the app has already reparented its window away */
1072 if (!xqueue_exists_local(find_reparent, self)) {
1073 /* according to the ICCCM - if the client doesn't reparent itself,
1074 then we will reparent the window to root for them */
1075 XReparentWindow(obt_display, self->client->window, obt_root(ob_screen),
1076 self->client->area.x, self->client->area.y);
1077 }
1078
1079 /* remove all the windows for the frame from the window_map */
1080 window_remove(self->window);
1081 window_remove(self->backback);
1082 window_remove(self->backfront);
1083 window_remove(self->innerleft);
1084 window_remove(self->innertop);
1085 window_remove(self->innerright);
1086 window_remove(self->innerbottom);
1087 window_remove(self->innerblb);
1088 window_remove(self->innerbll);
1089 window_remove(self->innerbrb);
1090 window_remove(self->innerbrr);
1091 window_remove(self->title);
1092 window_remove(self->label);
1093 window_remove(self->max);
1094 window_remove(self->close);
1095 window_remove(self->desk);
1096 window_remove(self->shade);
1097 window_remove(self->icon);
1098 window_remove(self->iconify);
1099 window_remove(self->handle);
1100 window_remove(self->lgrip);
1101 window_remove(self->rgrip);
1102 window_remove(self->topresize);
1103 window_remove(self->tltresize);
1104 window_remove(self->tllresize);
1105 window_remove(self->trtresize);
1106 window_remove(self->trrresize);
1107 window_remove(self->left);
1108 window_remove(self->right);
1109 window_remove(self->titleleft);
1110 window_remove(self->titletop);
1111 window_remove(self->titletopleft);
1112 window_remove(self->titletopright);
1113 window_remove(self->titleright);
1114 window_remove(self->titlebottom);
1115 window_remove(self->handleleft);
1116 window_remove(self->handletop);
1117 window_remove(self->handleright);
1118 window_remove(self->handlebottom);
1119 window_remove(self->lgripleft);
1120 window_remove(self->lgriptop);
1121 window_remove(self->lgripbottom);
1122 window_remove(self->rgripright);
1123 window_remove(self->rgriptop);
1124 window_remove(self->rgripbottom);
1125
1126 if (self->flash_timer) g_source_remove(self->flash_timer);
1127 }
1128
1129 /* is there anything present between us and the label? */
1130 static gboolean is_button_present(ObFrame *self, const gchar *lc, gint dir) {
1131 for (; *lc != '\0' && lc >= config_title_layout; lc += dir) {
1132 if (*lc == ' ') continue; /* it was invalid */
1133 if (*lc == 'N' && self->decorations & OB_FRAME_DECOR_ICON)
1134 return TRUE;
1135 if (*lc == 'D' && self->decorations & OB_FRAME_DECOR_ALLDESKTOPS)
1136 return TRUE;
1137 if (*lc == 'S' && self->decorations & OB_FRAME_DECOR_SHADE)
1138 return TRUE;
1139 if (*lc == 'I' && self->decorations & OB_FRAME_DECOR_ICONIFY)
1140 return TRUE;
1141 if (*lc == 'M' && self->decorations & OB_FRAME_DECOR_MAXIMIZE)
1142 return TRUE;
1143 if (*lc == 'C' && self->decorations & OB_FRAME_DECOR_CLOSE)
1144 return TRUE;
1145 if (*lc == 'L') return FALSE;
1146 }
1147 return FALSE;
1148 }
1149
1150 static void place_button(ObFrame *self, const char *lc, gint bwidth,
1151 gint left, gint i,
1152 gint *x, gint *button_on, gint *button_x)
1153 {
1154 if (!(*button_on = is_button_present(self, lc, i)))
1155 return;
1156
1157 self->label_width -= bwidth;
1158 if (i > 0)
1159 *button_x = *x;
1160 *x += i * bwidth;
1161 if (i < 0) {
1162 if (self->label_x <= left || *x > self->label_x) {
1163 *button_x = *x;
1164 } else {
1165 /* the button would have been drawn on top of another button */
1166 *button_on = FALSE;
1167 self->label_width += bwidth;
1168 }
1169 }
1170 }
1171
1172 static void layout_title(ObFrame *self)
1173 {
1174 gchar *lc;
1175 gint i;
1176
1177 const gint bwidth = ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
1178 /* position of the leftmost button */
1179 const gint left = ob_rr_theme->paddingx + 1;
1180 /* position of the rightmost button */
1181 const gint right = self->width;
1182
1183 /* turn them all off */
1184 self->icon_on = self->desk_on = self->shade_on = self->iconify_on =
1185 self->max_on = self->close_on = self->label_on = FALSE;
1186 self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2;
1187 self->leftmost = self->rightmost = OB_FRAME_CONTEXT_NONE;
1188
1189 /* figure out what's being shown, find each element's position, and the
1190 width of the label
1191
1192 do the ones before the label, then after the label,
1193 i will be +1 the first time through when working to the left,
1194 and -1 the second time through when working to the right */
1195 for (i = 1; i >= -1; i-=2) {
1196 gint x;
1197 ObFrameContext *firstcon;
1198
1199 if (i > 0) {
1200 x = left;
1201 lc = config_title_layout;
1202 firstcon = &self->leftmost;
1203 } else {
1204 x = right;
1205 lc = config_title_layout + strlen(config_title_layout)-1;
1206 firstcon = &self->rightmost;
1207 }
1208
1209 /* stop at the end of the string (or the label, which calls break) */
1210 for (; *lc != '\0' && lc >= config_title_layout; lc+=i) {
1211 if (*lc == 'L') {
1212 if (i > 0) {
1213 self->label_on = TRUE;
1214 self->label_x = x;
1215 }
1216 break; /* break the for loop, do other side of label */
1217 } else if (*lc == 'N') {
1218 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICON;
1219 /* icon is bigger than buttons */
1220 place_button(self, lc, bwidth + 2, left, i, &x, &self->icon_on, &self->icon_x);
1221 } else if (*lc == 'D') {
1222 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ALLDESKTOPS;
1223 place_button(self, lc, bwidth, left, i, &x, &self->desk_on, &self->desk_x);
1224 } else if (*lc == 'S') {
1225 if (firstcon) *firstcon = OB_FRAME_CONTEXT_SHADE;
1226 place_button(self, lc, bwidth, left, i, &x, &self->shade_on, &self->shade_x);
1227 } else if (*lc == 'I') {
1228 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICONIFY;
1229 place_button(self, lc, bwidth, left, i, &x, &self->iconify_on, &self->iconify_x);
1230 } else if (*lc == 'M') {
1231 if (firstcon) *firstcon = OB_FRAME_CONTEXT_MAXIMIZE;
1232 place_button(self, lc, bwidth, left, i, &x, &self->max_on, &self->max_x);
1233 } else if (*lc == 'C') {
1234 if (firstcon) *firstcon = OB_FRAME_CONTEXT_CLOSE;
1235 place_button(self, lc, bwidth, left, i, &x, &self->close_on, &self->close_x);
1236 } else
1237 continue; /* don't set firstcon */
1238 firstcon = NULL;
1239 }
1240 }
1241
1242 /* position and map the elements */
1243 if (self->icon_on) {
1244 XMapWindow(obt_display, self->icon);
1245 XMoveWindow(obt_display, self->icon, self->icon_x,
1246 ob_rr_theme->paddingy);
1247 } else
1248 XUnmapWindow(obt_display, self->icon);
1249
1250 if (self->desk_on) {
1251 XMapWindow(obt_display, self->desk);
1252 XMoveWindow(obt_display, self->desk, self->desk_x,
1253 ob_rr_theme->paddingy + 1);
1254 } else
1255 XUnmapWindow(obt_display, self->desk);
1256
1257 if (self->shade_on) {
1258 XMapWindow(obt_display, self->shade);
1259 XMoveWindow(obt_display, self->shade, self->shade_x,
1260 ob_rr_theme->paddingy + 1);
1261 } else
1262 XUnmapWindow(obt_display, self->shade);
1263
1264 if (self->iconify_on) {
1265 XMapWindow(obt_display, self->iconify);
1266 XMoveWindow(obt_display, self->iconify, self->iconify_x,
1267 ob_rr_theme->paddingy + 1);
1268 } else
1269 XUnmapWindow(obt_display, self->iconify);
1270
1271 if (self->max_on) {
1272 XMapWindow(obt_display, self->max);
1273 XMoveWindow(obt_display, self->max, self->max_x,
1274 ob_rr_theme->paddingy + 1);
1275 } else
1276 XUnmapWindow(obt_display, self->max);
1277
1278 if (self->close_on) {
1279 XMapWindow(obt_display, self->close);
1280 XMoveWindow(obt_display, self->close, self->close_x,
1281 ob_rr_theme->paddingy + 1);
1282 } else
1283 XUnmapWindow(obt_display, self->close);
1284
1285 if (self->label_on && self->label_width > 0) {
1286 XMapWindow(obt_display, self->label);
1287 XMoveWindow(obt_display, self->label, self->label_x,
1288 ob_rr_theme->paddingy);
1289 } else
1290 XUnmapWindow(obt_display, self->label);
1291 }
1292
1293 gboolean frame_next_context_from_string(gchar *names, ObFrameContext *cx)
1294 {
1295 gchar *p, *n;
1296
1297 if (!*names) /* empty string */
1298 return FALSE;
1299
1300 /* find the first space */
1301 for (p = names; *p; p = g_utf8_next_char(p)) {
1302 const gunichar c = g_utf8_get_char(p);
1303 if (g_unichar_isspace(c)) break;
1304 }
1305
1306 if (p == names) {
1307 /* leading spaces in the string */
1308 n = g_utf8_next_char(names);
1309 if (!frame_next_context_from_string(n, cx))
1310 return FALSE;
1311 } else {
1312 n = p;
1313 if (*p) {
1314 /* delete the space with null zero(s) */
1315 while (n < g_utf8_next_char(p))
1316 *(n++) = '\0';
1317 }
1318
1319 *cx = frame_context_from_string(names);
1320
1321 /* find the next non-space */
1322 for (; *n; n = g_utf8_next_char(n)) {
1323 const gunichar c = g_utf8_get_char(n);
1324 if (!g_unichar_isspace(c)) break;
1325 }
1326 }
1327
1328 /* delete everything we just read (copy everything at n to the start of
1329 the string */
1330 for (p = names; *n; ++p, ++n)
1331 *p = *n;
1332 *p = *n;
1333
1334 return TRUE;
1335 }
1336
1337 ObFrameContext frame_context_from_string(const gchar *name)
1338 {
1339 if (!g_ascii_strcasecmp("Desktop", name))
1340 return OB_FRAME_CONTEXT_DESKTOP;
1341 else if (!g_ascii_strcasecmp("Root", name))
1342 return OB_FRAME_CONTEXT_ROOT;
1343 else if (!g_ascii_strcasecmp("Client", name))
1344 return OB_FRAME_CONTEXT_CLIENT;
1345 else if (!g_ascii_strcasecmp("Titlebar", name))
1346 return OB_FRAME_CONTEXT_TITLEBAR;
1347 else if (!g_ascii_strcasecmp("Frame", name))
1348 return OB_FRAME_CONTEXT_FRAME;
1349 else if (!g_ascii_strcasecmp("TLCorner", name))
1350 return OB_FRAME_CONTEXT_TLCORNER;
1351 else if (!g_ascii_strcasecmp("TRCorner", name))
1352 return OB_FRAME_CONTEXT_TRCORNER;
1353 else if (!g_ascii_strcasecmp("BLCorner", name))
1354 return OB_FRAME_CONTEXT_BLCORNER;
1355 else if (!g_ascii_strcasecmp("BRCorner", name))
1356 return OB_FRAME_CONTEXT_BRCORNER;
1357 else if (!g_ascii_strcasecmp("Top", name))
1358 return OB_FRAME_CONTEXT_TOP;
1359 else if (!g_ascii_strcasecmp("Bottom", name))
1360 return OB_FRAME_CONTEXT_BOTTOM;
1361 else if (!g_ascii_strcasecmp("Left", name))
1362 return OB_FRAME_CONTEXT_LEFT;
1363 else if (!g_ascii_strcasecmp("Right", name))
1364 return OB_FRAME_CONTEXT_RIGHT;
1365 else if (!g_ascii_strcasecmp("Maximize", name))
1366 return OB_FRAME_CONTEXT_MAXIMIZE;
1367 else if (!g_ascii_strcasecmp("AllDesktops", name))
1368 return OB_FRAME_CONTEXT_ALLDESKTOPS;
1369 else if (!g_ascii_strcasecmp("Shade", name))
1370 return OB_FRAME_CONTEXT_SHADE;
1371 else if (!g_ascii_strcasecmp("Iconify", name))
1372 return OB_FRAME_CONTEXT_ICONIFY;
1373 else if (!g_ascii_strcasecmp("Icon", name))
1374 return OB_FRAME_CONTEXT_ICON;
1375 else if (!g_ascii_strcasecmp("Close", name))
1376 return OB_FRAME_CONTEXT_CLOSE;
1377 else if (!g_ascii_strcasecmp("MoveResize", name))
1378 return OB_FRAME_CONTEXT_MOVE_RESIZE;
1379 else if (!g_ascii_strcasecmp("Dock", name))
1380 return OB_FRAME_CONTEXT_DOCK;
1381
1382 return OB_FRAME_CONTEXT_NONE;
1383 }
1384
1385 ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y)
1386 {
1387 ObFrame *self;
1388 ObWindow *obwin;
1389
1390 if (moveresize_in_progress)
1391 return OB_FRAME_CONTEXT_MOVE_RESIZE;
1392
1393 if (win == obt_root(ob_screen))
1394 return OB_FRAME_CONTEXT_ROOT;
1395 if ((obwin = window_find(win))) {
1396 if (WINDOW_IS_DOCK(obwin)) {
1397 return OB_FRAME_CONTEXT_DOCK;
1398 }
1399 }
1400 if (client == NULL) return OB_FRAME_CONTEXT_NONE;
1401 if (win == client->window) {
1402 /* conceptually, this is the desktop, as far as users are
1403 concerned */
1404 if (client->type == OB_CLIENT_TYPE_DESKTOP)
1405 return OB_FRAME_CONTEXT_DESKTOP;
1406 return OB_FRAME_CONTEXT_CLIENT;
1407 }
1408
1409 self = client->frame;
1410
1411 /* when the user clicks in the corners of the titlebar and the client
1412 is fully maximized, then treat it like they clicked in the
1413 button that is there */
1414 if (self->max_horz && self->max_vert &&
1415 (win == self->title || win == self->titletop ||
1416 win == self->titleleft || win == self->titletopleft ||
1417 win == self->titleright || win == self->titletopright))
1418 {
1419 /* get the mouse coords in reference to the whole frame */
1420 gint fx = x;
1421 gint fy = y;
1422
1423 /* these windows are down a border width from the top of the frame */
1424 if (win == self->title ||
1425 win == self->titleleft || win == self->titleright)
1426 fy += self->bwidth;
1427
1428 /* title is a border width in from the edge */
1429 if (win == self->title)
1430 fx += self->bwidth;
1431 /* titletop is a bit to the right */
1432 else if (win == self->titletop)
1433 fx += ob_rr_theme->grip_width + self->bwidth;
1434 /* titletopright is way to the right edge */
1435 else if (win == self->titletopright)
1436 fx += self->area.width - (ob_rr_theme->grip_width + self->bwidth);
1437 /* titleright is even more way to the right edge */
1438 else if (win == self->titleright)
1439 fx += self->area.width - self->bwidth;
1440
1441 /* figure out if we're over the area that should be considered a
1442 button */
1443 if (fy < self->bwidth + ob_rr_theme->paddingy + 1 +
1444 ob_rr_theme->button_size)
1445 {
1446 if (fx < (self->bwidth + ob_rr_theme->paddingx + 1 +
1447 ob_rr_theme->button_size))
1448 {
1449 if (self->leftmost != OB_FRAME_CONTEXT_NONE)
1450 return self->leftmost;
1451 }
1452 else if (fx >= (self->area.width -
1453 (self->bwidth + ob_rr_theme->paddingx + 1 +
1454 ob_rr_theme->button_size)))
1455 {
1456 if (self->rightmost != OB_FRAME_CONTEXT_NONE)
1457 return self->rightmost;
1458 }
1459 }
1460
1461 /* there is no resizing maximized windows so make them the titlebar
1462 context */
1463 return OB_FRAME_CONTEXT_TITLEBAR;
1464 }
1465 else if (self->max_vert &&
1466 (win == self->titletop || win == self->topresize))
1467 /* can't resize vertically when max vert */
1468 return OB_FRAME_CONTEXT_TITLEBAR;
1469 else if (self->shaded &&
1470 (win == self->titletop || win == self->topresize))
1471 /* can't resize vertically when shaded */
1472 return OB_FRAME_CONTEXT_TITLEBAR;
1473
1474 if (win == self->window) return OB_FRAME_CONTEXT_FRAME;
1475 if (win == self->label) return OB_FRAME_CONTEXT_TITLEBAR;
1476 if (win == self->handle) return OB_FRAME_CONTEXT_BOTTOM;
1477 if (win == self->handletop) return OB_FRAME_CONTEXT_BOTTOM;
1478 if (win == self->handlebottom) return OB_FRAME_CONTEXT_BOTTOM;
1479 if (win == self->handleleft) return OB_FRAME_CONTEXT_BLCORNER;
1480 if (win == self->lgrip) return OB_FRAME_CONTEXT_BLCORNER;
1481 if (win == self->lgripleft) return OB_FRAME_CONTEXT_BLCORNER;
1482 if (win == self->lgriptop) return OB_FRAME_CONTEXT_BLCORNER;
1483 if (win == self->lgripbottom) return OB_FRAME_CONTEXT_BLCORNER;
1484 if (win == self->handleright) return OB_FRAME_CONTEXT_BRCORNER;
1485 if (win == self->rgrip) return OB_FRAME_CONTEXT_BRCORNER;
1486 if (win == self->rgripright) return OB_FRAME_CONTEXT_BRCORNER;
1487 if (win == self->rgriptop) return OB_FRAME_CONTEXT_BRCORNER;
1488 if (win == self->rgripbottom) return OB_FRAME_CONTEXT_BRCORNER;
1489 if (win == self->title) return OB_FRAME_CONTEXT_TITLEBAR;
1490 if (win == self->titlebottom) return OB_FRAME_CONTEXT_TITLEBAR;
1491 if (win == self->titleleft) return OB_FRAME_CONTEXT_TLCORNER;
1492 if (win == self->titletopleft) return OB_FRAME_CONTEXT_TLCORNER;
1493 if (win == self->titleright) return OB_FRAME_CONTEXT_TRCORNER;
1494 if (win == self->titletopright) return OB_FRAME_CONTEXT_TRCORNER;
1495 if (win == self->titletop) return OB_FRAME_CONTEXT_TOP;
1496 if (win == self->topresize) return OB_FRAME_CONTEXT_TOP;
1497 if (win == self->tltresize) return OB_FRAME_CONTEXT_TLCORNER;
1498 if (win == self->tllresize) return OB_FRAME_CONTEXT_TLCORNER;
1499 if (win == self->trtresize) return OB_FRAME_CONTEXT_TRCORNER;
1500 if (win == self->trrresize) return OB_FRAME_CONTEXT_TRCORNER;
1501 if (win == self->left) return OB_FRAME_CONTEXT_LEFT;
1502 if (win == self->right) return OB_FRAME_CONTEXT_RIGHT;
1503 if (win == self->innertop) return OB_FRAME_CONTEXT_TITLEBAR;
1504 if (win == self->innerleft) return OB_FRAME_CONTEXT_LEFT;
1505 if (win == self->innerbottom) return OB_FRAME_CONTEXT_BOTTOM;
1506 if (win == self->innerright) return OB_FRAME_CONTEXT_RIGHT;
1507 if (win == self->innerbll) return OB_FRAME_CONTEXT_BLCORNER;
1508 if (win == self->innerblb) return OB_FRAME_CONTEXT_BLCORNER;
1509 if (win == self->innerbrr) return OB_FRAME_CONTEXT_BRCORNER;
1510 if (win == self->innerbrb) return OB_FRAME_CONTEXT_BRCORNER;
1511 if (win == self->max) return OB_FRAME_CONTEXT_MAXIMIZE;
1512 if (win == self->iconify) return OB_FRAME_CONTEXT_ICONIFY;
1513 if (win == self->close) return OB_FRAME_CONTEXT_CLOSE;
1514 if (win == self->icon) return OB_FRAME_CONTEXT_ICON;
1515 if (win == self->desk) return OB_FRAME_CONTEXT_ALLDESKTOPS;
1516 if (win == self->shade) return OB_FRAME_CONTEXT_SHADE;
1517
1518 return OB_FRAME_CONTEXT_NONE;
1519 }
1520
1521 void frame_client_gravity(ObFrame *self, gint *x, gint *y)
1522 {
1523 /* horizontal */
1524 switch (self->client->gravity) {
1525 default:
1526 case NorthWestGravity:
1527 case SouthWestGravity:
1528 case WestGravity:
1529 break;
1530
1531 case NorthGravity:
1532 case SouthGravity:
1533 case CenterGravity:
1534 /* the middle of the client will be the middle of the frame */
1535 *x -= (self->size.right - self->size.left) / 2;
1536 break;
1537
1538 case NorthEastGravity:
1539 case SouthEastGravity:
1540 case EastGravity:
1541 /* the right side of the client will be the right side of the frame */
1542 *x -= self->size.right + self->size.left -
1543 self->client->border_width * 2;
1544 break;
1545
1546 case ForgetGravity:
1547 case StaticGravity:
1548 /* the client's position won't move */
1549 *x -= self->size.left - self->client->border_width;
1550 break;
1551 }
1552
1553 /* vertical */
1554 switch (self->client->gravity) {
1555 default:
1556 case NorthWestGravity:
1557 case NorthEastGravity:
1558 case NorthGravity:
1559 break;
1560
1561 case CenterGravity:
1562 case EastGravity:
1563 case WestGravity:
1564 /* the middle of the client will be the middle of the frame */
1565 *y -= (self->size.bottom - self->size.top) / 2;
1566 break;
1567
1568 case SouthWestGravity:
1569 case SouthEastGravity:
1570 case SouthGravity:
1571 /* the bottom of the client will be the bottom of the frame */
1572 *y -= self->size.bottom + self->size.top -
1573 self->client->border_width * 2;
1574 break;
1575
1576 case ForgetGravity:
1577 case StaticGravity:
1578 /* the client's position won't move */
1579 *y -= self->size.top - self->client->border_width;
1580 break;
1581 }
1582 }
1583
1584 void frame_frame_gravity(ObFrame *self, gint *x, gint *y)
1585 {
1586 /* horizontal */
1587 switch (self->client->gravity) {
1588 default:
1589 case NorthWestGravity:
1590 case WestGravity:
1591 case SouthWestGravity:
1592 break;
1593 case NorthGravity:
1594 case CenterGravity:
1595 case SouthGravity:
1596 /* the middle of the client will be the middle of the frame */
1597 *x += (self->size.right - self->size.left) / 2;
1598 break;
1599 case NorthEastGravity:
1600 case EastGravity:
1601 case SouthEastGravity:
1602 /* the right side of the client will be the right side of the frame */
1603 *x += self->size.right + self->size.left -
1604 self->client->border_width * 2;
1605 break;
1606 case StaticGravity:
1607 case ForgetGravity:
1608 /* the client's position won't move */
1609 *x += self->size.left - self->client->border_width;
1610 break;
1611 }
1612
1613 /* vertical */
1614 switch (self->client->gravity) {
1615 default:
1616 case NorthWestGravity:
1617 case NorthGravity:
1618 case NorthEastGravity:
1619 break;
1620 case WestGravity:
1621 case CenterGravity:
1622 case EastGravity:
1623 /* the middle of the client will be the middle of the frame */
1624 *y += (self->size.bottom - self->size.top) / 2;
1625 break;
1626 case SouthWestGravity:
1627 case SouthGravity:
1628 case SouthEastGravity:
1629 /* the bottom of the client will be the bottom of the frame */
1630 *y += self->size.bottom + self->size.top -
1631 self->client->border_width * 2;
1632 break;
1633 case StaticGravity:
1634 case ForgetGravity:
1635 /* the client's position won't move */
1636 *y += self->size.top - self->client->border_width;
1637 break;
1638 }
1639 }
1640
1641 void frame_rect_to_frame(ObFrame *self, Rect *r)
1642 {
1643 r->width += self->size.left + self->size.right;
1644 r->height += self->size.top + self->size.bottom;
1645 frame_client_gravity(self, &r->x, &r->y);
1646 }
1647
1648 void frame_rect_to_client(ObFrame *self, Rect *r)
1649 {
1650 r->width -= self->size.left + self->size.right;
1651 r->height -= self->size.top + self->size.bottom;
1652 frame_frame_gravity(self, &r->x, &r->y);
1653 }
1654
1655 static void flash_done(gpointer data)
1656 {
1657 ObFrame *self = data;
1658
1659 if (self->focused != self->flash_on)
1660 frame_adjust_focus(self, self->focused);
1661 }
1662
1663 static gboolean flash_timeout(gpointer data)
1664 {
1665 ObFrame *self = data;
1666 GTimeVal now;
1667
1668 if (config_frame_flash_duration != 0) {
1669 g_get_current_time(&now);
1670 if (now.tv_sec > self->flash_end.tv_sec ||
1671 (now.tv_sec == self->flash_end.tv_sec &&
1672 now.tv_usec >= self->flash_end.tv_usec))
1673 self->flashing = FALSE;
1674 }
1675
1676 if (!self->flashing)
1677 return FALSE; /* we are done */
1678
1679 self->flash_on = !self->flash_on;
1680 if (!self->focused) {
1681 frame_adjust_focus(self, self->flash_on);
1682 self->focused = FALSE;
1683 }
1684
1685 return TRUE; /* go again */
1686 }
1687
1688 void frame_flash_start(ObFrame *self)
1689 {
1690 if (config_frame_flash_delay == 0) return;
1691
1692 self->flash_on = self->focused;
1693
1694 if (!self->flashing)
1695 self->flash_timer = g_timeout_add_full(G_PRIORITY_DEFAULT,
1696 config_frame_flash_delay, flash_timeout, self,
1697 flash_done);
1698
1699 if (config_frame_flash_duration != 0) {
1700 g_get_current_time(&self->flash_end);
1701 g_time_val_add(&self->flash_end, 1000 * config_frame_flash_duration);
1702 }
1703
1704 self->flashing = TRUE;
1705 }
1706
1707 void frame_flash_stop(ObFrame *self)
1708 {
1709 self->flashing = FALSE;
1710 }
1711
1712 static gulong frame_animate_iconify_time_left(ObFrame *self,
1713 const GTimeVal *now)
1714 {
1715 glong sec, usec;
1716 sec = self->iconify_animation_end.tv_sec - now->tv_sec;
1717 usec = self->iconify_animation_end.tv_usec - now->tv_usec;
1718 if (usec < 0) {
1719 usec += G_USEC_PER_SEC;
1720 sec--;
1721 }
1722 /* no negative values */
1723 return MAX(sec * G_USEC_PER_SEC + usec, 0);
1724 }
1725
1726 static gboolean frame_animate_iconify(gpointer p)
1727 {
1728 ObFrame *self = p;
1729 gint x, y, w, h;
1730 gint iconx, icony, iconw;
1731 GTimeVal now;
1732 gulong time;
1733 gboolean iconifying;
1734
1735 if (self->client->icon_geometry.width == 0) {
1736 /* there is no icon geometry set so just go straight down */
1737 const Rect *a;
1738
1739 a = screen_physical_area_monitor(screen_find_monitor(&self->area));
1740 iconx = self->area.x + self->area.width / 2 + 32;
1741 icony = a->y + a->width;
1742 iconw = 64;
1743 } else {
1744 iconx = self->client->icon_geometry.x;
1745 icony = self->client->icon_geometry.y;
1746 iconw = self->client->icon_geometry.width;
1747 }
1748
1749 iconifying = self->iconify_animation_going > 0;
1750
1751 /* how far do we have left to go ? */
1752 g_get_current_time(&now);
1753 time = frame_animate_iconify_time_left(self, &now);
1754
1755 if ((time > 0 && iconifying) || (time == 0 && !iconifying)) {
1756 /* start where the frame is supposed to be */
1757 x = self->area.x;
1758 y = self->area.y;
1759 w = self->area.width;
1760 h = self->area.height;
1761 } else {
1762 /* start at the icon */
1763 x = iconx;
1764 y = icony;
1765 w = iconw;
1766 h = self->size.top; /* just the titlebar */
1767 }
1768
1769 if (time > 0) {
1770 glong dx, dy, dw;
1771 glong elapsed;
1772
1773 dx = self->area.x - iconx;
1774 dy = self->area.y - icony;
1775 dw = self->area.width - self->bwidth * 2 - iconw;
1776 /* if restoring, we move in the opposite direction */
1777 if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; }
1778
1779 elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
1780 x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1781 y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1782 w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1783 h = self->size.top; /* just the titlebar */
1784 }
1785
1786 XMoveResizeWindow(obt_display, self->window, x, y, w, h);
1787 XFlush(obt_display);
1788
1789 if (time == 0)
1790 frame_end_iconify_animation(self);
1791
1792 return time > 0; /* repeat until we're out of time */
1793 }
1794
1795 void frame_end_iconify_animation(ObFrame *self)
1796 {
1797 /* see if there is an animation going */
1798 if (self->iconify_animation_going == 0) return;
1799
1800 if (!self->visible)
1801 XUnmapWindow(obt_display, self->window);
1802 else {
1803 /* Send a ConfigureNotify when the animation is done, this fixes
1804 KDE's pager showing the window in the wrong place. since the
1805 window is mapped at a different location and is then moved, we
1806 need to send the synthetic configurenotify, since apps may have
1807 read the position when the client mapped, apparently. */
1808 client_reconfigure(self->client, TRUE);
1809 }
1810
1811 /* we're not animating any more ! */
1812 self->iconify_animation_going = 0;
1813
1814 XMoveResizeWindow(obt_display, self->window,
1815 self->area.x, self->area.y,
1816 self->area.width, self->area.height);
1817 /* we delay re-rendering until after we're done animating */
1818 framerender_frame(self);
1819 XFlush(obt_display);
1820 }
1821
1822 void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying)
1823 {
1824 gulong time;
1825 gboolean new_anim = FALSE;
1826 gboolean set_end = TRUE;
1827 GTimeVal now;
1828
1829 /* if there is no titlebar, just don't animate for now
1830 XXX it would be nice tho.. */
1831 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1832 return;
1833
1834 /* get the current time */
1835 g_get_current_time(&now);
1836
1837 /* get how long until the end */
1838 time = FRAME_ANIMATE_ICONIFY_TIME;
1839 if (self->iconify_animation_going) {
1840 if (!!iconifying != (self->iconify_animation_going > 0)) {
1841 /* animation was already going on in the opposite direction */
1842 time = time - frame_animate_iconify_time_left(self, &now);
1843 } else
1844 /* animation was already going in the same direction */
1845 set_end = FALSE;
1846 } else
1847 new_anim = TRUE;
1848 self->iconify_animation_going = iconifying ? 1 : -1;
1849
1850 /* set the ending time */
1851 if (set_end) {
1852 self->iconify_animation_end.tv_sec = now.tv_sec;
1853 self->iconify_animation_end.tv_usec = now.tv_usec;
1854 g_time_val_add(&self->iconify_animation_end, time);
1855 }
1856
1857 if (new_anim) {
1858 if (self->iconify_animation_timer)
1859 g_source_remove(self->iconify_animation_timer);
1860 self->iconify_animation_timer =
1861 g_timeout_add_full(G_PRIORITY_DEFAULT,
1862 FRAME_ANIMATE_ICONIFY_STEP_TIME,
1863 frame_animate_iconify, self, NULL);
1864
1865
1866 /* do the first step */
1867 frame_animate_iconify(self);
1868
1869 /* show it during the animation even if it is not "visible" */
1870 if (!self->visible)
1871 XMapWindow(obt_display, self->window);
1872 }
1873 }
This page took 0.120865 seconds and 5 git commands to generate.