1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 frame.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
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.
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.
17 See the COPYING file for a copy of the GNU General Public License.
23 #include "extensions.h"
26 #include "framerender.h"
28 #include "focus_cycle.h"
29 #include "focus_cycle_indicator.h"
30 #include "moveresize.h"
32 #include "render/theme.h"
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
44 #define INNER_EVENTMASK (ButtonPressMask)
46 #define FRAME_ANIMATE_ICONIFY_TIME 150000 /* .15 seconds */
47 #define FRAME_ANIMATE_ICONIFY_STEP_TIME (G_USEC_PER_SEC / 60) /* 60 Hz */
49 #define FRAME_HANDLE_Y(f) (f->size.top + f->client->area.height + f->cbwidth_y)
51 static void flash_done(gpointer data
);
52 static gboolean
flash_timeout(gpointer data
);
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
);
59 static Window
createWindow(Window parent
, Visual
*visual
,
60 gulong mask
, XSetWindowAttributes
*attrib
)
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
)),
69 static Visual
*check_32bit_client(ObClient
*c
)
71 XWindowAttributes wattrib
;
74 /* we're already running at 32 bit depth, yay. we don't need to use their
76 if (RrDepth(ob_rr_inst
) == 32)
79 ret
= XGetWindowAttributes(ob_display
, c
->window
, &wattrib
);
80 g_assert(ret
!= BadDrawable
);
81 g_assert(ret
!= BadWindow
);
83 if (wattrib
.depth
== 32)
84 return wattrib
.visual
;
88 ObFrame
*frame_new(ObClient
*client
)
90 XSetWindowAttributes attrib
;
95 self
= g_new0(ObFrame
, 1);
96 self
->client
= client
;
98 visual
= check_32bit_client(client
);
100 /* create the non-visible decor windows */
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
),
111 attrib
.background_pixel
= BlackPixel(ob_display
, ob_screen
);
112 attrib
.border_pixel
= BlackPixel(ob_display
, ob_screen
);
114 attrib
.event_mask
= FRAME_EVENTMASK
;
115 self
->window
= createWindow(RootWindow(ob_display
, ob_screen
), visual
,
118 attrib
.event_mask
= INNER_EVENTMASK
;
119 self
->inner
= createWindow(self
->window
, visual
, mask
, &attrib
);
121 mask
&= ~CWEventMask
;
122 self
->plate
= createWindow(self
->inner
, visual
, mask
, &attrib
);
124 /* create the visible decor windows */
128 /* client has a 32-bit visual */
129 mask
|= CWColormap
| CWBackPixel
| CWBorderPixel
;
130 attrib
.colormap
= RrColormap(ob_rr_inst
);
132 attrib
.event_mask
= ELEMENT_EVENTMASK
;
133 self
->title
= createWindow(self
->window
, NULL
, mask
, &attrib
);
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
);
141 self
->leftresize
= createWindow(self
->inner
, NULL
, mask
, &attrib
);
142 self
->rightresize
= createWindow(self
->inner
, NULL
, mask
, &attrib
);
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
);
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
);
156 self
->focused
= FALSE
;
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
);
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
;
170 set_theme_statics(self
);
172 return (ObFrame
*)self
;
175 static void set_theme_statics(ObFrame
*self
)
179 if (ob_rr_theme
->handle_height
> 0)
180 handle_height
= ob_rr_theme
->handle_height
;
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
);
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
);
222 static void free_theme_statics(ObFrame
*self
)
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
);
233 void frame_free(ObFrame
*self
)
235 free_theme_statics(self
);
237 XDestroyWindow(ob_display
, self
->window
);
239 XFreeColormap(ob_display
, self
->colormap
);
244 void frame_show(ObFrame
*self
)
246 if (!self
->visible
) {
247 self
->visible
= TRUE
;
248 XMapWindow(ob_display
, self
->client
->window
);
249 XMapWindow(ob_display
, self
->window
);
253 void frame_hide(ObFrame
*self
)
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;
266 void frame_adjust_theme(ObFrame
*self
)
268 free_theme_statics(self
);
269 set_theme_statics(self
);
272 void frame_adjust_shape(ObFrame
*self
)
278 if (!self
->client
->shaped
) {
279 /* clear the shape on the frame window */
280 XShapeCombineMask(ob_display
, self
->window
, ShapeBounding
,
285 /* make the frame's shape match the clients */
286 XShapeCombineShape(ob_display
, self
->window
, ShapeBounding
,
289 self
->client
->window
,
290 ShapeBounding
, ShapeSet
);
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
+
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
+
311 XShapeCombineRectangles(ob_display
, self
->window
,
312 ShapeBounding
, 0, 0, xrect
, num
,
313 ShapeUnion
, Unsorted
);
318 void frame_adjust_area(ObFrame
*self
, gboolean moved
,
319 gboolean resized
, gboolean fake
)
323 oldsize
= self
->size
;
326 self
->decorations
= self
->client
->decorations
;
327 self
->max_horz
= self
->client
->max_horz
;
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
;
334 self
->bwidth
= self
->cbwidth_x
= self
->cbwidth_y
= 0;
336 self
->rbwidth
= self
->bwidth
;
339 self
->bwidth
= self
->cbwidth_x
= 0;
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 */
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
);
351 /* set border widths */
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
);
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
);
367 /* position/size and map/unmap all the windows */
370 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
) {
371 XMoveResizeWindow(ob_display
, self
->title
,
373 self
->width
, ob_rr_theme
->title_height
);
374 XMapWindow(ob_display
, self
->title
);
376 if (self
->decorations
& OB_FRAME_DECOR_GRIPS
) {
377 XMoveResizeWindow(ob_display
, self
->topresize
,
378 ob_rr_theme
->grip_width
+ self
->bwidth
,
380 self
->width
- (ob_rr_theme
->grip_width
+
382 ob_rr_theme
->paddingy
+ 1);
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);
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
);
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
);
404 XUnmapWindow(ob_display
, self
->title
);
407 if ((self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
408 /* layout the title bar elements */
412 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
)
416 if (ob_rr_theme
->handle_height
> 0)
417 handle_height
= ob_rr_theme
->handle_height
;
421 XMoveResizeWindow(ob_display
, self
->handle
,
422 0, FRAME_HANDLE_Y(self
),
423 self
->width
, handle_height
);
424 XMapWindow(ob_display
, self
->handle
);
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
);
435 XUnmapWindow(ob_display
, self
->lgrip
);
436 XUnmapWindow(ob_display
, self
->rgrip
);
439 XUnmapWindow(ob_display
, self
->handle
);
441 if (self
->decorations
& OB_FRAME_DECOR_BORDER
) {
442 XMoveResizeWindow(ob_display
, self
->leftresize
,
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
,
453 self
->client
->area
.height
+
454 self
->cbwidth_y
* 2);
456 XMapWindow(ob_display
, self
->leftresize
);
457 XMapWindow(ob_display
, self
->rightresize
);
459 XUnmapWindow(ob_display
, self
->leftresize
);
460 XUnmapWindow(ob_display
, self
->rightresize
);
463 /* move and resize the inner border window which contains the plate
465 XMoveResizeWindow(ob_display
, self
->inner
,
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);
474 XMoveWindow(ob_display
, self
->plate
,
475 self
->bwidth
+ self
->cbwidth_x
, self
->cbwidth_y
);
477 /* when the client has StaticGravity, it likes to move around. */
478 XMoveWindow(ob_display
, self
->client
->window
, 0, 0);
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
));
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
);
502 if (!frame_iconify_animating(self
))
503 /* move and resize the top level frame.
504 shading can change without being moved or resized.
506 but don't do this during an iconify animation. it will be
507 reflected afterwards.
509 XMoveResizeWindow(ob_display
, self
->window
,
516 framerender_frame(self
);
517 frame_adjust_shape(self
);
520 if (!STRUT_EQUAL(self
->size
, oldsize
)) {
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
,
528 PROP_SETA32(self
->client
->window
, kde_net_wm_frame_strut
,
532 /* if this occurs while we are focus cycling, the indicator needs to
534 if (focus_cycle_target
== self
->client
)
535 focus_cycle_draw_indicator(self
->client
);
537 if (resized
&& (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
538 XResizeWindow(ob_display
, self
->label
, self
->label_width
,
539 ob_rr_theme
->label_height
);
543 (self
->functions
& OB_CLIENT_FUNC_RESIZE
) !=
544 (self
->client
->functions
& OB_CLIENT_FUNC_RESIZE
))
546 gboolean r
= self
->client
->functions
& OB_CLIENT_FUNC_RESIZE
;
547 XSetWindowAttributes a
;
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
);
568 self
->functions
= self
->client
->functions
;
572 void frame_adjust_client_area(ObFrame
*self
)
574 /* resize the plate */
575 XResizeWindow(ob_display
, self
->plate
,
576 self
->client
->area
.width
, self
->client
->area
.height
);
579 void frame_adjust_state(ObFrame
*self
)
581 framerender_frame(self
);
584 void frame_adjust_focus(ObFrame
*self
, gboolean hilite
)
586 self
->focused
= hilite
;
587 framerender_frame(self
);
591 void frame_adjust_title(ObFrame
*self
)
593 framerender_frame(self
);
596 void frame_adjust_icon(ObFrame
*self
)
598 framerender_frame(self
);
601 void frame_grab_client(ObFrame
*self
)
603 /* reparent the client to the frame */
604 XReparentWindow(ob_display
, self
->client
->window
, self
->plate
, 0, 0);
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.
614 if (ob_state() == OB_STATE_STARTING
)
615 self
->client
->ignore_unmaps
+= 2;
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
);
621 /* map the client so it maps when the frame does */
622 XMapWindow(ob_display
, self
->client
->window
);
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
);
648 void frame_release_client(ObFrame
*self
)
651 gboolean reparent
= TRUE
;
653 /* if there was any animation going on, kill it */
654 ob_main_loop_timeout_remove_data(ob_main_loop
, frame_animate_iconify
,
657 /* check if the app has already reparented its window away */
658 while (XCheckTypedWindowEvent(ob_display
, self
->client
->window
,
659 ReparentNotify
, &ev
))
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
665 Reparent events that are generated by us are just discarded here.
666 They are of no consequence to us anyhow.
668 if (ev
.xreparent
.parent
!= self
->plate
) {
670 XPutBackEvent(ob_display
, &ev
);
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
);
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
);
707 ob_main_loop_timeout_remove_data(ob_main_loop
, flash_timeout
, self
, TRUE
);
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
)
716 if (*lc
== 'D' && self
->decorations
& OB_FRAME_DECOR_ALLDESKTOPS
)
718 if (*lc
== 'S' && self
->decorations
& OB_FRAME_DECOR_SHADE
)
720 if (*lc
== 'I' && self
->decorations
& OB_FRAME_DECOR_ICONIFY
)
722 if (*lc
== 'M' && self
->decorations
& OB_FRAME_DECOR_MAXIMIZE
)
724 if (*lc
== 'C' && self
->decorations
& OB_FRAME_DECOR_CLOSE
)
726 if (*lc
== 'L') return FALSE
;
731 static void layout_title(ObFrame
*self
)
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
;
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
;
748 /* figure out what's being show, find each element's position, and the
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) {
756 ObFrameContext
*firstcon
;
760 lc
= config_title_layout
;
761 firstcon
= &self
->leftmost
;
764 lc
= config_title_layout
+ strlen(config_title_layout
)-1;
765 firstcon
= &self
->rightmost
;
768 /* stop at the end of the string (or the label, which calls break) */
769 for (; *lc
!= '\0' && lc
>= config_title_layout
; lc
+=i
) {
772 self
->label_on
= TRUE
;
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;
782 x
+= i
* (bwidth
+ 2);
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
;
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
;
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
;
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
;
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
;
820 continue; /* don't set firstcon */
825 /* position and map the elements */
827 XMapWindow(ob_display
, self
->icon
);
828 XMoveWindow(ob_display
, self
->icon
, self
->icon_x
,
829 ob_rr_theme
->paddingy
);
831 XUnmapWindow(ob_display
, self
->icon
);
834 XMapWindow(ob_display
, self
->desk
);
835 XMoveWindow(ob_display
, self
->desk
, self
->desk_x
,
836 ob_rr_theme
->paddingy
+ 1);
838 XUnmapWindow(ob_display
, self
->desk
);
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);
845 XUnmapWindow(ob_display
, self
->shade
);
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);
852 XUnmapWindow(ob_display
, self
->iconify
);
855 XMapWindow(ob_display
, self
->max
);
856 XMoveWindow(ob_display
, self
->max
, self
->max_x
,
857 ob_rr_theme
->paddingy
+ 1);
859 XUnmapWindow(ob_display
, self
->max
);
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);
866 XUnmapWindow(ob_display
, self
->close
);
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
);
874 XUnmapWindow(ob_display
, self
->label
);
877 ObFrameContext
frame_context_from_string(const gchar
*name
)
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
;
922 ObFrameContext
frame_context(ObClient
*client
, Window win
, gint x
, gint y
)
926 if (moveresize_in_progress
)
927 return OB_FRAME_CONTEXT_MOVE_RESIZE
;
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
935 if (client
->type
== OB_CLIENT_TYPE_DESKTOP
)
936 return OB_FRAME_CONTEXT_DESKTOP
;
937 return OB_FRAME_CONTEXT_CLIENT
;
940 self
= client
->frame
;
941 if (win
== self
->inner
|| win
== self
->plate
) {
942 /* conceptually, this is the desktop, as far as users are
944 if (client
->type
== OB_CLIENT_TYPE_DESKTOP
)
945 return OB_FRAME_CONTEXT_DESKTOP
;
946 return OB_FRAME_CONTEXT_CLIENT
;
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
)
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
;
961 else if (x
> (self
->width
-
962 (ob_rr_theme
->paddingx
+ 1 +
963 ob_rr_theme
->button_size
)))
965 if (self
->rightmost
!= OB_FRAME_CONTEXT_NONE
)
966 return self
->rightmost
;
969 return OB_FRAME_CONTEXT_TITLEBAR
;
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
;
991 return OB_FRAME_CONTEXT_NONE
;
994 void frame_client_gravity(ObFrame
*self
, gint
*x
, gint
*y
, gint w
, gint h
)
997 switch (self
->client
->gravity
) {
999 case NorthWestGravity
:
1000 case SouthWestGravity
:
1007 *x
-= (self
->size
.left
+ w
) / 2;
1010 case NorthEastGravity
:
1011 case SouthEastGravity
:
1013 *x
-= (self
->size
.left
+ self
->size
.right
+ w
) - 1;
1018 *x
-= self
->size
.left
;
1023 switch (self
->client
->gravity
) {
1025 case NorthWestGravity
:
1026 case NorthEastGravity
:
1033 *y
-= (self
->size
.top
+ h
) / 2;
1036 case SouthWestGravity
:
1037 case SouthEastGravity
:
1039 *y
-= (self
->size
.top
+ self
->size
.bottom
+ h
) - 1;
1044 *y
-= self
->size
.top
;
1049 void frame_frame_gravity(ObFrame
*self
, gint
*x
, gint
*y
, gint w
, gint h
)
1052 switch (self
->client
->gravity
) {
1054 case NorthWestGravity
:
1056 case SouthWestGravity
:
1061 *x
+= (self
->size
.left
+ w
) / 2;
1063 case NorthEastGravity
:
1065 case SouthEastGravity
:
1066 *x
+= (self
->size
.left
+ self
->size
.right
+ w
) - 1;
1070 *x
+= self
->size
.left
;
1075 switch (self
->client
->gravity
) {
1077 case NorthWestGravity
:
1079 case NorthEastGravity
:
1084 *y
+= (self
->size
.top
+ h
) / 2;
1086 case SouthWestGravity
:
1088 case SouthEastGravity
:
1089 *y
+= (self
->size
.top
+ self
->size
.bottom
+ h
) - 1;
1093 *y
+= self
->size
.top
;
1098 static void flash_done(gpointer data
)
1100 ObFrame
*self
= data
;
1102 if (self
->focused
!= self
->flash_on
)
1103 frame_adjust_focus(self
, self
->focused
);
1106 static gboolean
flash_timeout(gpointer data
)
1108 ObFrame
*self
= data
;
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
;
1117 if (!self
->flashing
)
1118 return FALSE
; /* we are done */
1120 self
->flash_on
= !self
->flash_on
;
1121 if (!self
->focused
) {
1122 frame_adjust_focus(self
, self
->flash_on
);
1123 self
->focused
= FALSE
;
1126 return TRUE
; /* go again */
1129 void frame_flash_start(ObFrame
*self
)
1131 self
->flash_on
= self
->focused
;
1133 if (!self
->flashing
)
1134 ob_main_loop_timeout_add(ob_main_loop
,
1135 G_USEC_PER_SEC
* 0.6,
1140 g_get_current_time(&self
->flash_end
);
1141 g_time_val_add(&self
->flash_end
, G_USEC_PER_SEC
* 5);
1143 self
->flashing
= TRUE
;
1146 void frame_flash_stop(ObFrame
*self
)
1148 self
->flashing
= FALSE
;
1151 static gulong
frame_animate_iconify_time_left(ObFrame
*self
,
1152 const GTimeVal
*now
)
1155 sec
= self
->iconify_animation_end
.tv_sec
- now
->tv_sec
;
1156 usec
= self
->iconify_animation_end
.tv_usec
- now
->tv_usec
;
1158 usec
+= G_USEC_PER_SEC
;
1161 /* no negative values */
1162 return MAX(sec
* G_USEC_PER_SEC
+ usec
, 0);
1165 static gboolean
frame_animate_iconify(gpointer p
)
1169 gint iconx
, icony
, iconw
;
1172 gboolean iconifying
;
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
;
1181 iconx
= self
->client
->icon_geometry
.x
;
1182 icony
= self
->client
->icon_geometry
.y
;
1183 iconw
= self
->client
->icon_geometry
.width
;
1186 iconifying
= self
->iconify_animation_going
> 0;
1188 /* how far do we have left to go ? */
1189 g_get_current_time(&now
);
1190 time
= frame_animate_iconify_time_left(self
, &now
);
1192 if (time
== 0 || iconifying
) {
1193 /* start where the frame is supposed to be */
1196 w
= self
->area
.width
- self
->bwidth
* 2;
1197 h
= self
->area
.height
- self
->bwidth
* 2;
1199 /* start at the icon */
1203 h
= self
->size
.top
; /* just the titlebar */
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
; }
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 */
1224 frame_end_iconify_animation(self
);
1226 XMoveResizeWindow(ob_display
, self
->window
, x
, y
, w
, h
);
1230 return time
> 0; /* repeat until we're out of time */
1233 void frame_end_iconify_animation(ObFrame
*self
)
1235 /* see if there is an animation going */
1236 if (self
->iconify_animation_going
== 0) return;
1239 XUnmapWindow(ob_display
, self
->window
);
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
);
1245 /* we're not animating any more ! */
1246 self
->iconify_animation_going
= 0;
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);
1255 void frame_begin_iconify_animation(ObFrame
*self
, gboolean iconifying
)
1258 gboolean new_anim
= FALSE
;
1259 gboolean set_end
= TRUE
;
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
))
1267 /* get the current time */
1268 g_get_current_time(&now
);
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
);
1277 /* animation was already going in the same direction */
1281 self
->iconify_animation_going
= iconifying
? 1 : -1;
1283 /* set the ending time */
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
);
1291 ob_main_loop_timeout_remove_data(ob_main_loop
, frame_animate_iconify
,
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
);
1298 /* do the first step */
1299 frame_animate_iconify(self
);
1301 /* show it during the animation even if it is not "visible" */
1303 XMapWindow(ob_display
, self
->window
);