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 | \
37 ButtonMotionMask | PointerMotionMask | \
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 / 60) /* 60 Hz */
48 #define FRAME_HANDLE_Y(f) (f->innersize.top + f->client->area.height + \
51 /* the offsets for the titlebar elements from the edge of the titlebar.
52 negative means from the right edge. */
62 static void flash_done(gpointer data
);
63 static gboolean
flash_timeout(gpointer data
);
65 static void layout_title(ObFrame
*self
);
66 static void set_theme_statics(ObFrame
*self
);
67 static void free_theme_statics(ObFrame
*self
);
68 static gboolean
frame_animate_iconify(gpointer self
);
70 static Window
createWindow(Window parent
, Visual
*visual
,
71 gulong mask
, XSetWindowAttributes
*attrib
)
73 return XCreateWindow(ob_display
, parent
, 0, 0, 1, 1, 0,
74 (visual
? 32 : RrDepth(ob_rr_inst
)), InputOutput
,
75 (visual
? visual
: RrVisual(ob_rr_inst
)),
80 static Visual
*check_32bit_client(ObClient
*c
)
82 XWindowAttributes wattrib
;
85 ret
= XGetWindowAttributes(ob_display
, c
->window
, &wattrib
);
86 g_assert(ret
!= BadDrawable
);
87 g_assert(ret
!= BadWindow
);
89 if (wattrib
.depth
== 32)
90 return wattrib
.visual
;
94 ObFrame
*frame_new(ObClient
*client
)
96 XSetWindowAttributes attrib
;
101 self
= g_new0(ObFrame
, 1);
102 self
->client
= client
;
104 visual
= check_32bit_client(client
);
106 /* create the non-visible decor windows */
110 /* client has a 32-bit visual */
111 mask
|= CWColormap
| CWBackPixel
| CWBorderPixel
;
112 /* create a colormap with the visual */
113 self
->colormap
= attrib
.colormap
=
114 XCreateColormap(ob_display
,
115 RootWindow(ob_display
, ob_screen
),
117 attrib
.background_pixel
= BlackPixel(ob_display
, 0);
118 attrib
.border_pixel
= BlackPixel(ob_display
, 0);
120 attrib
.event_mask
= FRAME_EVENTMASK
;
121 self
->window
= createWindow(RootWindow(ob_display
, ob_screen
), visual
,
124 attrib
.event_mask
= INNER_EVENTMASK
;
125 self
->inner
= createWindow(self
->window
, visual
, mask
, &attrib
);
127 mask
&= ~CWEventMask
;
128 self
->plate
= createWindow(self
->inner
, visual
, mask
, &attrib
);
130 /* create the visible decor windows */
134 /* client has a 32-bit visual */
135 mask
|= CWColormap
| CWBackPixel
| CWBorderPixel
;
136 attrib
.colormap
= RrColormap(ob_rr_inst
);
138 attrib
.event_mask
= ELEMENT_EVENTMASK
;
139 self
->title
= createWindow(self
->window
, NULL
, mask
, &attrib
);
142 attrib
.cursor
= ob_cursor(OB_CURSOR_NORTHWEST
);
143 self
->tltresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
144 self
->tllresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
145 attrib
.cursor
= ob_cursor(OB_CURSOR_NORTHEAST
);
146 self
->trtresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
147 self
->trrresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
150 self
->label
= createWindow(self
->title
, NULL
, mask
, &attrib
);
151 self
->max
= createWindow(self
->title
, NULL
, mask
, &attrib
);
152 self
->close
= createWindow(self
->title
, NULL
, mask
, &attrib
);
153 self
->desk
= createWindow(self
->title
, NULL
, mask
, &attrib
);
154 self
->shade
= createWindow(self
->title
, NULL
, mask
, &attrib
);
155 self
->icon
= createWindow(self
->title
, NULL
, mask
, &attrib
);
156 self
->iconify
= createWindow(self
->title
, NULL
, mask
, &attrib
);
157 self
->handle
= createWindow(self
->window
, NULL
, mask
, &attrib
);
160 attrib
.cursor
= ob_cursor(OB_CURSOR_SOUTHWEST
);
161 self
->lgrip
= createWindow(self
->handle
, NULL
, mask
, &attrib
);
162 attrib
.cursor
= ob_cursor(OB_CURSOR_SOUTHEAST
);
163 self
->rgrip
= createWindow(self
->handle
, NULL
, mask
, &attrib
);
165 self
->focused
= FALSE
;
167 /* the other stuff is shown based on decor settings */
168 XMapWindow(ob_display
, self
->plate
);
169 XMapWindow(ob_display
, self
->inner
);
170 XMapWindow(ob_display
, self
->lgrip
);
171 XMapWindow(ob_display
, self
->rgrip
);
172 XMapWindow(ob_display
, self
->label
);
174 self
->max_press
= self
->close_press
= self
->desk_press
=
175 self
->iconify_press
= self
->shade_press
= FALSE
;
176 self
->max_hover
= self
->close_hover
= self
->desk_hover
=
177 self
->iconify_hover
= self
->shade_hover
= FALSE
;
179 set_theme_statics(self
);
181 return (ObFrame
*)self
;
184 static void set_theme_statics(ObFrame
*self
)
186 /* set colors/appearance/sizes for stuff that doesn't change */
187 XResizeWindow(ob_display
, self
->max
,
188 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
189 XResizeWindow(ob_display
, self
->iconify
,
190 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
191 XResizeWindow(ob_display
, self
->icon
,
192 ob_rr_theme
->button_size
+ 2, ob_rr_theme
->button_size
+ 2);
193 XResizeWindow(ob_display
, self
->close
,
194 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
195 XResizeWindow(ob_display
, self
->desk
,
196 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
197 XResizeWindow(ob_display
, self
->shade
,
198 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
199 if (ob_rr_theme
->handle_height
> 0) {
200 XResizeWindow(ob_display
, self
->lgrip
,
201 ob_rr_theme
->grip_width
, ob_rr_theme
->handle_height
);
202 XResizeWindow(ob_display
, self
->rgrip
,
203 ob_rr_theme
->grip_width
, ob_rr_theme
->handle_height
);
205 XResizeWindow(ob_display
, self
->tltresize
,
206 ob_rr_theme
->grip_width
, ob_rr_theme
->paddingy
+ 1);
207 XResizeWindow(ob_display
, self
->trtresize
,
208 ob_rr_theme
->grip_width
, ob_rr_theme
->paddingy
+ 1);
209 XResizeWindow(ob_display
, self
->tllresize
,
210 ob_rr_theme
->paddingx
+ 1, ob_rr_theme
->title_height
);
211 XResizeWindow(ob_display
, self
->trrresize
,
212 ob_rr_theme
->paddingx
+ 1, ob_rr_theme
->title_height
);
214 /* set up the dynamic appearances */
215 self
->a_unfocused_title
= RrAppearanceCopy(ob_rr_theme
->a_unfocused_title
);
216 self
->a_focused_title
= RrAppearanceCopy(ob_rr_theme
->a_focused_title
);
217 self
->a_unfocused_label
= RrAppearanceCopy(ob_rr_theme
->a_unfocused_label
);
218 self
->a_focused_label
= RrAppearanceCopy(ob_rr_theme
->a_focused_label
);
219 self
->a_unfocused_handle
=
220 RrAppearanceCopy(ob_rr_theme
->a_unfocused_handle
);
221 self
->a_focused_handle
= RrAppearanceCopy(ob_rr_theme
->a_focused_handle
);
222 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_icon
);
225 static void free_theme_statics(ObFrame
*self
)
227 RrAppearanceFree(self
->a_unfocused_title
);
228 RrAppearanceFree(self
->a_focused_title
);
229 RrAppearanceFree(self
->a_unfocused_label
);
230 RrAppearanceFree(self
->a_focused_label
);
231 RrAppearanceFree(self
->a_unfocused_handle
);
232 RrAppearanceFree(self
->a_focused_handle
);
233 RrAppearanceFree(self
->a_icon
);
236 void frame_free(ObFrame
*self
)
238 free_theme_statics(self
);
240 XDestroyWindow(ob_display
, self
->window
);
242 XFreeColormap(ob_display
, self
->colormap
);
247 void frame_show(ObFrame
*self
)
249 if (!self
->visible
) {
250 self
->visible
= TRUE
;
251 XMapWindow(ob_display
, self
->client
->window
);
252 XMapWindow(ob_display
, self
->window
);
256 void frame_hide(ObFrame
*self
)
259 self
->visible
= FALSE
;
260 if (!frame_iconify_animating(self
))
261 XUnmapWindow(ob_display
, self
->window
);
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
->client
->window
);
265 self
->client
->ignore_unmaps
+= 1;
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 /* position/size and map/unmap all the windows */
374 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
) {
375 XMoveResizeWindow(ob_display
, self
->title
,
376 -self
->bwidth
, -self
->bwidth
,
377 self
->width
, ob_rr_theme
->title_height
);
378 XMapWindow(ob_display
, self
->title
);
380 if (self
->decorations
& OB_FRAME_DECOR_GRIPS
) {
381 XMoveWindow(ob_display
, self
->tltresize
, 0, 0);
382 XMoveWindow(ob_display
, self
->tllresize
, 0, 0);
383 XMoveWindow(ob_display
, self
->trtresize
,
384 self
->width
- ob_rr_theme
->grip_width
, 0);
385 XMoveWindow(ob_display
, self
->trrresize
,
386 self
->width
- ob_rr_theme
->paddingx
- 1, 0);
387 XMapWindow(ob_display
, self
->tltresize
);
388 XMapWindow(ob_display
, self
->tllresize
);
389 XMapWindow(ob_display
, self
->trtresize
);
390 XMapWindow(ob_display
, self
->trrresize
);
392 XUnmapWindow(ob_display
, self
->tltresize
);
393 XUnmapWindow(ob_display
, self
->tllresize
);
394 XUnmapWindow(ob_display
, self
->trtresize
);
395 XUnmapWindow(ob_display
, self
->trrresize
);
398 XUnmapWindow(ob_display
, self
->title
);
401 if ((self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
402 /* layout the title bar elements */
406 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
&&
407 ob_rr_theme
->handle_height
> 0)
409 XMoveResizeWindow(ob_display
, self
->handle
,
410 -self
->bwidth
, FRAME_HANDLE_Y(self
),
411 self
->width
, ob_rr_theme
->handle_height
);
412 XMapWindow(ob_display
, self
->handle
);
414 if (self
->decorations
& OB_FRAME_DECOR_GRIPS
) {
415 XMoveWindow(ob_display
, self
->lgrip
,
416 -self
->rbwidth
, -self
->rbwidth
);
417 XMoveWindow(ob_display
, self
->rgrip
,
418 -self
->rbwidth
+ self
->width
-
419 ob_rr_theme
->grip_width
, -self
->rbwidth
);
420 XMapWindow(ob_display
, self
->lgrip
);
421 XMapWindow(ob_display
, self
->rgrip
);
423 XUnmapWindow(ob_display
, self
->lgrip
);
424 XUnmapWindow(ob_display
, self
->rgrip
);
427 XUnmapWindow(ob_display
, self
->handle
);
429 /* move and resize the inner border window which contains the plate
431 XMoveResizeWindow(ob_display
, self
->inner
,
432 self
->innersize
.left
- self
->cbwidth_x
-
434 self
->innersize
.top
- self
->cbwidth_y
-
436 self
->client
->area
.width
+
438 self
->client
->area
.height
+
439 self
->cbwidth_y
* 2);
442 XMoveWindow(ob_display
, self
->plate
,
443 self
->cbwidth_x
, self
->cbwidth_y
);
445 /* when the client has StaticGravity, it likes to move around. */
446 XMoveWindow(ob_display
, self
->client
->window
, 0, 0);
449 STRUT_SET(self
->size
,
450 self
->innersize
.left
+ self
->bwidth
,
451 self
->innersize
.top
+ self
->bwidth
,
452 self
->innersize
.right
+ self
->bwidth
,
453 self
->innersize
.bottom
+ self
->bwidth
);
456 /* shading can change without being moved or resized */
457 RECT_SET_SIZE(self
->area
,
458 self
->client
->area
.width
+
459 self
->size
.left
+ self
->size
.right
,
460 (self
->client
->shaded
?
461 ob_rr_theme
->title_height
+ self
->rbwidth
* 2:
462 self
->client
->area
.height
+
463 self
->size
.top
+ self
->size
.bottom
));
465 if (moved
|| resized
) {
466 /* find the new coordinates, done after setting the frame.size, for
467 frame_client_gravity. */
468 self
->area
.x
= self
->client
->area
.x
;
469 self
->area
.y
= self
->client
->area
.y
;
470 frame_client_gravity(self
, &self
->area
.x
, &self
->area
.y
,
471 self
->client
->area
.width
,
472 self
->client
->area
.height
);
476 if (!frame_iconify_animating(self
))
477 /* move and resize the top level frame.
478 shading can change without being moved or resized.
480 but don't do this during an iconify animation. it will be
481 reflected afterwards.
483 XMoveResizeWindow(ob_display
, self
->window
,
484 self
->area
.x
, self
->area
.y
,
485 self
->area
.width
- self
->bwidth
* 2,
486 self
->area
.height
- self
->bwidth
* 2);
489 framerender_frame(self
);
490 frame_adjust_shape(self
);
493 if (!STRUT_EQUAL(self
->size
, oldsize
)) {
495 vals
[0] = self
->size
.left
;
496 vals
[1] = self
->size
.right
;
497 vals
[2] = self
->size
.top
;
498 vals
[3] = self
->size
.bottom
;
499 PROP_SETA32(self
->client
->window
, net_frame_extents
,
501 PROP_SETA32(self
->client
->window
, kde_net_wm_frame_strut
,
505 /* if this occurs while we are focus cycling, the indicator needs to
507 if (focus_cycle_target
== self
->client
)
508 focus_cycle_draw_indicator();
510 if (resized
&& (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
511 XResizeWindow(ob_display
, self
->label
, self
->label_width
,
512 ob_rr_theme
->label_height
);
515 void frame_adjust_client_area(ObFrame
*self
)
517 /* resize the plate */
518 XResizeWindow(ob_display
, self
->plate
,
519 self
->client
->area
.width
, self
->client
->area
.height
);
522 void frame_adjust_state(ObFrame
*self
)
524 framerender_frame(self
);
527 void frame_adjust_focus(ObFrame
*self
, gboolean hilite
)
529 self
->focused
= hilite
;
530 framerender_frame(self
);
534 void frame_adjust_title(ObFrame
*self
)
536 framerender_frame(self
);
539 void frame_adjust_icon(ObFrame
*self
)
541 framerender_frame(self
);
544 void frame_grab_client(ObFrame
*self
)
546 /* reparent the client to the frame */
547 XReparentWindow(ob_display
, self
->client
->window
, self
->plate
, 0, 0);
549 When reparenting the client window, it is usually not mapped yet, since
550 this occurs from a MapRequest. However, in the case where Openbox is
551 starting up, the window is already mapped, so we'll see unmap events for
552 it. There are 2 unmap events generated that we see, one with the 'event'
553 member set the root window, and one set to the client, but both get
554 handled and need to be ignored.
556 if (ob_state() == OB_STATE_STARTING
)
557 self
->client
->ignore_unmaps
+= 2;
559 /* select the event mask on the client's parent (to receive config/map
560 req's) the ButtonPress is to catch clicks on the client border */
561 XSelectInput(ob_display
, self
->plate
, PLATE_EVENTMASK
);
563 /* map the client so it maps when the frame does */
564 XMapWindow(ob_display
, self
->client
->window
);
566 /* set all the windows for the frame in the window_map */
567 g_hash_table_insert(window_map
, &self
->window
, self
->client
);
568 g_hash_table_insert(window_map
, &self
->plate
, self
->client
);
569 g_hash_table_insert(window_map
, &self
->inner
, self
->client
);
570 g_hash_table_insert(window_map
, &self
->title
, self
->client
);
571 g_hash_table_insert(window_map
, &self
->label
, self
->client
);
572 g_hash_table_insert(window_map
, &self
->max
, self
->client
);
573 g_hash_table_insert(window_map
, &self
->close
, self
->client
);
574 g_hash_table_insert(window_map
, &self
->desk
, self
->client
);
575 g_hash_table_insert(window_map
, &self
->shade
, self
->client
);
576 g_hash_table_insert(window_map
, &self
->icon
, self
->client
);
577 g_hash_table_insert(window_map
, &self
->iconify
, self
->client
);
578 g_hash_table_insert(window_map
, &self
->handle
, self
->client
);
579 g_hash_table_insert(window_map
, &self
->lgrip
, self
->client
);
580 g_hash_table_insert(window_map
, &self
->rgrip
, self
->client
);
581 g_hash_table_insert(window_map
, &self
->tltresize
, self
->client
);
582 g_hash_table_insert(window_map
, &self
->tllresize
, self
->client
);
583 g_hash_table_insert(window_map
, &self
->trtresize
, self
->client
);
584 g_hash_table_insert(window_map
, &self
->trrresize
, self
->client
);
587 void frame_release_client(ObFrame
*self
)
590 gboolean reparent
= TRUE
;
592 /* if there was any animation going on, kill it */
593 ob_main_loop_timeout_remove_data(ob_main_loop
, frame_animate_iconify
,
596 /* check if the app has already reparented its window away */
597 while (XCheckTypedWindowEvent(ob_display
, self
->client
->window
,
598 ReparentNotify
, &ev
))
600 /* This check makes sure we don't catch our own reparent action to
601 our frame window. This doesn't count as the app reparenting itself
604 Reparent events that are generated by us are just discarded here.
605 They are of no consequence to us anyhow.
607 if (ev
.xreparent
.parent
!= self
->plate
) {
609 XPutBackEvent(ob_display
, &ev
);
615 /* according to the ICCCM - if the client doesn't reparent itself,
616 then we will reparent the window to root for them */
617 XReparentWindow(ob_display
, self
->client
->window
,
618 RootWindow(ob_display
, ob_screen
),
619 self
->client
->area
.x
,
620 self
->client
->area
.y
);
623 /* remove all the windows for the frame from the window_map */
624 g_hash_table_remove(window_map
, &self
->window
);
625 g_hash_table_remove(window_map
, &self
->plate
);
626 g_hash_table_remove(window_map
, &self
->inner
);
627 g_hash_table_remove(window_map
, &self
->title
);
628 g_hash_table_remove(window_map
, &self
->label
);
629 g_hash_table_remove(window_map
, &self
->max
);
630 g_hash_table_remove(window_map
, &self
->close
);
631 g_hash_table_remove(window_map
, &self
->desk
);
632 g_hash_table_remove(window_map
, &self
->shade
);
633 g_hash_table_remove(window_map
, &self
->icon
);
634 g_hash_table_remove(window_map
, &self
->iconify
);
635 g_hash_table_remove(window_map
, &self
->handle
);
636 g_hash_table_remove(window_map
, &self
->lgrip
);
637 g_hash_table_remove(window_map
, &self
->rgrip
);
638 g_hash_table_remove(window_map
, &self
->tltresize
);
639 g_hash_table_remove(window_map
, &self
->tllresize
);
640 g_hash_table_remove(window_map
, &self
->trtresize
);
641 g_hash_table_remove(window_map
, &self
->trrresize
);
643 ob_main_loop_timeout_remove_data(ob_main_loop
, flash_timeout
, self
, TRUE
);
646 /* is there anything present between us and the label? */
647 static gboolean
is_button_present(ObFrame
*self
, const gchar
*lc
, gint dir
) {
648 for (; *lc
!= '\0' && lc
>= config_title_layout
; lc
+= dir
) {
649 if (*lc
== ' ') continue; /* it was invalid */
650 if (*lc
== 'N' && self
->decorations
& OB_FRAME_DECOR_ICON
)
652 if (*lc
== 'D' && self
->decorations
& OB_FRAME_DECOR_ALLDESKTOPS
)
654 if (*lc
== 'S' && self
->decorations
& OB_FRAME_DECOR_SHADE
)
656 if (*lc
== 'I' && self
->decorations
& OB_FRAME_DECOR_ICONIFY
)
658 if (*lc
== 'M' && self
->decorations
& OB_FRAME_DECOR_MAXIMIZE
)
660 if (*lc
== 'C' && self
->decorations
& OB_FRAME_DECOR_CLOSE
)
662 if (*lc
== 'L') return FALSE
;
667 static void layout_title(ObFrame
*self
)
672 const gint bwidth
= ob_rr_theme
->button_size
+ ob_rr_theme
->paddingx
+ 1;
673 /* position of the left most button */
674 const gint left
= ob_rr_theme
->paddingx
+ 1;
675 /* position of the right most button */
676 const gint right
= self
->width
- bwidth
;
678 /* turn them all off */
679 self
->icon_on
= self
->desk_on
= self
->shade_on
= self
->iconify_on
=
680 self
->max_on
= self
->close_on
= self
->label_on
= FALSE
;
681 self
->label_width
= self
->width
- (ob_rr_theme
->paddingx
+ 1) * 2;
682 self
->leftmost
= self
->rightmost
= OB_FRAME_CONTEXT_NONE
;
684 /* figure out what's being show, find each element's position, and the
687 do the ones before the label, then after the label,
688 i will be +1 the first time through when working to the left,
689 and -1 the second time through when working to the right */
690 for (i
= 1; i
>= -1; i
-=2) {
692 ObFrameContext
*firstcon
;
696 lc
= config_title_layout
;
697 firstcon
= &self
->leftmost
;
700 lc
= config_title_layout
+ strlen(config_title_layout
)-1;
701 firstcon
= &self
->rightmost
;
704 /* stop at the end of the string (or the label, which calls break) */
705 for (; *lc
!= '\0' && lc
>= config_title_layout
; lc
+=i
) {
708 self
->label_on
= TRUE
;
711 break; /* break the for loop, do other side of label */
712 } else if (*lc
== 'N') {
713 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_ICON
;
714 if ((self
->icon_on
= is_button_present(self
, lc
, i
))) {
715 /* icon is bigger than buttons */
716 self
->label_width
-= bwidth
+ 2;
718 x
+= i
* (bwidth
+ 2);
720 } else if (*lc
== 'D') {
721 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_ALLDESKTOPS
;
722 if ((self
->desk_on
= is_button_present(self
, lc
, i
))) {
723 self
->label_width
-= bwidth
;
727 } else if (*lc
== 'S') {
728 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_SHADE
;
729 if ((self
->shade_on
= is_button_present(self
, lc
, i
))) {
730 self
->label_width
-= bwidth
;
734 } else if (*lc
== 'I') {
735 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_ICONIFY
;
736 if ((self
->iconify_on
= is_button_present(self
, lc
, i
))) {
737 self
->label_width
-= bwidth
;
741 } else if (*lc
== 'M') {
742 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_MAXIMIZE
;
743 if ((self
->max_on
= is_button_present(self
, lc
, i
))) {
744 self
->label_width
-= bwidth
;
748 } else if (*lc
== 'C') {
749 if (firstcon
) *firstcon
= OB_FRAME_CONTEXT_CLOSE
;
750 if ((self
->close_on
= is_button_present(self
, lc
, i
))) {
751 self
->label_width
-= bwidth
;
756 continue; /* don't set firstcon */
761 /* position and map the elements */
763 XMapWindow(ob_display
, self
->icon
);
764 XMoveWindow(ob_display
, self
->icon
, self
->icon_x
,
765 ob_rr_theme
->paddingy
);
767 XUnmapWindow(ob_display
, self
->icon
);
770 XMapWindow(ob_display
, self
->desk
);
771 XMoveWindow(ob_display
, self
->desk
, self
->desk_x
,
772 ob_rr_theme
->paddingy
+ 1);
774 XUnmapWindow(ob_display
, self
->desk
);
776 if (self
->shade_on
) {
777 XMapWindow(ob_display
, self
->shade
);
778 XMoveWindow(ob_display
, self
->shade
, self
->shade_x
,
779 ob_rr_theme
->paddingy
+ 1);
781 XUnmapWindow(ob_display
, self
->shade
);
783 if (self
->iconify_on
) {
784 XMapWindow(ob_display
, self
->iconify
);
785 XMoveWindow(ob_display
, self
->iconify
, self
->iconify_x
,
786 ob_rr_theme
->paddingy
+ 1);
788 XUnmapWindow(ob_display
, self
->iconify
);
791 XMapWindow(ob_display
, self
->max
);
792 XMoveWindow(ob_display
, self
->max
, self
->max_x
,
793 ob_rr_theme
->paddingy
+ 1);
795 XUnmapWindow(ob_display
, self
->max
);
797 if (self
->close_on
) {
798 XMapWindow(ob_display
, self
->close
);
799 XMoveWindow(ob_display
, self
->close
, self
->close_x
,
800 ob_rr_theme
->paddingy
+ 1);
802 XUnmapWindow(ob_display
, self
->close
);
804 if (self
->label_on
) {
805 self
->label_width
= MAX(1, self
->label_width
); /* no lower than 1 */
806 XMapWindow(ob_display
, self
->label
);
807 XMoveWindow(ob_display
, self
->label
, self
->label_x
,
808 ob_rr_theme
->paddingy
);
810 XUnmapWindow(ob_display
, self
->label
);
813 ObFrameContext
frame_context_from_string(const gchar
*name
)
815 if (!g_ascii_strcasecmp("Desktop", name
))
816 return OB_FRAME_CONTEXT_DESKTOP
;
817 else if (!g_ascii_strcasecmp("Client", name
))
818 return OB_FRAME_CONTEXT_CLIENT
;
819 else if (!g_ascii_strcasecmp("Titlebar", name
))
820 return OB_FRAME_CONTEXT_TITLEBAR
;
821 else if (!g_ascii_strcasecmp("Handle", name
))
822 return OB_FRAME_CONTEXT_HANDLE
;
823 else if (!g_ascii_strcasecmp("Frame", name
))
824 return OB_FRAME_CONTEXT_FRAME
;
825 else if (!g_ascii_strcasecmp("TLCorner", name
))
826 return OB_FRAME_CONTEXT_TLCORNER
;
827 else if (!g_ascii_strcasecmp("TRCorner", name
))
828 return OB_FRAME_CONTEXT_TRCORNER
;
829 else if (!g_ascii_strcasecmp("BLCorner", name
))
830 return OB_FRAME_CONTEXT_BLCORNER
;
831 else if (!g_ascii_strcasecmp("BRCorner", name
))
832 return OB_FRAME_CONTEXT_BRCORNER
;
833 else if (!g_ascii_strcasecmp("Maximize", name
))
834 return OB_FRAME_CONTEXT_MAXIMIZE
;
835 else if (!g_ascii_strcasecmp("AllDesktops", name
))
836 return OB_FRAME_CONTEXT_ALLDESKTOPS
;
837 else if (!g_ascii_strcasecmp("Shade", name
))
838 return OB_FRAME_CONTEXT_SHADE
;
839 else if (!g_ascii_strcasecmp("Iconify", name
))
840 return OB_FRAME_CONTEXT_ICONIFY
;
841 else if (!g_ascii_strcasecmp("Icon", name
))
842 return OB_FRAME_CONTEXT_ICON
;
843 else if (!g_ascii_strcasecmp("Close", name
))
844 return OB_FRAME_CONTEXT_CLOSE
;
845 else if (!g_ascii_strcasecmp("MoveResize", name
))
846 return OB_FRAME_CONTEXT_MOVE_RESIZE
;
847 return OB_FRAME_CONTEXT_NONE
;
850 ObFrameContext
frame_context(ObClient
*client
, Window win
, gint x
, gint y
)
854 if (moveresize_in_progress
)
855 return OB_FRAME_CONTEXT_MOVE_RESIZE
;
857 if (win
== RootWindow(ob_display
, ob_screen
))
858 return OB_FRAME_CONTEXT_DESKTOP
;
859 if (client
== NULL
) return OB_FRAME_CONTEXT_NONE
;
860 if (win
== client
->window
) {
861 /* conceptually, this is the desktop, as far as users are
863 if (client
->type
== OB_CLIENT_TYPE_DESKTOP
)
864 return OB_FRAME_CONTEXT_DESKTOP
;
865 return OB_FRAME_CONTEXT_CLIENT
;
868 self
= client
->frame
;
869 if (win
== self
->inner
|| win
== self
->plate
) {
870 /* conceptually, this is the desktop, as far as users are
872 if (client
->type
== OB_CLIENT_TYPE_DESKTOP
)
873 return OB_FRAME_CONTEXT_DESKTOP
;
874 return OB_FRAME_CONTEXT_CLIENT
;
877 if (win
== self
->title
) {
878 /* when the user clicks in the corners of the titlebar and the client
879 is fully maximized, then treat it like they clicked in the
880 button that is there */
881 if (self
->client
->max_horz
&& self
->client
->max_vert
&&
882 y
< ob_rr_theme
->paddingy
+ 1 + ob_rr_theme
->button_size
)
884 if (x
< ((ob_rr_theme
->paddingx
+ 1) * 2 +
885 ob_rr_theme
->button_size
)) {
886 if (self
->leftmost
!= OB_FRAME_CONTEXT_NONE
)
887 return self
->leftmost
;
889 else if (x
> (self
->width
-
890 (ob_rr_theme
->paddingx
+ 1 +
891 ob_rr_theme
->button_size
)))
893 if (self
->rightmost
!= OB_FRAME_CONTEXT_NONE
)
894 return self
->rightmost
;
897 return OB_FRAME_CONTEXT_TITLEBAR
;
900 if (win
== self
->window
) return OB_FRAME_CONTEXT_FRAME
;
901 if (win
== self
->label
) return OB_FRAME_CONTEXT_TITLEBAR
;
902 if (win
== self
->handle
) return OB_FRAME_CONTEXT_HANDLE
;
903 if (win
== self
->lgrip
) return OB_FRAME_CONTEXT_BLCORNER
;
904 if (win
== self
->rgrip
) return OB_FRAME_CONTEXT_BRCORNER
;
905 if (win
== self
->tltresize
) return OB_FRAME_CONTEXT_TLCORNER
;
906 if (win
== self
->tllresize
) return OB_FRAME_CONTEXT_TLCORNER
;
907 if (win
== self
->trtresize
) return OB_FRAME_CONTEXT_TRCORNER
;
908 if (win
== self
->trrresize
) return OB_FRAME_CONTEXT_TRCORNER
;
909 if (win
== self
->max
) return OB_FRAME_CONTEXT_MAXIMIZE
;
910 if (win
== self
->iconify
) return OB_FRAME_CONTEXT_ICONIFY
;
911 if (win
== self
->close
) return OB_FRAME_CONTEXT_CLOSE
;
912 if (win
== self
->icon
) return OB_FRAME_CONTEXT_ICON
;
913 if (win
== self
->desk
) return OB_FRAME_CONTEXT_ALLDESKTOPS
;
914 if (win
== self
->shade
) return OB_FRAME_CONTEXT_SHADE
;
916 return OB_FRAME_CONTEXT_NONE
;
919 void frame_client_gravity(ObFrame
*self
, gint
*x
, gint
*y
, gint w
, gint h
)
922 switch (self
->client
->gravity
) {
924 case NorthWestGravity
:
925 case SouthWestGravity
:
932 *x
-= (self
->size
.left
+ w
) / 2;
935 case NorthEastGravity
:
936 case SouthEastGravity
:
938 *x
-= (self
->size
.left
+ self
->size
.right
+ w
) - 1;
943 *x
-= self
->size
.left
;
948 switch (self
->client
->gravity
) {
950 case NorthWestGravity
:
951 case NorthEastGravity
:
958 *y
-= (self
->size
.top
+ h
) / 2;
961 case SouthWestGravity
:
962 case SouthEastGravity
:
964 *y
-= (self
->size
.top
+ self
->size
.bottom
+ h
) - 1;
969 *y
-= self
->size
.top
;
974 void frame_frame_gravity(ObFrame
*self
, gint
*x
, gint
*y
, gint w
, gint h
)
977 switch (self
->client
->gravity
) {
979 case NorthWestGravity
:
981 case SouthWestGravity
:
986 *x
+= (self
->size
.left
+ w
) / 2;
988 case NorthEastGravity
:
990 case SouthEastGravity
:
991 *x
+= (self
->size
.left
+ self
->size
.right
+ w
) - 1;
995 *x
+= self
->size
.left
;
1000 switch (self
->client
->gravity
) {
1002 case NorthWestGravity
:
1004 case NorthEastGravity
:
1009 *y
+= (self
->size
.top
+ h
) / 2;
1011 case SouthWestGravity
:
1013 case SouthEastGravity
:
1014 *y
+= (self
->size
.top
+ self
->size
.bottom
+ h
) - 1;
1018 *y
+= self
->size
.top
;
1023 static void flash_done(gpointer data
)
1025 ObFrame
*self
= data
;
1027 if (self
->focused
!= self
->flash_on
)
1028 frame_adjust_focus(self
, self
->focused
);
1031 static gboolean
flash_timeout(gpointer data
)
1033 ObFrame
*self
= data
;
1036 g_get_current_time(&now
);
1037 if (now
.tv_sec
> self
->flash_end
.tv_sec
||
1038 (now
.tv_sec
== self
->flash_end
.tv_sec
&&
1039 now
.tv_usec
>= self
->flash_end
.tv_usec
))
1040 self
->flashing
= FALSE
;
1042 if (!self
->flashing
)
1043 return FALSE
; /* we are done */
1045 self
->flash_on
= !self
->flash_on
;
1046 if (!self
->focused
) {
1047 frame_adjust_focus(self
, self
->flash_on
);
1048 self
->focused
= FALSE
;
1051 return TRUE
; /* go again */
1054 void frame_flash_start(ObFrame
*self
)
1056 self
->flash_on
= self
->focused
;
1058 if (!self
->flashing
)
1059 ob_main_loop_timeout_add(ob_main_loop
,
1060 G_USEC_PER_SEC
* 0.6,
1065 g_get_current_time(&self
->flash_end
);
1066 g_time_val_add(&self
->flash_end
, G_USEC_PER_SEC
* 5);
1068 self
->flashing
= TRUE
;
1071 void frame_flash_stop(ObFrame
*self
)
1073 self
->flashing
= FALSE
;
1076 static gulong
frame_animate_iconify_time_left(ObFrame
*self
,
1077 const GTimeVal
*now
)
1080 sec
= self
->iconify_animation_end
.tv_sec
- now
->tv_sec
;
1081 usec
= self
->iconify_animation_end
.tv_usec
- now
->tv_usec
;
1083 usec
+= G_USEC_PER_SEC
;
1086 /* no negative values */
1087 return MAX(sec
* G_USEC_PER_SEC
+ usec
, 0);
1090 static gboolean
frame_animate_iconify(gpointer p
)
1094 gint iconx
, icony
, iconw
;
1097 gboolean iconifying
;
1099 if (self
->client
->icon_geometry
.width
== 0) {
1100 /* there is no icon geometry set so just go straight down */
1101 Rect
*a
= screen_physical_area();
1102 iconx
= self
->area
.x
+ self
->area
.width
/ 2 + 32;
1103 icony
= a
->y
+ a
->width
;
1106 iconx
= self
->client
->icon_geometry
.x
;
1107 icony
= self
->client
->icon_geometry
.y
;
1108 iconw
= self
->client
->icon_geometry
.width
;
1111 iconifying
= self
->iconify_animation_going
> 0;
1113 /* how far do we have left to go ? */
1114 g_get_current_time(&now
);
1115 time
= frame_animate_iconify_time_left(self
, &now
);
1117 if (time
== 0 || iconifying
) {
1118 /* start where the frame is supposed to be */
1121 w
= self
->area
.width
- self
->bwidth
* 2;
1122 h
= self
->area
.height
- self
->bwidth
* 2;
1124 /* start at the icon */
1128 h
= self
->innersize
.top
; /* just the titlebar */
1135 dx
= self
->area
.x
- iconx
;
1136 dy
= self
->area
.y
- icony
;
1137 dw
= self
->area
.width
- self
->bwidth
* 2 - iconw
;
1138 /* if restoring, we move in the opposite direction */
1139 if (!iconifying
) { dx
= -dx
; dy
= -dy
; dw
= -dw
; }
1141 elapsed
= FRAME_ANIMATE_ICONIFY_TIME
- time
;
1142 x
= x
- (dx
* elapsed
) / FRAME_ANIMATE_ICONIFY_TIME
;
1143 y
= y
- (dy
* elapsed
) / FRAME_ANIMATE_ICONIFY_TIME
;
1144 w
= w
- (dw
* elapsed
) / FRAME_ANIMATE_ICONIFY_TIME
;
1145 h
= self
->innersize
.top
; /* just the titlebar */
1149 frame_end_iconify_animation(self
);
1151 XMoveResizeWindow(ob_display
, self
->window
, x
, y
, w
, h
);
1155 return time
> 0; /* repeat until we're out of time */
1158 void frame_end_iconify_animation(ObFrame
*self
)
1160 /* see if there is an animation going */
1161 if (self
->iconify_animation_going
== 0) return;
1164 XUnmapWindow(ob_display
, self
->window
);
1166 /* we're not animating any more ! */
1167 self
->iconify_animation_going
= 0;
1169 XMoveResizeWindow(ob_display
, self
->window
,
1170 self
->area
.x
, self
->area
.y
,
1171 self
->area
.width
- self
->bwidth
* 2,
1172 self
->area
.height
- self
->bwidth
* 2);
1176 void frame_begin_iconify_animation(ObFrame
*self
, gboolean iconifying
)
1179 gboolean new_anim
= FALSE
;
1180 gboolean set_end
= TRUE
;
1183 /* if there is no titlebar, just don't animate for now
1184 XXX it would be nice tho.. */
1185 if (!(self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
1188 /* get the current time */
1189 g_get_current_time(&now
);
1191 /* get how long until the end */
1192 time
= FRAME_ANIMATE_ICONIFY_TIME
;
1193 if (self
->iconify_animation_going
) {
1194 if (!!iconifying
!= (self
->iconify_animation_going
> 0)) {
1195 /* animation was already going on in the opposite direction */
1196 time
= time
- frame_animate_iconify_time_left(self
, &now
);
1198 /* animation was already going in the same direction */
1202 self
->iconify_animation_going
= iconifying
? 1 : -1;
1204 /* set the ending time */
1206 self
->iconify_animation_end
.tv_sec
= now
.tv_sec
;
1207 self
->iconify_animation_end
.tv_usec
= now
.tv_usec
;
1208 g_time_val_add(&self
->iconify_animation_end
, time
);
1212 ob_main_loop_timeout_remove_data(ob_main_loop
, frame_animate_iconify
,
1214 ob_main_loop_timeout_add(ob_main_loop
,
1215 FRAME_ANIMATE_ICONIFY_STEP_TIME
,
1216 frame_animate_iconify
, self
,
1217 g_direct_equal
, NULL
);
1219 /* do the first step */
1220 frame_animate_iconify(self
);
1222 /* show it during the animation even if it is not "visible" */
1224 XMapWindow(ob_display
, self
->window
);