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"
29 #include "moveresize.h"
31 #include "render/theme.h"
33 #define PLATE_EVENTMASK (SubstructureRedirectMask | FocusChangeMask)
34 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
35 ButtonPressMask | ButtonReleaseMask)
36 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
38 EnterWindowMask | LeaveWindowMask)
39 /* The inner window does not need enter/leave events.
40 If it does get them, then it needs its own context for enter events
41 because sloppy focus will focus the window when you enter the inner window
43 #define INNER_EVENTMASK (ButtonPressMask)
45 #define FRAME_ANIMATE_ICONIFY_TIME 150000 /* .15 seconds */
46 #define FRAME_ANIMATE_ICONIFY_STEP_TIME (G_USEC_PER_SEC / 30) /* 30 Hz */
48 #define FRAME_HANDLE_Y(f) (f->innersize.top + f->client->area.height + \
51 static void layout_title(ObFrame
*self
);
52 static void flash_done(gpointer data
);
53 static gboolean
flash_timeout(gpointer data
);
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 ret
= XGetWindowAttributes(ob_display
, c
->window
, &wattrib
);
75 g_assert(ret
!= BadDrawable
);
76 g_assert(ret
!= BadWindow
);
78 if (wattrib
.depth
== 32)
79 return wattrib
.visual
;
83 ObFrame
*frame_new(ObClient
*client
)
85 XSetWindowAttributes attrib
;
90 self
= g_new0(ObFrame
, 1);
92 visual
= check_32bit_client(client
);
94 /* create the non-visible decor windows */
98 /* client has a 32-bit visual */
99 mask
|= CWColormap
| CWBackPixel
| CWBorderPixel
;
100 /* create a colormap with the visual */
101 self
->colormap
= attrib
.colormap
=
102 XCreateColormap(ob_display
,
103 RootWindow(ob_display
, ob_screen
),
105 attrib
.background_pixel
= BlackPixel(ob_display
, 0);
106 attrib
.border_pixel
= BlackPixel(ob_display
, 0);
108 attrib
.event_mask
= FRAME_EVENTMASK
;
109 self
->window
= createWindow(RootWindow(ob_display
, ob_screen
), visual
,
112 attrib
.event_mask
= INNER_EVENTMASK
;
113 self
->inner
= createWindow(self
->window
, visual
, mask
, &attrib
);
115 mask
&= ~CWEventMask
;
116 self
->plate
= createWindow(self
->inner
, visual
, mask
, &attrib
);
118 /* create the visible decor windows */
122 /* client has a 32-bit visual */
123 mask
|= CWColormap
| CWBackPixel
| CWBorderPixel
;
124 attrib
.colormap
= RrColormap(ob_rr_inst
);
126 attrib
.event_mask
= ELEMENT_EVENTMASK
;
127 self
->title
= createWindow(self
->window
, NULL
, mask
, &attrib
);
130 attrib
.cursor
= ob_cursor(OB_CURSOR_NORTHWEST
);
131 self
->tltresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
132 self
->tllresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
133 attrib
.cursor
= ob_cursor(OB_CURSOR_NORTHEAST
);
134 self
->trtresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
135 self
->trrresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
138 self
->label
= createWindow(self
->title
, NULL
, mask
, &attrib
);
139 self
->max
= createWindow(self
->title
, NULL
, mask
, &attrib
);
140 self
->close
= createWindow(self
->title
, NULL
, mask
, &attrib
);
141 self
->desk
= createWindow(self
->title
, NULL
, mask
, &attrib
);
142 self
->shade
= createWindow(self
->title
, NULL
, mask
, &attrib
);
143 self
->icon
= createWindow(self
->title
, NULL
, mask
, &attrib
);
144 self
->iconify
= createWindow(self
->title
, NULL
, mask
, &attrib
);
145 self
->handle
= createWindow(self
->window
, NULL
, mask
, &attrib
);
148 attrib
.cursor
= ob_cursor(OB_CURSOR_SOUTHWEST
);
149 self
->lgrip
= createWindow(self
->handle
, NULL
, mask
, &attrib
);
150 attrib
.cursor
= ob_cursor(OB_CURSOR_SOUTHEAST
);
151 self
->rgrip
= createWindow(self
->handle
, NULL
, mask
, &attrib
);
153 self
->focused
= FALSE
;
155 /* the other stuff is shown based on decor settings */
156 XMapWindow(ob_display
, self
->plate
);
157 XMapWindow(ob_display
, self
->inner
);
158 XMapWindow(ob_display
, self
->lgrip
);
159 XMapWindow(ob_display
, self
->rgrip
);
160 XMapWindow(ob_display
, self
->label
);
162 self
->max_press
= self
->close_press
= self
->desk_press
=
163 self
->iconify_press
= self
->shade_press
= FALSE
;
164 self
->max_hover
= self
->close_hover
= self
->desk_hover
=
165 self
->iconify_hover
= self
->shade_hover
= FALSE
;
167 set_theme_statics(self
);
169 return (ObFrame
*)self
;
172 static void set_theme_statics(ObFrame
*self
)
174 /* set colors/appearance/sizes for stuff that doesn't change */
175 XSetWindowBorder(ob_display
, self
->window
,
176 RrColorPixel(ob_rr_theme
->frame_b_color
));
177 XSetWindowBorder(ob_display
, self
->inner
,
178 RrColorPixel(ob_rr_theme
->frame_b_color
));
179 XSetWindowBorder(ob_display
, self
->title
,
180 RrColorPixel(ob_rr_theme
->frame_b_color
));
181 XSetWindowBorder(ob_display
, self
->handle
,
182 RrColorPixel(ob_rr_theme
->frame_b_color
));
183 XSetWindowBorder(ob_display
, self
->rgrip
,
184 RrColorPixel(ob_rr_theme
->frame_b_color
));
185 XSetWindowBorder(ob_display
, self
->lgrip
,
186 RrColorPixel(ob_rr_theme
->frame_b_color
));
188 XResizeWindow(ob_display
, self
->max
,
189 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
190 XResizeWindow(ob_display
, self
->iconify
,
191 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
192 XResizeWindow(ob_display
, self
->icon
,
193 ob_rr_theme
->button_size
+ 2, ob_rr_theme
->button_size
+ 2);
194 XResizeWindow(ob_display
, self
->close
,
195 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
196 XResizeWindow(ob_display
, self
->desk
,
197 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
198 XResizeWindow(ob_display
, self
->shade
,
199 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
200 if (ob_rr_theme
->handle_height
> 0) {
201 XResizeWindow(ob_display
, self
->lgrip
,
202 ob_rr_theme
->grip_width
, ob_rr_theme
->handle_height
);
203 XResizeWindow(ob_display
, self
->rgrip
,
204 ob_rr_theme
->grip_width
, ob_rr_theme
->handle_height
);
206 XResizeWindow(ob_display
, self
->tltresize
,
207 ob_rr_theme
->grip_width
, ob_rr_theme
->paddingy
+ 1);
208 XResizeWindow(ob_display
, self
->trtresize
,
209 ob_rr_theme
->grip_width
, ob_rr_theme
->paddingy
+ 1);
210 XResizeWindow(ob_display
, self
->tllresize
,
211 ob_rr_theme
->paddingx
+ 1, ob_rr_theme
->title_height
);
212 XResizeWindow(ob_display
, self
->trrresize
,
213 ob_rr_theme
->paddingx
+ 1, ob_rr_theme
->title_height
);
215 /* set up the dynamic appearances */
216 self
->a_unfocused_title
= RrAppearanceCopy(ob_rr_theme
->a_unfocused_title
);
217 self
->a_focused_title
= RrAppearanceCopy(ob_rr_theme
->a_focused_title
);
218 self
->a_unfocused_label
= RrAppearanceCopy(ob_rr_theme
->a_unfocused_label
);
219 self
->a_focused_label
= RrAppearanceCopy(ob_rr_theme
->a_focused_label
);
220 self
->a_unfocused_handle
=
221 RrAppearanceCopy(ob_rr_theme
->a_unfocused_handle
);
222 self
->a_focused_handle
= RrAppearanceCopy(ob_rr_theme
->a_focused_handle
);
223 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_icon
);
226 static void free_theme_statics(ObFrame
*self
)
228 RrAppearanceFree(self
->a_unfocused_title
);
229 RrAppearanceFree(self
->a_focused_title
);
230 RrAppearanceFree(self
->a_unfocused_label
);
231 RrAppearanceFree(self
->a_focused_label
);
232 RrAppearanceFree(self
->a_unfocused_handle
);
233 RrAppearanceFree(self
->a_focused_handle
);
234 RrAppearanceFree(self
->a_icon
);
237 static void frame_free(ObFrame
*self
)
239 free_theme_statics(self
);
241 XDestroyWindow(ob_display
, self
->window
);
243 XFreeColormap(ob_display
, self
->colormap
);
248 void frame_show(ObFrame
*self
)
250 if (!self
->visible
) {
251 self
->visible
= TRUE
;
252 XMapWindow(ob_display
, self
->client
->window
);
253 XMapWindow(ob_display
, self
->window
);
257 void frame_hide(ObFrame
*self
)
260 self
->visible
= FALSE
;
261 self
->client
->ignore_unmaps
+= 1;
262 /* we unmap the client itself so that we can get MapRequest
263 events, and because the ICCCM tells us to! */
264 XUnmapWindow(ob_display
, self
->window
);
265 XUnmapWindow(ob_display
, self
->client
->window
);
269 void frame_adjust_theme(ObFrame
*self
)
271 free_theme_statics(self
);
272 set_theme_statics(self
);
275 void frame_adjust_shape(ObFrame
*self
)
281 if (!self
->client
->shaped
) {
282 /* clear the shape on the frame window */
283 XShapeCombineMask(ob_display
, self
->window
, ShapeBounding
,
284 self
->innersize
.left
,
288 /* make the frame's shape match the clients */
289 XShapeCombineShape(ob_display
, self
->window
, ShapeBounding
,
290 self
->innersize
.left
,
292 self
->client
->window
,
293 ShapeBounding
, ShapeSet
);
296 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
) {
297 xrect
[0].x
= -ob_rr_theme
->fbwidth
;
298 xrect
[0].y
= -ob_rr_theme
->fbwidth
;
299 xrect
[0].width
= self
->width
+ self
->rbwidth
* 2;
300 xrect
[0].height
= ob_rr_theme
->title_height
+
305 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
) {
306 xrect
[1].x
= -ob_rr_theme
->fbwidth
;
307 xrect
[1].y
= FRAME_HANDLE_Y(self
);
308 xrect
[1].width
= self
->width
+ self
->rbwidth
* 2;
309 xrect
[1].height
= ob_rr_theme
->handle_height
+
314 XShapeCombineRectangles(ob_display
, self
->window
,
315 ShapeBounding
, 0, 0, xrect
, num
,
316 ShapeUnion
, Unsorted
);
321 void frame_adjust_area(ObFrame
*self
, gboolean moved
,
322 gboolean resized
, gboolean fake
)
326 oldsize
= self
->size
;
329 self
->decorations
= self
->client
->decorations
;
330 self
->max_horz
= self
->client
->max_horz
;
332 if (self
->decorations
& OB_FRAME_DECOR_BORDER
) {
333 self
->bwidth
= ob_rr_theme
->fbwidth
;
334 self
->cbwidth_x
= ob_rr_theme
->cbwidthx
;
335 self
->cbwidth_y
= ob_rr_theme
->cbwidthy
;
337 self
->bwidth
= self
->cbwidth_x
= self
->cbwidth_y
= 0;
339 self
->rbwidth
= self
->bwidth
;
342 self
->bwidth
= self
->cbwidth_x
= 0;
344 STRUT_SET(self
->innersize
,
349 self
->width
= self
->client
->area
.width
+ self
->cbwidth_x
* 2 -
350 (self
->max_horz
? self
->rbwidth
* 2 : 0);
351 self
->width
= MAX(self
->width
, 1); /* no lower than 1 */
353 /* set border widths */
355 XSetWindowBorderWidth(ob_display
, self
->window
, self
->bwidth
);
356 XSetWindowBorderWidth(ob_display
, self
->inner
, self
->bwidth
);
357 XSetWindowBorderWidth(ob_display
, self
->title
, self
->rbwidth
);
358 XSetWindowBorderWidth(ob_display
, self
->handle
, self
->rbwidth
);
359 XSetWindowBorderWidth(ob_display
, self
->lgrip
, self
->rbwidth
);
360 XSetWindowBorderWidth(ob_display
, self
->rgrip
, self
->rbwidth
);
363 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
)
364 self
->innersize
.top
+= ob_rr_theme
->title_height
+ self
->rbwidth
+
365 (self
->rbwidth
- self
->bwidth
);
366 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
&&
367 ob_rr_theme
->handle_height
> 0)
368 self
->innersize
.bottom
+= ob_rr_theme
->handle_height
+
369 self
->rbwidth
+ (self
->rbwidth
- self
->bwidth
);
371 /* they all default off, they're turned on in layout_title */
375 self
->iconify_x
= -1;
380 /* position/size and map/unmap all the windows */
383 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
) {
384 XMoveResizeWindow(ob_display
, self
->title
,
385 -self
->bwidth
, -self
->bwidth
,
386 self
->width
, ob_rr_theme
->title_height
);
387 XMapWindow(ob_display
, self
->title
);
389 if (self
->decorations
& OB_FRAME_DECOR_GRIPS
) {
390 XMoveWindow(ob_display
, self
->tltresize
, 0, 0);
391 XMoveWindow(ob_display
, self
->tllresize
, 0, 0);
392 XMoveWindow(ob_display
, self
->trtresize
,
393 self
->width
- ob_rr_theme
->grip_width
, 0);
394 XMoveWindow(ob_display
, self
->trrresize
,
395 self
->width
- ob_rr_theme
->paddingx
- 1, 0);
396 XMapWindow(ob_display
, self
->tltresize
);
397 XMapWindow(ob_display
, self
->tllresize
);
398 XMapWindow(ob_display
, self
->trtresize
);
399 XMapWindow(ob_display
, self
->trrresize
);
401 XUnmapWindow(ob_display
, self
->tltresize
);
402 XUnmapWindow(ob_display
, self
->tllresize
);
403 XUnmapWindow(ob_display
, self
->trtresize
);
404 XUnmapWindow(ob_display
, self
->trrresize
);
407 XUnmapWindow(ob_display
, self
->title
);
410 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
)
411 /* layout the title bar elements */
415 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
&&
416 ob_rr_theme
->handle_height
> 0)
418 XMoveResizeWindow(ob_display
, self
->handle
,
419 -self
->bwidth
, FRAME_HANDLE_Y(self
),
420 self
->width
, ob_rr_theme
->handle_height
);
421 XMapWindow(ob_display
, self
->handle
);
423 if (self
->decorations
& OB_FRAME_DECOR_GRIPS
) {
424 XMoveWindow(ob_display
, self
->lgrip
,
425 -self
->rbwidth
, -self
->rbwidth
);
426 XMoveWindow(ob_display
, self
->rgrip
,
427 -self
->rbwidth
+ self
->width
-
428 ob_rr_theme
->grip_width
, -self
->rbwidth
);
429 XMapWindow(ob_display
, self
->lgrip
);
430 XMapWindow(ob_display
, self
->rgrip
);
432 XUnmapWindow(ob_display
, self
->lgrip
);
433 XUnmapWindow(ob_display
, self
->rgrip
);
436 XUnmapWindow(ob_display
, self
->handle
);
438 /* move and resize the inner border window which contains the plate
440 XMoveResizeWindow(ob_display
, self
->inner
,
441 self
->innersize
.left
- self
->cbwidth_x
-
443 self
->innersize
.top
- self
->cbwidth_y
-
445 self
->client
->area
.width
+
447 self
->client
->area
.height
+
448 self
->cbwidth_y
* 2);
451 XMoveWindow(ob_display
, self
->plate
,
452 self
->cbwidth_x
, self
->cbwidth_y
);
454 /* when the client has StaticGravity, it likes to move around. */
455 XMoveWindow(ob_display
, self
->client
->window
, 0, 0);
458 STRUT_SET(self
->size
,
459 self
->innersize
.left
+ self
->bwidth
,
460 self
->innersize
.top
+ self
->bwidth
,
461 self
->innersize
.right
+ self
->bwidth
,
462 self
->innersize
.bottom
+ self
->bwidth
);
465 /* shading can change without being moved or resized */
466 RECT_SET_SIZE(self
->area
,
467 self
->client
->area
.width
+
468 self
->size
.left
+ self
->size
.right
,
469 (self
->client
->shaded
?
470 ob_rr_theme
->title_height
+ self
->rbwidth
* 2:
471 self
->client
->area
.height
+
472 self
->size
.top
+ self
->size
.bottom
));
474 if (moved
|| resized
) {
475 /* find the new coordinates, done after setting the frame.size, for
476 frame_client_gravity. */
477 self
->area
.x
= self
->client
->area
.x
;
478 self
->area
.y
= self
->client
->area
.y
;
479 frame_client_gravity(self
, &self
->area
.x
, &self
->area
.y
,
480 self
->client
->area
.width
,
481 self
->client
->area
.height
);
485 if (!frame_iconify_animating(self
))
486 /* move and resize the top level frame.
487 shading can change without being moved or resized.
489 but don't do this during an iconify animation. it will be
490 reflected afterwards.
492 XMoveResizeWindow(ob_display
, self
->window
,
493 self
->area
.x
, self
->area
.y
,
494 self
->area
.width
- self
->bwidth
* 2,
495 self
->area
.height
- self
->bwidth
* 2);
498 framerender_frame(self
);
499 frame_adjust_shape(self
);
502 if (!STRUT_EQUAL(self
->size
, oldsize
)) {
504 vals
[0] = self
->size
.left
;
505 vals
[1] = self
->size
.right
;
506 vals
[2] = self
->size
.top
;
507 vals
[3] = self
->size
.bottom
;
508 PROP_SETA32(self
->client
->window
, net_frame_extents
,
512 /* if this occurs while we are focus cycling, the indicator needs to
514 if (focus_cycle_target
== self
->client
)
515 focus_cycle_draw_indicator();
517 if (resized
&& (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
518 XResizeWindow(ob_display
, self
->label
, self
->label_width
,
519 ob_rr_theme
->label_height
);
522 void frame_adjust_client_area(ObFrame
*self
)
524 /* resize the plate */
525 XResizeWindow(ob_display
, self
->plate
,
526 self
->client
->area
.width
, self
->client
->area
.height
);
529 void frame_adjust_state(ObFrame
*self
)
531 framerender_frame(self
);
534 void frame_adjust_focus(ObFrame
*self
, gboolean hilite
)
536 self
->focused
= hilite
;
537 framerender_frame(self
);
541 void frame_adjust_title(ObFrame
*self
)
543 framerender_frame(self
);
546 void frame_adjust_icon(ObFrame
*self
)
548 framerender_frame(self
);
551 void frame_grab_client(ObFrame
*self
, ObClient
*client
)
553 self
->client
= client
;
555 /* reparent the client to the frame */
556 XReparentWindow(ob_display
, client
->window
, self
->plate
, 0, 0);
558 When reparenting the client window, it is usually not mapped yet, since
559 this occurs from a MapRequest. However, in the case where Openbox is
560 starting up, the window is already mapped, so we'll see unmap events for
561 it. There are 2 unmap events generated that we see, one with the 'event'
562 member set the root window, and one set to the client, but both get
563 handled and need to be ignored.
565 if (ob_state() == OB_STATE_STARTING
)
566 client
->ignore_unmaps
+= 2;
568 /* select the event mask on the client's parent (to receive config/map
569 req's) the ButtonPress is to catch clicks on the client border */
570 XSelectInput(ob_display
, self
->plate
, PLATE_EVENTMASK
);
572 frame_adjust_area(self
, TRUE
, TRUE
, FALSE
);
574 /* map the client so it maps when the frame does */
575 XMapWindow(ob_display
, client
->window
);
577 /* set all the windows for the frame in the window_map */
578 g_hash_table_insert(window_map
, &self
->window
, client
);
579 g_hash_table_insert(window_map
, &self
->plate
, client
);
580 g_hash_table_insert(window_map
, &self
->inner
, client
);
581 g_hash_table_insert(window_map
, &self
->title
, client
);
582 g_hash_table_insert(window_map
, &self
->label
, client
);
583 g_hash_table_insert(window_map
, &self
->max
, client
);
584 g_hash_table_insert(window_map
, &self
->close
, client
);
585 g_hash_table_insert(window_map
, &self
->desk
, client
);
586 g_hash_table_insert(window_map
, &self
->shade
, client
);
587 g_hash_table_insert(window_map
, &self
->icon
, client
);
588 g_hash_table_insert(window_map
, &self
->iconify
, client
);
589 g_hash_table_insert(window_map
, &self
->handle
, client
);
590 g_hash_table_insert(window_map
, &self
->lgrip
, client
);
591 g_hash_table_insert(window_map
, &self
->rgrip
, client
);
592 g_hash_table_insert(window_map
, &self
->tltresize
, client
);
593 g_hash_table_insert(window_map
, &self
->tllresize
, client
);
594 g_hash_table_insert(window_map
, &self
->trtresize
, client
);
595 g_hash_table_insert(window_map
, &self
->trrresize
, client
);
598 void frame_release_client(ObFrame
*self
, ObClient
*client
)
601 gboolean reparent
= TRUE
;
603 g_assert(self
->client
== client
);
605 /* if there was any animation going on, kill it */
606 ob_main_loop_timeout_remove_data(ob_main_loop
, frame_animate_iconify
,
609 /* check if the app has already reparented its window away */
610 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
611 ReparentNotify
, &ev
))
613 /* This check makes sure we don't catch our own reparent action to
614 our frame window. This doesn't count as the app reparenting itself
617 Reparent events that are generated by us are just discarded here.
618 They are of no consequence to us anyhow.
620 if (ev
.xreparent
.parent
!= self
->plate
) {
622 XPutBackEvent(ob_display
, &ev
);
628 /* according to the ICCCM - if the client doesn't reparent itself,
629 then we will reparent the window to root for them */
630 XReparentWindow(ob_display
, client
->window
,
631 RootWindow(ob_display
, ob_screen
),
636 /* remove all the windows for the frame from the window_map */
637 g_hash_table_remove(window_map
, &self
->window
);
638 g_hash_table_remove(window_map
, &self
->plate
);
639 g_hash_table_remove(window_map
, &self
->inner
);
640 g_hash_table_remove(window_map
, &self
->title
);
641 g_hash_table_remove(window_map
, &self
->label
);
642 g_hash_table_remove(window_map
, &self
->max
);
643 g_hash_table_remove(window_map
, &self
->close
);
644 g_hash_table_remove(window_map
, &self
->desk
);
645 g_hash_table_remove(window_map
, &self
->shade
);
646 g_hash_table_remove(window_map
, &self
->icon
);
647 g_hash_table_remove(window_map
, &self
->iconify
);
648 g_hash_table_remove(window_map
, &self
->handle
);
649 g_hash_table_remove(window_map
, &self
->lgrip
);
650 g_hash_table_remove(window_map
, &self
->rgrip
);
651 g_hash_table_remove(window_map
, &self
->tltresize
);
652 g_hash_table_remove(window_map
, &self
->tllresize
);
653 g_hash_table_remove(window_map
, &self
->trtresize
);
654 g_hash_table_remove(window_map
, &self
->trrresize
);
656 ob_main_loop_timeout_remove_data(ob_main_loop
, flash_timeout
, self
, TRUE
);
661 static void layout_title(ObFrame
*self
)
665 gboolean n
, d
, i
, l
, m
, c
, s
;
667 n
= d
= i
= l
= m
= c
= s
= FALSE
;
669 /* figure out whats being shown, and the width of the label */
670 self
->label_width
= self
->width
- (ob_rr_theme
->paddingx
+ 1) * 2;
671 for (lc
= config_title_layout
; *lc
!= '\0'; ++lc
) {
674 if (n
) { *lc
= ' '; break; } /* rm duplicates */
676 self
->label_width
-= (ob_rr_theme
->button_size
+ 2 +
677 ob_rr_theme
->paddingx
+ 1);
680 if (d
) { *lc
= ' '; break; }
681 if (!(self
->decorations
& OB_FRAME_DECOR_ALLDESKTOPS
)
682 && config_theme_hidedisabled
)
685 self
->label_width
-= (ob_rr_theme
->button_size
+
686 ob_rr_theme
->paddingx
+ 1);
689 if (s
) { *lc
= ' '; break; }
690 if (!(self
->decorations
& OB_FRAME_DECOR_SHADE
)
691 && config_theme_hidedisabled
)
694 self
->label_width
-= (ob_rr_theme
->button_size
+
695 ob_rr_theme
->paddingx
+ 1);
698 if (i
) { *lc
= ' '; break; }
699 if (!(self
->decorations
& OB_FRAME_DECOR_ICONIFY
)
700 && config_theme_hidedisabled
)
703 self
->label_width
-= (ob_rr_theme
->button_size
+
704 ob_rr_theme
->paddingx
+ 1);
707 if (l
) { *lc
= ' '; break; }
711 if (m
) { *lc
= ' '; break; }
712 if (!(self
->decorations
& OB_FRAME_DECOR_MAXIMIZE
)
713 && config_theme_hidedisabled
)
716 self
->label_width
-= (ob_rr_theme
->button_size
+
717 ob_rr_theme
->paddingx
+ 1);
720 if (c
) { *lc
= ' '; break; }
721 if (!(self
->decorations
& OB_FRAME_DECOR_CLOSE
)
722 && config_theme_hidedisabled
)
725 self
->label_width
-= (ob_rr_theme
->button_size
+
726 ob_rr_theme
->paddingx
+ 1);
730 if (self
->label_width
< 1) self
->label_width
= 1;
732 if (!n
) XUnmapWindow(ob_display
, self
->icon
);
733 if (!d
) XUnmapWindow(ob_display
, self
->desk
);
734 if (!s
) XUnmapWindow(ob_display
, self
->shade
);
735 if (!i
) XUnmapWindow(ob_display
, self
->iconify
);
736 if (!l
) XUnmapWindow(ob_display
, self
->label
);
737 if (!m
) XUnmapWindow(ob_display
, self
->max
);
738 if (!c
) XUnmapWindow(ob_display
, self
->close
);
740 x
= ob_rr_theme
->paddingx
+ 1;
741 for (lc
= config_title_layout
; *lc
!= '\0'; ++lc
) {
746 XMapWindow(ob_display
, self
->icon
);
747 XMoveWindow(ob_display
, self
->icon
, x
, ob_rr_theme
->paddingy
);
748 x
+= ob_rr_theme
->button_size
+ 2 + ob_rr_theme
->paddingx
+ 1;
753 XMapWindow(ob_display
, self
->desk
);
754 XMoveWindow(ob_display
, self
->desk
, x
, ob_rr_theme
->paddingy
+ 1);
755 x
+= ob_rr_theme
->button_size
+ ob_rr_theme
->paddingx
+ 1;
760 XMapWindow(ob_display
, self
->shade
);
761 XMoveWindow(ob_display
, self
->shade
, x
, ob_rr_theme
->paddingy
+ 1);
762 x
+= ob_rr_theme
->button_size
+ ob_rr_theme
->paddingx
+ 1;
767 XMapWindow(ob_display
, self
->iconify
);
768 XMoveWindow(ob_display
,self
->iconify
, x
, ob_rr_theme
->paddingy
+ 1);
769 x
+= ob_rr_theme
->button_size
+ ob_rr_theme
->paddingx
+ 1;
774 XMapWindow(ob_display
, self
->label
);
775 XMoveWindow(ob_display
, self
->label
, x
, ob_rr_theme
->paddingy
);
776 x
+= self
->label_width
+ ob_rr_theme
->paddingx
+ 1;
781 XMapWindow(ob_display
, self
->max
);
782 XMoveWindow(ob_display
, self
->max
, x
, ob_rr_theme
->paddingy
+ 1);
783 x
+= ob_rr_theme
->button_size
+ ob_rr_theme
->paddingx
+ 1;
788 XMapWindow(ob_display
, self
->close
);
789 XMoveWindow(ob_display
, self
->close
, x
, ob_rr_theme
->paddingy
+ 1);
790 x
+= ob_rr_theme
->button_size
+ ob_rr_theme
->paddingx
+ 1;
796 ObFrameContext
frame_context_from_string(const gchar
*name
)
798 if (!g_ascii_strcasecmp("Desktop", name
))
799 return OB_FRAME_CONTEXT_DESKTOP
;
800 else if (!g_ascii_strcasecmp("Client", name
))
801 return OB_FRAME_CONTEXT_CLIENT
;
802 else if (!g_ascii_strcasecmp("Titlebar", name
))
803 return OB_FRAME_CONTEXT_TITLEBAR
;
804 else if (!g_ascii_strcasecmp("Handle", name
))
805 return OB_FRAME_CONTEXT_HANDLE
;
806 else if (!g_ascii_strcasecmp("Frame", name
))
807 return OB_FRAME_CONTEXT_FRAME
;
808 else if (!g_ascii_strcasecmp("TLCorner", name
))
809 return OB_FRAME_CONTEXT_TLCORNER
;
810 else if (!g_ascii_strcasecmp("TRCorner", name
))
811 return OB_FRAME_CONTEXT_TRCORNER
;
812 else if (!g_ascii_strcasecmp("BLCorner", name
))
813 return OB_FRAME_CONTEXT_BLCORNER
;
814 else if (!g_ascii_strcasecmp("BRCorner", name
))
815 return OB_FRAME_CONTEXT_BRCORNER
;
816 else if (!g_ascii_strcasecmp("Maximize", name
))
817 return OB_FRAME_CONTEXT_MAXIMIZE
;
818 else if (!g_ascii_strcasecmp("AllDesktops", name
))
819 return OB_FRAME_CONTEXT_ALLDESKTOPS
;
820 else if (!g_ascii_strcasecmp("Shade", name
))
821 return OB_FRAME_CONTEXT_SHADE
;
822 else if (!g_ascii_strcasecmp("Iconify", name
))
823 return OB_FRAME_CONTEXT_ICONIFY
;
824 else if (!g_ascii_strcasecmp("Icon", name
))
825 return OB_FRAME_CONTEXT_ICON
;
826 else if (!g_ascii_strcasecmp("Close", name
))
827 return OB_FRAME_CONTEXT_CLOSE
;
828 else if (!g_ascii_strcasecmp("MoveResize", name
))
829 return OB_FRAME_CONTEXT_MOVE_RESIZE
;
830 return OB_FRAME_CONTEXT_NONE
;
833 ObFrameContext
frame_context(ObClient
*client
, Window win
)
837 if (moveresize_in_progress
)
838 return OB_FRAME_CONTEXT_MOVE_RESIZE
;
840 if (win
== RootWindow(ob_display
, ob_screen
))
841 return OB_FRAME_CONTEXT_DESKTOP
;
842 if (client
== NULL
) return OB_FRAME_CONTEXT_NONE
;
843 if (win
== client
->window
) {
844 /* conceptually, this is the desktop, as far as users are
846 if (client
->type
== OB_CLIENT_TYPE_DESKTOP
)
847 return OB_FRAME_CONTEXT_DESKTOP
;
848 return OB_FRAME_CONTEXT_CLIENT
;
851 self
= client
->frame
;
852 if (win
== self
->inner
|| win
== self
->plate
) {
853 /* conceptually, this is the desktop, as far as users are
855 if (client
->type
== OB_CLIENT_TYPE_DESKTOP
)
856 return OB_FRAME_CONTEXT_DESKTOP
;
857 return OB_FRAME_CONTEXT_CLIENT
;
860 if (win
== self
->window
) return OB_FRAME_CONTEXT_FRAME
;
861 if (win
== self
->title
) return OB_FRAME_CONTEXT_TITLEBAR
;
862 if (win
== self
->label
) return OB_FRAME_CONTEXT_TITLEBAR
;
863 if (win
== self
->handle
) return OB_FRAME_CONTEXT_HANDLE
;
864 if (win
== self
->lgrip
) return OB_FRAME_CONTEXT_BLCORNER
;
865 if (win
== self
->rgrip
) return OB_FRAME_CONTEXT_BRCORNER
;
866 if (win
== self
->tltresize
) return OB_FRAME_CONTEXT_TLCORNER
;
867 if (win
== self
->tllresize
) return OB_FRAME_CONTEXT_TLCORNER
;
868 if (win
== self
->trtresize
) return OB_FRAME_CONTEXT_TRCORNER
;
869 if (win
== self
->trrresize
) return OB_FRAME_CONTEXT_TRCORNER
;
870 if (win
== self
->max
) return OB_FRAME_CONTEXT_MAXIMIZE
;
871 if (win
== self
->iconify
) return OB_FRAME_CONTEXT_ICONIFY
;
872 if (win
== self
->close
) return OB_FRAME_CONTEXT_CLOSE
;
873 if (win
== self
->icon
) return OB_FRAME_CONTEXT_ICON
;
874 if (win
== self
->desk
) return OB_FRAME_CONTEXT_ALLDESKTOPS
;
875 if (win
== self
->shade
) return OB_FRAME_CONTEXT_SHADE
;
877 return OB_FRAME_CONTEXT_NONE
;
880 void frame_client_gravity(ObFrame
*self
, gint
*x
, gint
*y
, gint w
, gint h
)
883 switch (self
->client
->gravity
) {
885 case NorthWestGravity
:
886 case SouthWestGravity
:
893 *x
-= (self
->size
.left
+ w
) / 2;
896 case NorthEastGravity
:
897 case SouthEastGravity
:
899 *x
-= (self
->size
.left
+ self
->size
.right
+ w
) - 1;
904 *x
-= self
->size
.left
;
909 switch (self
->client
->gravity
) {
911 case NorthWestGravity
:
912 case NorthEastGravity
:
919 *y
-= (self
->size
.top
+ h
) / 2;
922 case SouthWestGravity
:
923 case SouthEastGravity
:
925 *y
-= (self
->size
.top
+ self
->size
.bottom
+ h
) - 1;
930 *y
-= self
->size
.top
;
935 void frame_frame_gravity(ObFrame
*self
, gint
*x
, gint
*y
, gint w
, gint h
)
938 switch (self
->client
->gravity
) {
940 case NorthWestGravity
:
942 case SouthWestGravity
:
947 *x
+= (self
->size
.left
+ w
) / 2;
949 case NorthEastGravity
:
951 case SouthEastGravity
:
952 *x
+= (self
->size
.left
+ self
->size
.right
+ w
) - 1;
956 *x
+= self
->size
.left
;
961 switch (self
->client
->gravity
) {
963 case NorthWestGravity
:
965 case NorthEastGravity
:
970 *y
+= (self
->size
.top
+ h
) / 2;
972 case SouthWestGravity
:
974 case SouthEastGravity
:
975 *y
+= (self
->size
.top
+ self
->size
.bottom
+ h
) - 1;
979 *y
+= self
->size
.top
;
984 static void flash_done(gpointer data
)
986 ObFrame
*self
= data
;
988 if (self
->focused
!= self
->flash_on
)
989 frame_adjust_focus(self
, self
->focused
);
992 static gboolean
flash_timeout(gpointer data
)
994 ObFrame
*self
= data
;
997 g_get_current_time(&now
);
998 if (now
.tv_sec
> self
->flash_end
.tv_sec
||
999 (now
.tv_sec
== self
->flash_end
.tv_sec
&&
1000 now
.tv_usec
>= self
->flash_end
.tv_usec
))
1001 self
->flashing
= FALSE
;
1003 if (!self
->flashing
)
1004 return FALSE
; /* we are done */
1006 self
->flash_on
= !self
->flash_on
;
1007 if (!self
->focused
) {
1008 frame_adjust_focus(self
, self
->flash_on
);
1009 self
->focused
= FALSE
;
1012 return TRUE
; /* go again */
1015 void frame_flash_start(ObFrame
*self
)
1017 self
->flash_on
= self
->focused
;
1019 if (!self
->flashing
)
1020 ob_main_loop_timeout_add(ob_main_loop
,
1021 G_USEC_PER_SEC
* 0.6,
1026 g_get_current_time(&self
->flash_end
);
1027 g_time_val_add(&self
->flash_end
, G_USEC_PER_SEC
* 5);
1029 self
->flashing
= TRUE
;
1032 void frame_flash_stop(ObFrame
*self
)
1034 self
->flashing
= FALSE
;
1037 static gulong
frame_animate_iconify_time_left(ObFrame
*self
,
1038 const GTimeVal
*now
)
1041 sec
= self
->iconify_animation_end
.tv_sec
- now
->tv_sec
;
1042 usec
= self
->iconify_animation_end
.tv_usec
- now
->tv_usec
;
1044 usec
+= G_USEC_PER_SEC
;
1047 /* no negative values */
1048 return MAX(sec
* G_USEC_PER_SEC
+ usec
, 0);
1051 static gboolean
frame_animate_iconify(gpointer p
)
1055 gint iconx
, icony
, iconw
;
1058 gboolean iconifying
;
1060 if (self
->client
->icon_geometry
.width
== 0) {
1061 /* there is no icon geometry set so just go straight down */
1062 Rect
*a
= screen_physical_area();
1063 iconx
= self
->area
.x
+ self
->area
.width
/ 2 + 32;
1064 icony
= a
->y
+ a
->width
;
1067 iconx
= self
->client
->icon_geometry
.x
;
1068 icony
= self
->client
->icon_geometry
.y
;
1069 iconw
= self
->client
->icon_geometry
.width
;
1072 iconifying
= self
->iconify_animation_going
> 0;
1074 /* how far do we have left to go ? */
1075 g_get_current_time(&now
);
1076 time
= frame_animate_iconify_time_left(self
, &now
);
1078 if (time
== 0 || iconifying
) {
1079 /* start where the frame is supposed to be */
1082 w
= self
->area
.width
- self
->bwidth
* 2;
1083 h
= self
->area
.height
- self
->bwidth
* 2;
1085 /* start at the icon */
1089 h
= self
->innersize
.top
; /* just the titlebar */
1096 dx
= self
->area
.x
- iconx
;
1097 dy
= self
->area
.y
- icony
;
1098 dw
= self
->area
.width
- self
->bwidth
* 2 - iconw
;
1099 /* if restoring, we move in the opposite direction */
1100 if (!iconifying
) { dx
= -dx
; dy
= -dy
; dw
= -dw
; }
1102 elapsed
= FRAME_ANIMATE_ICONIFY_TIME
- time
;
1103 x
= x
- (dx
* elapsed
) / FRAME_ANIMATE_ICONIFY_TIME
;
1104 y
= y
- (dy
* elapsed
) / FRAME_ANIMATE_ICONIFY_TIME
;
1105 w
= w
- (dw
* elapsed
) / FRAME_ANIMATE_ICONIFY_TIME
;
1106 h
= self
->innersize
.top
; /* just the titlebar */
1110 frame_end_iconify_animation(self
);
1112 XMoveResizeWindow(ob_display
, self
->window
, x
, y
, w
, h
);
1116 return time
> 0; /* repeat until we're out of time */
1119 void frame_end_iconify_animation(ObFrame
*self
)
1121 /* see if there is an animation going */
1122 if (self
->iconify_animation_going
== 0) return;
1124 /* call the callback when it's done */
1125 if (self
->iconify_animation_cb
)
1126 self
->iconify_animation_cb(self
->iconify_animation_data
);
1127 /* we're not animating any more ! */
1128 self
->iconify_animation_going
= 0;
1130 /* move after the callback for the animation ending */
1131 XMoveResizeWindow(ob_display
, self
->window
,
1132 self
->area
.x
, self
->area
.y
,
1133 self
->area
.width
- self
->bwidth
* 2,
1134 self
->area
.height
- self
->bwidth
* 2);
1138 void frame_begin_iconify_animation(ObFrame
*self
, gboolean iconifying
,
1139 ObFrameIconifyAnimateFunc callback
,
1143 gboolean new_anim
= FALSE
;
1144 gboolean set_end
= TRUE
;
1147 /* if there is no titlebar, just don't animate for now
1148 XXX it would be nice tho.. */
1149 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
)) {
1150 if (callback
) callback(data
);
1154 /* get the current time */
1155 g_get_current_time(&now
);
1157 /* get how long until the end */
1158 time
= FRAME_ANIMATE_ICONIFY_TIME
;
1159 if (self
->iconify_animation_going
) {
1160 if (!!iconifying
!= (self
->iconify_animation_going
> 0)) {
1161 /* animation was already going on in the opposite direction */
1162 time
= time
- frame_animate_iconify_time_left(self
, &now
);
1164 /* animation was already going in the same direction */
1168 self
->iconify_animation_going
= iconifying
? 1 : -1;
1170 self
->iconify_animation_cb
= callback
;
1171 self
->iconify_animation_data
= data
;
1173 /* set the ending time */
1175 self
->iconify_animation_end
.tv_sec
= now
.tv_sec
;
1176 self
->iconify_animation_end
.tv_usec
= now
.tv_usec
;
1177 g_time_val_add(&self
->iconify_animation_end
, time
);
1181 ob_main_loop_timeout_remove_data(ob_main_loop
, frame_animate_iconify
,
1183 ob_main_loop_timeout_add(ob_main_loop
,
1184 FRAME_ANIMATE_ICONIFY_STEP_TIME
,
1185 frame_animate_iconify
, self
,
1186 g_direct_equal
, NULL
);
1188 /* do the first step */
1189 frame_animate_iconify(self
);
1196 gboolean
frame_visible(ObFrame
*self
)
1198 /* if it is animating back from iconic state then it is considered
1199 visible. but if it is iconifying then it is not visible. */
1200 return self
->visible
&& self
->iconify_animation_going
<= 0;