3 #include "extensions.h"
4 #include "framerender.h"
5 #include "render2/theme.h"
7 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask | \
9 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
10 ButtonPressMask | ButtonReleaseMask | ExposureMask)
11 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
12 ButtonMotionMask | ExposureMask)
14 static void layout_title(Frame *self);
24 static Window createWindow(Window parent, unsigned long mask,
25 XSetWindowAttributes *attrib)
27 return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
28 RrInstanceDepth(ob_render_inst), InputOutput,
29 RrInstanceVisual(ob_render_inst), mask, attrib);
35 XSetWindowAttributes attrib;
39 self = g_new(Frame, 1);
41 self->visible = FALSE;
43 /* create all of the decor windows */
44 mask = CWOverrideRedirect | CWEventMask;
45 attrib.event_mask = FRAME_EVENTMASK;
46 attrib.override_redirect = TRUE;
47 self->window = createWindow(ob_root, mask, &attrib);
49 self->s_frame = RrSurfaceNew(ob_render_inst, 0, self->window, 0);
50 self->s_plate = RrSurfaceNewChild(0, self->s_frame, 0);
51 self->s_title = RrSurfaceNewChild(0, self->s_frame, 0);
52 self->s_label = RrSurfaceNewChild(0, self->s_title, 0);
53 self->s_max = RrSurfaceNewChild(0, self->s_title, 0);
54 self->s_close = RrSurfaceNewChild(0, self->s_title, 0);
55 self->s_desk = RrSurfaceNewChild(0, self->s_title, 0);
56 self->s_shade = RrSurfaceNewChild(0, self->s_title, 0);
57 self->s_iconify = RrSurfaceNewChild(0, self->s_title, 0);
58 self->s_icon = RrSurfaceNewChild(0, self->s_title, 0);
59 self->s_handle = RrSurfaceNewChild(0, self->s_frame, 0);
60 self->s_lgrip = RrSurfaceNewChild(0, self->s_handle, 0);
61 self->s_rgrip = RrSurfaceNewChild(0, self->s_handle, 0);
63 self->plate = RrSurfaceWindow(self->s_plate);
64 self->w_title = RrSurfaceWindow(self->s_title);
65 self->w_label = RrSurfaceWindow(self->s_label);
66 self->w_max = RrSurfaceWindow(self->s_max);
67 self->w_close = RrSurfaceWindow(self->s_close);
68 self->w_desk = RrSurfaceWindow(self->s_desk);
69 self->w_shade = RrSurfaceWindow(self->s_shade);
70 self->w_iconify = RrSurfaceWindow(self->s_iconify);
71 self->w_icon = RrSurfaceWindow(self->s_icon);
72 self->w_handle = RrSurfaceWindow(self->s_handle);
73 self->w_lgrip = RrSurfaceWindow(self->s_lgrip);
74 self->w_rgrip = RrSurfaceWindow(self->s_rgrip);
76 XSelectInput(ob_display, self->w_title, ELEMENT_EVENTMASK);
77 XSelectInput(ob_display, self->w_label, ELEMENT_EVENTMASK);
78 XSelectInput(ob_display, self->w_max, ELEMENT_EVENTMASK);
79 XSelectInput(ob_display, self->w_close, ELEMENT_EVENTMASK);
80 XSelectInput(ob_display, self->w_desk, ELEMENT_EVENTMASK);
81 XSelectInput(ob_display, self->w_shade, ELEMENT_EVENTMASK);
82 XSelectInput(ob_display, self->w_iconify, ELEMENT_EVENTMASK);
83 XSelectInput(ob_display, self->w_icon, ELEMENT_EVENTMASK);
84 XSelectInput(ob_display, self->w_handle, ELEMENT_EVENTMASK);
85 XSelectInput(ob_display, self->w_lgrip, ELEMENT_EVENTMASK);
86 XSelectInput(ob_display, self->w_rgrip, ELEMENT_EVENTMASK);
88 XDefineCursor(ob_display, self->w_lgrip, ob_cursors.bl);
89 XDefineCursor(ob_display, self->w_rgrip, ob_cursors.br);
91 self->focused = FALSE;
93 /* the other stuff is shown based on decor settings */
94 RrSurfaceShow(self->s_plate);
95 RrSurfaceShow(self->s_label);
96 RrSurfaceShow(self->s_lgrip);
97 RrSurfaceShow(self->s_rgrip);
99 self->max_press = self->close_press = self->desk_press =
100 self->iconify_press = self->shade_press = FALSE;
105 static void frame_free(Frame *self)
107 RrSurfaceFree(self->s_rgrip);
108 RrSurfaceFree(self->s_lgrip);
109 RrSurfaceFree(self->s_handle);
110 RrSurfaceFree(self->s_icon);
111 RrSurfaceFree(self->s_iconify);
112 RrSurfaceFree(self->s_shade);
113 RrSurfaceFree(self->s_desk);
114 RrSurfaceFree(self->s_close);
115 RrSurfaceFree(self->s_max);
116 RrSurfaceFree(self->s_label);
117 RrSurfaceFree(self->s_title);
118 RrSurfaceFree(self->s_plate);
119 RrSurfaceFree(self->s_frame);
121 XDestroyWindow(ob_display, self->window);
126 void frame_show(Frame *self)
128 if (!self->visible) {
129 self->visible = TRUE;
130 RrSurfaceShow(self->s_frame);
134 void frame_hide(Frame *self)
137 self->visible = FALSE;
138 self->client->ignore_unmaps++;
139 RrSurfaceHide(self->s_frame);
143 void frame_adjust_shape(Frame *self)
149 if (!self->client->shaped) {
150 /* clear the shape on the frame window */
151 XShapeCombineMask(ob_display, self->window, ShapeBounding,
156 /* make the frame's shape match the clients */
157 XShapeCombineShape(ob_display, self->window, ShapeBounding,
160 self->client->window,
161 ShapeBounding, ShapeSet);
164 if (self->client->decorations & Decor_Titlebar) {
167 xrect[0].width = self->area.width;
168 xrect[0].height = self->size.top - ob_theme->bwidth;
172 if (self->client->decorations & Decor_Handle) {
174 xrect[1].y = FRAME_HANDLE_Y(self);
175 xrect[1].width = self->area.width;
176 xrect[1].height = self->size.bottom - ob_theme->bwidth;
180 XShapeCombineRectangles(ob_display, self->window,
181 ShapeBounding, 0, 0, xrect, num,
182 ShapeUnion, Unsorted);
187 void frame_adjust_area(Frame *self, gboolean moved, gboolean resized)
190 if (self->client->decorations & Decor_Border) {
191 self->bwidth = ob_theme->bwidth;
192 self->cbwidth = ob_theme->cbwidth;
194 self->bwidth = self->cbwidth = 0;
196 STRUT_SET(self->size,
197 self->bwidth + self->cbwidth,
198 self->bwidth + self->cbwidth,
199 self->bwidth + self->cbwidth,
200 self->bwidth + self->cbwidth);
201 self->width = self->client->area.width +
202 (self->bwidth + self->cbwidth) * 2;
203 g_assert(self->width > 0);
205 /* position/size and map/unmap all the windows */
207 /* they all default off, they're turned on in layout_title */
211 self->iconify_x = -1;
216 if (self->client->decorations & Decor_Titlebar) {
217 RrSurfaceSetArea(self->s_title, 0, 0,
218 self->width, RrThemeTitleHeight(ob_theme));
219 self->size.top += RrThemeTitleHeight(ob_theme) - self->bwidth;
220 RrSurfaceShow(self->s_title);
222 /* layout the title bar elements */
225 RrSurfaceHide(self->s_title);
227 if (self->client->decorations & Decor_Handle) {
228 RrSurfaceSetArea(self->s_handle, 0, FRAME_HANDLE_Y(self),
229 self->width, ob_theme->handle_height);
230 RrSurfaceSetArea(self->s_lgrip, 0, 0,
231 RrThemeGripWidth(ob_theme),
232 ob_theme->handle_height);
233 RrSurfaceSetArea(self->s_rgrip,
234 self->width - RrThemeGripWidth(ob_theme), 0,
235 RrThemeGripWidth(ob_theme),
236 ob_theme->handle_height);
237 self->size.bottom += ob_theme->handle_height - ob_theme->bwidth;
238 RrSurfaceShow(self->s_handle);
240 RrSurfaceHide(self->s_handle);
244 /* move and resize the plate */
245 RrSurfaceSetArea(self->s_plate,
246 self->size.left - self->cbwidth,
247 self->size.top - self->cbwidth,
248 self->client->area.width - self->cbwidth * 2,
249 self->client->area.height - self->cbwidth * 2);
250 /* when the client has StaticGravity, it likes to move around. */
251 XMoveWindow(ob_display, self->client->window,
252 self->cbwidth, self->cbwidth);
255 /* shading can change without being moved or resized */
256 RECT_SET_SIZE(self->area,
257 self->client->area.width +
258 self->size.left + self->size.right,
259 (self->client->shaded ? RrThemeTitleHeight(ob_theme) :
260 self->client->area.height +
261 self->size.top + self->size.bottom));
264 /* find the new coordinates, done after setting the frame.size, for
265 frame_client_gravity. */
266 self->area.x = self->client->area.x;
267 self->area.y = self->client->area.y;
268 frame_client_gravity((Frame*)self,
269 &self->area.x, &self->area.y);
272 /* move and resize the top level frame.
273 shading can change without being moved or resized */
274 RrSurfaceSetArea(self->s_frame, self->area.x, self->area.y,
275 self->area.width, self->area.height);
278 framerender_frame(self);
280 frame_adjust_shape(self);
284 void frame_adjust_state(Frame *self)
286 framerender_frame(self);
287 RrPaint(self->s_frame, 1);
290 void frame_adjust_focus(Frame *self, gboolean hilite)
292 self->focused = hilite;
293 framerender_frame(self);
294 RrPaint(self->s_frame, 1);
297 void frame_adjust_title(Frame *self)
299 framerender_frame(self);
300 RrPaint(self->s_label, 1);
303 void frame_adjust_icon(Frame *self)
305 framerender_frame(self);
306 RrPaint(self->s_icon, 1);
309 void frame_grab_client(Frame *self, Client *client)
311 self->client = client;
313 /* reparent the client to the frame */
314 XReparentWindow(ob_display, client->window, self->plate, 0, 0);
316 When reparenting the client window, it is usually not mapped yet, since
317 this occurs from a MapRequest. However, in the case where Openbox is
318 starting up, the window is already mapped, so we'll see unmap events for
319 it. There are 2 unmap events generated that we see, one with the 'event'
320 member set the root window, and one set to the client, but both get
321 handled and need to be ignored.
323 if (ob_state == State_Starting)
324 client->ignore_unmaps += 2;
326 /* select the event mask on the client's parent (to receive config/map
327 req's) the ButtonPress is to catch clicks on the client border */
328 XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
330 /* map the client so it maps when the frame does */
331 XMapWindow(ob_display, client->window);
333 frame_adjust_area(self, TRUE, TRUE);
335 /* set all the windows for the frame in the window_map */
336 g_hash_table_insert(window_map, &self->window, client);
337 g_hash_table_insert(window_map, &self->plate, client);
338 g_hash_table_insert(window_map, &self->w_title, client);
339 g_hash_table_insert(window_map, &self->w_label, client);
340 g_hash_table_insert(window_map, &self->w_max, client);
341 g_hash_table_insert(window_map, &self->w_close, client);
342 g_hash_table_insert(window_map, &self->w_desk, client);
343 g_hash_table_insert(window_map, &self->w_shade, client);
344 g_hash_table_insert(window_map, &self->w_iconify, client);
345 g_hash_table_insert(window_map, &self->w_icon, client);
346 g_hash_table_insert(window_map, &self->w_handle, client);
347 g_hash_table_insert(window_map, &self->w_lgrip, client);
348 g_hash_table_insert(window_map, &self->w_rgrip, client);
351 void frame_release_client(Frame *self, Client *client)
355 g_assert(self->client == client);
357 /* check if the app has already reparented its window away */
358 if (XCheckTypedWindowEvent(ob_display, client->window,
359 ReparentNotify, &ev)) {
360 XPutBackEvent(ob_display, &ev);
362 /* re-map the window since the unmanaging process unmaps it */
364 /* XXX ... um no it doesnt it unmaps its parent, the window itself
365 retains its mapped state, no?! XXX
366 XMapWindow(ob_display, client->window); */
368 /* according to the ICCCM - if the client doesn't reparent itself,
369 then we will reparent the window to root for them */
370 XReparentWindow(ob_display, client->window, ob_root,
375 /* remove all the windows for the frame from the window_map */
376 g_hash_table_remove(window_map, &self->window);
377 g_hash_table_remove(window_map, &self->plate);
378 g_hash_table_remove(window_map, &self->w_title);
379 g_hash_table_remove(window_map, &self->w_label);
380 g_hash_table_remove(window_map, &self->w_max);
381 g_hash_table_remove(window_map, &self->w_close);
382 g_hash_table_remove(window_map, &self->w_desk);
383 g_hash_table_remove(window_map, &self->w_shade);
384 g_hash_table_remove(window_map, &self->w_icon);
385 g_hash_table_remove(window_map, &self->w_iconify);
386 g_hash_table_remove(window_map, &self->w_handle);
387 g_hash_table_remove(window_map, &self->w_lgrip);
388 g_hash_table_remove(window_map, &self->w_rgrip);
393 static void layout_title(Frame *self)
397 gboolean n, d, i, l, m, c, s;
399 n = d = i = l = m = c = s = FALSE;
401 /* figure out whats being shown, and the width of the label */
402 self->label_width = self->width - (ob_theme->bevel + 1) * 2;
403 for (lc = ob_theme->title_layout; *lc != '\0'; ++lc) {
406 if (!(self->client->decorations & Decor_Icon)) break;
407 if (n) { *lc = ' '; break; } /* rm duplicates */
409 self->label_width -= RrThemeButtonSize(ob_theme) + 2 +
413 if (!(self->client->decorations & Decor_AllDesktops)) break;
414 if (d) { *lc = ' '; break; } /* rm duplicates */
416 self->label_width -= RrThemeButtonSize(ob_theme) +
420 if (!(self->client->decorations & Decor_Shade)) break;
421 if (s) { *lc = ' '; break; } /* rm duplicates */
423 self->label_width -= RrThemeButtonSize(ob_theme) +
427 if (!(self->client->decorations & Decor_Iconify)) break;
428 if (i) { *lc = ' '; break; } /* rm duplicates */
430 self->label_width -= RrThemeButtonSize(ob_theme) +
434 if (l) { *lc = ' '; break; } /* rm duplicates */
438 if (!(self->client->decorations & Decor_Maximize)) break;
439 if (m) { *lc = ' '; break; } /* rm duplicates */
441 self->label_width -= RrThemeButtonSize(ob_theme) +
445 if (!(self->client->decorations & Decor_Close)) break;
446 if (c) { *lc = ' '; break; } /* rm duplicates */
448 self->label_width -= RrThemeButtonSize(ob_theme) +
453 if (self->label_width < 1) self->label_width = 1;
455 if (!n) RrSurfaceHide(self->s_icon);
456 if (!d) RrSurfaceHide(self->s_desk);
457 if (!s) RrSurfaceHide(self->s_shade);
458 if (!i) RrSurfaceHide(self->s_iconify);
459 if (!l) RrSurfaceHide(self->s_label);
460 if (!m) RrSurfaceHide(self->s_max);
461 if (!c) RrSurfaceHide(self->s_close);
464 x = ob_theme->bevel + 1;
465 y = ob_theme->bevel + ob_theme->bwidth + 1;
466 for (lc = ob_theme->title_layout; *lc != '\0'; ++lc) {
471 RrSurfaceSetArea(self->s_icon, x, y - 1,
472 RrThemeButtonSize(ob_theme) + 2,
473 RrThemeButtonSize(ob_theme) + 2);
474 RrSurfaceShow(self->s_icon);
475 x += RrThemeButtonSize(ob_theme) + 2 + ob_theme->bevel + 1;
480 RrSurfaceSetArea(self->s_desk, x, y,
481 RrThemeButtonSize(ob_theme),
482 RrThemeButtonSize(ob_theme));
483 RrSurfaceShow(self->s_desk);
484 x += RrThemeButtonSize(ob_theme) + ob_theme->bevel + 1;
489 RrSurfaceSetArea(self->s_shade, x, y,
490 RrThemeButtonSize(ob_theme),
491 RrThemeButtonSize(ob_theme));
492 RrSurfaceShow(self->s_shade);
493 x += RrThemeButtonSize(ob_theme) + ob_theme->bevel + 1;
498 RrSurfaceSetArea(self->s_iconify, x, y,
499 RrThemeButtonSize(ob_theme),
500 RrThemeButtonSize(ob_theme));
501 RrSurfaceShow(self->s_iconify);
502 x += RrThemeButtonSize(ob_theme) + ob_theme->bevel + 1;
507 RrSurfaceSetArea(self->s_label, x, y - 1,
508 self->label_width, RrThemeLabelHeight(ob_theme));
509 RrSurfaceShow(self->s_label);
510 x += self->label_width + ob_theme->bevel + 1;
515 RrSurfaceSetArea(self->s_max, x, y,
516 RrThemeButtonSize(ob_theme),
517 RrThemeButtonSize(ob_theme));
518 RrSurfaceShow(self->s_max);
519 x += RrThemeButtonSize(ob_theme) + ob_theme->bevel + 1;
524 RrSurfaceSetArea(self->s_close, x, y,
525 RrThemeButtonSize(ob_theme),
526 RrThemeButtonSize(ob_theme));
527 RrSurfaceShow(self->s_close);
528 x += RrThemeButtonSize(ob_theme) + ob_theme->bevel + 1;
534 Context frame_context_from_string(char *name)
536 if (!g_ascii_strcasecmp("root", name))
538 else if (!g_ascii_strcasecmp("client", name))
539 return Context_Client;
540 else if (!g_ascii_strcasecmp("titlebar", name))
541 return Context_Titlebar;
542 else if (!g_ascii_strcasecmp("handle", name))
543 return Context_Handle;
544 else if (!g_ascii_strcasecmp("frame", name))
545 return Context_Frame;
546 else if (!g_ascii_strcasecmp("blcorner", name))
547 return Context_BLCorner;
548 else if (!g_ascii_strcasecmp("tlcorner", name))
549 return Context_TLCorner;
550 else if (!g_ascii_strcasecmp("brcorner", name))
551 return Context_BRCorner;
552 else if (!g_ascii_strcasecmp("trcorner", name))
553 return Context_TRCorner;
554 else if (!g_ascii_strcasecmp("maximize", name))
555 return Context_Maximize;
556 else if (!g_ascii_strcasecmp("alldesktops", name))
557 return Context_AllDesktops;
558 else if (!g_ascii_strcasecmp("shade", name))
559 return Context_Shade;
560 else if (!g_ascii_strcasecmp("iconify", name))
561 return Context_Iconify;
562 else if (!g_ascii_strcasecmp("icon", name))
564 else if (!g_ascii_strcasecmp("close", name))
565 return Context_Close;
569 Context frame_context(Client *client, Window win)
573 if (win == ob_root) return Context_Root;
574 if (client == NULL) return Context_None;
575 if (win == client->window) return Context_Client;
577 self = client->frame;
578 if (win == self->window) return Context_Frame;
579 if (win == self->plate) return Context_Client;
580 if (win == self->w_title) return Context_Titlebar;
581 if (win == self->w_label) return Context_Titlebar;
582 if (win == self->w_handle) return Context_Handle;
583 if (win == self->w_lgrip) return Context_BLCorner;
584 if (win == self->w_rgrip) return Context_BRCorner;
585 if (win == self->w_max) return Context_Maximize;
586 if (win == self->w_iconify)return Context_Iconify;
587 if (win == self->w_close) return Context_Close;
588 if (win == self->w_icon) return Context_Icon;
589 if (win == self->w_desk) return Context_AllDesktops;
590 if (win == self->w_shade) return Context_Shade;
595 void frame_client_gravity(Frame *self, int *x, int *y)
598 switch (self->client->gravity) {
600 case NorthWestGravity:
601 case SouthWestGravity:
608 *x -= (self->size.left + self->size.right) / 2;
611 case NorthEastGravity:
612 case SouthEastGravity:
614 *x -= self->size.left + self->size.right;
619 *x -= self->size.left;
624 switch (self->client->gravity) {
626 case NorthWestGravity:
627 case NorthEastGravity:
634 *y -= (self->size.top + self->size.bottom) / 2;
637 case SouthWestGravity:
638 case SouthEastGravity:
640 *y -= self->size.top + self->size.bottom;
645 *y -= self->size.top;
650 void frame_frame_gravity(Frame *self, int *x, int *y)
653 switch (self->client->gravity) {
655 case NorthWestGravity:
657 case SouthWestGravity:
662 *x += (self->size.left + self->size.right) / 2;
664 case NorthEastGravity:
666 case SouthEastGravity:
667 *x += self->size.left + self->size.right;
671 *x += self->size.left;
676 switch (self->client->gravity) {
678 case NorthWestGravity:
680 case SouthWestGravity:
685 *y += (self->size.top + self->size.bottom) / 2;
687 case NorthEastGravity:
689 case SouthEastGravity:
690 *y += self->size.top + self->size.bottom;
694 *y += self->size.top;