3 #include "extensions.h"
4 #include "framerender.h"
6 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask)
7 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
8 ButtonPressMask | ButtonReleaseMask)
9 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
10 ButtonMotionMask | ExposureMask)
13 static int theme_bwidth = 3;
14 static int theme_cbwidth = 3;
15 static int theme_title_height = 8;
25 static Window createWindow(Window parent, unsigned long mask,
26 XSetWindowAttributes *attrib)
28 return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
29 RrInstanceDepth(ob_render_inst),
30 InputOutput, RrInstanceVisual(ob_render_inst),
37 XSetWindowAttributes attrib;
41 self = g_new(Frame, 1);
43 self->visible = FALSE;
45 /* create all of the decor windows */
46 mask = CWOverrideRedirect | CWEventMask;
47 attrib.event_mask = FRAME_EVENTMASK;
48 attrib.override_redirect = TRUE;
49 self->window = createWindow(ob_root, mask, &attrib);
50 XSetWindowBorderWidth(ob_display, self->window, 1);
53 self->plate = createWindow(self->window, mask, &attrib);
54 XMapWindow(ob_display, self->plate);
57 attrib.event_mask = ELEMENT_EVENTMASK;
59 self->framedecors = 2;
60 self->framedecor = g_new(FrameDecor, self->framedecors);
61 self->framedecor[0].obwin.type = Window_Decoration;
62 self->framedecor[0].window = createWindow(self->window, mask, &attrib);
63 self->framedecor[0].anchor = Decor_Top;
64 RECT_SET(self->framedecor[0].area, 0, 0, 150, 5);
65 self->framedecor[0].type = Decor_Titlebar;
66 self->framedecor[0].context = Context_Titlebar;
67 self->framedecor[0].sizetypex = Decor_Absolute;
68 self->framedecor[0].sizetypey = Decor_Relative;
69 self->framedecor[0].frame = self;
70 XSetWindowBorderWidth(ob_display, self->framedecor[0].window, 1);
71 XMapWindow(ob_display, self->framedecor[0].window);
73 self->framedecor[1].obwin.type = Window_Decoration;
74 self->framedecor[1].window = createWindow(self->window, mask, &attrib);
75 self->framedecor[1].anchor = Decor_Right;
76 RECT_SET(self->framedecor[1].area, 0, 0, 10, 100);
77 self->framedecor[1].type = Decor_Titlebar;
78 self->framedecor[1].context = Context_Titlebar;
79 self->framedecor[1].sizetypex = Decor_Absolute;
80 self->framedecor[1].sizetypey = Decor_Relative;
81 self->framedecor[1].frame = self;
82 XSetWindowBorderWidth(ob_display, self->framedecor[1].window, 1);
83 XMapWindow(ob_display, self->framedecor[1].window);
85 self->focused = FALSE;
87 self->max_press = self->close_press = self->desk_press =
88 self->iconify_press = self->shade_press = FALSE;
92 static void frame_free(Frame *self)
95 XDestroyWindow(ob_display, self->window);
100 void frame_show(Frame *self)
102 if (!self->visible) {
103 self->visible = TRUE;
104 XMapWindow(ob_display, self->window);
105 XSync(ob_display, FALSE);
109 void frame_hide(Frame *self)
112 self->visible = FALSE;
113 self->client->ignore_unmaps++;
114 XUnmapWindow(ob_display, self->window);
118 void frame_adjust_shape(Frame *self)
120 #ifdef SHAPEAGAERGGREA
124 if (!self->client->shaped) {
125 /* clear the shape on the frame window */
126 XShapeCombineMask(ob_display, self->window, ShapeBounding,
127 self->innersize.left,
131 /* make the frame's shape match the clients */
132 XShapeCombineShape(ob_display, self->window, ShapeBounding,
133 self->innersize.left,
135 self->client->window,
136 ShapeBounding, ShapeSet);
139 if (self->client->decorations & Decor_Titlebar) {
140 xrect[0].x = -theme_bevel;
141 xrect[0].y = -theme_bevel;
142 xrect[0].width = self->width + self->bwidth * 2;
143 xrect[0].height = theme_title_height +
148 if (self->client->decorations & Decor_Handle) {
149 xrect[1].x = -theme_bevel;
150 xrect[1].y = FRAME_HANDLE_Y(self);
151 xrect[1].width = self->width + self->bwidth * 2;
152 xrect[1].height = theme_handle_height +
157 XShapeCombineRectangles(ob_display, self->window,
158 ShapeBounding, 0, 0, xrect, num,
159 ShapeUnion, Unsorted);
164 void frame_adjust_state(Frame *self)
166 framerender_frame(self);
169 void frame_adjust_focus(Frame *self, gboolean hilite)
171 self->focused = hilite;
172 framerender_frame(self);
175 void frame_adjust_title(Frame *self)
177 framerender_frame(self);
180 void frame_adjust_icon(Frame *self)
182 framerender_frame(self);
185 void frame_grab_client(Frame *self, Client *client)
188 self->client = client;
190 /* reparent the client to the frame */
191 XReparentWindow(ob_display, client->window, self->plate, 0, 0);
193 When reparenting the client window, it is usually not mapped yet, since
194 this occurs from a MapRequest. However, in the case where Openbox is
195 starting up, the window is already mapped, so we'll see unmap events for
196 it. There are 2 unmap events generated that we see, one with the 'event'
197 member set the root window, and one set to the client, but both get
198 handled and need to be ignored.
200 if (ob_state == State_Starting)
201 client->ignore_unmaps += 2;
203 /* select the event mask on the client's parent (to receive config/map
204 req's) the ButtonPress is to catch clicks on the client border */
205 XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
207 /* map the client so it maps when the frame does */
208 XMapWindow(ob_display, client->window);
210 frame_adjust_area(self, TRUE, TRUE);
212 /* set all the windows for the frame in the window_map */
213 g_hash_table_insert(window_map, &self->window, client);
214 g_hash_table_insert(window_map, &self->plate, client);
216 for (i = 0; i < self->framedecors; i++)
217 g_hash_table_insert(window_map, &self->framedecor[i].window,
218 &self->framedecor[i]);
221 void frame_release_client(Frame *self, Client *client)
226 g_assert(self->client == client);
228 /* check if the app has already reparented its window away */
229 if (XCheckTypedWindowEvent(ob_display, client->window,
230 ReparentNotify, &ev)) {
231 XPutBackEvent(ob_display, &ev);
233 /* re-map the window since the unmanaging process unmaps it */
235 /* XXX ... um no it doesnt it unmaps its parent, the window itself
236 retains its mapped state, no?! XXX
237 XMapWindow(ob_display, client->window); */
239 /* according to the ICCCM - if the client doesn't reparent itself,
240 then we will reparent the window to root for them */
241 XReparentWindow(ob_display, client->window, ob_root,
246 /* remove all the windows for the frame from the window_map */
247 g_hash_table_remove(window_map, &self->window);
248 g_hash_table_remove(window_map, &self->plate);
250 for (i = 0; i < self->framedecors; i++)
251 g_hash_table_remove(window_map, &self->framedecor[i].window);
256 Context frame_context_from_string(char *name)
258 if (!g_ascii_strcasecmp("root", name))
260 else if (!g_ascii_strcasecmp("client", name))
261 return Context_Client;
262 else if (!g_ascii_strcasecmp("titlebar", name))
263 return Context_Titlebar;
264 else if (!g_ascii_strcasecmp("handle", name))
265 return Context_Handle;
266 else if (!g_ascii_strcasecmp("frame", name))
267 return Context_Frame;
268 else if (!g_ascii_strcasecmp("blcorner", name))
269 return Context_BLCorner;
270 else if (!g_ascii_strcasecmp("tlcorner", name))
271 return Context_TLCorner;
272 else if (!g_ascii_strcasecmp("brcorner", name))
273 return Context_BRCorner;
274 else if (!g_ascii_strcasecmp("trcorner", name))
275 return Context_TRCorner;
276 else if (!g_ascii_strcasecmp("maximize", name))
277 return Context_Maximize;
278 else if (!g_ascii_strcasecmp("alldesktops", name))
279 return Context_AllDesktops;
280 else if (!g_ascii_strcasecmp("shade", name))
281 return Context_Shade;
282 else if (!g_ascii_strcasecmp("iconify", name))
283 return Context_Iconify;
284 else if (!g_ascii_strcasecmp("icon", name))
286 else if (!g_ascii_strcasecmp("close", name))
287 return Context_Close;
291 Context frame_context(Client *client, Window win)
294 if (win == ob_root) return Context_Root;
295 if (client == NULL) return Context_None;
296 if (win == client->window) return Context_Client;
298 obwin = g_hash_table_lookup(window_map, &win);
301 if (client->frame->window == win) {
302 g_print("frame context\n");
303 return Context_Frame;
305 if (client->frame->plate == win)
306 return Context_Client;
307 g_print("decoration clicked\n");
308 g_assert(WINDOW_IS_DECORATION(obwin));
309 return WINDOW_AS_DECORATION(obwin)->context;
312 void frame_client_gravity(Frame *self, int *x, int *y)
315 switch (self->client->gravity) {
317 case NorthWestGravity:
318 case SouthWestGravity:
325 *x -= (self->size.left + self->size.right) / 2;
328 case NorthEastGravity:
329 case SouthEastGravity:
331 *x -= self->size.left + self->size.right;
336 *x -= self->size.left;
341 switch (self->client->gravity) {
343 case NorthWestGravity:
344 case NorthEastGravity:
351 *y -= (self->size.top + self->size.bottom) / 2;
354 case SouthWestGravity:
355 case SouthEastGravity:
357 *y -= self->size.top + self->size.bottom;
362 *y -= self->size.top;
367 void frame_frame_gravity(Frame *self, int *x, int *y)
370 switch (self->client->gravity) {
372 case NorthWestGravity:
374 case SouthWestGravity:
379 *x += (self->size.left + self->size.right) / 2;
381 case NorthEastGravity:
383 case SouthEastGravity:
384 *x += self->size.left + self->size.right;
388 *x += self->size.left;
393 switch (self->client->gravity) {
395 case NorthWestGravity:
397 case SouthWestGravity:
402 *y += (self->size.top + self->size.bottom) / 2;
404 case NorthEastGravity:
406 case SouthEastGravity:
407 *y += self->size.top + self->size.bottom;
411 *y += self->size.top;
416 void decor_calculate_size(FrameDecor *d, Rect *r)
421 switch (d->sizetypex) {
423 r->width = d->area.width;
426 r->width = d->frame->client->area.width * d->area.width / 100;
430 switch (d->sizetypey) {
432 r->height = d->area.height;
435 r->height = d->frame->client->area.height * d->area.height / 100;
438 g_print("area of decoration is %d, %d, %d, %d\n", r->x, r->y, r->width,
442 void frame_adjust_area(Frame *self, gboolean moved, gboolean resized)
446 int i, le = 0, re = 0, te = 0, be = 0, temp, x, y;
449 for (i = 0; i < self->framedecors; i++) {
450 dec = &self->framedecor[i];
451 cr = &self->client->area;
452 decor_calculate_size(dec, &area);
453 if (dec->type & self->client->decorations)
454 switch (dec->anchor) {
456 temp = area.x + area.width;
457 if (temp > le) le = temp;
458 temp = area.y + area.height;
459 if (temp > te) te = temp;
463 temp = area.y + area.height;
464 if (temp > te) te = temp;
465 if (area.width > cr->width) {
466 temp = (area.width - cr->width)/2;
467 if (temp > re) re = temp;
468 if (temp > le) le = temp;
473 temp = area.x + area.width;
474 if (temp > re) re = temp;
475 temp = area.y + area.height;
476 if (temp > te) te = temp;
480 temp = area.x + area.width;
481 if (temp > le) le = temp;
482 if (area.height > cr->height) {
483 temp = (area.height - cr->height)/2;
484 if (temp > be) be = temp;
485 if (temp > te) te = temp;
490 temp = area.x + area.width;
491 if (temp > re) re = temp;
492 if (area.height > cr->height) {
493 temp = (area.height - cr->height)/2;
494 if (temp > be) be = temp;
495 if (temp > te) te = temp;
499 case Decor_BottomLeft:
500 temp = area.x + area.width;
501 if (temp > le) le = temp;
502 temp = area.y + area.height;
503 if (temp > be) be = temp;
507 temp = area.y + area.height;
508 if (temp > be) be = temp;
509 if (area.width > cr->width) {
510 temp = (area.width - cr->width)/2;
511 if (temp > re) re = temp;
512 if (temp > le) le = temp;
516 case Decor_BottomRight:
517 temp = area.x + area.width;
518 if (temp > re) re = temp;
519 temp = area.y + area.height;
520 if (temp > be) te = temp;
524 printf("frame extends by %d, %d, %d, %d\n", le, te, le, be);
526 if (self->client->decorations & Decor_Border) {
527 self->bwidth = theme_bwidth;
528 self->cbwidth = theme_cbwidth;
530 self->bwidth = self->cbwidth = 0;
532 STRUT_SET(self->size, self->cbwidth + le,
533 self->cbwidth + te, self->cbwidth + re, self->cbwidth + be);
535 self->width = self->client->area.width + self->cbwidth * 2 + re + le;
536 g_assert(self->width > 0);
539 STRUT_SET(self->size,
548 /* move and resize the plate */
549 XMoveResizeWindow(ob_display, self->plate,
550 self->size.left - self->cbwidth,
551 self->size.top - self->cbwidth,
552 self->client->area.width,
553 self->client->area.height);
555 /* when the client has StaticGravity, it likes to move around. */
556 XMoveWindow(ob_display, self->client->window, 0, 0);
559 /* shading can change without being moved or resized */
560 RECT_SET_SIZE(self->area,
561 self->client->area.width +
562 self->size.left + self->size.right,
563 (self->client->shaded ? theme_title_height + self->bwidth*2:
564 self->client->area.height +
565 self->size.top + self->size.bottom));
568 /* find the new coordinates, done after setting the frame.size, for
569 frame_client_gravity. */
570 self->area.x = self->client->area.x;
571 self->area.y = self->client->area.y;
572 frame_client_gravity((Frame*)self,
573 &self->area.x, &self->area.y);
577 for (i = 0; i < self->framedecors; i++) {
578 dec = &self->framedecor[i];
579 cr = &self->client->area;
580 decor_calculate_size(dec, &area);
581 if (!(dec->type & self->client->decorations))
582 XUnmapWindow(ob_display, dec->window);
584 switch (dec->anchor) {
586 x = self->size.left - area.x - area.width;
587 y = self->size.top - area.y - area.height;
588 XMoveResizeWindow(ob_display, dec->window, x, y,
594 x = cr->width/2 + self->size.left - area.x
596 y = self->size.top - area.y - area.height;
597 XMoveResizeWindow(ob_display, dec->window, x, y,
603 x = self->size.left + cr->width
605 y = self->size.top - area.y - area.height;
606 XMoveResizeWindow(ob_display, dec->window, x, y,
612 x = self->size.left - area.x
614 y = cr->height/2 + self->size.top - area.y
616 XMoveResizeWindow(ob_display, dec->window, x, y,
622 x = self->size.left + cr->width + area.x;
623 y = cr->height/2 + self->size.top - area.y
625 XMoveResizeWindow(ob_display, dec->window, x, y,
630 case Decor_BottomLeft:
631 x = self->size.left - area.x - area.width;
632 y = self->size.top + cr->height
633 - area.y - area.height;
634 XMoveResizeWindow(ob_display, dec->window, x, y,
640 x = cr->width/2 + self->size.left - area.x
642 y = self->size.top + cr->height
643 - area.y - area.height;
644 XMoveResizeWindow(ob_display, dec->window, x, y,
649 case Decor_BottomRight:
650 x = self->size.left + cr->width + area.x;
651 y = self->size.top + cr->height - area.y - area.height;
652 XMoveResizeWindow(ob_display, dec->window, x, y,
653 area.width, area.height);
657 /* move and resize the top level frame.
658 shading can change without being moved or resized */
659 XMoveResizeWindow(ob_display, self->window,
660 self->area.x, self->area.y,
662 self->area.height - self->bwidth * 2);
665 framerender_frame(self);
667 frame_adjust_shape(self);