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
);
134 self
->titleleft
= createWindow(self
->window
, NULL
, mask
, &attrib
);
135 self
->titletop
= createWindow(self
->window
, NULL
, mask
, &attrib
);
136 self
->titletopleft
= createWindow(self
->window
, NULL
, mask
, &attrib
);
137 self
->titletopright
= createWindow(self
->window
, NULL
, mask
, &attrib
);
138 self
->titleright
= createWindow(self
->window
, NULL
, mask
, &attrib
);
139 self
->titlebottom
= createWindow(self
->window
, NULL
, mask
, &attrib
);
141 self
->topresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
142 self
->tltresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
143 self
->tllresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
144 self
->trtresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
145 self
->trrresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
147 self
->leftresize
= createWindow(self
->window
, NULL
, mask
, &attrib
);
148 self
->rightresize
= createWindow(self
->window
, NULL
, mask
, &attrib
);
150 self
->label
= createWindow(self
->title
, NULL
, mask
, &attrib
);
151 self
->max
= createWindow(self
->title
, NULL
, mask
, &attrib
);
152 self
->close
= createWindow(self
->title
, NULL
, mask
, &attrib
);
153 self
->desk
= createWindow(self
->title
, NULL
, mask
, &attrib
);
154 self
->shade
= createWindow(self
->title
, NULL
, mask
, &attrib
);
155 self
->icon
= createWindow(self
->title
, NULL
, mask
, &attrib
);
156 self
->iconify
= createWindow(self
->title
, NULL
, mask
, &attrib
);
158 self
->handle
= createWindow(self
->window
, NULL
, mask
, &attrib
);
159 self
->lgrip
= createWindow(self
->handle
, NULL
, mask
, &attrib
);
160 self
->rgrip
= createWindow(self
->handle
, NULL
, mask
, &attrib
);
162 self
->focused
= FALSE
;
164 /* the other stuff is shown based on decor settings */
165 XMapWindow(ob_display
, self
->plate
);
166 XMapWindow(ob_display
, self
->inner
);
167 XMapWindow(ob_display
, self
->lgrip
);
168 XMapWindow(ob_display
, self
->rgrip
);
169 XMapWindow(ob_display
, self
->label
);
171 self
->max_press
= self
->close_press
= self
->desk_press
=
172 self
->iconify_press
= self
->shade_press
= FALSE
;
173 self
->max_hover
= self
->close_hover
= self
->desk_hover
=
174 self
->iconify_hover
= self
->shade_hover
= FALSE
;
176 set_theme_statics(self
);
178 return (ObFrame
*)self
;
181 static void set_theme_statics(ObFrame
*self
)
185 if (ob_rr_theme
->handle_height
> 0)
186 handle_height
= ob_rr_theme
->handle_height
;
191 /* set colors/appearance/sizes for stuff that doesn't change */
192 XResizeWindow(ob_display
, self
->max
,
193 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
194 XResizeWindow(ob_display
, self
->iconify
,
195 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
196 XResizeWindow(ob_display
, self
->icon
,
197 ob_rr_theme
->button_size
+ 2, ob_rr_theme
->button_size
+ 2);
198 XResizeWindow(ob_display
, self
->close
,
199 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
200 XResizeWindow(ob_display
, self
->desk
,
201 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
202 XResizeWindow(ob_display
, self
->shade
,
203 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
204 XResizeWindow(ob_display
, self
->lgrip
,
205 ob_rr_theme
->grip_width
, handle_height
);
206 XResizeWindow(ob_display
, self
->rgrip
,
207 ob_rr_theme
->grip_width
, handle_height
);
208 XResizeWindow(ob_display
, self
->tltresize
,
209 ob_rr_theme
->grip_width
, ob_rr_theme
->paddingy
+ 1);
210 XResizeWindow(ob_display
, self
->trtresize
,
211 ob_rr_theme
->grip_width
, ob_rr_theme
->paddingy
+ 1);
212 XResizeWindow(ob_display
, self
->tllresize
,
213 ob_rr_theme
->paddingx
+ 1, ob_rr_theme
->title_height
);
214 XResizeWindow(ob_display
, self
->trrresize
,
215 ob_rr_theme
->paddingx
+ 1, ob_rr_theme
->title_height
);
217 /* set up the dynamic appearances */
218 self
->a_unfocused_title
= RrAppearanceCopy(ob_rr_theme
->a_unfocused_title
);
219 self
->a_focused_title
= RrAppearanceCopy(ob_rr_theme
->a_focused_title
);
220 self
->a_unfocused_label
= RrAppearanceCopy(ob_rr_theme
->a_unfocused_label
);
221 self
->a_focused_label
= RrAppearanceCopy(ob_rr_theme
->a_focused_label
);
222 self
->a_unfocused_handle
=
223 RrAppearanceCopy(ob_rr_theme
->a_unfocused_handle
);
224 self
->a_focused_handle
= RrAppearanceCopy(ob_rr_theme
->a_focused_handle
);
225 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_icon
);
228 static void free_theme_statics(ObFrame
*self
)
230 RrAppearanceFree(self
->a_unfocused_title
);
231 RrAppearanceFree(self
->a_focused_title
);
232 RrAppearanceFree(self
->a_unfocused_label
);
233 RrAppearanceFree(self
->a_focused_label
);
234 RrAppearanceFree(self
->a_unfocused_handle
);
235 RrAppearanceFree(self
->a_focused_handle
);
236 RrAppearanceFree(self
->a_icon
);
239 void frame_free(ObFrame
*self
)
241 free_theme_statics(self
);
243 XDestroyWindow(ob_display
, self
->window
);
245 XFreeColormap(ob_display
, self
->colormap
);
250 void frame_show(ObFrame
*self
)
252 if (!self
->visible
) {
253 self
->visible
= TRUE
;
254 XMapWindow(ob_display
, self
->client
->window
);
255 XMapWindow(ob_display
, self
->window
);
259 void frame_hide(ObFrame
*self
)
262 self
->visible
= FALSE
;
263 if (!frame_iconify_animating(self
))
264 XUnmapWindow(ob_display
, self
->window
);
265 /* we unmap the client itself so that we can get MapRequest
266 events, and because the ICCCM tells us to! */
267 XUnmapWindow(ob_display
, self
->client
->window
);
268 self
->client
->ignore_unmaps
+= 1;
272 void frame_adjust_theme(ObFrame
*self
)
274 free_theme_statics(self
);
275 set_theme_statics(self
);
278 void frame_adjust_shape(ObFrame
*self
)
284 if (!self
->client
->shaped
) {
285 /* clear the shape on the frame window */
286 XShapeCombineMask(ob_display
, self
->window
, ShapeBounding
,
291 /* make the frame's shape match the clients */
292 XShapeCombineShape(ob_display
, self
->window
, ShapeBounding
,
295 self
->client
->window
,
296 ShapeBounding
, ShapeSet
);
299 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
) {
300 xrect
[0].x
= -ob_rr_theme
->fbwidth
;
301 xrect
[0].y
= -ob_rr_theme
->fbwidth
;
302 xrect
[0].width
= self
->width
+ self
->bwidth
* 2;
303 xrect
[0].height
= ob_rr_theme
->title_height
+
308 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
) {
309 xrect
[1].x
= -ob_rr_theme
->fbwidth
;
310 xrect
[1].y
= FRAME_HANDLE_Y(self
);
311 xrect
[1].width
= self
->width
+ self
->bwidth
* 2;
312 xrect
[1].height
= ob_rr_theme
->handle_height
+
317 XShapeCombineRectangles(ob_display
, self
->window
,
318 ShapeBounding
, 0, 0, xrect
, num
,
319 ShapeUnion
, Unsorted
);
324 void frame_adjust_area(ObFrame
*self
, gboolean moved
,
325 gboolean resized
, gboolean fake
)
329 oldsize
= self
->size
;
332 self
->decorations
= self
->client
->decorations
;
333 self
->max_horz
= self
->client
->max_horz
;
335 if (self
->decorations
& OB_FRAME_DECOR_BORDER
) {
336 self
->bwidth
= ob_rr_theme
->fbwidth
;
337 self
->cbwidth_x
= ob_rr_theme
->cbwidthx
;
338 self
->cbwidth_y
= ob_rr_theme
->cbwidthy
;
340 self
->bwidth
= self
->cbwidth_x
= self
->cbwidth_y
= 0;
342 self
->rbwidth
= self
->bwidth
;
347 self
->width
= self
->client
->area
.width
+ self
->cbwidth_x
* 2;
348 self
->width
= MAX(self
->width
, 1); /* no lower than 1 */
350 STRUT_SET(self
->size
,
351 self
->cbwidth_x
+ self
->bwidth
,
352 self
->cbwidth_y
+ self
->bwidth
,
353 self
->cbwidth_x
+ self
->bwidth
,
354 self
->cbwidth_y
+ self
->bwidth
);
356 /* set border widths */
358 XSetWindowBorderWidth(ob_display
, self
->handle
, self
->bwidth
);
359 XSetWindowBorderWidth(ob_display
, self
->lgrip
, self
->bwidth
);
360 XSetWindowBorderWidth(ob_display
, self
->rgrip
, self
->bwidth
);
363 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
)
364 self
->size
.top
+= ob_rr_theme
->title_height
+ self
->bwidth
+
365 (self
->bwidth
- self
->bwidth
);
366 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
&&
367 ob_rr_theme
->handle_height
> 0)
368 self
->size
.bottom
+= ob_rr_theme
->handle_height
+
369 self
->bwidth
+ (self
->bwidth
- self
->bwidth
);
371 /* position/size and map/unmap all the windows */
375 XMoveResizeWindow(ob_display
, self
->titletop
,
376 ob_rr_theme
->grip_width
+ self
->bwidth
, 0,
377 self
->client
->area
.width
+
378 self
->cbwidth_x
* 2 + self
->bwidth
* 2 -
379 (ob_rr_theme
->grip_width
+ self
->bwidth
) * 2,
381 XMoveResizeWindow(ob_display
, self
->titletopleft
,
383 ob_rr_theme
->grip_width
+ self
->bwidth
,
385 XMoveResizeWindow(ob_display
, self
->titletopright
,
386 self
->client
->area
.width
+
387 self
->cbwidth_x
* 2 + self
->bwidth
* 2 -
388 ob_rr_theme
->grip_width
- self
->bwidth
,
390 ob_rr_theme
->grip_width
+ self
->bwidth
,
392 XMoveResizeWindow(ob_display
, self
->titleleft
,
395 ob_rr_theme
->grip_width
);
396 XMoveResizeWindow(ob_display
, self
->titleright
,
397 self
->client
->area
.width
+
398 self
->cbwidth_x
* 2 + self
->bwidth
,
401 ob_rr_theme
->grip_width
);
403 XMapWindow(ob_display
, self
->titletop
);
404 XMapWindow(ob_display
, self
->titletopleft
);
405 XMapWindow(ob_display
, self
->titletopright
);
406 XMapWindow(ob_display
, self
->titleleft
);
407 XMapWindow(ob_display
, self
->titleright
);
409 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
) {
410 XMoveResizeWindow(ob_display
, self
->titlebottom
,
412 ob_rr_theme
->title_height
+ self
->bwidth
,
413 self
->client
->area
.width
+
417 XMapWindow(ob_display
, self
->titlebottom
);
419 XUnmapWindow(ob_display
, self
->titlebottom
);
421 XUnmapWindow(ob_display
, self
->titletop
);
422 XUnmapWindow(ob_display
, self
->titletopleft
);
423 XUnmapWindow(ob_display
, self
->titletopright
);
424 XUnmapWindow(ob_display
, self
->titleleft
);
425 XUnmapWindow(ob_display
, self
->titleright
);
428 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
) {
429 XMoveResizeWindow(ob_display
, self
->title
,
430 self
->bwidth
, self
->bwidth
,
431 self
->width
, ob_rr_theme
->title_height
);
433 XMapWindow(ob_display
, self
->title
);
435 if (self
->decorations
& OB_FRAME_DECOR_GRIPS
) {
436 XMoveResizeWindow(ob_display
, self
->topresize
,
437 ob_rr_theme
->grip_width
+ self
->bwidth
,
439 self
->width
- (ob_rr_theme
->grip_width
+
441 ob_rr_theme
->paddingy
+ 1);
443 XMoveWindow(ob_display
, self
->tltresize
, 0, 0);
444 XMoveWindow(ob_display
, self
->tllresize
, 0, 0);
445 XMoveWindow(ob_display
, self
->trtresize
,
446 self
->width
- ob_rr_theme
->grip_width
, 0);
447 XMoveWindow(ob_display
, self
->trrresize
,
448 self
->width
- ob_rr_theme
->paddingx
- 1, 0);
450 XMapWindow(ob_display
, self
->topresize
);
451 XMapWindow(ob_display
, self
->tltresize
);
452 XMapWindow(ob_display
, self
->tllresize
);
453 XMapWindow(ob_display
, self
->trtresize
);
454 XMapWindow(ob_display
, self
->trrresize
);
456 XUnmapWindow(ob_display
, self
->topresize
);
457 XUnmapWindow(ob_display
, self
->tltresize
);
458 XUnmapWindow(ob_display
, self
->tllresize
);
459 XUnmapWindow(ob_display
, self
->trtresize
);
460 XUnmapWindow(ob_display
, self
->trrresize
);
463 XUnmapWindow(ob_display
, self
->title
);
466 if ((self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
467 /* layout the title bar elements */
471 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
)
475 if (ob_rr_theme
->handle_height
> 0)
476 handle_height
= ob_rr_theme
->handle_height
;
480 XMoveResizeWindow(ob_display
, self
->handle
,
481 0, FRAME_HANDLE_Y(self
),
482 self
->width
, handle_height
);
483 XMapWindow(ob_display
, self
->handle
);
485 if (self
->decorations
& OB_FRAME_DECOR_GRIPS
) {
486 XMoveWindow(ob_display
, self
->lgrip
,
487 -self
->bwidth
, -self
->bwidth
);
488 XMoveWindow(ob_display
, self
->rgrip
,
489 -self
->bwidth
+ self
->width
-
490 ob_rr_theme
->grip_width
, -self
->bwidth
);
491 XMapWindow(ob_display
, self
->lgrip
);
492 XMapWindow(ob_display
, self
->rgrip
);
494 XUnmapWindow(ob_display
, self
->lgrip
);
495 XUnmapWindow(ob_display
, self
->rgrip
);
498 XUnmapWindow(ob_display
, self
->handle
);
500 if (self
->bwidth
&& !self
->max_horz
) {
501 XMoveResizeWindow(ob_display
, self
->leftresize
,
503 self
->bwidth
+ ob_rr_theme
->grip_width
,
505 self
->client
->area
.height
+
506 self
->cbwidth_y
* 2);
507 XMoveResizeWindow(ob_display
, self
->rightresize
,
508 self
->client
->area
.width
+
509 self
->cbwidth_x
* 2 + self
->bwidth
,
510 self
->bwidth
+ ob_rr_theme
->grip_width
,
512 self
->client
->area
.height
+
513 self
->cbwidth_y
* 2);
515 XMapWindow(ob_display
, self
->leftresize
);
516 XMapWindow(ob_display
, self
->rightresize
);
518 XUnmapWindow(ob_display
, self
->leftresize
);
519 XUnmapWindow(ob_display
, self
->rightresize
);
522 /* move and resize the inner border window which contains the plate
524 XMoveResizeWindow(ob_display
, self
->inner
,
526 self
->size
.top
- self
->cbwidth_y
,
527 self
->client
->area
.width
+
528 self
->cbwidth_x
* 2 + self
->bwidth
* 2,
529 self
->client
->area
.height
+
530 self
->cbwidth_y
* 2);
533 XMoveWindow(ob_display
, self
->plate
,
534 self
->bwidth
+ self
->cbwidth_x
, self
->cbwidth_y
);
536 /* when the client has StaticGravity, it likes to move around. */
537 XMoveWindow(ob_display
, self
->client
->window
, 0, 0);
541 /* shading can change without being moved or resized */
542 RECT_SET_SIZE(self
->area
,
543 self
->client
->area
.width
+
544 self
->size
.left
+ self
->size
.right
,
545 (self
->client
->shaded
?
546 ob_rr_theme
->title_height
+ self
->bwidth
* 2:
547 self
->client
->area
.height
+
548 self
->size
.top
+ self
->size
.bottom
));
550 if (moved
|| resized
) {
551 /* find the new coordinates, done after setting the frame.size, for
552 frame_client_gravity. */
553 self
->area
.x
= self
->client
->area
.x
;
554 self
->area
.y
= self
->client
->area
.y
;
555 frame_client_gravity(self
, &self
->area
.x
, &self
->area
.y
,
556 self
->client
->area
.width
,
557 self
->client
->area
.height
);
561 if (!frame_iconify_animating(self
))
562 /* move and resize the top level frame.
563 shading can change without being moved or resized.
565 but don't do this during an iconify animation. it will be
566 reflected afterwards.
568 XMoveResizeWindow(ob_display
, self
->window
,
575 framerender_frame(self
);
576 frame_adjust_shape(self
);
579 if (!STRUT_EQUAL(self
->size
, oldsize
)) {
581 vals
[0] = self
->size
.left
;
582 vals
[1] = self
->size
.right
;
583 vals
[2] = self
->size
.top
;
584 vals
[3] = self
->size
.bottom
;
585 PROP_SETA32(self
->client
->window
, net_frame_extents
,
587 PROP_SETA32(self
->client
->window
, kde_net_wm_frame_strut
,
591 /* if this occurs while we are focus cycling, the indicator needs to
593 if (focus_cycle_target
== self
->client
)
594 focus_cycle_draw_indicator(self
->client
);
596 if (resized
&& (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
597 XResizeWindow(ob_display
, self
->label
, self
->label_width
,
598 ob_rr_theme
->label_height
);
602 (self
->functions
& OB_CLIENT_FUNC_RESIZE
) !=
603 (self
->client
->functions
& OB_CLIENT_FUNC_RESIZE
))
605 gboolean r
= self
->client
->functions
& OB_CLIENT_FUNC_RESIZE
;
606 XSetWindowAttributes a
;
608 a
.cursor
= ob_cursor(r
? OB_CURSOR_NORTH
: OB_CURSOR_NONE
);
609 XChangeWindowAttributes(ob_display
, self
->topresize
, CWCursor
, &a
);
610 XChangeWindowAttributes(ob_display
, self
->titletop
, CWCursor
, &a
);
611 a
.cursor
= ob_cursor(r
? OB_CURSOR_NORTHWEST
: OB_CURSOR_NONE
);
612 XChangeWindowAttributes(ob_display
, self
->tltresize
, CWCursor
, &a
);
613 XChangeWindowAttributes(ob_display
, self
->tllresize
, CWCursor
, &a
);
614 XChangeWindowAttributes(ob_display
, self
->titletopleft
, CWCursor
, &a
);
615 XChangeWindowAttributes(ob_display
, self
->titleleft
, CWCursor
, &a
);
616 a
.cursor
= ob_cursor(r
? OB_CURSOR_NORTHEAST
: OB_CURSOR_NONE
);
617 XChangeWindowAttributes(ob_display
, self
->trtresize
, CWCursor
, &a
);
618 XChangeWindowAttributes(ob_display
, self
->trrresize
, CWCursor
, &a
);
619 XChangeWindowAttributes(ob_display
, self
->titletopright
, CWCursor
, &a
);
620 XChangeWindowAttributes(ob_display
, self
->titleright
, CWCursor
, &a
);
621 a
.cursor
= ob_cursor(r
? OB_CURSOR_WEST
: OB_CURSOR_NONE
);
622 XChangeWindowAttributes(ob_display
, self
->leftresize
, CWCursor
, &a
);
623 a
.cursor
= ob_cursor(r
? OB_CURSOR_EAST
: OB_CURSOR_NONE
);
624 XChangeWindowAttributes(ob_display
, self
->rightresize
, CWCursor
, &a
);
625 a
.cursor
= ob_cursor(r
? OB_CURSOR_SOUTH
: OB_CURSOR_NONE
);
626 XChangeWindowAttributes(ob_display
, self
->handle
, CWCursor
, &a
);
627 a
.cursor
= ob_cursor(r
? OB_CURSOR_SOUTHWEST
: OB_CURSOR_NONE
);
628 XChangeWindowAttributes(ob_display
, self
->lgrip
, CWCursor
, &a
);
629 a
.cursor
= ob_cursor(r
? OB_CURSOR_SOUTHEAST
: OB_CURSOR_NONE
);
630 XChangeWindowAttributes(ob_display
, self
->rgrip
, CWCursor
, &a
);
632 self
->functions
= self
->client
->functions
;
636 void frame_adjust_client_area(ObFrame
*self
)
638 /* resize the plate */
639 XResizeWindow(ob_display
, self
->plate
,
640 self
->client
->area
.width
, self
->client
->area
.height
);
643 void frame_adjust_state(ObFrame
*self
)
645 framerender_frame(self
);
648 void frame_adjust_focus(ObFrame
*self
, gboolean hilite
)
650 self
->focused
= hilite
;
651 framerender_frame(self
);
655 void frame_adjust_title(ObFrame
*self
)
657 framerender_frame(self
);
660 void frame_adjust_icon(ObFrame
*self
)
662 framerender_frame(self
);
665 void frame_grab_client(ObFrame
*self
)
667 /* reparent the client to the frame */
668 XReparentWindow(ob_display
, self
->client
->window
, self
->plate
, 0, 0);
671 When reparenting the client window, it is usually not mapped yet, since
672 this occurs from a MapRequest. However, in the case where Openbox is
673 starting up, the window is already mapped, so we'll see unmap events for
674 it. There are 2 unmap events generated that we see, one with the 'event'
675 member set the root window, and one set to the client, but both get
676 handled and need to be ignored.
678 if (ob_state() == OB_STATE_STARTING
)
679 self
->client
->ignore_unmaps
+= 2;
681 /* select the event mask on the client's parent (to receive config/map
682 req's) the ButtonPress is to catch clicks on the client border */
683 XSelectInput(ob_display
, self
->plate
, PLATE_EVENTMASK
);
685 /* map the client so it maps when the frame does */
686 XMapWindow(ob_display
, self
->client
->window
);
688 /* set all the windows for the frame in the window_map */
689 g_hash_table_insert(window_map
, &self
->window
, self
->client
);
690 g_hash_table_insert(window_map
, &self
->plate
, self
->client
);
691 g_hash_table_insert(window_map
, &self
->inner
, self
->client
);
692 g_hash_table_insert(window_map
, &self
->title
, self
->client
);
693 g_hash_table_insert(window_map
, &self
->label
, self
->client
);
694 g_hash_table_insert(window_map
, &self
->max
, self
->client
);
695 g_hash_table_insert(window_map
, &self
->close
, self
->client
);
696 g_hash_table_insert(window_map
, &self
->desk
, self
->client
);
697 g_hash_table_insert(window_map
, &self
->shade
, self
->client
);
698 g_hash_table_insert(window_map
, &self
->icon
, self
->client
);
699 g_hash_table_insert(window_map
, &self
->iconify
, self
->client
);
700 g_hash_table_insert(window_map
, &self
->handle
, self
->client
);
701 g_hash_table_insert(window_map
, &self
->lgrip
, self
->client
);
702 g_hash_table_insert(window_map
, &self
->rgrip
, self
->client
);
703 g_hash_table_insert(window_map
, &self
->topresize
, self
->client
);
704 g_hash_table_insert(window_map
, &self
->tltresize
, self
->client
);
705 g_hash_table_insert(window_map
, &self
->tllresize
, self
->client
);
706 g_hash_table_insert(window_map
, &self
->trtresize
, self
->client
);
707 g_hash_table_insert(window_map
, &self
->trrresize
, self
->client
);
708 g_hash_table_insert(window_map
, &self
->leftresize
, self
->client
);
709 g_hash_table_insert(window_map
, &self
->rightresize
, self
->client
);
710 g_hash_table_insert(window_map
, &self
->titleleft
, self
->client
);
711 g_hash_table_insert(window_map
, &self
->titletop
, self
->client
);
712 g_hash_table_insert(window_map
, &self
->titletopleft
, self
->client
);
713 g_hash_table_insert(window_map
, &self
->titletopright
, self
->client
);
714 g_hash_table_insert(window_map
, &self
->titleright
, self
->client
);
715 g_hash_table_insert(window_map
, &self
->titlebottom
, self
->client
);
718 void frame_release_client(ObFrame
*self
)
721 gboolean reparent
= TRUE
;
723 /* if there was any animation going on, kill it */
724 ob_main_loop_timeout_remove_data(ob_main_loop
, frame_animate_iconify
,
727 /* check if the app has already reparented its window away */
728 while (XCheckTypedWindowEvent(ob_display
, self
->client
->window
,
729 ReparentNotify
, &ev
))
731 /* This check makes sure we don't catch our own reparent action to
732 our frame window. This doesn't count as the app reparenting itself
735 Reparent events that are generated by us are just discarded here.
736 They are of no consequence to us anyhow.
738 if (ev
.xreparent
.parent
!= self
->plate
) {
740 XPutBackEvent(ob_display
, &ev
);
746 /* according to the ICCCM - if the client doesn't reparent itself,
747 then we will reparent the window to root for them */
748 XReparentWindow(ob_display
, self
->client
->window
,
749 RootWindow(ob_display
, ob_screen
),
750 self
->client
->area
.x
,
751 self
->client
->area
.y
);
754 /* remove all the windows for the frame from the window_map */
755 g_hash_table_remove(window_map
, &self
->window
);
756 g_hash_table_remove(window_map
, &self
->plate
);
757 g_hash_table_remove(window_map
, &self
->inner
);
758 g_hash_table_remove(window_map
, &self
->title
);
759 g_hash_table_remove(window_map
, &self
->label
);
760 g_hash_table_remove(window_map
, &self
->max
);
761 g_hash_table_remove(window_map
, &self
->close
);
762 g_hash_table_remove(window_map
, &self
->desk
);
763 g_hash_table_remove(window_map
, &self
->shade
);
764 g_hash_table_remove(window_map
, &self
->icon
);
765 g_hash_table_remove(window_map
, &self
->iconify
);
766 g_hash_table_remove(window_map
, &self
->handle
);
767 g_hash_table_remove(window_map
, &self
->lgrip
);
768 g_hash_table_remove(window_map
, &self
->rgrip
);
769 g_hash_table_remove(window_map
, &self
->topresize
);
770 g_hash_table_remove(window_map
, &self
->tltresize
);
771 g_hash_table_remove(window_map
, &self
->tllresize
);
772 g_hash_table_remove(window_map
, &self
->trtresize
);
773 g_hash_table_remove(window_map
, &self
->trrresize
);
774 g_hash_table_remove(window_map
, &self
->leftresize
);
775 g_hash_table_remove(window_map
, &self
->rightresize
);
776 g_hash_table_remove(window_map
, &self
->titleleft
);
777 g_hash_table_remove(window_map
, &self
->titletop
);
778 g_hash_table_remove(window_map
, &self
->titletopleft
);
779 g_hash_table_remove(window_map
, &self
->titletopright
);
780 g_hash_table_remove(window_map
, &self
->titleright
);
781 g_hash_table_remove(window_map
, &self
->titlebottom
);
783 ob_main_loop_timeout_remove_data(ob_main_loop
, flash_timeout
, self
, TRUE
);
786 /* is there anything present between us and the label? */
787 static gboolean
is_button_present(ObFrame
*self
, const gchar
*lc
, gint dir
) {
788 for (; *lc
!= '\0' && lc
>= config_title_layout
; lc
+= dir
) {
789 if (*lc
== ' ') continue; /* it was invalid */
790 if (*lc
== 'N' && self
->decorations
& OB_FRAME_DECOR_ICON
)
792 if (*lc
== 'D' && self
->decorations
& OB_FRAME_DECOR_ALLDESKTOPS
)
794 if (*lc
== 'S' && self
->decorations
& OB_FRAME_DECOR_SHADE
)
796 if (*lc
== 'I' && self
->decorations
& OB_FRAME_DECOR_ICONIFY
)
798 if (*lc
== 'M' && self
->decorations
& OB_FRAME_DECOR_MAXIMIZE
)
800 if (*lc
== 'C' && self
->decorations
& OB_FRAME_DECOR_CLOSE
)
802 if (*lc
== 'L') return FALSE
;
807 static void layout_title(ObFrame
*self
)
812 const gint bwidth
= ob_rr_theme
->button_size
+ ob_rr_theme
->paddingx
+ 1;
813 /* position of the left most button */
814 const gint left
= ob_rr_theme
->paddingx
+ 1;
815 /* position of the right most button */
816 const gint right
= self
->width
- bwidth
;
818 /* turn them all off */
819 self
->icon_on
= self
->desk_on
= self
->shade_on
= self
->iconify_on
=
820 self
->max_on
= self
->close_on
= self
->label_on
= FALSE
;
821 self
->label_width
= self
->width
- (ob_rr_theme
->paddingx
+ 1) * 2;
822 self
->leftmost
= self
->rightmost
= OB_FRAME_CONTEXT_NONE
;
824 /* figure out what's being show, find each element's position, and the
827 do the ones before the label, then after the label,
828 i will be +1 the first time through when working to the left,
829 and -1 the second time through when working to the right */
830 for (i
= 1; i
>= -1; i
-=2) {
832 ObFrameContext
*firstcon
;
836 lc
= config_title_layout
;
837 firstcon
= &self
->leftmost
;
840 lc
= config_title_layout
+ strlen(config_title_layout
)-1;
841 firstcon
= &self
->rightmost
;
844 /* stop at the end of the string (or the label, which calls break) */
845 for (; *lc
!= '\0' && lc
>= config_title_layout
; lc
+=i
) {
848 self
->label_on
= TRUE
;
851 break; /* break the for loop, do other side of label */
852 } else if (*lc
== 'N') {
853 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_ICON
;
854 if ((self
->icon_on
= is_button_present(self
, lc
, i
))) {
855 /* icon is bigger than buttons */
856 self
->label_width
-= bwidth
+ 2;
858 x
+= i
* (bwidth
+ 2);
860 } else if (*lc
== 'D') {
861 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_ALLDESKTOPS
;
862 if ((self
->desk_on
= is_button_present(self
, lc
, i
))) {
863 self
->label_width
-= bwidth
;
867 } else if (*lc
== 'S') {
868 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_SHADE
;
869 if ((self
->shade_on
= is_button_present(self
, lc
, i
))) {
870 self
->label_width
-= bwidth
;
874 } else if (*lc
== 'I') {
875 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_ICONIFY
;
876 if ((self
->iconify_on
= is_button_present(self
, lc
, i
))) {
877 self
->label_width
-= bwidth
;
881 } else if (*lc
== 'M') {
882 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_MAXIMIZE
;
883 if ((self
->max_on
= is_button_present(self
, lc
, i
))) {
884 self
->label_width
-= bwidth
;
888 } else if (*lc
== 'C') {
889 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_CLOSE
;
890 if ((self
->close_on
= is_button_present(self
, lc
, i
))) {
891 self
->label_width
-= bwidth
;
896 continue; /* don't set firstcon */
901 /* position and map the elements */
903 XMapWindow(ob_display
, self
->icon
);
904 XMoveWindow(ob_display
, self
->icon
, self
->icon_x
,
905 ob_rr_theme
->paddingy
);
907 XUnmapWindow(ob_display
, self
->icon
);
910 XMapWindow(ob_display
, self
->desk
);
911 XMoveWindow(ob_display
, self
->desk
, self
->desk_x
,
912 ob_rr_theme
->paddingy
+ 1);
914 XUnmapWindow(ob_display
, self
->desk
);
916 if (self
->shade_on
) {
917 XMapWindow(ob_display
, self
->shade
);
918 XMoveWindow(ob_display
, self
->shade
, self
->shade_x
,
919 ob_rr_theme
->paddingy
+ 1);
921 XUnmapWindow(ob_display
, self
->shade
);
923 if (self
->iconify_on
) {
924 XMapWindow(ob_display
, self
->iconify
);
925 XMoveWindow(ob_display
, self
->iconify
, self
->iconify_x
,
926 ob_rr_theme
->paddingy
+ 1);
928 XUnmapWindow(ob_display
, self
->iconify
);
931 XMapWindow(ob_display
, self
->max
);
932 XMoveWindow(ob_display
, self
->max
, self
->max_x
,
933 ob_rr_theme
->paddingy
+ 1);
935 XUnmapWindow(ob_display
, self
->max
);
937 if (self
->close_on
) {
938 XMapWindow(ob_display
, self
->close
);
939 XMoveWindow(ob_display
, self
->close
, self
->close_x
,
940 ob_rr_theme
->paddingy
+ 1);
942 XUnmapWindow(ob_display
, self
->close
);
944 if (self
->label_on
) {
945 self
->label_width
= MAX(1, self
->label_width
); /* no lower than 1 */
946 XMapWindow(ob_display
, self
->label
);
947 XMoveWindow(ob_display
, self
->label
, self
->label_x
,
948 ob_rr_theme
->paddingy
);
950 XUnmapWindow(ob_display
, self
->label
);
953 ObFrameContext
frame_context_from_string(const gchar
*name
)
955 if (!g_ascii_strcasecmp("Desktop", name
))
956 return OB_FRAME_CONTEXT_DESKTOP
;
957 else if (!g_ascii_strcasecmp("Root", name
))
958 return OB_FRAME_CONTEXT_ROOT
;
959 else if (!g_ascii_strcasecmp("Client", name
))
960 return OB_FRAME_CONTEXT_CLIENT
;
961 else if (!g_ascii_strcasecmp("Titlebar", name
))
962 return OB_FRAME_CONTEXT_TITLEBAR
;
963 else if (!g_ascii_strcasecmp("Frame", name
))
964 return OB_FRAME_CONTEXT_FRAME
;
965 else if (!g_ascii_strcasecmp("TLCorner", name
))
966 return OB_FRAME_CONTEXT_TLCORNER
;
967 else if (!g_ascii_strcasecmp("TRCorner", name
))
968 return OB_FRAME_CONTEXT_TRCORNER
;
969 else if (!g_ascii_strcasecmp("BLCorner", name
))
970 return OB_FRAME_CONTEXT_BLCORNER
;
971 else if (!g_ascii_strcasecmp("BRCorner", name
))
972 return OB_FRAME_CONTEXT_BRCORNER
;
973 else if (!g_ascii_strcasecmp("Top", name
))
974 return OB_FRAME_CONTEXT_TOP
;
975 else if (!g_ascii_strcasecmp("Bottom", name
))
976 return OB_FRAME_CONTEXT_BOTTOM
;
977 else if (!g_ascii_strcasecmp("Left", name
))
978 return OB_FRAME_CONTEXT_LEFT
;
979 else if (!g_ascii_strcasecmp("Right", name
))
980 return OB_FRAME_CONTEXT_RIGHT
;
981 else if (!g_ascii_strcasecmp("Maximize", name
))
982 return OB_FRAME_CONTEXT_MAXIMIZE
;
983 else if (!g_ascii_strcasecmp("AllDesktops", name
))
984 return OB_FRAME_CONTEXT_ALLDESKTOPS
;
985 else if (!g_ascii_strcasecmp("Shade", name
))
986 return OB_FRAME_CONTEXT_SHADE
;
987 else if (!g_ascii_strcasecmp("Iconify", name
))
988 return OB_FRAME_CONTEXT_ICONIFY
;
989 else if (!g_ascii_strcasecmp("Icon", name
))
990 return OB_FRAME_CONTEXT_ICON
;
991 else if (!g_ascii_strcasecmp("Close", name
))
992 return OB_FRAME_CONTEXT_CLOSE
;
993 else if (!g_ascii_strcasecmp("MoveResize", name
))
994 return OB_FRAME_CONTEXT_MOVE_RESIZE
;
995 return OB_FRAME_CONTEXT_NONE
;
998 ObFrameContext
frame_context(ObClient
*client
, Window win
, gint x
, gint y
)
1002 if (moveresize_in_progress
)
1003 return OB_FRAME_CONTEXT_MOVE_RESIZE
;
1005 if (win
== RootWindow(ob_display
, ob_screen
))
1006 return OB_FRAME_CONTEXT_ROOT
;
1007 if (client
== NULL
) return OB_FRAME_CONTEXT_NONE
;
1008 if (win
== client
->window
) {
1009 /* conceptually, this is the desktop, as far as users are
1011 if (client
->type
== OB_CLIENT_TYPE_DESKTOP
)
1012 return OB_FRAME_CONTEXT_DESKTOP
;
1013 return OB_FRAME_CONTEXT_CLIENT
;
1016 self
= client
->frame
;
1017 if (win
== self
->inner
|| win
== self
->plate
) {
1018 /* conceptually, this is the desktop, as far as users are
1020 if (client
->type
== OB_CLIENT_TYPE_DESKTOP
)
1021 return OB_FRAME_CONTEXT_DESKTOP
;
1022 return OB_FRAME_CONTEXT_CLIENT
;
1025 if (win
== self
->title
) {
1026 /* when the user clicks in the corners of the titlebar and the client
1027 is fully maximized, then treat it like they clicked in the
1028 button that is there */
1029 if (self
->client
->max_horz
&& self
->client
->max_vert
&&
1030 y
< ob_rr_theme
->paddingy
+ 1 + ob_rr_theme
->button_size
)
1032 if (x
< ((ob_rr_theme
->paddingx
+ 1) * 2 +
1033 ob_rr_theme
->button_size
)) {
1034 if (self
->leftmost
!= OB_FRAME_CONTEXT_NONE
)
1035 return self
->leftmost
;
1037 else if (x
> (self
->width
-
1038 (ob_rr_theme
->paddingx
+ 1 +
1039 ob_rr_theme
->button_size
)))
1041 if (self
->rightmost
!= OB_FRAME_CONTEXT_NONE
)
1042 return self
->rightmost
;
1045 return OB_FRAME_CONTEXT_TITLEBAR
;
1048 if (win
== self
->window
) return OB_FRAME_CONTEXT_FRAME
;
1049 if (win
== self
->label
) return OB_FRAME_CONTEXT_TITLEBAR
;
1050 if (win
== self
->handle
) return OB_FRAME_CONTEXT_BOTTOM
;
1051 if (win
== self
->lgrip
) return OB_FRAME_CONTEXT_BLCORNER
;
1052 if (win
== self
->rgrip
) return OB_FRAME_CONTEXT_BRCORNER
;
1053 if (win
== self
->titletop
) return OB_FRAME_CONTEXT_TOP
;
1054 if (win
== self
->topresize
) return OB_FRAME_CONTEXT_TOP
;
1055 if (win
== self
->tltresize
) return OB_FRAME_CONTEXT_TLCORNER
;
1056 if (win
== self
->tllresize
) return OB_FRAME_CONTEXT_TLCORNER
;
1057 if (win
== self
->titleleft
) return OB_FRAME_CONTEXT_TLCORNER
;
1058 if (win
== self
->titletopleft
) return OB_FRAME_CONTEXT_TLCORNER
;
1059 if (win
== self
->trtresize
) return OB_FRAME_CONTEXT_TRCORNER
;
1060 if (win
== self
->trrresize
) return OB_FRAME_CONTEXT_TRCORNER
;
1061 if (win
== self
->titleright
) return OB_FRAME_CONTEXT_TRCORNER
;
1062 if (win
== self
->titletopright
) return OB_FRAME_CONTEXT_TRCORNER
;
1063 if (win
== self
->leftresize
) return OB_FRAME_CONTEXT_LEFT
;
1064 if (win
== self
->rightresize
) return OB_FRAME_CONTEXT_RIGHT
;
1065 if (win
== self
->max
) return OB_FRAME_CONTEXT_MAXIMIZE
;
1066 if (win
== self
->iconify
) return OB_FRAME_CONTEXT_ICONIFY
;
1067 if (win
== self
->close
) return OB_FRAME_CONTEXT_CLOSE
;
1068 if (win
== self
->icon
) return OB_FRAME_CONTEXT_ICON
;
1069 if (win
== self
->desk
) return OB_FRAME_CONTEXT_ALLDESKTOPS
;
1070 if (win
== self
->shade
) return OB_FRAME_CONTEXT_SHADE
;
1072 return OB_FRAME_CONTEXT_NONE
;
1075 void frame_client_gravity(ObFrame
*self
, gint
*x
, gint
*y
, gint w
, gint h
)
1078 switch (self
->client
->gravity
) {
1080 case NorthWestGravity
:
1081 case SouthWestGravity
:
1088 *x
-= (self
->size
.left
+ w
) / 2;
1091 case NorthEastGravity
:
1092 case SouthEastGravity
:
1094 *x
-= (self
->size
.left
+ self
->size
.right
+ w
) - 1;
1099 *x
-= self
->size
.left
;
1104 switch (self
->client
->gravity
) {
1106 case NorthWestGravity
:
1107 case NorthEastGravity
:
1114 *y
-= (self
->size
.top
+ h
) / 2;
1117 case SouthWestGravity
:
1118 case SouthEastGravity
:
1120 *y
-= (self
->size
.top
+ self
->size
.bottom
+ h
) - 1;
1125 *y
-= self
->size
.top
;
1130 void frame_frame_gravity(ObFrame
*self
, gint
*x
, gint
*y
, gint w
, gint h
)
1133 switch (self
->client
->gravity
) {
1135 case NorthWestGravity
:
1137 case SouthWestGravity
:
1142 *x
+= (self
->size
.left
+ w
) / 2;
1144 case NorthEastGravity
:
1146 case SouthEastGravity
:
1147 *x
+= (self
->size
.left
+ self
->size
.right
+ w
) - 1;
1151 *x
+= self
->size
.left
;
1156 switch (self
->client
->gravity
) {
1158 case NorthWestGravity
:
1160 case NorthEastGravity
:
1165 *y
+= (self
->size
.top
+ h
) / 2;
1167 case SouthWestGravity
:
1169 case SouthEastGravity
:
1170 *y
+= (self
->size
.top
+ self
->size
.bottom
+ h
) - 1;
1174 *y
+= self
->size
.top
;
1179 static void flash_done(gpointer data
)
1181 ObFrame
*self
= data
;
1183 if (self
->focused
!= self
->flash_on
)
1184 frame_adjust_focus(self
, self
->focused
);
1187 static gboolean
flash_timeout(gpointer data
)
1189 ObFrame
*self
= data
;
1192 g_get_current_time(&now
);
1193 if (now
.tv_sec
> self
->flash_end
.tv_sec
||
1194 (now
.tv_sec
== self
->flash_end
.tv_sec
&&
1195 now
.tv_usec
>= self
->flash_end
.tv_usec
))
1196 self
->flashing
= FALSE
;
1198 if (!self
->flashing
)
1199 return FALSE
; /* we are done */
1201 self
->flash_on
= !self
->flash_on
;
1202 if (!self
->focused
) {
1203 frame_adjust_focus(self
, self
->flash_on
);
1204 self
->focused
= FALSE
;
1207 return TRUE
; /* go again */
1210 void frame_flash_start(ObFrame
*self
)
1212 self
->flash_on
= self
->focused
;
1214 if (!self
->flashing
)
1215 ob_main_loop_timeout_add(ob_main_loop
,
1216 G_USEC_PER_SEC
* 0.6,
1221 g_get_current_time(&self
->flash_end
);
1222 g_time_val_add(&self
->flash_end
, G_USEC_PER_SEC
* 5);
1224 self
->flashing
= TRUE
;
1227 void frame_flash_stop(ObFrame
*self
)
1229 self
->flashing
= FALSE
;
1232 static gulong
frame_animate_iconify_time_left(ObFrame
*self
,
1233 const GTimeVal
*now
)
1236 sec
= self
->iconify_animation_end
.tv_sec
- now
->tv_sec
;
1237 usec
= self
->iconify_animation_end
.tv_usec
- now
->tv_usec
;
1239 usec
+= G_USEC_PER_SEC
;
1242 /* no negative values */
1243 return MAX(sec
* G_USEC_PER_SEC
+ usec
, 0);
1246 static gboolean
frame_animate_iconify(gpointer p
)
1250 gint iconx
, icony
, iconw
;
1253 gboolean iconifying
;
1255 if (self
->client
->icon_geometry
.width
== 0) {
1256 /* there is no icon geometry set so just go straight down */
1257 Rect
*a
= screen_physical_area();
1258 iconx
= self
->area
.x
+ self
->area
.width
/ 2 + 32;
1259 icony
= a
->y
+ a
->width
;
1262 iconx
= self
->client
->icon_geometry
.x
;
1263 icony
= self
->client
->icon_geometry
.y
;
1264 iconw
= self
->client
->icon_geometry
.width
;
1267 iconifying
= self
->iconify_animation_going
> 0;
1269 /* how far do we have left to go ? */
1270 g_get_current_time(&now
);
1271 time
= frame_animate_iconify_time_left(self
, &now
);
1273 if (time
== 0 || iconifying
) {
1274 /* start where the frame is supposed to be */
1277 w
= self
->area
.width
- self
->bwidth
* 2;
1278 h
= self
->area
.height
- self
->bwidth
* 2;
1280 /* start at the icon */
1284 h
= self
->size
.top
; /* just the titlebar */
1291 dx
= self
->area
.x
- iconx
;
1292 dy
= self
->area
.y
- icony
;
1293 dw
= self
->area
.width
- self
->bwidth
* 2 - iconw
;
1294 /* if restoring, we move in the opposite direction */
1295 if (!iconifying
) { dx
= -dx
; dy
= -dy
; dw
= -dw
; }
1297 elapsed
= FRAME_ANIMATE_ICONIFY_TIME
- time
;
1298 x
= x
- (dx
* elapsed
) / FRAME_ANIMATE_ICONIFY_TIME
;
1299 y
= y
- (dy
* elapsed
) / FRAME_ANIMATE_ICONIFY_TIME
;
1300 w
= w
- (dw
* elapsed
) / FRAME_ANIMATE_ICONIFY_TIME
;
1301 h
= self
->size
.top
; /* just the titlebar */
1305 frame_end_iconify_animation(self
);
1307 XMoveResizeWindow(ob_display
, self
->window
, x
, y
, w
, h
);
1311 return time
> 0; /* repeat until we're out of time */
1314 void frame_end_iconify_animation(ObFrame
*self
)
1316 /* see if there is an animation going */
1317 if (self
->iconify_animation_going
== 0) return;
1320 XUnmapWindow(ob_display
, self
->window
);
1322 /* Send a ConfigureNotify when the animation is done, this fixes
1323 KDE's pager showing the window in the wrong place. */
1324 client_reconfigure(self
->client
);
1326 /* we're not animating any more ! */
1327 self
->iconify_animation_going
= 0;
1329 XMoveResizeWindow(ob_display
, self
->window
,
1330 self
->area
.x
, self
->area
.y
,
1331 self
->area
.width
- self
->bwidth
* 2,
1332 self
->area
.height
- self
->bwidth
* 2);
1336 void frame_begin_iconify_animation(ObFrame
*self
, gboolean iconifying
)
1339 gboolean new_anim
= FALSE
;
1340 gboolean set_end
= TRUE
;
1343 /* if there is no titlebar, just don't animate for now
1344 XXX it would be nice tho.. */
1345 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1348 /* get the current time */
1349 g_get_current_time(&now
);
1351 /* get how long until the end */
1352 time
= FRAME_ANIMATE_ICONIFY_TIME
;
1353 if (self
->iconify_animation_going
) {
1354 if (!!iconifying
!= (self
->iconify_animation_going
> 0)) {
1355 /* animation was already going on in the opposite direction */
1356 time
= time
- frame_animate_iconify_time_left(self
, &now
);
1358 /* animation was already going in the same direction */
1362 self
->iconify_animation_going
= iconifying
? 1 : -1;
1364 /* set the ending time */
1366 self
->iconify_animation_end
.tv_sec
= now
.tv_sec
;
1367 self
->iconify_animation_end
.tv_usec
= now
.tv_usec
;
1368 g_time_val_add(&self
->iconify_animation_end
, time
);
1372 ob_main_loop_timeout_remove_data(ob_main_loop
, frame_animate_iconify
,
1374 ob_main_loop_timeout_add(ob_main_loop
,
1375 FRAME_ANIMATE_ICONIFY_STEP_TIME
,
1376 frame_animate_iconify
, self
,
1377 g_direct_equal
, NULL
);
1379 /* do the first step */
1380 frame_animate_iconify(self
);
1382 /* show it during the animation even if it is not "visible" */
1384 XMapWindow(ob_display
, self
->window
);