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