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"
30 #include "render/theme.h"
32 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask | \
34 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
35 ButtonPressMask | ButtonReleaseMask | \
37 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
38 ButtonMotionMask | ExposureMask | \
39 EnterWindowMask | LeaveWindowMask)
41 #define FRAME_HANDLE_Y(f) (f->innersize.top + f->client->area.height + \
44 static void layout_title(ObFrame
*self
);
45 static void flash_done(gpointer data
);
46 static gboolean
flash_timeout(gpointer data
);
48 static void set_theme_statics(ObFrame
*self
);
49 static void free_theme_statics(ObFrame
*self
);
51 static Window
createWindow(Window parent
, Visual
*visual
,
52 gulong mask
, XSetWindowAttributes
*attrib
)
54 return XCreateWindow(ob_display
, parent
, 0, 0, 1, 1, 0,
55 (visual
? 32 : RrDepth(ob_rr_inst
)), InputOutput
,
56 (visual
? visual
: RrVisual(ob_rr_inst
)),
61 static Visual
*check_32bit_client(ObClient
*c
)
63 XWindowAttributes wattrib
;
66 ret
= XGetWindowAttributes(ob_display
, c
->window
, &wattrib
);
67 g_assert(ret
!= BadDrawable
);
68 g_assert(ret
!= BadWindow
);
70 if (wattrib
.depth
== 32)
71 return wattrib
.visual
;
75 ObFrame
*frame_new(ObClient
*client
)
77 XSetWindowAttributes attrib
;
82 self
= g_new0(ObFrame
, 1);
84 self
->obscured
= TRUE
;
86 visual
= check_32bit_client(client
);
88 /* create the non-visible decor windows */
92 /* client has a 32-bit visual */
93 mask
|= CWColormap
| CWBackPixel
| CWBorderPixel
;
94 /* create a colormap with the visual */
95 self
->colormap
= attrib
.colormap
=
96 XCreateColormap(ob_display
,
97 RootWindow(ob_display
, ob_screen
),
99 attrib
.background_pixel
= BlackPixel(ob_display
, 0);
100 attrib
.border_pixel
= BlackPixel(ob_display
, 0);
102 attrib
.event_mask
= FRAME_EVENTMASK
;
103 self
->window
= createWindow(RootWindow(ob_display
, ob_screen
), visual
,
105 mask
&= ~CWEventMask
;
106 self
->plate
= createWindow(self
->window
, visual
, mask
, &attrib
);
108 /* create the visible decor windows */
112 /* client has a 32-bit visual */
113 mask
|= CWColormap
| CWBackPixel
| CWBorderPixel
;
114 attrib
.colormap
= RrColormap(ob_rr_inst
);
116 attrib
.event_mask
= ELEMENT_EVENTMASK
;
117 self
->title
= createWindow(self
->window
, NULL
, mask
, &attrib
);
120 attrib
.cursor
= ob_cursor(OB_CURSOR_NORTHWEST
);
121 self
->tltresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
122 self
->tllresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
123 attrib
.cursor
= ob_cursor(OB_CURSOR_NORTHEAST
);
124 self
->trtresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
125 self
->trrresize
= createWindow(self
->title
, NULL
, mask
, &attrib
);
128 self
->label
= createWindow(self
->title
, NULL
, mask
, &attrib
);
129 self
->max
= createWindow(self
->title
, NULL
, mask
, &attrib
);
130 self
->close
= createWindow(self
->title
, NULL
, mask
, &attrib
);
131 self
->desk
= createWindow(self
->title
, NULL
, mask
, &attrib
);
132 self
->shade
= createWindow(self
->title
, NULL
, mask
, &attrib
);
133 self
->icon
= createWindow(self
->title
, NULL
, mask
, &attrib
);
134 self
->iconify
= createWindow(self
->title
, NULL
, mask
, &attrib
);
135 self
->handle
= createWindow(self
->window
, NULL
, mask
, &attrib
);
138 attrib
.cursor
= ob_cursor(OB_CURSOR_SOUTHWEST
);
139 self
->lgrip
= createWindow(self
->handle
, NULL
, mask
, &attrib
);
140 attrib
.cursor
= ob_cursor(OB_CURSOR_SOUTHEAST
);
141 self
->rgrip
= createWindow(self
->handle
, NULL
, mask
, &attrib
);
143 self
->focused
= FALSE
;
145 /* the other stuff is shown based on decor settings */
146 XMapWindow(ob_display
, self
->plate
);
147 XMapWindow(ob_display
, self
->lgrip
);
148 XMapWindow(ob_display
, self
->rgrip
);
149 XMapWindow(ob_display
, self
->label
);
151 self
->max_press
= self
->close_press
= self
->desk_press
=
152 self
->iconify_press
= self
->shade_press
= FALSE
;
153 self
->max_hover
= self
->close_hover
= self
->desk_hover
=
154 self
->iconify_hover
= self
->shade_hover
= FALSE
;
156 set_theme_statics(self
);
158 return (ObFrame
*)self
;
161 static void set_theme_statics(ObFrame
*self
)
163 /* set colors/appearance/sizes for stuff that doesn't change */
164 XSetWindowBorder(ob_display
, self
->window
,
165 RrColorPixel(ob_rr_theme
->frame_b_color
));
166 XSetWindowBorder(ob_display
, self
->title
,
167 RrColorPixel(ob_rr_theme
->frame_b_color
));
168 XSetWindowBorder(ob_display
, self
->handle
,
169 RrColorPixel(ob_rr_theme
->frame_b_color
));
170 XSetWindowBorder(ob_display
, self
->rgrip
,
171 RrColorPixel(ob_rr_theme
->frame_b_color
));
172 XSetWindowBorder(ob_display
, self
->lgrip
,
173 RrColorPixel(ob_rr_theme
->frame_b_color
));
175 XResizeWindow(ob_display
, self
->max
,
176 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
177 XResizeWindow(ob_display
, self
->iconify
,
178 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
179 XResizeWindow(ob_display
, self
->icon
,
180 ob_rr_theme
->button_size
+ 2, ob_rr_theme
->button_size
+ 2);
181 XResizeWindow(ob_display
, self
->close
,
182 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
183 XResizeWindow(ob_display
, self
->desk
,
184 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
185 XResizeWindow(ob_display
, self
->shade
,
186 ob_rr_theme
->button_size
, ob_rr_theme
->button_size
);
187 if (ob_rr_theme
->handle_height
> 0) {
188 XResizeWindow(ob_display
, self
->lgrip
,
189 ob_rr_theme
->grip_width
, ob_rr_theme
->handle_height
);
190 XResizeWindow(ob_display
, self
->rgrip
,
191 ob_rr_theme
->grip_width
, ob_rr_theme
->handle_height
);
193 XResizeWindow(ob_display
, self
->tltresize
,
194 ob_rr_theme
->grip_width
, ob_rr_theme
->paddingy
+ 1);
195 XResizeWindow(ob_display
, self
->trtresize
,
196 ob_rr_theme
->grip_width
, ob_rr_theme
->paddingy
+ 1);
197 XResizeWindow(ob_display
, self
->tllresize
,
198 ob_rr_theme
->paddingx
+ 1, ob_rr_theme
->title_height
);
199 XResizeWindow(ob_display
, self
->trrresize
,
200 ob_rr_theme
->paddingx
+ 1, ob_rr_theme
->title_height
);
202 /* set up the dynamic appearances */
203 self
->a_unfocused_title
= RrAppearanceCopy(ob_rr_theme
->a_unfocused_title
);
204 self
->a_focused_title
= RrAppearanceCopy(ob_rr_theme
->a_focused_title
);
205 self
->a_unfocused_label
= RrAppearanceCopy(ob_rr_theme
->a_unfocused_label
);
206 self
->a_focused_label
= RrAppearanceCopy(ob_rr_theme
->a_focused_label
);
207 self
->a_unfocused_handle
=
208 RrAppearanceCopy(ob_rr_theme
->a_unfocused_handle
);
209 self
->a_focused_handle
= RrAppearanceCopy(ob_rr_theme
->a_focused_handle
);
210 self
->a_icon
= RrAppearanceCopy(ob_rr_theme
->a_icon
);
213 static void free_theme_statics(ObFrame
*self
)
215 RrAppearanceFree(self
->a_unfocused_title
);
216 RrAppearanceFree(self
->a_focused_title
);
217 RrAppearanceFree(self
->a_unfocused_label
);
218 RrAppearanceFree(self
->a_focused_label
);
219 RrAppearanceFree(self
->a_unfocused_handle
);
220 RrAppearanceFree(self
->a_focused_handle
);
221 RrAppearanceFree(self
->a_icon
);
224 static void frame_free(ObFrame
*self
)
226 free_theme_statics(self
);
228 XDestroyWindow(ob_display
, self
->window
);
230 XFreeColormap(ob_display
, self
->colormap
);
235 void frame_show(ObFrame
*self
)
237 ob_debug("frame_show for window 0x%x : %d\n", self
->client
->window
,
239 if (!self
->visible
) {
240 self
->visible
= TRUE
;
241 XMapWindow(ob_display
, self
->client
->window
);
242 XMapWindow(ob_display
, self
->window
);
246 void frame_hide(ObFrame
*self
)
248 ob_debug("frame_hide for window 0x%x : %d\n", self
->client
->window
,
251 self
->visible
= FALSE
;
252 self
->client
->ignore_unmaps
+= 1;
253 /* we unmap the client itself so that we can get MapRequest
254 events, and because the ICCCM tells us to! */
255 XUnmapWindow(ob_display
, self
->window
);
256 XUnmapWindow(ob_display
, self
->client
->window
);
260 void frame_adjust_theme(ObFrame
*self
)
262 free_theme_statics(self
);
263 set_theme_statics(self
);
266 void frame_adjust_shape(ObFrame
*self
)
272 if (!self
->client
->shaped
) {
273 /* clear the shape on the frame window */
274 XShapeCombineMask(ob_display
, self
->window
, ShapeBounding
,
275 self
->innersize
.left
,
279 /* make the frame's shape match the clients */
280 XShapeCombineShape(ob_display
, self
->window
, ShapeBounding
,
281 self
->innersize
.left
,
283 self
->client
->window
,
284 ShapeBounding
, ShapeSet
);
287 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
) {
288 xrect
[0].x
= -ob_rr_theme
->fbwidth
;
289 xrect
[0].y
= -ob_rr_theme
->fbwidth
;
290 xrect
[0].width
= self
->width
+ self
->rbwidth
* 2;
291 xrect
[0].height
= ob_rr_theme
->title_height
+
296 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
) {
297 xrect
[1].x
= -ob_rr_theme
->fbwidth
;
298 xrect
[1].y
= FRAME_HANDLE_Y(self
);
299 xrect
[1].width
= self
->width
+ self
->rbwidth
* 2;
300 xrect
[1].height
= ob_rr_theme
->handle_height
+
305 XShapeCombineRectangles(ob_display
, self
->window
,
306 ShapeBounding
, 0, 0, xrect
, num
,
307 ShapeUnion
, Unsorted
);
312 void frame_adjust_area(ObFrame
*self
, gboolean moved
,
313 gboolean resized
, gboolean fake
)
317 oldsize
= self
->size
;
320 self
->decorations
= self
->client
->decorations
;
321 self
->max_horz
= self
->client
->max_horz
;
323 if (self
->decorations
& OB_FRAME_DECOR_BORDER
) {
324 self
->bwidth
= ob_rr_theme
->fbwidth
;
325 self
->cbwidth_x
= ob_rr_theme
->cbwidthx
;
326 self
->cbwidth_y
= ob_rr_theme
->cbwidthy
;
328 self
->bwidth
= self
->cbwidth_x
= self
->cbwidth_y
= 0;
330 self
->rbwidth
= self
->bwidth
;
333 self
->bwidth
= self
->cbwidth_x
= 0;
335 STRUT_SET(self
->innersize
,
340 self
->width
= self
->client
->area
.width
+ self
->cbwidth_x
* 2 -
341 (self
->max_horz
? self
->rbwidth
* 2 : 0);
342 self
->width
= MAX(self
->width
, 1); /* no lower than 1 */
344 /* set border widths */
346 XSetWindowBorderWidth(ob_display
, self
->window
, self
->bwidth
);
347 XSetWindowBorderWidth(ob_display
, self
->title
, self
->rbwidth
);
348 XSetWindowBorderWidth(ob_display
, self
->handle
, self
->rbwidth
);
349 XSetWindowBorderWidth(ob_display
, self
->lgrip
, self
->rbwidth
);
350 XSetWindowBorderWidth(ob_display
, self
->rgrip
, self
->rbwidth
);
353 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
)
354 self
->innersize
.top
+= ob_rr_theme
->title_height
+ self
->rbwidth
+
355 (self
->rbwidth
- self
->bwidth
);
356 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
&&
357 ob_rr_theme
->handle_height
> 0)
358 self
->innersize
.bottom
+= ob_rr_theme
->handle_height
+
359 self
->rbwidth
+ (self
->rbwidth
- self
->bwidth
);
361 /* they all default off, they're turned on in layout_title */
365 self
->iconify_x
= -1;
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 XMoveWindow(ob_display
, self
->tltresize
, 0, 0);
381 XMoveWindow(ob_display
, self
->tllresize
, 0, 0);
382 XMoveWindow(ob_display
, self
->trtresize
,
383 self
->width
- ob_rr_theme
->grip_width
, 0);
384 XMoveWindow(ob_display
, self
->trrresize
,
385 self
->width
- ob_rr_theme
->paddingx
- 1, 0);
386 XMapWindow(ob_display
, self
->tltresize
);
387 XMapWindow(ob_display
, self
->tllresize
);
388 XMapWindow(ob_display
, self
->trtresize
);
389 XMapWindow(ob_display
, self
->trrresize
);
391 XUnmapWindow(ob_display
, self
->tltresize
);
392 XUnmapWindow(ob_display
, self
->tllresize
);
393 XUnmapWindow(ob_display
, self
->trtresize
);
394 XUnmapWindow(ob_display
, self
->trrresize
);
397 XUnmapWindow(ob_display
, self
->title
);
400 if (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
)
401 /* layout the title bar elements */
405 if (self
->decorations
& OB_FRAME_DECOR_HANDLE
&&
406 ob_rr_theme
->handle_height
> 0)
408 XMoveResizeWindow(ob_display
, self
->handle
,
409 -self
->bwidth
, FRAME_HANDLE_Y(self
),
410 self
->width
, ob_rr_theme
->handle_height
);
411 XMapWindow(ob_display
, self
->handle
);
413 if (self
->decorations
& OB_FRAME_DECOR_GRIPS
) {
414 XMoveWindow(ob_display
, self
->lgrip
,
415 -self
->rbwidth
, -self
->rbwidth
);
416 XMoveWindow(ob_display
, self
->rgrip
,
417 -self
->rbwidth
+ self
->width
-
418 ob_rr_theme
->grip_width
, -self
->rbwidth
);
419 XMapWindow(ob_display
, self
->lgrip
);
420 XMapWindow(ob_display
, self
->rgrip
);
422 XUnmapWindow(ob_display
, self
->lgrip
);
423 XUnmapWindow(ob_display
, self
->rgrip
);
426 XUnmapWindow(ob_display
, self
->handle
);
428 /* move and resize the plate */
429 XMoveResizeWindow(ob_display
, self
->plate
,
430 self
->innersize
.left
- self
->cbwidth_x
,
431 self
->innersize
.top
- self
->cbwidth_y
,
432 self
->client
->area
.width
+ self
->cbwidth_x
* 2,
433 self
->client
->area
.height
+ self
->cbwidth_y
* 2);
434 /* when the client has StaticGravity, it likes to move around. */
435 XMoveWindow(ob_display
, self
->client
->window
,
436 self
->cbwidth_x
, self
->cbwidth_y
);
439 STRUT_SET(self
->size
,
440 self
->innersize
.left
+ self
->bwidth
,
441 self
->innersize
.top
+ self
->bwidth
,
442 self
->innersize
.right
+ self
->bwidth
,
443 self
->innersize
.bottom
+ self
->bwidth
);
446 /* shading can change without being moved or resized */
447 RECT_SET_SIZE(self
->area
,
448 self
->client
->area
.width
+
449 self
->size
.left
+ self
->size
.right
,
450 (self
->client
->shaded
?
451 ob_rr_theme
->title_height
+ self
->rbwidth
* 2:
452 self
->client
->area
.height
+
453 self
->size
.top
+ self
->size
.bottom
));
456 /* find the new coordinates, done after setting the frame.size, for
457 frame_client_gravity. */
458 self
->area
.x
= self
->client
->area
.x
;
459 self
->area
.y
= self
->client
->area
.y
;
460 frame_client_gravity(self
, &self
->area
.x
, &self
->area
.y
);
464 /* move and resize the top level frame.
465 shading can change without being moved or resized */
466 XMoveResizeWindow(ob_display
, self
->window
,
467 self
->area
.x
, self
->area
.y
,
468 self
->area
.width
- self
->bwidth
* 2,
469 self
->area
.height
- self
->bwidth
* 2);
472 framerender_frame(self
);
473 frame_adjust_shape(self
);
476 if (!STRUT_EQUAL(self
->size
, oldsize
)) {
478 vals
[0] = self
->size
.left
;
479 vals
[1] = self
->size
.right
;
480 vals
[2] = self
->size
.top
;
481 vals
[3] = self
->size
.bottom
;
482 PROP_SETA32(self
->client
->window
, net_frame_extents
,
486 /* if this occurs while we are focus cycling, the indicator needs to
488 if (focus_cycle_target
== self
->client
)
489 focus_cycle_draw_indicator();
491 if (resized
&& (self
->decorations
& OB_FRAME_DECOR_TITLEBAR
))
492 XResizeWindow(ob_display
, self
->label
, self
->label_width
,
493 ob_rr_theme
->label_height
);
496 void frame_adjust_state(ObFrame
*self
)
498 framerender_frame(self
);
501 void frame_adjust_focus(ObFrame
*self
, gboolean hilite
)
503 self
->focused
= hilite
;
504 framerender_frame(self
);
507 void frame_adjust_title(ObFrame
*self
)
509 framerender_frame(self
);
512 void frame_adjust_icon(ObFrame
*self
)
514 framerender_frame(self
);
517 void frame_grab_client(ObFrame
*self
, ObClient
*client
)
519 self
->client
= client
;
521 /* reparent the client to the frame */
522 XReparentWindow(ob_display
, client
->window
, self
->plate
, 0, 0);
524 When reparenting the client window, it is usually not mapped yet, since
525 this occurs from a MapRequest. However, in the case where Openbox is
526 starting up, the window is already mapped, so we'll see unmap events for
527 it. There are 2 unmap events generated that we see, one with the 'event'
528 member set the root window, and one set to the client, but both get
529 handled and need to be ignored.
531 if (ob_state() == OB_STATE_STARTING
)
532 client
->ignore_unmaps
+= 2;
534 /* select the event mask on the client's parent (to receive config/map
535 req's) the ButtonPress is to catch clicks on the client border */
536 XSelectInput(ob_display
, self
->plate
, PLATE_EVENTMASK
);
538 frame_adjust_area(self
, TRUE
, TRUE
, FALSE
);
540 /* map the client so it maps when the frame does */
541 XMapWindow(ob_display
, client
->window
);
543 /* set all the windows for the frame in the window_map */
544 g_hash_table_insert(window_map
, &self
->window
, client
);
545 g_hash_table_insert(window_map
, &self
->plate
, client
);
546 g_hash_table_insert(window_map
, &self
->title
, client
);
547 g_hash_table_insert(window_map
, &self
->label
, client
);
548 g_hash_table_insert(window_map
, &self
->max
, client
);
549 g_hash_table_insert(window_map
, &self
->close
, client
);
550 g_hash_table_insert(window_map
, &self
->desk
, client
);
551 g_hash_table_insert(window_map
, &self
->shade
, client
);
552 g_hash_table_insert(window_map
, &self
->icon
, client
);
553 g_hash_table_insert(window_map
, &self
->iconify
, client
);
554 g_hash_table_insert(window_map
, &self
->handle
, client
);
555 g_hash_table_insert(window_map
, &self
->lgrip
, client
);
556 g_hash_table_insert(window_map
, &self
->rgrip
, client
);
557 g_hash_table_insert(window_map
, &self
->tltresize
, client
);
558 g_hash_table_insert(window_map
, &self
->tllresize
, client
);
559 g_hash_table_insert(window_map
, &self
->trtresize
, client
);
560 g_hash_table_insert(window_map
, &self
->trrresize
, client
);
563 void frame_release_client(ObFrame
*self
, ObClient
*client
)
566 gboolean reparent
= TRUE
;
568 g_assert(self
->client
== client
);
570 /* check if the app has already reparented its window away */
571 while (XCheckTypedWindowEvent(ob_display
, client
->window
,
572 ReparentNotify
, &ev
))
574 /* This check makes sure we don't catch our own reparent action to
575 our frame window. This doesn't count as the app reparenting itself
578 Reparent events that are generated by us are just discarded here.
579 They are of no consequence to us anyhow.
581 if (ev
.xreparent
.parent
!= self
->plate
) {
583 XPutBackEvent(ob_display
, &ev
);
589 /* according to the ICCCM - if the client doesn't reparent itself,
590 then we will reparent the window to root for them */
591 XReparentWindow(ob_display
, client
->window
,
592 RootWindow(ob_display
, ob_screen
),
597 /* remove all the windows for the frame from the window_map */
598 g_hash_table_remove(window_map
, &self
->window
);
599 g_hash_table_remove(window_map
, &self
->plate
);
600 g_hash_table_remove(window_map
, &self
->title
);
601 g_hash_table_remove(window_map
, &self
->label
);
602 g_hash_table_remove(window_map
, &self
->max
);
603 g_hash_table_remove(window_map
, &self
->close
);
604 g_hash_table_remove(window_map
, &self
->desk
);
605 g_hash_table_remove(window_map
, &self
->shade
);
606 g_hash_table_remove(window_map
, &self
->icon
);
607 g_hash_table_remove(window_map
, &self
->iconify
);
608 g_hash_table_remove(window_map
, &self
->handle
);
609 g_hash_table_remove(window_map
, &self
->lgrip
);
610 g_hash_table_remove(window_map
, &self
->rgrip
);
611 g_hash_table_remove(window_map
, &self
->tltresize
);
612 g_hash_table_remove(window_map
, &self
->tllresize
);
613 g_hash_table_remove(window_map
, &self
->trtresize
);
614 g_hash_table_remove(window_map
, &self
->trrresize
);
616 ob_main_loop_timeout_remove_data(ob_main_loop
, flash_timeout
, self
, TRUE
);
621 static void layout_title(ObFrame
*self
)
625 gboolean n
, d
, i
, l
, m
, c
, s
;
627 n
= d
= i
= l
= m
= c
= s
= FALSE
;
629 /* figure out whats being shown, and the width of the label */
630 self
->label_width
= self
->width
- (ob_rr_theme
->paddingx
+ 1) * 2;
631 for (lc
= config_title_layout
; *lc
!= '\0'; ++lc
) {
634 if (n
) { *lc
= ' '; break; } /* rm duplicates */
636 self
->label_width
-= (ob_rr_theme
->button_size
+ 2 +
637 ob_rr_theme
->paddingx
+ 1);
640 if (d
) { *lc
= ' '; break; }
641 if (!(self
->decorations
& OB_FRAME_DECOR_ALLDESKTOPS
)
642 && config_theme_hidedisabled
)
645 self
->label_width
-= (ob_rr_theme
->button_size
+
646 ob_rr_theme
->paddingx
+ 1);
649 if (s
) { *lc
= ' '; break; }
650 if (!(self
->decorations
& OB_FRAME_DECOR_SHADE
)
651 && config_theme_hidedisabled
)
654 self
->label_width
-= (ob_rr_theme
->button_size
+
655 ob_rr_theme
->paddingx
+ 1);
658 if (i
) { *lc
= ' '; break; }
659 if (!(self
->decorations
& OB_FRAME_DECOR_ICONIFY
)
660 && config_theme_hidedisabled
)
663 self
->label_width
-= (ob_rr_theme
->button_size
+
664 ob_rr_theme
->paddingx
+ 1);
667 if (l
) { *lc
= ' '; break; }
671 if (m
) { *lc
= ' '; break; }
672 if (!(self
->decorations
& OB_FRAME_DECOR_MAXIMIZE
)
673 && config_theme_hidedisabled
)
676 self
->label_width
-= (ob_rr_theme
->button_size
+
677 ob_rr_theme
->paddingx
+ 1);
680 if (c
) { *lc
= ' '; break; }
681 if (!(self
->decorations
& OB_FRAME_DECOR_CLOSE
)
682 && config_theme_hidedisabled
)
685 self
->label_width
-= (ob_rr_theme
->button_size
+
686 ob_rr_theme
->paddingx
+ 1);
690 if (self
->label_width
< 1) self
->label_width
= 1;
692 if (!n
) XUnmapWindow(ob_display
, self
->icon
);
693 if (!d
) XUnmapWindow(ob_display
, self
->desk
);
694 if (!s
) XUnmapWindow(ob_display
, self
->shade
);
695 if (!i
) XUnmapWindow(ob_display
, self
->iconify
);
696 if (!l
) XUnmapWindow(ob_display
, self
->label
);
697 if (!m
) XUnmapWindow(ob_display
, self
->max
);
698 if (!c
) XUnmapWindow(ob_display
, self
->close
);
700 x
= ob_rr_theme
->paddingx
+ 1;
701 for (lc
= config_title_layout
; *lc
!= '\0'; ++lc
) {
706 XMapWindow(ob_display
, self
->icon
);
707 XMoveWindow(ob_display
, self
->icon
, x
, ob_rr_theme
->paddingy
);
708 x
+= ob_rr_theme
->button_size
+ 2 + ob_rr_theme
->paddingx
+ 1;
713 XMapWindow(ob_display
, self
->desk
);
714 XMoveWindow(ob_display
, self
->desk
, x
, ob_rr_theme
->paddingy
+ 1);
715 x
+= ob_rr_theme
->button_size
+ ob_rr_theme
->paddingx
+ 1;
720 XMapWindow(ob_display
, self
->shade
);
721 XMoveWindow(ob_display
, self
->shade
, x
, ob_rr_theme
->paddingy
+ 1);
722 x
+= ob_rr_theme
->button_size
+ ob_rr_theme
->paddingx
+ 1;
727 XMapWindow(ob_display
, self
->iconify
);
728 XMoveWindow(ob_display
,self
->iconify
, x
, ob_rr_theme
->paddingy
+ 1);
729 x
+= ob_rr_theme
->button_size
+ ob_rr_theme
->paddingx
+ 1;
734 XMapWindow(ob_display
, self
->label
);
735 XMoveWindow(ob_display
, self
->label
, x
, ob_rr_theme
->paddingy
);
736 x
+= self
->label_width
+ ob_rr_theme
->paddingx
+ 1;
741 XMapWindow(ob_display
, self
->max
);
742 XMoveWindow(ob_display
, self
->max
, x
, ob_rr_theme
->paddingy
+ 1);
743 x
+= ob_rr_theme
->button_size
+ ob_rr_theme
->paddingx
+ 1;
748 XMapWindow(ob_display
, self
->close
);
749 XMoveWindow(ob_display
, self
->close
, x
, ob_rr_theme
->paddingy
+ 1);
750 x
+= ob_rr_theme
->button_size
+ ob_rr_theme
->paddingx
+ 1;
756 ObFrameContext
frame_context_from_string(const gchar
*name
)
758 if (!g_ascii_strcasecmp("Desktop", name
))
759 return OB_FRAME_CONTEXT_DESKTOP
;
760 else if (!g_ascii_strcasecmp("Client", name
))
761 return OB_FRAME_CONTEXT_CLIENT
;
762 else if (!g_ascii_strcasecmp("Titlebar", name
))
763 return OB_FRAME_CONTEXT_TITLEBAR
;
764 else if (!g_ascii_strcasecmp("Handle", name
))
765 return OB_FRAME_CONTEXT_HANDLE
;
766 else if (!g_ascii_strcasecmp("Frame", name
))
767 return OB_FRAME_CONTEXT_FRAME
;
768 else if (!g_ascii_strcasecmp("TLCorner", name
))
769 return OB_FRAME_CONTEXT_TLCORNER
;
770 else if (!g_ascii_strcasecmp("TRCorner", name
))
771 return OB_FRAME_CONTEXT_TRCORNER
;
772 else if (!g_ascii_strcasecmp("BLCorner", name
))
773 return OB_FRAME_CONTEXT_BLCORNER
;
774 else if (!g_ascii_strcasecmp("BRCorner", name
))
775 return OB_FRAME_CONTEXT_BRCORNER
;
776 else if (!g_ascii_strcasecmp("Maximize", name
))
777 return OB_FRAME_CONTEXT_MAXIMIZE
;
778 else if (!g_ascii_strcasecmp("AllDesktops", name
))
779 return OB_FRAME_CONTEXT_ALLDESKTOPS
;
780 else if (!g_ascii_strcasecmp("Shade", name
))
781 return OB_FRAME_CONTEXT_SHADE
;
782 else if (!g_ascii_strcasecmp("Iconify", name
))
783 return OB_FRAME_CONTEXT_ICONIFY
;
784 else if (!g_ascii_strcasecmp("Icon", name
))
785 return OB_FRAME_CONTEXT_ICON
;
786 else if (!g_ascii_strcasecmp("Close", name
))
787 return OB_FRAME_CONTEXT_CLOSE
;
788 else if (!g_ascii_strcasecmp("MoveResize", name
))
789 return OB_FRAME_CONTEXT_MOVE_RESIZE
;
790 return OB_FRAME_CONTEXT_NONE
;
793 ObFrameContext
frame_context(ObClient
*client
, Window win
)
797 if (moveresize_in_progress
)
798 return OB_FRAME_CONTEXT_MOVE_RESIZE
;
800 if (win
== RootWindow(ob_display
, ob_screen
))
801 return OB_FRAME_CONTEXT_DESKTOP
;
802 if (client
== NULL
) return OB_FRAME_CONTEXT_NONE
;
803 if (win
== client
->window
) {
804 /* conceptually, this is the desktop, as far as users are
806 if (client
->type
== OB_CLIENT_TYPE_DESKTOP
)
807 return OB_FRAME_CONTEXT_DESKTOP
;
808 return OB_FRAME_CONTEXT_CLIENT
;
811 self
= client
->frame
;
812 if (win
== self
->plate
) {
813 /* conceptually, this is the desktop, as far as users are
815 if (client
->type
== OB_CLIENT_TYPE_DESKTOP
)
816 return OB_FRAME_CONTEXT_DESKTOP
;
817 return OB_FRAME_CONTEXT_CLIENT
;
820 if (win
== self
->window
) return OB_FRAME_CONTEXT_FRAME
;
821 if (win
== self
->title
) return OB_FRAME_CONTEXT_TITLEBAR
;
822 if (win
== self
->label
) return OB_FRAME_CONTEXT_TITLEBAR
;
823 if (win
== self
->handle
) return OB_FRAME_CONTEXT_HANDLE
;
824 if (win
== self
->lgrip
) return OB_FRAME_CONTEXT_BLCORNER
;
825 if (win
== self
->rgrip
) return OB_FRAME_CONTEXT_BRCORNER
;
826 if (win
== self
->tltresize
) return OB_FRAME_CONTEXT_TLCORNER
;
827 if (win
== self
->tllresize
) return OB_FRAME_CONTEXT_TLCORNER
;
828 if (win
== self
->trtresize
) return OB_FRAME_CONTEXT_TRCORNER
;
829 if (win
== self
->trrresize
) return OB_FRAME_CONTEXT_TRCORNER
;
830 if (win
== self
->max
) return OB_FRAME_CONTEXT_MAXIMIZE
;
831 if (win
== self
->iconify
) return OB_FRAME_CONTEXT_ICONIFY
;
832 if (win
== self
->close
) return OB_FRAME_CONTEXT_CLOSE
;
833 if (win
== self
->icon
) return OB_FRAME_CONTEXT_ICON
;
834 if (win
== self
->desk
) return OB_FRAME_CONTEXT_ALLDESKTOPS
;
835 if (win
== self
->shade
) return OB_FRAME_CONTEXT_SHADE
;
837 return OB_FRAME_CONTEXT_NONE
;
840 void frame_client_gravity(ObFrame
*self
, gint
*x
, gint
*y
)
843 switch (self
->client
->gravity
) {
845 case NorthWestGravity
:
846 case SouthWestGravity
:
853 *x
-= (self
->size
.left
+ self
->size
.right
) / 2;
856 case NorthEastGravity
:
857 case SouthEastGravity
:
859 *x
-= self
->size
.left
+ self
->size
.right
;
864 *x
-= self
->size
.left
;
869 switch (self
->client
->gravity
) {
871 case NorthWestGravity
:
872 case NorthEastGravity
:
879 *y
-= (self
->size
.top
+ self
->size
.bottom
) / 2;
882 case SouthWestGravity
:
883 case SouthEastGravity
:
885 *y
-= self
->size
.top
+ self
->size
.bottom
;
890 *y
-= self
->size
.top
;
895 void frame_frame_gravity(ObFrame
*self
, gint
*x
, gint
*y
)
898 switch (self
->client
->gravity
) {
900 case NorthWestGravity
:
902 case SouthWestGravity
:
907 *x
+= (self
->size
.left
+ self
->size
.right
) / 2;
909 case NorthEastGravity
:
911 case SouthEastGravity
:
912 *x
+= self
->size
.left
+ self
->size
.right
;
916 *x
+= self
->size
.left
;
921 switch (self
->client
->gravity
) {
923 case NorthWestGravity
:
925 case NorthEastGravity
:
930 *y
+= (self
->size
.top
+ self
->size
.bottom
) / 2;
932 case SouthWestGravity
:
934 case SouthEastGravity
:
935 *y
+= self
->size
.top
+ self
->size
.bottom
;
939 *y
+= self
->size
.top
;
944 static void flash_done(gpointer data
)
946 ObFrame
*self
= data
;
948 if (self
->focused
!= self
->flash_on
)
949 frame_adjust_focus(self
, self
->focused
);
952 static gboolean
flash_timeout(gpointer data
)
954 ObFrame
*self
= data
;
957 g_get_current_time(&now
);
958 if (now
.tv_sec
> self
->flash_end
.tv_sec
||
959 (now
.tv_sec
== self
->flash_end
.tv_sec
&&
960 now
.tv_usec
>= self
->flash_end
.tv_usec
))
961 self
->flashing
= FALSE
;
964 return FALSE
; /* we are done */
966 self
->flash_on
= !self
->flash_on
;
967 if (!self
->focused
) {
968 frame_adjust_focus(self
, self
->flash_on
);
969 self
->focused
= FALSE
;
972 return TRUE
; /* go again */
975 void frame_flash_start(ObFrame
*self
)
977 self
->flash_on
= self
->focused
;
980 ob_main_loop_timeout_add(ob_main_loop
,
981 G_USEC_PER_SEC
* 0.6,
985 g_get_current_time(&self
->flash_end
);
986 g_time_val_add(&self
->flash_end
, G_USEC_PER_SEC
* 5);
988 self
->flashing
= TRUE
;
991 void frame_flash_stop(ObFrame
*self
)
993 self
->flashing
= FALSE
;