3 #include "extensions.h"
4 #include "framerender.h"
5 #include "render/theme.h"
7 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask)
8 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
9 ButtonPressMask | ButtonReleaseMask)
10 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
11 ButtonMotionMask | ExposureMask)
13 static void layout_title(Frame
*self
);
23 static Window
createWindow(Window parent
, unsigned long mask
,
24 XSetWindowAttributes
*attrib
)
26 return XCreateWindow(ob_display
, parent
, 0, 0, 1, 1, 0,
27 RrDepth(ob_rr_inst
), InputOutput
,
28 RrVisual(ob_rr_inst
), mask
, attrib
);
34 XSetWindowAttributes attrib
;
38 self
= g_new(Frame
, 1);
40 self
->visible
= FALSE
;
42 /* create all of the decor windows */
43 mask
= CWOverrideRedirect
| CWEventMask
;
44 attrib
.event_mask
= FRAME_EVENTMASK
;
45 attrib
.override_redirect
= TRUE
;
46 self
->window
= createWindow(ob_root
, mask
, &attrib
);
49 self
->plate
= createWindow(self
->window
, mask
, &attrib
);
52 attrib
.event_mask
= ELEMENT_EVENTMASK
;
53 self
->title
= createWindow(self
->window
, mask
, &attrib
);
54 self
->label
= createWindow(self
->title
, mask
, &attrib
);
55 self
->max
= createWindow(self
->title
, mask
, &attrib
);
56 self
->close
= createWindow(self
->title
, mask
, &attrib
);
57 self
->desk
= createWindow(self
->title
, mask
, &attrib
);
58 self
->shade
= createWindow(self
->title
, mask
, &attrib
);
59 self
->icon
= createWindow(self
->title
, mask
, &attrib
);
60 self
->iconify
= createWindow(self
->title
, mask
, &attrib
);
61 self
->handle
= createWindow(self
->window
, mask
, &attrib
);
63 attrib
.cursor
= ob_cursors
.bl
;
64 self
->lgrip
= createWindow(self
->handle
, mask
, &attrib
);
65 attrib
.cursor
= ob_cursors
.br
;
66 self
->rgrip
= createWindow(self
->handle
, mask
, &attrib
);
68 self
->focused
= FALSE
;
70 /* the other stuff is shown based on decor settings */
71 XMapWindow(ob_display
, self
->plate
);
72 XMapWindow(ob_display
, self
->lgrip
);
73 XMapWindow(ob_display
, self
->rgrip
);
74 XMapWindow(ob_display
, self
->label
);
76 /* set colors/appearance/sizes for stuff that doesn't change */
77 XSetWindowBorder(ob_display
, self
->window
, theme_b_color
->pixel
);
78 XSetWindowBorder(ob_display
, self
->label
, theme_b_color
->pixel
);
79 XSetWindowBorder(ob_display
, self
->rgrip
, theme_b_color
->pixel
);
80 XSetWindowBorder(ob_display
, self
->lgrip
, theme_b_color
->pixel
);
82 XResizeWindow(ob_display
, self
->max
, theme_button_size
, theme_button_size
);
83 XResizeWindow(ob_display
, self
->iconify
,
84 theme_button_size
, theme_button_size
);
85 XResizeWindow(ob_display
, self
->icon
,
86 theme_button_size
+ 2, theme_button_size
+ 2);
87 XResizeWindow(ob_display
, self
->close
,
88 theme_button_size
, theme_button_size
);
89 XResizeWindow(ob_display
, self
->desk
,
90 theme_button_size
, theme_button_size
);
91 XResizeWindow(ob_display
, self
->shade
,
92 theme_button_size
, theme_button_size
);
93 XResizeWindow(ob_display
, self
->lgrip
,
94 theme_grip_width
, theme_handle_height
);
95 XResizeWindow(ob_display
, self
->rgrip
,
96 theme_grip_width
, theme_handle_height
);
98 /* set up the dynamic appearances */
99 self
->a_unfocused_title
= RrAppearanceCopy(theme_a_unfocused_title
);
100 self
->a_focused_title
= RrAppearanceCopy(theme_a_focused_title
);
101 self
->a_unfocused_label
= RrAppearanceCopy(theme_a_unfocused_label
);
102 self
->a_focused_label
= RrAppearanceCopy(theme_a_focused_label
);
103 self
->a_unfocused_handle
= RrAppearanceCopy(theme_a_unfocused_handle
);
104 self
->a_focused_handle
= RrAppearanceCopy(theme_a_focused_handle
);
105 self
->a_icon
= RrAppearanceCopy(theme_a_icon
);
107 self
->max_press
= self
->close_press
= self
->desk_press
=
108 self
->iconify_press
= self
->shade_press
= FALSE
;
113 static void frame_free(Frame
*self
)
115 RrAppearanceFree(self
->a_unfocused_title
);
116 RrAppearanceFree(self
->a_focused_title
);
117 RrAppearanceFree(self
->a_unfocused_label
);
118 RrAppearanceFree(self
->a_focused_label
);
119 RrAppearanceFree(self
->a_unfocused_handle
);
120 RrAppearanceFree(self
->a_focused_handle
);
121 RrAppearanceFree(self
->a_icon
);
123 XDestroyWindow(ob_display
, self
->window
);
128 void frame_show(Frame
*self
)
130 if (!self
->visible
) {
131 self
->visible
= TRUE
;
132 XMapWindow(ob_display
, self
->window
);
136 void frame_hide(Frame
*self
)
139 self
->visible
= FALSE
;
140 self
->client
->ignore_unmaps
++;
141 XUnmapWindow(ob_display
, self
->window
);
145 void frame_adjust_shape(Frame
*self
)
151 if (!self
->client
->shaped
) {
152 /* clear the shape on the frame window */
153 XShapeCombineMask(ob_display
, self
->window
, ShapeBounding
,
154 self
->innersize
.left
,
158 /* make the frame's shape match the clients */
159 XShapeCombineShape(ob_display
, self
->window
, ShapeBounding
,
160 self
->innersize
.left
,
162 self
->client
->window
,
163 ShapeBounding
, ShapeSet
);
166 if (self
->client
->decorations
& Decor_Titlebar
) {
167 xrect
[0].x
= -theme_bevel
;
168 xrect
[0].y
= -theme_bevel
;
169 xrect
[0].width
= self
->width
+ self
->bwidth
* 2;
170 xrect
[0].height
= theme_title_height
+
175 if (self
->client
->decorations
& Decor_Handle
) {
176 xrect
[1].x
= -theme_bevel
;
177 xrect
[1].y
= FRAME_HANDLE_Y(self
);
178 xrect
[1].width
= self
->width
+ self
->bwidth
* 2;
179 xrect
[1].height
= theme_handle_height
+
184 XShapeCombineRectangles(ob_display
, self
->window
,
185 ShapeBounding
, 0, 0, xrect
, num
,
186 ShapeUnion
, Unsorted
);
191 void frame_adjust_area(Frame
*self
, gboolean moved
, gboolean resized
)
194 if (self
->client
->decorations
& Decor_Border
) {
195 self
->bwidth
= theme_bwidth
;
196 self
->cbwidth
= theme_cbwidth
;
198 self
->bwidth
= self
->cbwidth
= 0;
200 STRUT_SET(self
->innersize
, self
->cbwidth
, self
->cbwidth
,
201 self
->cbwidth
, self
->cbwidth
);
202 self
->width
= self
->client
->area
.width
+ self
->cbwidth
* 2;
203 g_assert(self
->width
> 0);
205 /* set border widths */
206 XSetWindowBorderWidth(ob_display
, self
->plate
, self
->cbwidth
);
207 XSetWindowBorderWidth(ob_display
, self
->window
, self
->bwidth
);
208 XSetWindowBorderWidth(ob_display
, self
->title
, self
->bwidth
);
209 XSetWindowBorderWidth(ob_display
, self
->handle
, self
->bwidth
);
210 XSetWindowBorderWidth(ob_display
, self
->lgrip
, self
->bwidth
);
211 XSetWindowBorderWidth(ob_display
, self
->rgrip
, self
->bwidth
);
213 /* position/size and map/unmap all the windows */
215 /* they all default off, they're turned on in layout_title */
219 self
->iconify_x
= -1;
224 if (self
->client
->decorations
& Decor_Titlebar
) {
225 XMoveResizeWindow(ob_display
, self
->title
,
226 -self
->bwidth
, -self
->bwidth
,
227 self
->width
, theme_title_height
);
228 self
->innersize
.top
+= theme_title_height
+ self
->bwidth
;
229 XMapWindow(ob_display
, self
->title
);
231 /* layout the title bar elements */
234 XUnmapWindow(ob_display
, self
->title
);
236 if (self
->client
->decorations
& Decor_Handle
) {
237 XMoveResizeWindow(ob_display
, self
->handle
,
238 -self
->bwidth
, FRAME_HANDLE_Y(self
),
239 self
->width
, theme_handle_height
);
240 XMoveWindow(ob_display
, self
->lgrip
,
241 -self
->bwidth
, -self
->bwidth
);
242 XMoveWindow(ob_display
, self
->rgrip
,
243 -self
->bwidth
+ self
->width
-
244 theme_grip_width
, -self
->bwidth
);
245 self
->innersize
.bottom
+= theme_handle_height
+
247 XMapWindow(ob_display
, self
->handle
);
249 /* XXX make a subwindow with these dimentions?
250 theme_grip_width + self->bwidth, 0,
251 self->width - (theme_grip_width + self->bwidth) * 2,
252 theme_handle_height);
255 XUnmapWindow(ob_display
, self
->handle
);
259 /* move and resize the plate */
260 XMoveResizeWindow(ob_display
, self
->plate
,
261 self
->innersize
.left
- self
->cbwidth
,
262 self
->innersize
.top
- self
->cbwidth
,
263 self
->client
->area
.width
,
264 self
->client
->area
.height
);
265 /* when the client has StaticGravity, it likes to move around. */
266 XMoveWindow(ob_display
, self
->client
->window
, 0, 0);
270 STRUT_SET(self
->size
,
271 self
->innersize
.left
+ self
->bwidth
,
272 self
->innersize
.top
+ self
->bwidth
,
273 self
->innersize
.right
+ self
->bwidth
,
274 self
->innersize
.bottom
+ self
->bwidth
);
277 /* shading can change without being moved or resized */
278 RECT_SET_SIZE(self
->area
,
279 self
->client
->area
.width
+
280 self
->size
.left
+ self
->size
.right
,
281 (self
->client
->shaded
? theme_title_height
+ self
->bwidth
*2:
282 self
->client
->area
.height
+
283 self
->size
.top
+ self
->size
.bottom
));
286 /* find the new coordinates, done after setting the frame.size, for
287 frame_client_gravity. */
288 self
->area
.x
= self
->client
->area
.x
;
289 self
->area
.y
= self
->client
->area
.y
;
290 frame_client_gravity((Frame
*)self
,
291 &self
->area
.x
, &self
->area
.y
);
294 /* move and resize the top level frame.
295 shading can change without being moved or resized */
296 XMoveResizeWindow(ob_display
, self
->window
,
297 self
->area
.x
, self
->area
.y
,
299 self
->area
.height
- self
->bwidth
* 2);
302 framerender_frame(self
);
304 frame_adjust_shape(self
);
308 void frame_adjust_state(Frame
*self
)
310 framerender_frame(self
);
313 void frame_adjust_focus(Frame
*self
, gboolean hilite
)
315 self
->focused
= hilite
;
316 framerender_frame(self
);
319 void frame_adjust_title(Frame
*self
)
321 framerender_frame(self
);
324 void frame_adjust_icon(Frame
*self
)
326 framerender_frame(self
);
329 void frame_grab_client(Frame
*self
, Client
*client
)
331 self
->client
= client
;
333 /* reparent the client to the frame */
334 XReparentWindow(ob_display
, client
->window
, self
->plate
, 0, 0);
336 When reparenting the client window, it is usually not mapped yet, since
337 this occurs from a MapRequest. However, in the case where Openbox is
338 starting up, the window is already mapped, so we'll see unmap events for
339 it. There are 2 unmap events generated that we see, one with the 'event'
340 member set the root window, and one set to the client, but both get
341 handled and need to be ignored.
343 if (ob_state
== State_Starting
)
344 client
->ignore_unmaps
+= 2;
346 /* select the event mask on the client's parent (to receive config/map
347 req's) the ButtonPress is to catch clicks on the client border */
348 XSelectInput(ob_display
, self
->plate
, PLATE_EVENTMASK
);
350 /* map the client so it maps when the frame does */
351 XMapWindow(ob_display
, client
->window
);
353 frame_adjust_area(self
, TRUE
, TRUE
);
355 /* set all the windows for the frame in the window_map */
356 g_hash_table_insert(window_map
, &self
->window
, client
);
357 g_hash_table_insert(window_map
, &self
->plate
, client
);
358 g_hash_table_insert(window_map
, &self
->title
, client
);
359 g_hash_table_insert(window_map
, &self
->label
, client
);
360 g_hash_table_insert(window_map
, &self
->max
, client
);
361 g_hash_table_insert(window_map
, &self
->close
, client
);
362 g_hash_table_insert(window_map
, &self
->desk
, client
);
363 g_hash_table_insert(window_map
, &self
->shade
, client
);
364 g_hash_table_insert(window_map
, &self
->icon
, client
);
365 g_hash_table_insert(window_map
, &self
->iconify
, client
);
366 g_hash_table_insert(window_map
, &self
->handle
, client
);
367 g_hash_table_insert(window_map
, &self
->lgrip
, client
);
368 g_hash_table_insert(window_map
, &self
->rgrip
, client
);
371 void frame_release_client(Frame
*self
, Client
*client
)
375 g_assert(self
->client
== client
);
377 /* check if the app has already reparented its window away */
378 if (XCheckTypedWindowEvent(ob_display
, client
->window
,
379 ReparentNotify
, &ev
)) {
380 XPutBackEvent(ob_display
, &ev
);
382 /* re-map the window since the unmanaging process unmaps it */
384 /* XXX ... um no it doesnt it unmaps its parent, the window itself
385 retains its mapped state, no?! XXX
386 XMapWindow(ob_display, client->window); */
388 /* according to the ICCCM - if the client doesn't reparent itself,
389 then we will reparent the window to root for them */
390 XReparentWindow(ob_display
, client
->window
, ob_root
,
395 /* remove all the windows for the frame from the window_map */
396 g_hash_table_remove(window_map
, &self
->window
);
397 g_hash_table_remove(window_map
, &self
->plate
);
398 g_hash_table_remove(window_map
, &self
->title
);
399 g_hash_table_remove(window_map
, &self
->label
);
400 g_hash_table_remove(window_map
, &self
->max
);
401 g_hash_table_remove(window_map
, &self
->close
);
402 g_hash_table_remove(window_map
, &self
->desk
);
403 g_hash_table_remove(window_map
, &self
->shade
);
404 g_hash_table_remove(window_map
, &self
->icon
);
405 g_hash_table_remove(window_map
, &self
->iconify
);
406 g_hash_table_remove(window_map
, &self
->handle
);
407 g_hash_table_remove(window_map
, &self
->lgrip
);
408 g_hash_table_remove(window_map
, &self
->rgrip
);
413 static void layout_title(Frame
*self
)
417 gboolean n
, d
, i
, l
, m
, c
, s
;
419 n
= d
= i
= l
= m
= c
= s
= FALSE
;
421 /* figure out whats being shown, and the width of the label */
422 self
->label_width
= self
->width
- (theme_bevel
+ 1) * 2;
423 for (lc
= theme_title_layout
; *lc
!= '\0'; ++lc
) {
426 if (!(self
->client
->decorations
& Decor_Icon
)) break;
427 if (n
) { *lc
= ' '; break; } /* rm duplicates */
429 self
->label_width
-= theme_button_size
+ 2 + theme_bevel
+ 1;
432 if (!(self
->client
->decorations
& Decor_AllDesktops
)) break;
433 if (d
) { *lc
= ' '; break; } /* rm duplicates */
435 self
->label_width
-= theme_button_size
+ theme_bevel
+ 1;
438 if (!(self
->client
->decorations
& Decor_Shade
)) break;
439 if (s
) { *lc
= ' '; break; } /* rm duplicates */
441 self
->label_width
-= theme_button_size
+ theme_bevel
+ 1;
444 if (!(self
->client
->decorations
& Decor_Iconify
)) break;
445 if (i
) { *lc
= ' '; break; } /* rm duplicates */
447 self
->label_width
-= theme_button_size
+ theme_bevel
+ 1;
450 if (l
) { *lc
= ' '; break; } /* rm duplicates */
454 if (!(self
->client
->decorations
& Decor_Maximize
)) break;
455 if (m
) { *lc
= ' '; break; } /* rm duplicates */
457 self
->label_width
-= theme_button_size
+ theme_bevel
+ 1;
460 if (!(self
->client
->decorations
& Decor_Close
)) break;
461 if (c
) { *lc
= ' '; break; } /* rm duplicates */
463 self
->label_width
-= theme_button_size
+ theme_bevel
+ 1;
467 if (self
->label_width
< 1) self
->label_width
= 1;
469 XResizeWindow(ob_display
, self
->label
, self
->label_width
,
472 if (!n
) XUnmapWindow(ob_display
, self
->icon
);
473 if (!d
) XUnmapWindow(ob_display
, self
->desk
);
474 if (!s
) XUnmapWindow(ob_display
, self
->shade
);
475 if (!i
) XUnmapWindow(ob_display
, self
->iconify
);
476 if (!l
) XUnmapWindow(ob_display
, self
->label
);
477 if (!m
) XUnmapWindow(ob_display
, self
->max
);
478 if (!c
) XUnmapWindow(ob_display
, self
->close
);
481 for (lc
= theme_title_layout
; *lc
!= '\0'; ++lc
) {
486 XMapWindow(ob_display
, self
->icon
);
487 XMoveWindow(ob_display
, self
->icon
, x
, theme_bevel
);
488 x
+= theme_button_size
+ 2 + theme_bevel
+ 1;
493 XMapWindow(ob_display
, self
->desk
);
494 XMoveWindow(ob_display
, self
->desk
, x
, theme_bevel
+ 1);
495 x
+= theme_button_size
+ theme_bevel
+ 1;
500 XMapWindow(ob_display
, self
->shade
);
501 XMoveWindow(ob_display
, self
->shade
, x
, theme_bevel
+ 1);
502 x
+= theme_button_size
+ theme_bevel
+ 1;
507 XMapWindow(ob_display
, self
->iconify
);
508 XMoveWindow(ob_display
, self
->iconify
, x
, theme_bevel
+ 1);
509 x
+= theme_button_size
+ theme_bevel
+ 1;
514 XMapWindow(ob_display
, self
->label
);
515 XMoveWindow(ob_display
, self
->label
, x
, theme_bevel
);
516 x
+= self
->label_width
+ theme_bevel
+ 1;
521 XMapWindow(ob_display
, self
->max
);
522 XMoveWindow(ob_display
, self
->max
, x
, theme_bevel
+ 1);
523 x
+= theme_button_size
+ theme_bevel
+ 1;
528 XMapWindow(ob_display
, self
->close
);
529 XMoveWindow(ob_display
, self
->close
, x
, theme_bevel
+ 1);
530 x
+= theme_button_size
+ theme_bevel
+ 1;
536 Context
frame_context_from_string(char *name
)
538 if (!g_ascii_strcasecmp("root", name
))
540 else if (!g_ascii_strcasecmp("client", name
))
541 return Context_Client
;
542 else if (!g_ascii_strcasecmp("titlebar", name
))
543 return Context_Titlebar
;
544 else if (!g_ascii_strcasecmp("handle", name
))
545 return Context_Handle
;
546 else if (!g_ascii_strcasecmp("frame", name
))
547 return Context_Frame
;
548 else if (!g_ascii_strcasecmp("blcorner", name
))
549 return Context_BLCorner
;
550 else if (!g_ascii_strcasecmp("tlcorner", name
))
551 return Context_TLCorner
;
552 else if (!g_ascii_strcasecmp("brcorner", name
))
553 return Context_BRCorner
;
554 else if (!g_ascii_strcasecmp("trcorner", name
))
555 return Context_TRCorner
;
556 else if (!g_ascii_strcasecmp("maximize", name
))
557 return Context_Maximize
;
558 else if (!g_ascii_strcasecmp("alldesktops", name
))
559 return Context_AllDesktops
;
560 else if (!g_ascii_strcasecmp("shade", name
))
561 return Context_Shade
;
562 else if (!g_ascii_strcasecmp("iconify", name
))
563 return Context_Iconify
;
564 else if (!g_ascii_strcasecmp("icon", name
))
566 else if (!g_ascii_strcasecmp("close", name
))
567 return Context_Close
;
571 Context
frame_context(Client
*client
, Window win
)
575 if (win
== ob_root
) return Context_Root
;
576 if (client
== NULL
) return Context_None
;
577 if (win
== client
->window
) return Context_Client
;
579 self
= client
->frame
;
580 if (win
== self
->window
) return Context_Frame
;
581 if (win
== self
->plate
) return Context_Client
;
582 if (win
== self
->title
) return Context_Titlebar
;
583 if (win
== self
->label
) return Context_Titlebar
;
584 if (win
== self
->handle
) return Context_Handle
;
585 if (win
== self
->lgrip
) return Context_BLCorner
;
586 if (win
== self
->rgrip
) return Context_BRCorner
;
587 if (win
== self
->max
) return Context_Maximize
;
588 if (win
== self
->iconify
)return Context_Iconify
;
589 if (win
== self
->close
) return Context_Close
;
590 if (win
== self
->icon
) return Context_Icon
;
591 if (win
== self
->desk
) return Context_AllDesktops
;
592 if (win
== self
->shade
) return Context_Shade
;
597 void frame_client_gravity(Frame
*self
, int *x
, int *y
)
600 switch (self
->client
->gravity
) {
602 case NorthWestGravity
:
603 case SouthWestGravity
:
610 *x
-= (self
->size
.left
+ self
->size
.right
) / 2;
613 case NorthEastGravity
:
614 case SouthEastGravity
:
616 *x
-= self
->size
.left
+ self
->size
.right
;
621 *x
-= self
->size
.left
;
626 switch (self
->client
->gravity
) {
628 case NorthWestGravity
:
629 case NorthEastGravity
:
636 *y
-= (self
->size
.top
+ self
->size
.bottom
) / 2;
639 case SouthWestGravity
:
640 case SouthEastGravity
:
642 *y
-= self
->size
.top
+ self
->size
.bottom
;
647 *y
-= self
->size
.top
;
652 void frame_frame_gravity(Frame
*self
, int *x
, int *y
)
655 switch (self
->client
->gravity
) {
657 case NorthWestGravity
:
659 case SouthWestGravity
:
664 *x
+= (self
->size
.left
+ self
->size
.right
) / 2;
666 case NorthEastGravity
:
668 case SouthEastGravity
:
669 *x
+= self
->size
.left
+ self
->size
.right
;
673 *x
+= self
->size
.left
;
678 switch (self
->client
->gravity
) {
680 case NorthWestGravity
:
682 case SouthWestGravity
:
687 *y
+= (self
->size
.top
+ self
->size
.bottom
) / 2;
689 case NorthEastGravity
:
691 case SouthEastGravity
:
692 *y
+= self
->size
.top
+ self
->size
.bottom
;
696 *y
+= self
->size
.top
;