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