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
);
142 self
->label
= createWindow(self
->title
, NULL
, mask
, &attrib
);
143 self
->max
= createWindow(self
->title
, NULL
, mask
, &attrib
);
144 self
->close
= createWindow(self
->title
, NULL
, mask
, &attrib
);
145 self
->desk
= createWindow(self
->title
, NULL
, mask
, &attrib
);
146 self
->shade
= createWindow(self
->title
, NULL
, mask
, &attrib
);
147 self
->icon
= createWindow(self
->title
, NULL
, mask
, &attrib
);
148 self
->iconify
= createWindow(self
->title
, NULL
, mask
, &attrib
);
151 attrib
.cursor
= ob_cursor(OB_CURSOR_SOUTH
);
152 self
->handle
= createWindow(self
->window
, NULL
, mask
, &attrib
);
153 attrib
.cursor
= ob_cursor(OB_CURSOR_SOUTHWEST
);
154 self
->lgrip
= createWindow(self
->handle
, NULL
, mask
, &attrib
);
155 attrib
.cursor
= ob_cursor(OB_CURSOR_SOUTHEAST
);
156 self
->rgrip
= createWindow(self
->handle
, NULL
, mask
, &attrib
);
158 self
->focused
= FALSE
;
160 /* the other stuff is shown based on decor settings */
161 XMapWindow(ob_display
, self
->plate
);
162 XMapWindow(ob_display
, self
->inner
);
163 XMapWindow(ob_display
, self
->lgrip
);
164 XMapWindow(ob_display
, self
->rgrip
);
165 XMapWindow(ob_display
, self
->label
);
167 self
->max_press
= self
->close_press
= self
->desk_press
=
168 self
->iconify_press
= self
->shade_press
= FALSE
;
169 self
->max_hover
= self
->close_hover
= self
->desk_hover
=
170 self
->iconify_hover
= self
->shade_hover
= FALSE
;
172 set_theme_statics(self
);
174 return (ObFrame
*)self
;
177 static void set_theme_statics(ObFrame
*self
)
181 if (ob_rr_theme
->handle_height
> 0)
182 handle_height
= ob_rr_theme
->handle_height
;
187 /* set colors/appearance/sizes for stuff that doesn't change */
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 XResizeWindow(ob_display
, self
->lgrip
,
201 ob_rr_theme
->grip_width
, handle_height
);
202 XResizeWindow(ob_display
, self
->rgrip
,
203 ob_rr_theme
->grip_width
, handle_height
);
204 XResizeWindow(ob_display
, self
->tltresize
,
205 ob_rr_theme
->grip_width
, ob_rr_theme
->paddingy
+ 1);
206 XResizeWindow(ob_display
, self
->trtresize
,
207 ob_rr_theme
->grip_width
, ob_rr_theme
->paddingy
+ 1);
208 XResizeWindow(ob_display
, self
->tllresize
,
209 ob_rr_theme
->paddingx
+ 1, ob_rr_theme
->title_height
);
210 XResizeWindow(ob_display
, self
->trrresize
,
211 ob_rr_theme
->paddingx
+ 1, ob_rr_theme
->title_height
);
213 /* set up the dynamic appearances */
214 self
->a_unfocused_title
= RrAppearanceCopy(ob_rr_theme
->a_unfocused_title
);
215 self
->a_focused_title
= RrAppearanceCopy(ob_rr_theme
->a_focused_title
);
216 self
->a_unfocused_label
= RrAppearanceCopy(ob_rr_theme
->a_unfocused_label
);
217 self
->a_focused_label
= RrAppearanceCopy(ob_rr_theme
->a_focused_label
);
218 self
->a_unfocused_handle
=
219 RrAppearanceCopy(ob_rr_theme
->a_unfocused_handle
);
220 self
->a_focused_handle
= RrAppearanceCopy(ob_rr_theme
->a_focused_handle
);
221 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_icon
);
224 static void free_theme_statics(ObFrame
*self
)
226 RrAppearanceFree(self
->a_unfocused_title
);
227 RrAppearanceFree(self
->a_focused_title
);
228 RrAppearanceFree(self
->a_unfocused_label
);
229 RrAppearanceFree(self
->a_focused_label
);
230 RrAppearanceFree(self
->a_unfocused_handle
);
231 RrAppearanceFree(self
->a_focused_handle
);
232 RrAppearanceFree(self
->a_icon
);
235 void frame_free(ObFrame
*self
)
237 free_theme_statics(self
);
239 XDestroyWindow(ob_display
, self
->window
);
241 XFreeColormap(ob_display
, self
->colormap
);
246 void frame_show(ObFrame
*self
)
248 if (!self
->visible
) {
249 self
->visible
= TRUE
;
250 XMapWindow(ob_display
, self
->client
->window
);
251 XMapWindow(ob_display
, self
->window
);
255 void frame_hide(ObFrame
*self
)
258 self
->visible
= FALSE
;
259 if (!frame_iconify_animating(self
))
260 XUnmapWindow(ob_display
, self
->window
);
261 /* we unmap the client itself so that we can get MapRequest
262 events, and because the ICCCM tells us to! */
263 XUnmapWindow(ob_display
, self
->client
->window
);
264 self
->client
->ignore_unmaps
+= 1;
268 void frame_adjust_theme(ObFrame
*self
)
270 free_theme_statics(self
);
271 set_theme_statics(self
);
274 void frame_adjust_shape(ObFrame
*self
)
280 if (!self
->client
->shaped
) {
281 /* clear the shape on the frame window */
282 XShapeCombineMask(ob_display
, self
->window
, ShapeBounding
,
283 self
->innersize
.left
,
287 /* make the frame's shape match the clients */
288 XShapeCombineShape(ob_display
, self
->window
, ShapeBounding
,
289 self
->innersize
.left
,
291 self
->client
->window
,
292 ShapeBounding
, ShapeSet
);
295 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
) {
296 xrect
[0].x
= -ob_rr_theme
->fbwidth
;
297 xrect
[0].y
= -ob_rr_theme
->fbwidth
;
298 xrect
[0].width
= self
->width
+ self
->rbwidth
* 2;
299 xrect
[0].height
= ob_rr_theme
->title_height
+
304 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
) {
305 xrect
[1].x
= -ob_rr_theme
->fbwidth
;
306 xrect
[1].y
= FRAME_HANDLE_Y(self
);
307 xrect
[1].width
= self
->width
+ self
->rbwidth
* 2;
308 xrect
[1].height
= ob_rr_theme
->handle_height
+
313 XShapeCombineRectangles(ob_display
, self
->window
,
314 ShapeBounding
, 0, 0, xrect
, num
,
315 ShapeUnion
, Unsorted
);
320 void frame_adjust_area(ObFrame
*self
, gboolean moved
,
321 gboolean resized
, gboolean fake
)
325 oldsize
= self
->size
;
328 self
->decorations
= self
->client
->decorations
;
329 self
->max_horz
= self
->client
->max_horz
;
331 if (self
->decorations
& OB_FRAME_DECOR_BORDER
) {
332 self
->bwidth
= ob_rr_theme
->fbwidth
;
333 self
->cbwidth_x
= ob_rr_theme
->cbwidthx
;
334 self
->cbwidth_y
= ob_rr_theme
->cbwidthy
;
336 self
->bwidth
= self
->cbwidth_x
= self
->cbwidth_y
= 0;
338 self
->rbwidth
= self
->bwidth
;
341 self
->bwidth
= self
->cbwidth_x
= 0;
343 STRUT_SET(self
->innersize
,
348 self
->width
= self
->client
->area
.width
+ self
->cbwidth_x
* 2 -
349 (self
->max_horz
? self
->rbwidth
* 2 : 0);
350 self
->width
= MAX(self
->width
, 1); /* no lower than 1 */
352 /* set border widths */
354 XSetWindowBorderWidth(ob_display
, self
->window
, self
->bwidth
);
355 XSetWindowBorderWidth(ob_display
, self
->inner
, self
->bwidth
);
356 XSetWindowBorderWidth(ob_display
, self
->title
, self
->rbwidth
);
357 XSetWindowBorderWidth(ob_display
, self
->handle
, self
->rbwidth
);
358 XSetWindowBorderWidth(ob_display
, self
->lgrip
, self
->rbwidth
);
359 XSetWindowBorderWidth(ob_display
, self
->rgrip
, self
->rbwidth
);
362 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
)
363 self
->innersize
.top
+= ob_rr_theme
->title_height
+ self
->rbwidth
+
364 (self
->rbwidth
- self
->bwidth
);
365 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
&&
366 ob_rr_theme
->handle_height
> 0)
367 self
->innersize
.bottom
+= ob_rr_theme
->handle_height
+
368 self
->rbwidth
+ (self
->rbwidth
- self
->bwidth
);
370 /* position/size and map/unmap all the windows */
373 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
) {
374 XMoveResizeWindow(ob_display
, self
->title
,
375 -self
->bwidth
, -self
->bwidth
,
376 self
->width
, ob_rr_theme
->title_height
);
377 XMapWindow(ob_display
, self
->title
);
379 if (self
->decorations
& OB_FRAME_DECOR_GRIPS
) {
380 XMoveResizeWindow(ob_display
, self
->topresize
,
381 ob_rr_theme
->grip_width
+ self
->bwidth
,
383 self
->width
- (ob_rr_theme
->grip_width
+
385 ob_rr_theme
->paddingy
+ 1);
387 XMoveWindow(ob_display
, self
->tltresize
, 0, 0);
388 XMoveWindow(ob_display
, self
->tllresize
, 0, 0);
389 XMoveWindow(ob_display
, self
->trtresize
,
390 self
->width
- ob_rr_theme
->grip_width
, 0);
391 XMoveWindow(ob_display
, self
->trrresize
,
392 self
->width
- ob_rr_theme
->paddingx
- 1, 0);
394 XMapWindow(ob_display
, self
->topresize
);
395 XMapWindow(ob_display
, self
->tltresize
);
396 XMapWindow(ob_display
, self
->tllresize
);
397 XMapWindow(ob_display
, self
->trtresize
);
398 XMapWindow(ob_display
, self
->trrresize
);
400 XUnmapWindow(ob_display
, self
->topresize
);
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
)
419 if (ob_rr_theme
->handle_height
> 0)
420 handle_height
= ob_rr_theme
->handle_height
;
424 XMoveResizeWindow(ob_display
, self
->handle
,
425 -self
->bwidth
, FRAME_HANDLE_Y(self
),
426 self
->width
, handle_height
);
427 XMapWindow(ob_display
, self
->handle
);
429 if (self
->decorations
& OB_FRAME_DECOR_GRIPS
) {
430 XMoveWindow(ob_display
, self
->lgrip
,
431 -self
->rbwidth
, -self
->rbwidth
);
432 XMoveWindow(ob_display
, self
->rgrip
,
433 -self
->rbwidth
+ self
->width
-
434 ob_rr_theme
->grip_width
, -self
->rbwidth
);
435 XMapWindow(ob_display
, self
->lgrip
);
436 XMapWindow(ob_display
, self
->rgrip
);
438 XUnmapWindow(ob_display
, self
->lgrip
);
439 XUnmapWindow(ob_display
, self
->rgrip
);
442 XUnmapWindow(ob_display
, self
->handle
);
444 /* move and resize the inner border window which contains the plate
446 XMoveResizeWindow(ob_display
, self
->inner
,
447 self
->innersize
.left
- self
->cbwidth_x
-
449 self
->innersize
.top
- self
->cbwidth_y
-
451 self
->client
->area
.width
+
453 self
->client
->area
.height
+
454 self
->cbwidth_y
* 2);
457 XMoveWindow(ob_display
, self
->plate
,
458 self
->cbwidth_x
, self
->cbwidth_y
);
460 /* when the client has StaticGravity, it likes to move around. */
461 XMoveWindow(ob_display
, self
->client
->window
, 0, 0);
464 STRUT_SET(self
->size
,
465 self
->innersize
.left
+ self
->bwidth
,
466 self
->innersize
.top
+ self
->bwidth
,
467 self
->innersize
.right
+ self
->bwidth
,
468 self
->innersize
.bottom
+ self
->bwidth
);
471 /* shading can change without being moved or resized */
472 RECT_SET_SIZE(self
->area
,
473 self
->client
->area
.width
+
474 self
->size
.left
+ self
->size
.right
,
475 (self
->client
->shaded
?
476 ob_rr_theme
->title_height
+ self
->rbwidth
* 2:
477 self
->client
->area
.height
+
478 self
->size
.top
+ self
->size
.bottom
));
480 if (moved
|| resized
) {
481 /* find the new coordinates, done after setting the frame.size, for
482 frame_client_gravity. */
483 self
->area
.x
= self
->client
->area
.x
;
484 self
->area
.y
= self
->client
->area
.y
;
485 frame_client_gravity(self
, &self
->area
.x
, &self
->area
.y
,
486 self
->client
->area
.width
,
487 self
->client
->area
.height
);
491 if (!frame_iconify_animating(self
))
492 /* move and resize the top level frame.
493 shading can change without being moved or resized.
495 but don't do this during an iconify animation. it will be
496 reflected afterwards.
498 XMoveResizeWindow(ob_display
, self
->window
,
499 self
->area
.x
, self
->area
.y
,
500 self
->area
.width
- self
->bwidth
* 2,
501 self
->area
.height
- self
->bwidth
* 2);
504 framerender_frame(self
);
505 frame_adjust_shape(self
);
508 if (!STRUT_EQUAL(self
->size
, oldsize
)) {
510 vals
[0] = self
->size
.left
;
511 vals
[1] = self
->size
.right
;
512 vals
[2] = self
->size
.top
;
513 vals
[3] = self
->size
.bottom
;
514 PROP_SETA32(self
->client
->window
, net_frame_extents
,
516 PROP_SETA32(self
->client
->window
, kde_net_wm_frame_strut
,
520 /* if this occurs while we are focus cycling, the indicator needs to
522 if (focus_cycle_target
== self
->client
)
523 focus_cycle_draw_indicator(self
->client
);
525 if (resized
&& (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
526 XResizeWindow(ob_display
, self
->label
, self
->label_width
,
527 ob_rr_theme
->label_height
);
530 void frame_adjust_client_area(ObFrame
*self
)
532 /* resize the plate */
533 XResizeWindow(ob_display
, self
->plate
,
534 self
->client
->area
.width
, self
->client
->area
.height
);
537 void frame_adjust_state(ObFrame
*self
)
539 framerender_frame(self
);
542 void frame_adjust_focus(ObFrame
*self
, gboolean hilite
)
544 self
->focused
= hilite
;
545 framerender_frame(self
);
549 void frame_adjust_title(ObFrame
*self
)
551 framerender_frame(self
);
554 void frame_adjust_icon(ObFrame
*self
)
556 framerender_frame(self
);
559 void frame_grab_client(ObFrame
*self
)
561 /* reparent the client to the frame */
562 XReparentWindow(ob_display
, self
->client
->window
, self
->plate
, 0, 0);
564 When reparenting the client window, it is usually not mapped yet, since
565 this occurs from a MapRequest. However, in the case where Openbox is
566 starting up, the window is already mapped, so we'll see unmap events for
567 it. There are 2 unmap events generated that we see, one with the 'event'
568 member set the root window, and one set to the client, but both get
569 handled and need to be ignored.
571 if (ob_state() == OB_STATE_STARTING
)
572 self
->client
->ignore_unmaps
+= 2;
574 /* select the event mask on the client's parent (to receive config/map
575 req's) the ButtonPress is to catch clicks on the client border */
576 XSelectInput(ob_display
, self
->plate
, PLATE_EVENTMASK
);
578 /* map the client so it maps when the frame does */
579 XMapWindow(ob_display
, self
->client
->window
);
581 /* set all the windows for the frame in the window_map */
582 g_hash_table_insert(window_map
, &self
->window
, self
->client
);
583 g_hash_table_insert(window_map
, &self
->plate
, self
->client
);
584 g_hash_table_insert(window_map
, &self
->inner
, self
->client
);
585 g_hash_table_insert(window_map
, &self
->title
, self
->client
);
586 g_hash_table_insert(window_map
, &self
->label
, self
->client
);
587 g_hash_table_insert(window_map
, &self
->max
, self
->client
);
588 g_hash_table_insert(window_map
, &self
->close
, self
->client
);
589 g_hash_table_insert(window_map
, &self
->desk
, self
->client
);
590 g_hash_table_insert(window_map
, &self
->shade
, self
->client
);
591 g_hash_table_insert(window_map
, &self
->icon
, self
->client
);
592 g_hash_table_insert(window_map
, &self
->iconify
, self
->client
);
593 g_hash_table_insert(window_map
, &self
->handle
, self
->client
);
594 g_hash_table_insert(window_map
, &self
->lgrip
, self
->client
);
595 g_hash_table_insert(window_map
, &self
->rgrip
, self
->client
);
596 g_hash_table_insert(window_map
, &self
->topresize
, self
->client
);
597 g_hash_table_insert(window_map
, &self
->tltresize
, self
->client
);
598 g_hash_table_insert(window_map
, &self
->tllresize
, self
->client
);
599 g_hash_table_insert(window_map
, &self
->trtresize
, self
->client
);
600 g_hash_table_insert(window_map
, &self
->trrresize
, self
->client
);
603 void frame_release_client(ObFrame
*self
)
606 gboolean reparent
= TRUE
;
608 /* if there was any animation going on, kill it */
609 ob_main_loop_timeout_remove_data(ob_main_loop
, frame_animate_iconify
,
612 /* check if the app has already reparented its window away */
613 while (XCheckTypedWindowEvent(ob_display
, self
->client
->window
,
614 ReparentNotify
, &ev
))
616 /* This check makes sure we don't catch our own reparent action to
617 our frame window. This doesn't count as the app reparenting itself
620 Reparent events that are generated by us are just discarded here.
621 They are of no consequence to us anyhow.
623 if (ev
.xreparent
.parent
!= self
->plate
) {
625 XPutBackEvent(ob_display
, &ev
);
631 /* according to the ICCCM - if the client doesn't reparent itself,
632 then we will reparent the window to root for them */
633 XReparentWindow(ob_display
, self
->client
->window
,
634 RootWindow(ob_display
, ob_screen
),
635 self
->client
->area
.x
,
636 self
->client
->area
.y
);
639 /* remove all the windows for the frame from the window_map */
640 g_hash_table_remove(window_map
, &self
->window
);
641 g_hash_table_remove(window_map
, &self
->plate
);
642 g_hash_table_remove(window_map
, &self
->inner
);
643 g_hash_table_remove(window_map
, &self
->title
);
644 g_hash_table_remove(window_map
, &self
->label
);
645 g_hash_table_remove(window_map
, &self
->max
);
646 g_hash_table_remove(window_map
, &self
->close
);
647 g_hash_table_remove(window_map
, &self
->desk
);
648 g_hash_table_remove(window_map
, &self
->shade
);
649 g_hash_table_remove(window_map
, &self
->icon
);
650 g_hash_table_remove(window_map
, &self
->iconify
);
651 g_hash_table_remove(window_map
, &self
->handle
);
652 g_hash_table_remove(window_map
, &self
->lgrip
);
653 g_hash_table_remove(window_map
, &self
->rgrip
);
654 g_hash_table_remove(window_map
, &self
->topresize
);
655 g_hash_table_remove(window_map
, &self
->tltresize
);
656 g_hash_table_remove(window_map
, &self
->tllresize
);
657 g_hash_table_remove(window_map
, &self
->trtresize
);
658 g_hash_table_remove(window_map
, &self
->trrresize
);
660 ob_main_loop_timeout_remove_data(ob_main_loop
, flash_timeout
, self
, TRUE
);
663 /* is there anything present between us and the label? */
664 static gboolean
is_button_present(ObFrame
*self
, const gchar
*lc
, gint dir
) {
665 for (; *lc
!= '\0' && lc
>= config_title_layout
; lc
+= dir
) {
666 if (*lc
== ' ') continue; /* it was invalid */
667 if (*lc
== 'N' && self
->decorations
& OB_FRAME_DECOR_ICON
)
669 if (*lc
== 'D' && self
->decorations
& OB_FRAME_DECOR_ALLDESKTOPS
)
671 if (*lc
== 'S' && self
->decorations
& OB_FRAME_DECOR_SHADE
)
673 if (*lc
== 'I' && self
->decorations
& OB_FRAME_DECOR_ICONIFY
)
675 if (*lc
== 'M' && self
->decorations
& OB_FRAME_DECOR_MAXIMIZE
)
677 if (*lc
== 'C' && self
->decorations
& OB_FRAME_DECOR_CLOSE
)
679 if (*lc
== 'L') return FALSE
;
684 static void layout_title(ObFrame
*self
)
689 const gint bwidth
= ob_rr_theme
->button_size
+ ob_rr_theme
->paddingx
+ 1;
690 /* position of the left most button */
691 const gint left
= ob_rr_theme
->paddingx
+ 1;
692 /* position of the right most button */
693 const gint right
= self
->width
- bwidth
;
695 /* turn them all off */
696 self
->icon_on
= self
->desk_on
= self
->shade_on
= self
->iconify_on
=
697 self
->max_on
= self
->close_on
= self
->label_on
= FALSE
;
698 self
->label_width
= self
->width
- (ob_rr_theme
->paddingx
+ 1) * 2;
699 self
->leftmost
= self
->rightmost
= OB_FRAME_CONTEXT_NONE
;
701 /* figure out what's being show, find each element's position, and the
704 do the ones before the label, then after the label,
705 i will be +1 the first time through when working to the left,
706 and -1 the second time through when working to the right */
707 for (i
= 1; i
>= -1; i
-=2) {
709 ObFrameContext
*firstcon
;
713 lc
= config_title_layout
;
714 firstcon
= &self
->leftmost
;
717 lc
= config_title_layout
+ strlen(config_title_layout
)-1;
718 firstcon
= &self
->rightmost
;
721 /* stop at the end of the string (or the label, which calls break) */
722 for (; *lc
!= '\0' && lc
>= config_title_layout
; lc
+=i
) {
725 self
->label_on
= TRUE
;
728 break; /* break the for loop, do other side of label */
729 } else if (*lc
== 'N') {
730 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_ICON
;
731 if ((self
->icon_on
= is_button_present(self
, lc
, i
))) {
732 /* icon is bigger than buttons */
733 self
->label_width
-= bwidth
+ 2;
735 x
+= i
* (bwidth
+ 2);
737 } else if (*lc
== 'D') {
738 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_ALLDESKTOPS
;
739 if ((self
->desk_on
= is_button_present(self
, lc
, i
))) {
740 self
->label_width
-= bwidth
;
744 } else if (*lc
== 'S') {
745 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_SHADE
;
746 if ((self
->shade_on
= is_button_present(self
, lc
, i
))) {
747 self
->label_width
-= bwidth
;
751 } else if (*lc
== 'I') {
752 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_ICONIFY
;
753 if ((self
->iconify_on
= is_button_present(self
, lc
, i
))) {
754 self
->label_width
-= bwidth
;
758 } else if (*lc
== 'M') {
759 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_MAXIMIZE
;
760 if ((self
->max_on
= is_button_present(self
, lc
, i
))) {
761 self
->label_width
-= bwidth
;
765 } else if (*lc
== 'C') {
766 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_CLOSE
;
767 if ((self
->close_on
= is_button_present(self
, lc
, i
))) {
768 self
->label_width
-= bwidth
;
773 continue; /* don't set firstcon */
778 /* position and map the elements */
780 XMapWindow(ob_display
, self
->icon
);
781 XMoveWindow(ob_display
, self
->icon
, self
->icon_x
,
782 ob_rr_theme
->paddingy
);
784 XUnmapWindow(ob_display
, self
->icon
);
787 XMapWindow(ob_display
, self
->desk
);
788 XMoveWindow(ob_display
, self
->desk
, self
->desk_x
,
789 ob_rr_theme
->paddingy
+ 1);
791 XUnmapWindow(ob_display
, self
->desk
);
793 if (self
->shade_on
) {
794 XMapWindow(ob_display
, self
->shade
);
795 XMoveWindow(ob_display
, self
->shade
, self
->shade_x
,
796 ob_rr_theme
->paddingy
+ 1);
798 XUnmapWindow(ob_display
, self
->shade
);
800 if (self
->iconify_on
) {
801 XMapWindow(ob_display
, self
->iconify
);
802 XMoveWindow(ob_display
, self
->iconify
, self
->iconify_x
,
803 ob_rr_theme
->paddingy
+ 1);
805 XUnmapWindow(ob_display
, self
->iconify
);
808 XMapWindow(ob_display
, self
->max
);
809 XMoveWindow(ob_display
, self
->max
, self
->max_x
,
810 ob_rr_theme
->paddingy
+ 1);
812 XUnmapWindow(ob_display
, self
->max
);
814 if (self
->close_on
) {
815 XMapWindow(ob_display
, self
->close
);
816 XMoveWindow(ob_display
, self
->close
, self
->close_x
,
817 ob_rr_theme
->paddingy
+ 1);
819 XUnmapWindow(ob_display
, self
->close
);
821 if (self
->label_on
) {
822 self
->label_width
= MAX(1, self
->label_width
); /* no lower than 1 */
823 XMapWindow(ob_display
, self
->label
);
824 XMoveWindow(ob_display
, self
->label
, self
->label_x
,
825 ob_rr_theme
->paddingy
);
827 XUnmapWindow(ob_display
, self
->label
);
830 ObFrameContext
frame_context_from_string(const gchar
*name
)
832 if (!g_ascii_strcasecmp("Desktop", name
))
833 return OB_FRAME_CONTEXT_DESKTOP
;
834 else if (!g_ascii_strcasecmp("Client", name
))
835 return OB_FRAME_CONTEXT_CLIENT
;
836 else if (!g_ascii_strcasecmp("Titlebar", name
))
837 return OB_FRAME_CONTEXT_TITLEBAR
;
838 else if (!g_ascii_strcasecmp("Frame", name
))
839 return OB_FRAME_CONTEXT_FRAME
;
840 else if (!g_ascii_strcasecmp("TLCorner", name
))
841 return OB_FRAME_CONTEXT_TLCORNER
;
842 else if (!g_ascii_strcasecmp("TRCorner", name
))
843 return OB_FRAME_CONTEXT_TRCORNER
;
844 else if (!g_ascii_strcasecmp("BLCorner", name
))
845 return OB_FRAME_CONTEXT_BLCORNER
;
846 else if (!g_ascii_strcasecmp("BRCorner", name
))
847 return OB_FRAME_CONTEXT_BRCORNER
;
848 else if (!g_ascii_strcasecmp("Top", name
))
849 return OB_FRAME_CONTEXT_TOP
;
850 else if (!g_ascii_strcasecmp("Bottom", name
))
851 return OB_FRAME_CONTEXT_BOTTOM
;
852 else if (!g_ascii_strcasecmp("Maximize", name
))
853 return OB_FRAME_CONTEXT_MAXIMIZE
;
854 else if (!g_ascii_strcasecmp("AllDesktops", name
))
855 return OB_FRAME_CONTEXT_ALLDESKTOPS
;
856 else if (!g_ascii_strcasecmp("Shade", name
))
857 return OB_FRAME_CONTEXT_SHADE
;
858 else if (!g_ascii_strcasecmp("Iconify", name
))
859 return OB_FRAME_CONTEXT_ICONIFY
;
860 else if (!g_ascii_strcasecmp("Icon", name
))
861 return OB_FRAME_CONTEXT_ICON
;
862 else if (!g_ascii_strcasecmp("Close", name
))
863 return OB_FRAME_CONTEXT_CLOSE
;
864 else if (!g_ascii_strcasecmp("MoveResize", name
))
865 return OB_FRAME_CONTEXT_MOVE_RESIZE
;
866 return OB_FRAME_CONTEXT_NONE
;
869 ObFrameContext
frame_context(ObClient
*client
, Window win
, gint x
, gint y
)
873 if (moveresize_in_progress
)
874 return OB_FRAME_CONTEXT_MOVE_RESIZE
;
876 if (win
== RootWindow(ob_display
, ob_screen
))
877 return OB_FRAME_CONTEXT_DESKTOP
;
878 if (client
== NULL
) return OB_FRAME_CONTEXT_NONE
;
879 if (win
== client
->window
) {
880 /* conceptually, this is the desktop, as far as users are
882 if (client
->type
== OB_CLIENT_TYPE_DESKTOP
)
883 return OB_FRAME_CONTEXT_DESKTOP
;
884 return OB_FRAME_CONTEXT_CLIENT
;
887 self
= client
->frame
;
888 if (win
== self
->inner
|| win
== self
->plate
) {
889 /* conceptually, this is the desktop, as far as users are
891 if (client
->type
== OB_CLIENT_TYPE_DESKTOP
)
892 return OB_FRAME_CONTEXT_DESKTOP
;
893 return OB_FRAME_CONTEXT_CLIENT
;
896 if (win
== self
->title
) {
897 /* when the user clicks in the corners of the titlebar and the client
898 is fully maximized, then treat it like they clicked in the
899 button that is there */
900 if (self
->client
->max_horz
&& self
->client
->max_vert
&&
901 y
< ob_rr_theme
->paddingy
+ 1 + ob_rr_theme
->button_size
)
903 if (x
< ((ob_rr_theme
->paddingx
+ 1) * 2 +
904 ob_rr_theme
->button_size
)) {
905 if (self
->leftmost
!= OB_FRAME_CONTEXT_NONE
)
906 return self
->leftmost
;
908 else if (x
> (self
->width
-
909 (ob_rr_theme
->paddingx
+ 1 +
910 ob_rr_theme
->button_size
)))
912 if (self
->rightmost
!= OB_FRAME_CONTEXT_NONE
)
913 return self
->rightmost
;
916 return OB_FRAME_CONTEXT_TITLEBAR
;
919 if (win
== self
->window
) return OB_FRAME_CONTEXT_FRAME
;
920 if (win
== self
->label
) return OB_FRAME_CONTEXT_TITLEBAR
;
921 if (win
== self
->handle
) return OB_FRAME_CONTEXT_BOTTOM
;
922 if (win
== self
->lgrip
) return OB_FRAME_CONTEXT_BLCORNER
;
923 if (win
== self
->rgrip
) return OB_FRAME_CONTEXT_BRCORNER
;
924 if (win
== self
->topresize
) return OB_FRAME_CONTEXT_TOP
;
925 if (win
== self
->tltresize
) return OB_FRAME_CONTEXT_TLCORNER
;
926 if (win
== self
->tllresize
) return OB_FRAME_CONTEXT_TLCORNER
;
927 if (win
== self
->trtresize
) return OB_FRAME_CONTEXT_TRCORNER
;
928 if (win
== self
->trrresize
) return OB_FRAME_CONTEXT_TRCORNER
;
929 if (win
== self
->max
) return OB_FRAME_CONTEXT_MAXIMIZE
;
930 if (win
== self
->iconify
) return OB_FRAME_CONTEXT_ICONIFY
;
931 if (win
== self
->close
) return OB_FRAME_CONTEXT_CLOSE
;
932 if (win
== self
->icon
) return OB_FRAME_CONTEXT_ICON
;
933 if (win
== self
->desk
) return OB_FRAME_CONTEXT_ALLDESKTOPS
;
934 if (win
== self
->shade
) return OB_FRAME_CONTEXT_SHADE
;
936 return OB_FRAME_CONTEXT_NONE
;
939 void frame_client_gravity(ObFrame
*self
, gint
*x
, gint
*y
, gint w
, gint h
)
942 switch (self
->client
->gravity
) {
944 case NorthWestGravity
:
945 case SouthWestGravity
:
952 *x
-= (self
->size
.left
+ w
) / 2;
955 case NorthEastGravity
:
956 case SouthEastGravity
:
958 *x
-= (self
->size
.left
+ self
->size
.right
+ w
) - 1;
963 *x
-= self
->size
.left
;
968 switch (self
->client
->gravity
) {
970 case NorthWestGravity
:
971 case NorthEastGravity
:
978 *y
-= (self
->size
.top
+ h
) / 2;
981 case SouthWestGravity
:
982 case SouthEastGravity
:
984 *y
-= (self
->size
.top
+ self
->size
.bottom
+ h
) - 1;
989 *y
-= self
->size
.top
;
994 void frame_frame_gravity(ObFrame
*self
, gint
*x
, gint
*y
, gint w
, gint h
)
997 switch (self
->client
->gravity
) {
999 case NorthWestGravity
:
1001 case SouthWestGravity
:
1006 *x
+= (self
->size
.left
+ w
) / 2;
1008 case NorthEastGravity
:
1010 case SouthEastGravity
:
1011 *x
+= (self
->size
.left
+ self
->size
.right
+ w
) - 1;
1015 *x
+= self
->size
.left
;
1020 switch (self
->client
->gravity
) {
1022 case NorthWestGravity
:
1024 case NorthEastGravity
:
1029 *y
+= (self
->size
.top
+ h
) / 2;
1031 case SouthWestGravity
:
1033 case SouthEastGravity
:
1034 *y
+= (self
->size
.top
+ self
->size
.bottom
+ h
) - 1;
1038 *y
+= self
->size
.top
;
1043 static void flash_done(gpointer data
)
1045 ObFrame
*self
= data
;
1047 if (self
->focused
!= self
->flash_on
)
1048 frame_adjust_focus(self
, self
->focused
);
1051 static gboolean
flash_timeout(gpointer data
)
1053 ObFrame
*self
= data
;
1056 g_get_current_time(&now
);
1057 if (now
.tv_sec
> self
->flash_end
.tv_sec
||
1058 (now
.tv_sec
== self
->flash_end
.tv_sec
&&
1059 now
.tv_usec
>= self
->flash_end
.tv_usec
))
1060 self
->flashing
= FALSE
;
1062 if (!self
->flashing
)
1063 return FALSE
; /* we are done */
1065 self
->flash_on
= !self
->flash_on
;
1066 if (!self
->focused
) {
1067 frame_adjust_focus(self
, self
->flash_on
);
1068 self
->focused
= FALSE
;
1071 return TRUE
; /* go again */
1074 void frame_flash_start(ObFrame
*self
)
1076 self
->flash_on
= self
->focused
;
1078 if (!self
->flashing
)
1079 ob_main_loop_timeout_add(ob_main_loop
,
1080 G_USEC_PER_SEC
* 0.6,
1085 g_get_current_time(&self
->flash_end
);
1086 g_time_val_add(&self
->flash_end
, G_USEC_PER_SEC
* 5);
1088 self
->flashing
= TRUE
;
1091 void frame_flash_stop(ObFrame
*self
)
1093 self
->flashing
= FALSE
;
1096 static gulong
frame_animate_iconify_time_left(ObFrame
*self
,
1097 const GTimeVal
*now
)
1100 sec
= self
->iconify_animation_end
.tv_sec
- now
->tv_sec
;
1101 usec
= self
->iconify_animation_end
.tv_usec
- now
->tv_usec
;
1103 usec
+= G_USEC_PER_SEC
;
1106 /* no negative values */
1107 return MAX(sec
* G_USEC_PER_SEC
+ usec
, 0);
1110 static gboolean
frame_animate_iconify(gpointer p
)
1114 gint iconx
, icony
, iconw
;
1117 gboolean iconifying
;
1119 if (self
->client
->icon_geometry
.width
== 0) {
1120 /* there is no icon geometry set so just go straight down */
1121 Rect
*a
= screen_physical_area();
1122 iconx
= self
->area
.x
+ self
->area
.width
/ 2 + 32;
1123 icony
= a
->y
+ a
->width
;
1126 iconx
= self
->client
->icon_geometry
.x
;
1127 icony
= self
->client
->icon_geometry
.y
;
1128 iconw
= self
->client
->icon_geometry
.width
;
1131 iconifying
= self
->iconify_animation_going
> 0;
1133 /* how far do we have left to go ? */
1134 g_get_current_time(&now
);
1135 time
= frame_animate_iconify_time_left(self
, &now
);
1137 if (time
== 0 || iconifying
) {
1138 /* start where the frame is supposed to be */
1141 w
= self
->area
.width
- self
->bwidth
* 2;
1142 h
= self
->area
.height
- self
->bwidth
* 2;
1144 /* start at the icon */
1148 h
= self
->innersize
.top
; /* just the titlebar */
1155 dx
= self
->area
.x
- iconx
;
1156 dy
= self
->area
.y
- icony
;
1157 dw
= self
->area
.width
- self
->bwidth
* 2 - iconw
;
1158 /* if restoring, we move in the opposite direction */
1159 if (!iconifying
) { dx
= -dx
; dy
= -dy
; dw
= -dw
; }
1161 elapsed
= FRAME_ANIMATE_ICONIFY_TIME
- time
;
1162 x
= x
- (dx
* elapsed
) / FRAME_ANIMATE_ICONIFY_TIME
;
1163 y
= y
- (dy
* elapsed
) / FRAME_ANIMATE_ICONIFY_TIME
;
1164 w
= w
- (dw
* elapsed
) / FRAME_ANIMATE_ICONIFY_TIME
;
1165 h
= self
->innersize
.top
; /* just the titlebar */
1169 frame_end_iconify_animation(self
);
1171 XMoveResizeWindow(ob_display
, self
->window
, x
, y
, w
, h
);
1175 return time
> 0; /* repeat until we're out of time */
1178 void frame_end_iconify_animation(ObFrame
*self
)
1180 /* see if there is an animation going */
1181 if (self
->iconify_animation_going
== 0) return;
1184 XUnmapWindow(ob_display
, self
->window
);
1186 /* Send a ConfigureNotify when the animation is done, this fixes
1187 KDE's pager showing the window in the wrong place. */
1188 client_reconfigure(self
->client
);
1190 /* we're not animating any more ! */
1191 self
->iconify_animation_going
= 0;
1193 XMoveResizeWindow(ob_display
, self
->window
,
1194 self
->area
.x
, self
->area
.y
,
1195 self
->area
.width
- self
->bwidth
* 2,
1196 self
->area
.height
- self
->bwidth
* 2);
1200 void frame_begin_iconify_animation(ObFrame
*self
, gboolean iconifying
)
1203 gboolean new_anim
= FALSE
;
1204 gboolean set_end
= TRUE
;
1207 /* if there is no titlebar, just don't animate for now
1208 XXX it would be nice tho.. */
1209 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1212 /* get the current time */
1213 g_get_current_time(&now
);
1215 /* get how long until the end */
1216 time
= FRAME_ANIMATE_ICONIFY_TIME
;
1217 if (self
->iconify_animation_going
) {
1218 if (!!iconifying
!= (self
->iconify_animation_going
> 0)) {
1219 /* animation was already going on in the opposite direction */
1220 time
= time
- frame_animate_iconify_time_left(self
, &now
);
1222 /* animation was already going in the same direction */
1226 self
->iconify_animation_going
= iconifying
? 1 : -1;
1228 /* set the ending time */
1230 self
->iconify_animation_end
.tv_sec
= now
.tv_sec
;
1231 self
->iconify_animation_end
.tv_usec
= now
.tv_usec
;
1232 g_time_val_add(&self
->iconify_animation_end
, time
);
1236 ob_main_loop_timeout_remove_data(ob_main_loop
, frame_animate_iconify
,
1238 ob_main_loop_timeout_add(ob_main_loop
,
1239 FRAME_ANIMATE_ICONIFY_STEP_TIME
,
1240 frame_animate_iconify
, self
,
1241 g_direct_equal
, NULL
);
1243 /* do the first step */
1244 frame_animate_iconify(self
);
1246 /* show it during the animation even if it is not "visible" */
1248 XMapWindow(ob_display
, self
->window
);