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->innersize.top + f->client->area.height + \
52 static void flash_done(gpointer data
);
53 static gboolean
flash_timeout(gpointer data
);
55 static void layout_title(ObFrame
*self
);
56 static void set_theme_statics(ObFrame
*self
);
57 static void free_theme_statics(ObFrame
*self
);
58 static gboolean
frame_animate_iconify(gpointer self
);
60 static Window
createWindow(Window parent
, Visual
*visual
,
61 gulong mask
, XSetWindowAttributes
*attrib
)
63 return XCreateWindow(ob_display
, parent
, 0, 0, 1, 1, 0,
64 (visual
? 32 : RrDepth(ob_rr_inst
)), InputOutput
,
65 (visual
? visual
: RrVisual(ob_rr_inst
)),
70 static Visual
*check_32bit_client(ObClient
*c
)
72 XWindowAttributes wattrib
;
75 ret
= XGetWindowAttributes(ob_display
, c
->window
, &wattrib
);
76 g_assert(ret
!= BadDrawable
);
77 g_assert(ret
!= BadWindow
);
79 if (wattrib
.depth
== 32)
80 return wattrib
.visual
;
84 ObFrame
*frame_new(ObClient
*client
)
86 XSetWindowAttributes attrib
;
91 self
= g_new0(ObFrame
, 1);
92 self
->client
= client
;
94 visual
= check_32bit_client(client
);
96 /* create the non-visible decor windows */
100 /* client has a 32-bit visual */
101 mask
|= CWColormap
| CWBackPixel
| CWBorderPixel
;
102 /* create a colormap with the visual */
103 self
->colormap
= attrib
.colormap
=
104 XCreateColormap(ob_display
,
105 RootWindow(ob_display
, ob_screen
),
107 attrib
.background_pixel
= BlackPixel(ob_display
, 0);
108 attrib
.border_pixel
= BlackPixel(ob_display
, 0);
110 attrib
.event_mask
= FRAME_EVENTMASK
;
111 self
->window
= createWindow(RootWindow(ob_display
, ob_screen
), visual
,
114 attrib
.event_mask
= INNER_EVENTMASK
;
115 self
->inner
= createWindow(self
->window
, visual
, mask
, &attrib
);
117 mask
&= ~CWEventMask
;
118 self
->plate
= createWindow(self
->inner
, visual
, mask
, &attrib
);
120 /* create the visible decor windows */
124 /* client has a 32-bit visual */
125 mask
|= CWColormap
| CWBackPixel
| CWBorderPixel
;
126 attrib
.colormap
= RrColormap(ob_rr_inst
);
128 attrib
.event_mask
= ELEMENT_EVENTMASK
;
129 self
->title
= createWindow(self
->window
, NULL
, mask
, &attrib
);
132 attrib
.cursor
= ob_cursor(OB_CURSOR_NORTH
);
133 self
->topresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
134 attrib
.cursor
= ob_cursor(OB_CURSOR_NORTHWEST
);
135 self
->tltresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
136 self
->tllresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
137 attrib
.cursor
= ob_cursor(OB_CURSOR_NORTHEAST
);
138 self
->trtresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
139 self
->trrresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
141 attrib
.cursor
= ob_cursor(OB_CURSOR_WEST
);
142 self
->leftresize
= createWindow(self
->inner
, NULL
, mask
, &attrib
);
143 attrib
.cursor
= ob_cursor(OB_CURSOR_EAST
);
144 self
->rightresize
= createWindow(self
->inner
, NULL
, mask
, &attrib
);
147 self
->label
= createWindow(self
->title
, NULL
, mask
, &attrib
);
148 self
->max
= createWindow(self
->title
, NULL
, mask
, &attrib
);
149 self
->close
= createWindow(self
->title
, NULL
, mask
, &attrib
);
150 self
->desk
= createWindow(self
->title
, NULL
, mask
, &attrib
);
151 self
->shade
= createWindow(self
->title
, NULL
, mask
, &attrib
);
152 self
->icon
= createWindow(self
->title
, NULL
, mask
, &attrib
);
153 self
->iconify
= createWindow(self
->title
, NULL
, mask
, &attrib
);
156 attrib
.cursor
= ob_cursor(OB_CURSOR_SOUTH
);
157 self
->handle
= createWindow(self
->window
, NULL
, mask
, &attrib
);
158 attrib
.cursor
= ob_cursor(OB_CURSOR_SOUTHWEST
);
159 self
->lgrip
= createWindow(self
->handle
, NULL
, mask
, &attrib
);
160 attrib
.cursor
= ob_cursor(OB_CURSOR_SOUTHEAST
);
161 self
->rgrip
= createWindow(self
->handle
, NULL
, mask
, &attrib
);
163 self
->focused
= FALSE
;
165 /* the other stuff is shown based on decor settings */
166 XMapWindow(ob_display
, self
->plate
);
167 XMapWindow(ob_display
, self
->inner
);
168 XMapWindow(ob_display
, self
->lgrip
);
169 XMapWindow(ob_display
, self
->rgrip
);
170 XMapWindow(ob_display
, self
->label
);
172 self
->max_press
= self
->close_press
= self
->desk_press
=
173 self
->iconify_press
= self
->shade_press
= FALSE
;
174 self
->max_hover
= self
->close_hover
= self
->desk_hover
=
175 self
->iconify_hover
= self
->shade_hover
= FALSE
;
177 set_theme_statics(self
);
179 return (ObFrame
*)self
;
182 static void set_theme_statics(ObFrame
*self
)
186 if (ob_rr_theme
->handle_height
> 0)
187 handle_height
= ob_rr_theme
->handle_height
;
192 /* set colors/appearance/sizes for stuff that doesn't change */
193 XResizeWindow(ob_display
, self
->max
,
194 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
195 XResizeWindow(ob_display
, self
->iconify
,
196 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
197 XResizeWindow(ob_display
, self
->icon
,
198 ob_rr_theme
->button_size
+ 2, ob_rr_theme
->button_size
+ 2);
199 XResizeWindow(ob_display
, self
->close
,
200 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
201 XResizeWindow(ob_display
, self
->desk
,
202 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
203 XResizeWindow(ob_display
, self
->shade
,
204 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
205 XResizeWindow(ob_display
, self
->lgrip
,
206 ob_rr_theme
->grip_width
, handle_height
);
207 XResizeWindow(ob_display
, self
->rgrip
,
208 ob_rr_theme
->grip_width
, handle_height
);
209 XResizeWindow(ob_display
, self
->tltresize
,
210 ob_rr_theme
->grip_width
, ob_rr_theme
->paddingy
+ 1);
211 XResizeWindow(ob_display
, self
->trtresize
,
212 ob_rr_theme
->grip_width
, ob_rr_theme
->paddingy
+ 1);
213 XResizeWindow(ob_display
, self
->tllresize
,
214 ob_rr_theme
->paddingx
+ 1, ob_rr_theme
->title_height
);
215 XResizeWindow(ob_display
, self
->trrresize
,
216 ob_rr_theme
->paddingx
+ 1, ob_rr_theme
->title_height
);
218 /* set up the dynamic appearances */
219 self
->a_unfocused_title
= RrAppearanceCopy(ob_rr_theme
->a_unfocused_title
);
220 self
->a_focused_title
= RrAppearanceCopy(ob_rr_theme
->a_focused_title
);
221 self
->a_unfocused_label
= RrAppearanceCopy(ob_rr_theme
->a_unfocused_label
);
222 self
->a_focused_label
= RrAppearanceCopy(ob_rr_theme
->a_focused_label
);
223 self
->a_unfocused_handle
=
224 RrAppearanceCopy(ob_rr_theme
->a_unfocused_handle
);
225 self
->a_focused_handle
= RrAppearanceCopy(ob_rr_theme
->a_focused_handle
);
226 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_icon
);
229 static void free_theme_statics(ObFrame
*self
)
231 RrAppearanceFree(self
->a_unfocused_title
);
232 RrAppearanceFree(self
->a_focused_title
);
233 RrAppearanceFree(self
->a_unfocused_label
);
234 RrAppearanceFree(self
->a_focused_label
);
235 RrAppearanceFree(self
->a_unfocused_handle
);
236 RrAppearanceFree(self
->a_focused_handle
);
237 RrAppearanceFree(self
->a_icon
);
240 void frame_free(ObFrame
*self
)
242 free_theme_statics(self
);
244 XDestroyWindow(ob_display
, self
->window
);
246 XFreeColormap(ob_display
, self
->colormap
);
251 void frame_show(ObFrame
*self
)
253 if (!self
->visible
) {
254 self
->visible
= TRUE
;
255 XMapWindow(ob_display
, self
->client
->window
);
256 XMapWindow(ob_display
, self
->window
);
260 void frame_hide(ObFrame
*self
)
263 self
->visible
= FALSE
;
264 if (!frame_iconify_animating(self
))
265 XUnmapWindow(ob_display
, self
->window
);
266 /* we unmap the client itself so that we can get MapRequest
267 events, and because the ICCCM tells us to! */
268 XUnmapWindow(ob_display
, self
->client
->window
);
269 self
->client
->ignore_unmaps
+= 1;
273 void frame_adjust_theme(ObFrame
*self
)
275 free_theme_statics(self
);
276 set_theme_statics(self
);
279 void frame_adjust_shape(ObFrame
*self
)
285 if (!self
->client
->shaped
) {
286 /* clear the shape on the frame window */
287 XShapeCombineMask(ob_display
, self
->window
, ShapeBounding
,
288 self
->innersize
.left
,
292 /* make the frame's shape match the clients */
293 XShapeCombineShape(ob_display
, self
->window
, ShapeBounding
,
294 self
->innersize
.left
,
296 self
->client
->window
,
297 ShapeBounding
, ShapeSet
);
300 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
) {
301 xrect
[0].x
= -ob_rr_theme
->fbwidth
;
302 xrect
[0].y
= -ob_rr_theme
->fbwidth
;
303 xrect
[0].width
= self
->width
+ self
->rbwidth
* 2;
304 xrect
[0].height
= ob_rr_theme
->title_height
+
309 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
) {
310 xrect
[1].x
= -ob_rr_theme
->fbwidth
;
311 xrect
[1].y
= FRAME_HANDLE_Y(self
);
312 xrect
[1].width
= self
->width
+ self
->rbwidth
* 2;
313 xrect
[1].height
= ob_rr_theme
->handle_height
+
318 XShapeCombineRectangles(ob_display
, self
->window
,
319 ShapeBounding
, 0, 0, xrect
, num
,
320 ShapeUnion
, Unsorted
);
325 void frame_adjust_area(ObFrame
*self
, gboolean moved
,
326 gboolean resized
, gboolean fake
)
330 oldsize
= self
->size
;
333 self
->decorations
= self
->client
->decorations
;
334 self
->max_horz
= self
->client
->max_horz
;
336 if (self
->decorations
& OB_FRAME_DECOR_BORDER
) {
337 self
->bwidth
= ob_rr_theme
->fbwidth
;
338 self
->cbwidth_x
= ob_rr_theme
->cbwidthx
;
339 self
->cbwidth_y
= ob_rr_theme
->cbwidthy
;
341 self
->bwidth
= self
->cbwidth_x
= self
->cbwidth_y
= 0;
343 self
->rbwidth
= self
->bwidth
;
346 self
->bwidth
= self
->cbwidth_x
= 0;
348 STRUT_SET(self
->innersize
,
353 self
->width
= self
->client
->area
.width
+ self
->cbwidth_x
* 2 -
354 (self
->max_horz
? self
->rbwidth
* 2 : 0);
355 self
->width
= MAX(self
->width
, 1); /* no lower than 1 */
357 /* set border widths */
359 XSetWindowBorderWidth(ob_display
, self
->window
, self
->bwidth
);
360 XSetWindowBorderWidth(ob_display
, self
->inner
, self
->bwidth
);
361 XSetWindowBorderWidth(ob_display
, self
->title
, self
->rbwidth
);
362 XSetWindowBorderWidth(ob_display
, self
->handle
, self
->rbwidth
);
363 XSetWindowBorderWidth(ob_display
, self
->lgrip
, self
->rbwidth
);
364 XSetWindowBorderWidth(ob_display
, self
->rgrip
, self
->rbwidth
);
365 XSetWindowBorderWidth(ob_display
, self
->leftresize
, self
->bwidth
);
366 XSetWindowBorderWidth(ob_display
, self
->rightresize
, self
->bwidth
);
369 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
)
370 self
->innersize
.top
+= ob_rr_theme
->title_height
+ self
->rbwidth
+
371 (self
->rbwidth
- self
->bwidth
);
372 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
&&
373 ob_rr_theme
->handle_height
> 0)
374 self
->innersize
.bottom
+= ob_rr_theme
->handle_height
+
375 self
->rbwidth
+ (self
->rbwidth
- self
->bwidth
);
377 /* position/size and map/unmap all the windows */
380 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
) {
381 XMoveResizeWindow(ob_display
, self
->title
,
382 -self
->bwidth
, -self
->bwidth
,
383 self
->width
, ob_rr_theme
->title_height
);
384 XMapWindow(ob_display
, self
->title
);
386 if (self
->decorations
& OB_FRAME_DECOR_GRIPS
) {
387 XMoveResizeWindow(ob_display
, self
->topresize
,
388 ob_rr_theme
->grip_width
+ self
->bwidth
,
390 self
->width
- (ob_rr_theme
->grip_width
+
392 ob_rr_theme
->paddingy
+ 1);
394 XMoveWindow(ob_display
, self
->tltresize
, 0, 0);
395 XMoveWindow(ob_display
, self
->tllresize
, 0, 0);
396 XMoveWindow(ob_display
, self
->trtresize
,
397 self
->width
- ob_rr_theme
->grip_width
, 0);
398 XMoveWindow(ob_display
, self
->trrresize
,
399 self
->width
- ob_rr_theme
->paddingx
- 1, 0);
401 XMapWindow(ob_display
, self
->topresize
);
402 XMapWindow(ob_display
, self
->tltresize
);
403 XMapWindow(ob_display
, self
->tllresize
);
404 XMapWindow(ob_display
, self
->trtresize
);
405 XMapWindow(ob_display
, self
->trrresize
);
407 XUnmapWindow(ob_display
, self
->topresize
);
408 XUnmapWindow(ob_display
, self
->tltresize
);
409 XUnmapWindow(ob_display
, self
->tllresize
);
410 XUnmapWindow(ob_display
, self
->trtresize
);
411 XUnmapWindow(ob_display
, self
->trrresize
);
414 XUnmapWindow(ob_display
, self
->title
);
417 if ((self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
418 /* layout the title bar elements */
422 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
)
426 if (ob_rr_theme
->handle_height
> 0)
427 handle_height
= ob_rr_theme
->handle_height
;
431 XMoveResizeWindow(ob_display
, self
->handle
,
432 -self
->bwidth
, FRAME_HANDLE_Y(self
),
433 self
->width
, handle_height
);
434 XMapWindow(ob_display
, self
->handle
);
436 if (self
->decorations
& OB_FRAME_DECOR_GRIPS
) {
437 XMoveWindow(ob_display
, self
->lgrip
,
438 -self
->rbwidth
, -self
->rbwidth
);
439 XMoveWindow(ob_display
, self
->rgrip
,
440 -self
->rbwidth
+ self
->width
-
441 ob_rr_theme
->grip_width
, -self
->rbwidth
);
442 XMapWindow(ob_display
, self
->lgrip
);
443 XMapWindow(ob_display
, self
->rgrip
);
445 XUnmapWindow(ob_display
, self
->lgrip
);
446 XUnmapWindow(ob_display
, self
->rgrip
);
449 XUnmapWindow(ob_display
, self
->handle
);
451 if (self
->decorations
& OB_FRAME_DECOR_GRIPS
) {
452 XMoveResizeWindow(ob_display
, self
->leftresize
,
453 -(ob_rr_theme
->fbwidth
* 2) - 1,
456 self
->client
->area
.height
+
457 self
->cbwidth_y
* 2);
458 XMoveResizeWindow(ob_display
, self
->rightresize
,
459 self
->client
->area
.width
+
463 self
->client
->area
.height
+
464 self
->cbwidth_y
* 2);
466 XMapWindow(ob_display
, self
->leftresize
);
467 XMapWindow(ob_display
, self
->rightresize
);
469 XUnmapWindow(ob_display
, self
->leftresize
);
470 XUnmapWindow(ob_display
, self
->rightresize
);
473 /* move and resize the inner border window which contains the plate
475 XMoveResizeWindow(ob_display
, self
->inner
,
476 self
->innersize
.left
- self
->cbwidth_x
-
478 self
->innersize
.top
- self
->cbwidth_y
-
480 self
->client
->area
.width
+
482 self
->client
->area
.height
+
483 self
->cbwidth_y
* 2);
486 XMoveWindow(ob_display
, self
->plate
,
487 self
->cbwidth_x
, self
->cbwidth_y
);
489 /* when the client has StaticGravity, it likes to move around. */
490 XMoveWindow(ob_display
, self
->client
->window
, 0, 0);
493 STRUT_SET(self
->size
,
494 self
->innersize
.left
+ self
->bwidth
,
495 self
->innersize
.top
+ self
->bwidth
,
496 self
->innersize
.right
+ self
->bwidth
,
497 self
->innersize
.bottom
+ self
->bwidth
);
500 /* shading can change without being moved or resized */
501 RECT_SET_SIZE(self
->area
,
502 self
->client
->area
.width
+
503 self
->size
.left
+ self
->size
.right
,
504 (self
->client
->shaded
?
505 ob_rr_theme
->title_height
+ self
->rbwidth
* 2:
506 self
->client
->area
.height
+
507 self
->size
.top
+ self
->size
.bottom
));
509 if (moved
|| resized
) {
510 /* find the new coordinates, done after setting the frame.size, for
511 frame_client_gravity. */
512 self
->area
.x
= self
->client
->area
.x
;
513 self
->area
.y
= self
->client
->area
.y
;
514 frame_client_gravity(self
, &self
->area
.x
, &self
->area
.y
,
515 self
->client
->area
.width
,
516 self
->client
->area
.height
);
520 if (!frame_iconify_animating(self
))
521 /* move and resize the top level frame.
522 shading can change without being moved or resized.
524 but don't do this during an iconify animation. it will be
525 reflected afterwards.
527 XMoveResizeWindow(ob_display
, self
->window
,
528 self
->area
.x
, self
->area
.y
,
529 self
->area
.width
- self
->bwidth
* 2,
530 self
->area
.height
- self
->bwidth
* 2);
533 framerender_frame(self
);
534 frame_adjust_shape(self
);
537 if (!STRUT_EQUAL(self
->size
, oldsize
)) {
539 vals
[0] = self
->size
.left
;
540 vals
[1] = self
->size
.right
;
541 vals
[2] = self
->size
.top
;
542 vals
[3] = self
->size
.bottom
;
543 PROP_SETA32(self
->client
->window
, net_frame_extents
,
545 PROP_SETA32(self
->client
->window
, kde_net_wm_frame_strut
,
549 /* if this occurs while we are focus cycling, the indicator needs to
551 if (focus_cycle_target
== self
->client
)
552 focus_cycle_draw_indicator(self
->client
);
554 if (resized
&& (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
555 XResizeWindow(ob_display
, self
->label
, self
->label_width
,
556 ob_rr_theme
->label_height
);
559 void frame_adjust_client_area(ObFrame
*self
)
561 /* resize the plate */
562 XResizeWindow(ob_display
, self
->plate
,
563 self
->client
->area
.width
, self
->client
->area
.height
);
566 void frame_adjust_state(ObFrame
*self
)
568 framerender_frame(self
);
571 void frame_adjust_focus(ObFrame
*self
, gboolean hilite
)
573 self
->focused
= hilite
;
574 framerender_frame(self
);
578 void frame_adjust_title(ObFrame
*self
)
580 framerender_frame(self
);
583 void frame_adjust_icon(ObFrame
*self
)
585 framerender_frame(self
);
588 void frame_grab_client(ObFrame
*self
)
590 /* reparent the client to the frame */
591 XReparentWindow(ob_display
, self
->client
->window
, self
->plate
, 0, 0);
594 When reparenting the client window, it is usually not mapped yet, since
595 this occurs from a MapRequest. However, in the case where Openbox is
596 starting up, the window is already mapped, so we'll see unmap events for
597 it. There are 2 unmap events generated that we see, one with the 'event'
598 member set the root window, and one set to the client, but both get
599 handled and need to be ignored.
601 if (ob_state() == OB_STATE_STARTING
)
602 self
->client
->ignore_unmaps
+= 2;
604 /* select the event mask on the client's parent (to receive config/map
605 req's) the ButtonPress is to catch clicks on the client border */
606 XSelectInput(ob_display
, self
->plate
, PLATE_EVENTMASK
);
608 /* map the client so it maps when the frame does */
609 XMapWindow(ob_display
, self
->client
->window
);
611 /* set all the windows for the frame in the window_map */
612 g_hash_table_insert(window_map
, &self
->window
, self
->client
);
613 g_hash_table_insert(window_map
, &self
->plate
, self
->client
);
614 g_hash_table_insert(window_map
, &self
->inner
, self
->client
);
615 g_hash_table_insert(window_map
, &self
->title
, self
->client
);
616 g_hash_table_insert(window_map
, &self
->label
, self
->client
);
617 g_hash_table_insert(window_map
, &self
->max
, self
->client
);
618 g_hash_table_insert(window_map
, &self
->close
, self
->client
);
619 g_hash_table_insert(window_map
, &self
->desk
, self
->client
);
620 g_hash_table_insert(window_map
, &self
->shade
, self
->client
);
621 g_hash_table_insert(window_map
, &self
->icon
, self
->client
);
622 g_hash_table_insert(window_map
, &self
->iconify
, self
->client
);
623 g_hash_table_insert(window_map
, &self
->handle
, self
->client
);
624 g_hash_table_insert(window_map
, &self
->lgrip
, self
->client
);
625 g_hash_table_insert(window_map
, &self
->rgrip
, self
->client
);
626 g_hash_table_insert(window_map
, &self
->topresize
, self
->client
);
627 g_hash_table_insert(window_map
, &self
->tltresize
, self
->client
);
628 g_hash_table_insert(window_map
, &self
->tllresize
, self
->client
);
629 g_hash_table_insert(window_map
, &self
->trtresize
, self
->client
);
630 g_hash_table_insert(window_map
, &self
->trrresize
, self
->client
);
631 g_hash_table_insert(window_map
, &self
->leftresize
, self
->client
);
632 g_hash_table_insert(window_map
, &self
->rightresize
, self
->client
);
635 void frame_release_client(ObFrame
*self
)
638 gboolean reparent
= TRUE
;
640 /* if there was any animation going on, kill it */
641 ob_main_loop_timeout_remove_data(ob_main_loop
, frame_animate_iconify
,
644 /* check if the app has already reparented its window away */
645 while (XCheckTypedWindowEvent(ob_display
, self
->client
->window
,
646 ReparentNotify
, &ev
))
648 /* This check makes sure we don't catch our own reparent action to
649 our frame window. This doesn't count as the app reparenting itself
652 Reparent events that are generated by us are just discarded here.
653 They are of no consequence to us anyhow.
655 if (ev
.xreparent
.parent
!= self
->plate
) {
657 XPutBackEvent(ob_display
, &ev
);
663 /* according to the ICCCM - if the client doesn't reparent itself,
664 then we will reparent the window to root for them */
665 XReparentWindow(ob_display
, self
->client
->window
,
666 RootWindow(ob_display
, ob_screen
),
667 self
->client
->area
.x
,
668 self
->client
->area
.y
);
671 /* remove all the windows for the frame from the window_map */
672 g_hash_table_remove(window_map
, &self
->window
);
673 g_hash_table_remove(window_map
, &self
->plate
);
674 g_hash_table_remove(window_map
, &self
->inner
);
675 g_hash_table_remove(window_map
, &self
->title
);
676 g_hash_table_remove(window_map
, &self
->label
);
677 g_hash_table_remove(window_map
, &self
->max
);
678 g_hash_table_remove(window_map
, &self
->close
);
679 g_hash_table_remove(window_map
, &self
->desk
);
680 g_hash_table_remove(window_map
, &self
->shade
);
681 g_hash_table_remove(window_map
, &self
->icon
);
682 g_hash_table_remove(window_map
, &self
->iconify
);
683 g_hash_table_remove(window_map
, &self
->handle
);
684 g_hash_table_remove(window_map
, &self
->lgrip
);
685 g_hash_table_remove(window_map
, &self
->rgrip
);
686 g_hash_table_remove(window_map
, &self
->topresize
);
687 g_hash_table_remove(window_map
, &self
->tltresize
);
688 g_hash_table_remove(window_map
, &self
->tllresize
);
689 g_hash_table_remove(window_map
, &self
->trtresize
);
690 g_hash_table_remove(window_map
, &self
->trrresize
);
691 g_hash_table_remove(window_map
, &self
->leftresize
);
692 g_hash_table_remove(window_map
, &self
->rightresize
);
694 ob_main_loop_timeout_remove_data(ob_main_loop
, flash_timeout
, self
, TRUE
);
697 /* is there anything present between us and the label? */
698 static gboolean
is_button_present(ObFrame
*self
, const gchar
*lc
, gint dir
) {
699 for (; *lc
!= '\0' && lc
>= config_title_layout
; lc
+= dir
) {
700 if (*lc
== ' ') continue; /* it was invalid */
701 if (*lc
== 'N' && self
->decorations
& OB_FRAME_DECOR_ICON
)
703 if (*lc
== 'D' && self
->decorations
& OB_FRAME_DECOR_ALLDESKTOPS
)
705 if (*lc
== 'S' && self
->decorations
& OB_FRAME_DECOR_SHADE
)
707 if (*lc
== 'I' && self
->decorations
& OB_FRAME_DECOR_ICONIFY
)
709 if (*lc
== 'M' && self
->decorations
& OB_FRAME_DECOR_MAXIMIZE
)
711 if (*lc
== 'C' && self
->decorations
& OB_FRAME_DECOR_CLOSE
)
713 if (*lc
== 'L') return FALSE
;
718 static void layout_title(ObFrame
*self
)
723 const gint bwidth
= ob_rr_theme
->button_size
+ ob_rr_theme
->paddingx
+ 1;
724 /* position of the left most button */
725 const gint left
= ob_rr_theme
->paddingx
+ 1;
726 /* position of the right most button */
727 const gint right
= self
->width
- bwidth
;
729 /* turn them all off */
730 self
->icon_on
= self
->desk_on
= self
->shade_on
= self
->iconify_on
=
731 self
->max_on
= self
->close_on
= self
->label_on
= FALSE
;
732 self
->label_width
= self
->width
- (ob_rr_theme
->paddingx
+ 1) * 2;
733 self
->leftmost
= self
->rightmost
= OB_FRAME_CONTEXT_NONE
;
735 /* figure out what's being show, find each element's position, and the
738 do the ones before the label, then after the label,
739 i will be +1 the first time through when working to the left,
740 and -1 the second time through when working to the right */
741 for (i
= 1; i
>= -1; i
-=2) {
743 ObFrameContext
*firstcon
;
747 lc
= config_title_layout
;
748 firstcon
= &self
->leftmost
;
751 lc
= config_title_layout
+ strlen(config_title_layout
)-1;
752 firstcon
= &self
->rightmost
;
755 /* stop at the end of the string (or the label, which calls break) */
756 for (; *lc
!= '\0' && lc
>= config_title_layout
; lc
+=i
) {
759 self
->label_on
= TRUE
;
762 break; /* break the for loop, do other side of label */
763 } else if (*lc
== 'N') {
764 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_ICON
;
765 if ((self
->icon_on
= is_button_present(self
, lc
, i
))) {
766 /* icon is bigger than buttons */
767 self
->label_width
-= bwidth
+ 2;
769 x
+= i
* (bwidth
+ 2);
771 } else if (*lc
== 'D') {
772 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_ALLDESKTOPS
;
773 if ((self
->desk_on
= is_button_present(self
, lc
, i
))) {
774 self
->label_width
-= bwidth
;
778 } else if (*lc
== 'S') {
779 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_SHADE
;
780 if ((self
->shade_on
= is_button_present(self
, lc
, i
))) {
781 self
->label_width
-= bwidth
;
785 } else if (*lc
== 'I') {
786 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_ICONIFY
;
787 if ((self
->iconify_on
= is_button_present(self
, lc
, i
))) {
788 self
->label_width
-= bwidth
;
792 } else if (*lc
== 'M') {
793 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_MAXIMIZE
;
794 if ((self
->max_on
= is_button_present(self
, lc
, i
))) {
795 self
->label_width
-= bwidth
;
799 } else if (*lc
== 'C') {
800 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_CLOSE
;
801 if ((self
->close_on
= is_button_present(self
, lc
, i
))) {
802 self
->label_width
-= bwidth
;
807 continue; /* don't set firstcon */
812 /* position and map the elements */
814 XMapWindow(ob_display
, self
->icon
);
815 XMoveWindow(ob_display
, self
->icon
, self
->icon_x
,
816 ob_rr_theme
->paddingy
);
818 XUnmapWindow(ob_display
, self
->icon
);
821 XMapWindow(ob_display
, self
->desk
);
822 XMoveWindow(ob_display
, self
->desk
, self
->desk_x
,
823 ob_rr_theme
->paddingy
+ 1);
825 XUnmapWindow(ob_display
, self
->desk
);
827 if (self
->shade_on
) {
828 XMapWindow(ob_display
, self
->shade
);
829 XMoveWindow(ob_display
, self
->shade
, self
->shade_x
,
830 ob_rr_theme
->paddingy
+ 1);
832 XUnmapWindow(ob_display
, self
->shade
);
834 if (self
->iconify_on
) {
835 XMapWindow(ob_display
, self
->iconify
);
836 XMoveWindow(ob_display
, self
->iconify
, self
->iconify_x
,
837 ob_rr_theme
->paddingy
+ 1);
839 XUnmapWindow(ob_display
, self
->iconify
);
842 XMapWindow(ob_display
, self
->max
);
843 XMoveWindow(ob_display
, self
->max
, self
->max_x
,
844 ob_rr_theme
->paddingy
+ 1);
846 XUnmapWindow(ob_display
, self
->max
);
848 if (self
->close_on
) {
849 XMapWindow(ob_display
, self
->close
);
850 XMoveWindow(ob_display
, self
->close
, self
->close_x
,
851 ob_rr_theme
->paddingy
+ 1);
853 XUnmapWindow(ob_display
, self
->close
);
855 if (self
->label_on
) {
856 self
->label_width
= MAX(1, self
->label_width
); /* no lower than 1 */
857 XMapWindow(ob_display
, self
->label
);
858 XMoveWindow(ob_display
, self
->label
, self
->label_x
,
859 ob_rr_theme
->paddingy
);
861 XUnmapWindow(ob_display
, self
->label
);
864 ObFrameContext
frame_context_from_string(const gchar
*name
)
866 if (!g_ascii_strcasecmp("Desktop", name
))
867 return OB_FRAME_CONTEXT_DESKTOP
;
868 else if (!g_ascii_strcasecmp("Root", name
))
869 return OB_FRAME_CONTEXT_ROOT
;
870 else if (!g_ascii_strcasecmp("Client", name
))
871 return OB_FRAME_CONTEXT_CLIENT
;
872 else if (!g_ascii_strcasecmp("Titlebar", name
))
873 return OB_FRAME_CONTEXT_TITLEBAR
;
874 else if (!g_ascii_strcasecmp("Frame", name
))
875 return OB_FRAME_CONTEXT_FRAME
;
876 else if (!g_ascii_strcasecmp("TLCorner", name
))
877 return OB_FRAME_CONTEXT_TLCORNER
;
878 else if (!g_ascii_strcasecmp("TRCorner", name
))
879 return OB_FRAME_CONTEXT_TRCORNER
;
880 else if (!g_ascii_strcasecmp("BLCorner", name
))
881 return OB_FRAME_CONTEXT_BLCORNER
;
882 else if (!g_ascii_strcasecmp("BRCorner", name
))
883 return OB_FRAME_CONTEXT_BRCORNER
;
884 else if (!g_ascii_strcasecmp("Top", name
))
885 return OB_FRAME_CONTEXT_TOP
;
886 else if (!g_ascii_strcasecmp("Bottom", name
))
887 return OB_FRAME_CONTEXT_BOTTOM
;
888 else if (!g_ascii_strcasecmp("Left", name
))
889 return OB_FRAME_CONTEXT_LEFT
;
890 else if (!g_ascii_strcasecmp("Right", name
))
891 return OB_FRAME_CONTEXT_RIGHT
;
892 else if (!g_ascii_strcasecmp("Maximize", name
))
893 return OB_FRAME_CONTEXT_MAXIMIZE
;
894 else if (!g_ascii_strcasecmp("AllDesktops", name
))
895 return OB_FRAME_CONTEXT_ALLDESKTOPS
;
896 else if (!g_ascii_strcasecmp("Shade", name
))
897 return OB_FRAME_CONTEXT_SHADE
;
898 else if (!g_ascii_strcasecmp("Iconify", name
))
899 return OB_FRAME_CONTEXT_ICONIFY
;
900 else if (!g_ascii_strcasecmp("Icon", name
))
901 return OB_FRAME_CONTEXT_ICON
;
902 else if (!g_ascii_strcasecmp("Close", name
))
903 return OB_FRAME_CONTEXT_CLOSE
;
904 else if (!g_ascii_strcasecmp("MoveResize", name
))
905 return OB_FRAME_CONTEXT_MOVE_RESIZE
;
906 return OB_FRAME_CONTEXT_NONE
;
909 ObFrameContext
frame_context(ObClient
*client
, Window win
, gint x
, gint y
)
913 if (moveresize_in_progress
)
914 return OB_FRAME_CONTEXT_MOVE_RESIZE
;
916 if (win
== RootWindow(ob_display
, ob_screen
))
917 return OB_FRAME_CONTEXT_ROOT
;
918 if (client
== NULL
) return OB_FRAME_CONTEXT_NONE
;
919 if (win
== client
->window
) {
920 /* conceptually, this is the desktop, as far as users are
922 if (client
->type
== OB_CLIENT_TYPE_DESKTOP
)
923 return OB_FRAME_CONTEXT_DESKTOP
;
924 return OB_FRAME_CONTEXT_CLIENT
;
927 self
= client
->frame
;
928 if (win
== self
->inner
|| win
== self
->plate
) {
929 /* conceptually, this is the desktop, as far as users are
931 if (client
->type
== OB_CLIENT_TYPE_DESKTOP
)
932 return OB_FRAME_CONTEXT_DESKTOP
;
933 return OB_FRAME_CONTEXT_CLIENT
;
936 if (win
== self
->title
) {
937 /* when the user clicks in the corners of the titlebar and the client
938 is fully maximized, then treat it like they clicked in the
939 button that is there */
940 if (self
->client
->max_horz
&& self
->client
->max_vert
&&
941 y
< ob_rr_theme
->paddingy
+ 1 + ob_rr_theme
->button_size
)
943 if (x
< ((ob_rr_theme
->paddingx
+ 1) * 2 +
944 ob_rr_theme
->button_size
)) {
945 if (self
->leftmost
!= OB_FRAME_CONTEXT_NONE
)
946 return self
->leftmost
;
948 else if (x
> (self
->width
-
949 (ob_rr_theme
->paddingx
+ 1 +
950 ob_rr_theme
->button_size
)))
952 if (self
->rightmost
!= OB_FRAME_CONTEXT_NONE
)
953 return self
->rightmost
;
956 return OB_FRAME_CONTEXT_TITLEBAR
;
959 if (win
== self
->window
) return OB_FRAME_CONTEXT_FRAME
;
960 if (win
== self
->label
) return OB_FRAME_CONTEXT_TITLEBAR
;
961 if (win
== self
->handle
) return OB_FRAME_CONTEXT_BOTTOM
;
962 if (win
== self
->lgrip
) return OB_FRAME_CONTEXT_BLCORNER
;
963 if (win
== self
->rgrip
) return OB_FRAME_CONTEXT_BRCORNER
;
964 if (win
== self
->topresize
) return OB_FRAME_CONTEXT_TOP
;
965 if (win
== self
->tltresize
) return OB_FRAME_CONTEXT_TLCORNER
;
966 if (win
== self
->tllresize
) return OB_FRAME_CONTEXT_TLCORNER
;
967 if (win
== self
->trtresize
) return OB_FRAME_CONTEXT_TRCORNER
;
968 if (win
== self
->trrresize
) return OB_FRAME_CONTEXT_TRCORNER
;
969 if (win
== self
->leftresize
) return OB_FRAME_CONTEXT_LEFT
;
970 if (win
== self
->rightresize
) return OB_FRAME_CONTEXT_RIGHT
;
971 if (win
== self
->max
) return OB_FRAME_CONTEXT_MAXIMIZE
;
972 if (win
== self
->iconify
) return OB_FRAME_CONTEXT_ICONIFY
;
973 if (win
== self
->close
) return OB_FRAME_CONTEXT_CLOSE
;
974 if (win
== self
->icon
) return OB_FRAME_CONTEXT_ICON
;
975 if (win
== self
->desk
) return OB_FRAME_CONTEXT_ALLDESKTOPS
;
976 if (win
== self
->shade
) return OB_FRAME_CONTEXT_SHADE
;
978 return OB_FRAME_CONTEXT_NONE
;
981 void frame_client_gravity(ObFrame
*self
, gint
*x
, gint
*y
, gint w
, gint h
)
984 switch (self
->client
->gravity
) {
986 case NorthWestGravity
:
987 case SouthWestGravity
:
994 *x
-= (self
->size
.left
+ w
) / 2;
997 case NorthEastGravity
:
998 case SouthEastGravity
:
1000 *x
-= (self
->size
.left
+ self
->size
.right
+ w
) - 1;
1005 *x
-= self
->size
.left
;
1010 switch (self
->client
->gravity
) {
1012 case NorthWestGravity
:
1013 case NorthEastGravity
:
1020 *y
-= (self
->size
.top
+ h
) / 2;
1023 case SouthWestGravity
:
1024 case SouthEastGravity
:
1026 *y
-= (self
->size
.top
+ self
->size
.bottom
+ h
) - 1;
1031 *y
-= self
->size
.top
;
1036 void frame_frame_gravity(ObFrame
*self
, gint
*x
, gint
*y
, gint w
, gint h
)
1039 switch (self
->client
->gravity
) {
1041 case NorthWestGravity
:
1043 case SouthWestGravity
:
1048 *x
+= (self
->size
.left
+ w
) / 2;
1050 case NorthEastGravity
:
1052 case SouthEastGravity
:
1053 *x
+= (self
->size
.left
+ self
->size
.right
+ w
) - 1;
1057 *x
+= self
->size
.left
;
1062 switch (self
->client
->gravity
) {
1064 case NorthWestGravity
:
1066 case NorthEastGravity
:
1071 *y
+= (self
->size
.top
+ h
) / 2;
1073 case SouthWestGravity
:
1075 case SouthEastGravity
:
1076 *y
+= (self
->size
.top
+ self
->size
.bottom
+ h
) - 1;
1080 *y
+= self
->size
.top
;
1085 static void flash_done(gpointer data
)
1087 ObFrame
*self
= data
;
1089 if (self
->focused
!= self
->flash_on
)
1090 frame_adjust_focus(self
, self
->focused
);
1093 static gboolean
flash_timeout(gpointer data
)
1095 ObFrame
*self
= data
;
1098 g_get_current_time(&now
);
1099 if (now
.tv_sec
> self
->flash_end
.tv_sec
||
1100 (now
.tv_sec
== self
->flash_end
.tv_sec
&&
1101 now
.tv_usec
>= self
->flash_end
.tv_usec
))
1102 self
->flashing
= FALSE
;
1104 if (!self
->flashing
)
1105 return FALSE
; /* we are done */
1107 self
->flash_on
= !self
->flash_on
;
1108 if (!self
->focused
) {
1109 frame_adjust_focus(self
, self
->flash_on
);
1110 self
->focused
= FALSE
;
1113 return TRUE
; /* go again */
1116 void frame_flash_start(ObFrame
*self
)
1118 self
->flash_on
= self
->focused
;
1120 if (!self
->flashing
)
1121 ob_main_loop_timeout_add(ob_main_loop
,
1122 G_USEC_PER_SEC
* 0.6,
1127 g_get_current_time(&self
->flash_end
);
1128 g_time_val_add(&self
->flash_end
, G_USEC_PER_SEC
* 5);
1130 self
->flashing
= TRUE
;
1133 void frame_flash_stop(ObFrame
*self
)
1135 self
->flashing
= FALSE
;
1138 static gulong
frame_animate_iconify_time_left(ObFrame
*self
,
1139 const GTimeVal
*now
)
1142 sec
= self
->iconify_animation_end
.tv_sec
- now
->tv_sec
;
1143 usec
= self
->iconify_animation_end
.tv_usec
- now
->tv_usec
;
1145 usec
+= G_USEC_PER_SEC
;
1148 /* no negative values */
1149 return MAX(sec
* G_USEC_PER_SEC
+ usec
, 0);
1152 static gboolean
frame_animate_iconify(gpointer p
)
1156 gint iconx
, icony
, iconw
;
1159 gboolean iconifying
;
1161 if (self
->client
->icon_geometry
.width
== 0) {
1162 /* there is no icon geometry set so just go straight down */
1163 Rect
*a
= screen_physical_area();
1164 iconx
= self
->area
.x
+ self
->area
.width
/ 2 + 32;
1165 icony
= a
->y
+ a
->width
;
1168 iconx
= self
->client
->icon_geometry
.x
;
1169 icony
= self
->client
->icon_geometry
.y
;
1170 iconw
= self
->client
->icon_geometry
.width
;
1173 iconifying
= self
->iconify_animation_going
> 0;
1175 /* how far do we have left to go ? */
1176 g_get_current_time(&now
);
1177 time
= frame_animate_iconify_time_left(self
, &now
);
1179 if (time
== 0 || iconifying
) {
1180 /* start where the frame is supposed to be */
1183 w
= self
->area
.width
- self
->bwidth
* 2;
1184 h
= self
->area
.height
- self
->bwidth
* 2;
1186 /* start at the icon */
1190 h
= self
->innersize
.top
; /* just the titlebar */
1197 dx
= self
->area
.x
- iconx
;
1198 dy
= self
->area
.y
- icony
;
1199 dw
= self
->area
.width
- self
->bwidth
* 2 - iconw
;
1200 /* if restoring, we move in the opposite direction */
1201 if (!iconifying
) { dx
= -dx
; dy
= -dy
; dw
= -dw
; }
1203 elapsed
= FRAME_ANIMATE_ICONIFY_TIME
- time
;
1204 x
= x
- (dx
* elapsed
) / FRAME_ANIMATE_ICONIFY_TIME
;
1205 y
= y
- (dy
* elapsed
) / FRAME_ANIMATE_ICONIFY_TIME
;
1206 w
= w
- (dw
* elapsed
) / FRAME_ANIMATE_ICONIFY_TIME
;
1207 h
= self
->innersize
.top
; /* just the titlebar */
1211 frame_end_iconify_animation(self
);
1213 XMoveResizeWindow(ob_display
, self
->window
, x
, y
, w
, h
);
1217 return time
> 0; /* repeat until we're out of time */
1220 void frame_end_iconify_animation(ObFrame
*self
)
1222 /* see if there is an animation going */
1223 if (self
->iconify_animation_going
== 0) return;
1226 XUnmapWindow(ob_display
, self
->window
);
1228 /* Send a ConfigureNotify when the animation is done, this fixes
1229 KDE's pager showing the window in the wrong place. */
1230 client_reconfigure(self
->client
);
1232 /* we're not animating any more ! */
1233 self
->iconify_animation_going
= 0;
1235 XMoveResizeWindow(ob_display
, self
->window
,
1236 self
->area
.x
, self
->area
.y
,
1237 self
->area
.width
- self
->bwidth
* 2,
1238 self
->area
.height
- self
->bwidth
* 2);
1242 void frame_begin_iconify_animation(ObFrame
*self
, gboolean iconifying
)
1245 gboolean new_anim
= FALSE
;
1246 gboolean set_end
= TRUE
;
1249 /* if there is no titlebar, just don't animate for now
1250 XXX it would be nice tho.. */
1251 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1254 /* get the current time */
1255 g_get_current_time(&now
);
1257 /* get how long until the end */
1258 time
= FRAME_ANIMATE_ICONIFY_TIME
;
1259 if (self
->iconify_animation_going
) {
1260 if (!!iconifying
!= (self
->iconify_animation_going
> 0)) {
1261 /* animation was already going on in the opposite direction */
1262 time
= time
- frame_animate_iconify_time_left(self
, &now
);
1264 /* animation was already going in the same direction */
1268 self
->iconify_animation_going
= iconifying
? 1 : -1;
1270 /* set the ending time */
1272 self
->iconify_animation_end
.tv_sec
= now
.tv_sec
;
1273 self
->iconify_animation_end
.tv_usec
= now
.tv_usec
;
1274 g_time_val_add(&self
->iconify_animation_end
, time
);
1278 ob_main_loop_timeout_remove_data(ob_main_loop
, frame_animate_iconify
,
1280 ob_main_loop_timeout_add(ob_main_loop
,
1281 FRAME_ANIMATE_ICONIFY_STEP_TIME
,
1282 frame_animate_iconify
, self
,
1283 g_direct_equal
, NULL
);
1285 /* do the first step */
1286 frame_animate_iconify(self
);
1288 /* show it during the animation even if it is not "visible" */
1290 XMapWindow(ob_display
, self
->window
);