3 #include "extensions.h"
4 #include "framerender.h"
5 #include "render2/theme.h"
7 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask)
8 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
9 ButtonPressMask | ButtonReleaseMask | ExposureMask)
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 RrInstanceDepth(ob_render_inst), InputOutput,
28 RrInstanceVisual(ob_render_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);
51 self->s_frame = RrSurfaceNew(ob_render_inst, 0, self->window, 0);
52 self->s_title = RrSurfaceNewChild(0, self->s_frame, 0);
53 self->s_label = RrSurfaceNewChild(0, self->s_title, 0);
54 self->s_max = RrSurfaceNewChild(0, self->s_title, 0);
55 self->s_close = RrSurfaceNewChild(0, self->s_title, 0);
56 self->s_desk = RrSurfaceNewChild(0, self->s_title, 0);
57 self->s_shade = RrSurfaceNewChild(0, self->s_title, 0);
58 self->s_iconify = RrSurfaceNewChild(0, self->s_title, 0);
59 self->s_icon = RrSurfaceNewChild(0, self->s_title, 0);
60 self->s_handle = RrSurfaceNewChild(0, self->s_frame, 0);
61 self->s_lgrip = RrSurfaceNewChild(0, self->s_handle, 0);
62 self->s_rgrip = RrSurfaceNewChild(0, self->s_handle, 0);
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 XMapWindow(ob_display, self->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_frame);
120 XDestroyWindow(ob_display, self->plate);
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 XMoveResizeWindow(ob_display, self->plate,
246 self->size.left - self->cbwidth,
247 self->size.top - self->cbwidth,
248 self->client->area.width,
249 self->client->area.height);
250 /* when the client has StaticGravity, it likes to move around. */
251 XMoveWindow(ob_display, self->client->window, 0, 0);
254 /* shading can change without being moved or resized */
255 RECT_SET_SIZE(self->area,
256 self->client->area.width +
257 self->size.left + self->size.right,
258 (self->client->shaded ? RrThemeTitleHeight(ob_theme) :
259 self->client->area.height +
260 self->size.top + self->size.bottom));
263 /* find the new coordinates, done after setting the frame.size, for
264 frame_client_gravity. */
265 self->area.x = self->client->area.x;
266 self->area.y = self->client->area.y;
267 frame_client_gravity((Frame*)self,
268 &self->area.x, &self->area.y);
271 /* move and resize the top level frame.
272 shading can change without being moved or resized */
273 RrSurfaceSetArea(self->s_frame, self->area.x, self->area.y,
274 self->area.width, self->area.height);
277 framerender_frame(self);
279 frame_adjust_shape(self);
283 void frame_adjust_state(Frame *self)
285 framerender_frame(self);
288 void frame_adjust_focus(Frame *self, gboolean hilite)
290 self->focused = hilite;
291 framerender_frame(self);
294 void frame_adjust_title(Frame *self)
296 framerender_frame(self);
299 void frame_adjust_icon(Frame *self)
301 framerender_frame(self);
304 void frame_grab_client(Frame *self, Client *client)
306 self->client = client;
308 /* reparent the client to the frame */
309 XReparentWindow(ob_display, client->window, self->plate, 0, 0);
311 When reparenting the client window, it is usually not mapped yet, since
312 this occurs from a MapRequest. However, in the case where Openbox is
313 starting up, the window is already mapped, so we'll see unmap events for
314 it. There are 2 unmap events generated that we see, one with the 'event'
315 member set the root window, and one set to the client, but both get
316 handled and need to be ignored.
318 if (ob_state == State_Starting)
319 client->ignore_unmaps += 2;
321 /* select the event mask on the client's parent (to receive config/map
322 req's) the ButtonPress is to catch clicks on the client border */
323 XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
325 /* map the client so it maps when the frame does */
326 XMapWindow(ob_display, client->window);
328 frame_adjust_area(self, TRUE, TRUE);
330 /* set all the windows for the frame in the window_map */
331 g_hash_table_insert(window_map, &self->window, client);
332 g_hash_table_insert(window_map, &self->plate, client);
333 g_hash_table_insert(window_map, &self->w_title, client);
334 g_hash_table_insert(window_map, &self->w_label, client);
335 g_hash_table_insert(window_map, &self->w_max, client);
336 g_hash_table_insert(window_map, &self->w_close, client);
337 g_hash_table_insert(window_map, &self->w_desk, client);
338 g_hash_table_insert(window_map, &self->w_shade, client);
339 g_hash_table_insert(window_map, &self->w_iconify, client);
340 g_hash_table_insert(window_map, &self->w_icon, client);
341 g_hash_table_insert(window_map, &self->w_handle, client);
342 g_hash_table_insert(window_map, &self->w_lgrip, client);
343 g_hash_table_insert(window_map, &self->w_rgrip, client);
346 void frame_release_client(Frame *self, Client *client)
350 g_assert(self->client == client);
352 /* check if the app has already reparented its window away */
353 if (XCheckTypedWindowEvent(ob_display, client->window,
354 ReparentNotify, &ev)) {
355 XPutBackEvent(ob_display, &ev);
357 /* re-map the window since the unmanaging process unmaps it */
359 /* XXX ... um no it doesnt it unmaps its parent, the window itself
360 retains its mapped state, no?! XXX
361 XMapWindow(ob_display, client->window); */
363 /* according to the ICCCM - if the client doesn't reparent itself,
364 then we will reparent the window to root for them */
365 XReparentWindow(ob_display, client->window, ob_root,
370 /* remove all the windows for the frame from the window_map */
371 g_hash_table_remove(window_map, &self->window);
372 g_hash_table_remove(window_map, &self->plate);
373 g_hash_table_remove(window_map, &self->w_title);
374 g_hash_table_remove(window_map, &self->w_label);
375 g_hash_table_remove(window_map, &self->w_max);
376 g_hash_table_remove(window_map, &self->w_close);
377 g_hash_table_remove(window_map, &self->w_desk);
378 g_hash_table_remove(window_map, &self->w_shade);
379 g_hash_table_remove(window_map, &self->w_icon);
380 g_hash_table_remove(window_map, &self->w_iconify);
381 g_hash_table_remove(window_map, &self->w_handle);
382 g_hash_table_remove(window_map, &self->w_lgrip);
383 g_hash_table_remove(window_map, &self->w_rgrip);
388 static void layout_title(Frame *self)
392 gboolean n, d, i, l, m, c, s;
394 n = d = i = l = m = c = s = FALSE;
396 /* figure out whats being shown, and the width of the label */
397 self->label_width = self->width - (ob_theme->bevel + 1) * 2;
398 for (lc = ob_theme->title_layout; *lc != '\0'; ++lc) {
401 if (!(self->client->decorations & Decor_Icon)) break;
402 if (n) { *lc = ' '; break; } /* rm duplicates */
404 self->label_width -= RrThemeButtonSize(ob_theme) + 2 +
408 if (!(self->client->decorations & Decor_AllDesktops)) break;
409 if (d) { *lc = ' '; break; } /* rm duplicates */
411 self->label_width -= RrThemeButtonSize(ob_theme) +
415 if (!(self->client->decorations & Decor_Shade)) break;
416 if (s) { *lc = ' '; break; } /* rm duplicates */
418 self->label_width -= RrThemeButtonSize(ob_theme) +
422 if (!(self->client->decorations & Decor_Iconify)) break;
423 if (i) { *lc = ' '; break; } /* rm duplicates */
425 self->label_width -= RrThemeButtonSize(ob_theme) +
429 if (l) { *lc = ' '; break; } /* rm duplicates */
433 if (!(self->client->decorations & Decor_Maximize)) break;
434 if (m) { *lc = ' '; break; } /* rm duplicates */
436 self->label_width -= RrThemeButtonSize(ob_theme) +
440 if (!(self->client->decorations & Decor_Close)) break;
441 if (c) { *lc = ' '; break; } /* rm duplicates */
443 self->label_width -= RrThemeButtonSize(ob_theme) +
448 if (self->label_width < 1) self->label_width = 1;
450 if (!n) RrSurfaceHide(self->s_icon);
451 if (!d) RrSurfaceHide(self->s_desk);
452 if (!s) RrSurfaceHide(self->s_shade);
453 if (!i) RrSurfaceHide(self->s_iconify);
454 if (!l) RrSurfaceHide(self->s_label);
455 if (!m) RrSurfaceHide(self->s_max);
456 if (!c) RrSurfaceHide(self->s_close);
459 x = ob_theme->bevel + 1;
460 for (lc = ob_theme->title_layout; *lc != '\0'; ++lc) {
465 RrSurfaceSetArea(self->s_icon, x, ob_theme->bevel,
466 RrThemeButtonSize(ob_theme) + 2,
467 RrThemeButtonSize(ob_theme) + 2);
468 RrSurfaceShow(self->s_icon);
469 x += RrThemeButtonSize(ob_theme) + 2 + ob_theme->bevel + 1;
474 RrSurfaceSetArea(self->s_desk, x, ob_theme->bevel + 1,
475 RrThemeButtonSize(ob_theme),
476 RrThemeButtonSize(ob_theme));
477 RrSurfaceShow(self->s_desk);
478 x += RrThemeButtonSize(ob_theme) + ob_theme->bevel + 1;
483 RrSurfaceSetArea(self->s_shade, x, ob_theme->bevel + 1,
484 RrThemeButtonSize(ob_theme),
485 RrThemeButtonSize(ob_theme));
486 RrSurfaceShow(self->s_shade);
487 x += RrThemeButtonSize(ob_theme) + ob_theme->bevel + 1;
492 RrSurfaceSetArea(self->s_iconify, x, ob_theme->bevel + 1,
493 RrThemeButtonSize(ob_theme),
494 RrThemeButtonSize(ob_theme));
495 RrSurfaceShow(self->s_iconify);
496 x += RrThemeButtonSize(ob_theme) + ob_theme->bevel + 1;
501 RrSurfaceSetArea(self->s_label, x, ob_theme->bevel,
502 self->label_width, RrThemeLabelHeight(ob_theme));
503 RrSurfaceShow(self->s_label);
504 x += self->label_width + ob_theme->bevel + 1;
509 RrSurfaceSetArea(self->s_max, x, ob_theme->bevel + 1,
510 RrThemeButtonSize(ob_theme),
511 RrThemeButtonSize(ob_theme));
512 RrSurfaceShow(self->s_max);
513 x += RrThemeButtonSize(ob_theme) + ob_theme->bevel + 1;
518 RrSurfaceSetArea(self->s_close, x, ob_theme->bevel + 1,
519 RrThemeButtonSize(ob_theme),
520 RrThemeButtonSize(ob_theme));
521 RrSurfaceShow(self->s_close);
522 x += RrThemeButtonSize(ob_theme) + ob_theme->bevel + 1;
528 Context frame_context_from_string(char *name)
530 if (!g_ascii_strcasecmp("root", name))
532 else if (!g_ascii_strcasecmp("client", name))
533 return Context_Client;
534 else if (!g_ascii_strcasecmp("titlebar", name))
535 return Context_Titlebar;
536 else if (!g_ascii_strcasecmp("handle", name))
537 return Context_Handle;
538 else if (!g_ascii_strcasecmp("frame", name))
539 return Context_Frame;
540 else if (!g_ascii_strcasecmp("blcorner", name))
541 return Context_BLCorner;
542 else if (!g_ascii_strcasecmp("tlcorner", name))
543 return Context_TLCorner;
544 else if (!g_ascii_strcasecmp("brcorner", name))
545 return Context_BRCorner;
546 else if (!g_ascii_strcasecmp("trcorner", name))
547 return Context_TRCorner;
548 else if (!g_ascii_strcasecmp("maximize", name))
549 return Context_Maximize;
550 else if (!g_ascii_strcasecmp("alldesktops", name))
551 return Context_AllDesktops;
552 else if (!g_ascii_strcasecmp("shade", name))
553 return Context_Shade;
554 else if (!g_ascii_strcasecmp("iconify", name))
555 return Context_Iconify;
556 else if (!g_ascii_strcasecmp("icon", name))
558 else if (!g_ascii_strcasecmp("close", name))
559 return Context_Close;
563 Context frame_context(Client *client, Window win)
567 if (win == ob_root) return Context_Root;
568 if (client == NULL) return Context_None;
569 if (win == client->window) return Context_Client;
571 self = client->frame;
572 if (win == self->window) return Context_Frame;
573 if (win == self->plate) return Context_Client;
574 if (win == self->w_title) return Context_Titlebar;
575 if (win == self->w_label) return Context_Titlebar;
576 if (win == self->w_handle) return Context_Handle;
577 if (win == self->w_lgrip) return Context_BLCorner;
578 if (win == self->w_rgrip) return Context_BRCorner;
579 if (win == self->w_max) return Context_Maximize;
580 if (win == self->w_iconify)return Context_Iconify;
581 if (win == self->w_close) return Context_Close;
582 if (win == self->w_icon) return Context_Icon;
583 if (win == self->w_desk) return Context_AllDesktops;
584 if (win == self->w_shade) return Context_Shade;
589 void frame_client_gravity(Frame *self, int *x, int *y)
592 switch (self->client->gravity) {
594 case NorthWestGravity:
595 case SouthWestGravity:
602 *x -= (self->size.left + self->size.right) / 2;
605 case NorthEastGravity:
606 case SouthEastGravity:
608 *x -= self->size.left + self->size.right;
613 *x -= self->size.left;
618 switch (self->client->gravity) {
620 case NorthWestGravity:
621 case NorthEastGravity:
628 *y -= (self->size.top + self->size.bottom) / 2;
631 case SouthWestGravity:
632 case SouthEastGravity:
634 *y -= self->size.top + self->size.bottom;
639 *y -= self->size.top;
644 void frame_frame_gravity(Frame *self, int *x, int *y)
647 switch (self->client->gravity) {
649 case NorthWestGravity:
651 case SouthWestGravity:
656 *x += (self->size.left + self->size.right) / 2;
658 case NorthEastGravity:
660 case SouthEastGravity:
661 *x += self->size.left + self->size.right;
665 *x += self->size.left;
670 switch (self->client->gravity) {
672 case NorthWestGravity:
674 case SouthWestGravity:
679 *y += (self->size.top + self->size.bottom) / 2;
681 case NorthEastGravity:
683 case SouthEastGravity:
684 *y += self->size.top + self->size.bottom;
688 *y += self->size.top;