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.
26 #include "framerender.h"
27 #include "focus_cycle.h"
28 #include "focus_cycle_indicator.h"
29 #include "moveresize.h"
32 #include "obrender/theme.h"
33 #include "obt/display.h"
34 #include "obt/xqueue.h"
37 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
38 ButtonPressMask | ButtonReleaseMask | \
39 SubstructureRedirectMask | FocusChangeMask)
40 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
41 ButtonMotionMask | PointerMotionMask | \
42 EnterWindowMask | LeaveWindowMask)
44 #define FRAME_ANIMATE_ICONIFY_TIME 150000 /* .15 seconds */
45 #define FRAME_ANIMATE_ICONIFY_STEP_TIME (1000 / 60) /* 60 Hz */
47 #define FRAME_HANDLE_Y(f) (f->size.top + f->client->area.height + f->cbwidth_b)
49 static void flash_done(gpointer data);
50 static gboolean flash_timeout(gpointer data);
52 static void layout_title(ObFrame *self);
53 static void set_theme_statics(ObFrame *self);
54 static void free_theme_statics(ObFrame *self);
55 static gboolean frame_animate_iconify(gpointer self);
56 static void frame_adjust_cursors(ObFrame *self);
58 static Window createWindow(Window parent, Visual *visual,
59 gulong mask, XSetWindowAttributes *attrib)
61 return XCreateWindow(obt_display, parent, 0, 0, 1, 1, 0,
62 (visual ? 32 : RrDepth(ob_rr_inst)), InputOutput,
63 (visual ? visual : RrVisual(ob_rr_inst)),
68 static Visual *check_32bit_client(ObClient *c)
70 XWindowAttributes wattrib;
73 /* we're already running at 32 bit depth, yay. we don't need to use their
75 if (RrDepth(ob_rr_inst) == 32)
78 ret = XGetWindowAttributes(obt_display, c->window, &wattrib);
79 g_assert(ret != BadDrawable);
80 g_assert(ret != BadWindow);
82 if (wattrib.depth == 32)
83 return wattrib.visual;
87 ObFrame *frame_new(ObClient *client)
89 XSetWindowAttributes attrib;
94 self = g_slice_new0(ObFrame);
95 self->client = client;
97 visual = check_32bit_client(client);
99 /* create the non-visible decor windows */
103 /* client has a 32-bit visual */
104 mask = CWColormap | CWBackPixel | CWBorderPixel;
105 /* create a colormap with the visual */
106 self->colormap = attrib.colormap =
107 XCreateColormap(obt_display, obt_root(ob_screen),
109 attrib.background_pixel = BlackPixel(obt_display, ob_screen);
110 attrib.border_pixel = BlackPixel(obt_display, ob_screen);
112 self->window = createWindow(obt_root(ob_screen), visual,
115 /* create the visible decor windows */
119 /* client has a 32-bit visual */
120 mask = CWColormap | CWBackPixel | CWBorderPixel;
121 attrib.colormap = RrColormap(ob_rr_inst);
124 self->backback = createWindow(self->window, NULL, mask, &attrib);
125 self->backfront = createWindow(self->backback, NULL, mask, &attrib);
128 attrib.event_mask = ELEMENT_EVENTMASK;
129 self->innerleft = createWindow(self->window, NULL, mask, &attrib);
130 self->innertop = createWindow(self->window, NULL, mask, &attrib);
131 self->innerright = createWindow(self->window, NULL, mask, &attrib);
132 self->innerbottom = createWindow(self->window, NULL, mask, &attrib);
134 self->innerblb = createWindow(self->innerbottom, NULL, mask, &attrib);
135 self->innerbrb = createWindow(self->innerbottom, NULL, mask, &attrib);
136 self->innerbll = createWindow(self->innerleft, NULL, mask, &attrib);
137 self->innerbrr = createWindow(self->innerright, NULL, mask, &attrib);
139 self->title = createWindow(self->window, NULL, mask, &attrib);
140 self->titleleft = createWindow(self->window, NULL, mask, &attrib);
141 self->titletop = createWindow(self->window, NULL, mask, &attrib);
142 self->titletopleft = createWindow(self->window, NULL, mask, &attrib);
143 self->titletopright = createWindow(self->window, NULL, mask, &attrib);
144 self->titleright = createWindow(self->window, NULL, mask, &attrib);
145 self->titlebottom = createWindow(self->window, NULL, mask, &attrib);
147 self->topresize = createWindow(self->title, NULL, mask, &attrib);
148 self->tltresize = createWindow(self->title, NULL, mask, &attrib);
149 self->tllresize = createWindow(self->title, NULL, mask, &attrib);
150 self->trtresize = createWindow(self->title, NULL, mask, &attrib);
151 self->trrresize = createWindow(self->title, NULL, mask, &attrib);
153 self->left = createWindow(self->window, NULL, mask, &attrib);
154 self->right = createWindow(self->window, NULL, mask, &attrib);
156 self->label = createWindow(self->title, NULL, mask, &attrib);
157 self->max = createWindow(self->title, NULL, mask, &attrib);
158 self->close = createWindow(self->title, NULL, mask, &attrib);
159 self->desk = createWindow(self->title, NULL, mask, &attrib);
160 self->shade = createWindow(self->title, NULL, mask, &attrib);
161 self->icon = createWindow(self->title, NULL, mask, &attrib);
162 self->iconify = createWindow(self->title, NULL, mask, &attrib);
164 self->handle = createWindow(self->window, NULL, mask, &attrib);
165 self->lgrip = createWindow(self->handle, NULL, mask, &attrib);
166 self->rgrip = createWindow(self->handle, NULL, mask, &attrib);
168 self->handleleft = createWindow(self->handle, NULL, mask, &attrib);
169 self->handleright = createWindow(self->handle, NULL, mask, &attrib);
171 self->handletop = createWindow(self->window, NULL, mask, &attrib);
172 self->handlebottom = createWindow(self->window, NULL, mask, &attrib);
173 self->lgripleft = createWindow(self->window, NULL, mask, &attrib);
174 self->lgriptop = createWindow(self->window, NULL, mask, &attrib);
175 self->lgripbottom = createWindow(self->window, NULL, mask, &attrib);
176 self->rgripright = createWindow(self->window, NULL, mask, &attrib);
177 self->rgriptop = createWindow(self->window, NULL, mask, &attrib);
178 self->rgripbottom = createWindow(self->window, NULL, mask, &attrib);
180 self->focused = FALSE;
182 /* the other stuff is shown based on decor settings */
183 XMapWindow(obt_display, self->label);
184 XMapWindow(obt_display, self->backback);
185 XMapWindow(obt_display, self->backfront);
187 self->max_press = self->close_press = self->desk_press =
188 self->iconify_press = self->shade_press = FALSE;
189 self->max_hover = self->close_hover = self->desk_hover =
190 self->iconify_hover = self->shade_hover = FALSE;
192 /* make sure the size will be different the first time, so the extent hints
194 STRUT_SET(self->oldsize, -1, -1, -1, -1);
196 set_theme_statics(self);
201 static void set_theme_statics(ObFrame *self)
203 /* set colors/appearance/sizes for stuff that doesn't change */
204 XResizeWindow(obt_display, self->max,
205 ob_rr_theme->button_size, ob_rr_theme->button_size);
206 XResizeWindow(obt_display, self->iconify,
207 ob_rr_theme->button_size, ob_rr_theme->button_size);
208 XResizeWindow(obt_display, self->icon,
209 ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
210 XResizeWindow(obt_display, self->close,
211 ob_rr_theme->button_size, ob_rr_theme->button_size);
212 XResizeWindow(obt_display, self->desk,
213 ob_rr_theme->button_size, ob_rr_theme->button_size);
214 XResizeWindow(obt_display, self->shade,
215 ob_rr_theme->button_size, ob_rr_theme->button_size);
216 XResizeWindow(obt_display, self->tltresize,
217 ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
218 XResizeWindow(obt_display, self->trtresize,
219 ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
220 XResizeWindow(obt_display, self->tllresize,
221 ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
222 XResizeWindow(obt_display, self->trrresize,
223 ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
226 static void free_theme_statics(ObFrame *self)
230 void frame_free(ObFrame *self)
232 free_theme_statics(self);
234 XDestroyWindow(obt_display, self->window);
236 XFreeColormap(obt_display, self->colormap);
238 g_slice_free(ObFrame, self);
241 void frame_show(ObFrame *self)
243 if (!self->visible) {
244 self->visible = TRUE;
245 framerender_frame(self);
246 /* Grab the server to make sure that the frame window is mapped before
247 the client gets its MapNotify, i.e. to make sure the client is
248 _visible_ when it gets MapNotify. */
250 XMapWindow(obt_display, self->client->window);
251 XMapWindow(obt_display, self->window);
256 void frame_hide(ObFrame *self)
259 self->visible = FALSE;
260 if (!frame_iconify_animating(self))
261 XUnmapWindow(obt_display, self->window);
262 /* we unmap the client itself so that we can get MapRequest
263 events, and because the ICCCM tells us to! */
264 XUnmapWindow(obt_display, self->client->window);
265 self->client->ignore_unmaps += 1;
269 void frame_adjust_theme(ObFrame *self)
271 free_theme_statics(self);
272 set_theme_statics(self);
276 void frame_adjust_shape_kind(ObFrame *self, int kind)
282 shaped = (kind == ShapeBounding && self->client->shaped);
284 shaped |= (kind == ShapeInput && self->client->shaped_input);
288 /* clear the shape on the frame window */
289 XShapeCombineMask(obt_display, self->window, kind,
294 /* make the frame's shape match the clients */
295 XShapeCombineShape(obt_display, self->window, kind,
298 self->client->window,
302 if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
305 xrect[0].width = self->area.width;
306 xrect[0].height = self->size.top;
310 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
311 ob_rr_theme->handle_height > 0)
314 xrect[1].y = FRAME_HANDLE_Y(self);
315 xrect[1].width = self->area.width;
316 xrect[1].height = ob_rr_theme->handle_height +
321 XShapeCombineRectangles(obt_display, self->window,
322 ShapeBounding, 0, 0, xrect, num,
323 ShapeUnion, Unsorted);
328 void frame_adjust_shape(ObFrame *self)
331 frame_adjust_shape_kind(self, ShapeBounding);
333 frame_adjust_shape_kind(self, ShapeInput);
338 void frame_adjust_area(ObFrame *self, gboolean moved,
339 gboolean resized, gboolean fake)
342 /* do this before changing the frame's status like max_horz max_vert */
343 frame_adjust_cursors(self);
345 self->functions = self->client->functions;
346 self->decorations = self->client->decorations;
347 self->max_horz = self->client->max_horz;
348 self->max_vert = self->client->max_vert;
349 self->shaded = self->client->shaded;
351 if (self->decorations & OB_FRAME_DECOR_BORDER)
352 self->bwidth = self->client->undecorated ?
353 ob_rr_theme->ubwidth : ob_rr_theme->fbwidth;
357 if (self->decorations & OB_FRAME_DECOR_BORDER &&
358 !self->client->undecorated)
360 self->cbwidth_l = self->cbwidth_r = ob_rr_theme->cbwidthx;
361 self->cbwidth_t = self->cbwidth_b = ob_rr_theme->cbwidthy;
363 self->cbwidth_l = self->cbwidth_t =
364 self->cbwidth_r = self->cbwidth_b = 0;
366 if (self->max_horz) {
367 self->cbwidth_l = self->cbwidth_r = 0;
368 self->width = self->client->area.width;
372 self->width = self->client->area.width +
373 self->cbwidth_l + self->cbwidth_r;
375 /* some elements are sized based of the width, so don't let them have
377 self->width = MAX(self->width,
378 (ob_rr_theme->grip_width + self->bwidth) * 2 + 1);
380 STRUT_SET(self->size,
381 self->cbwidth_l + (!self->max_horz ? self->bwidth : 0),
383 (!self->max_horz || !self->max_vert ? self->bwidth : 0),
384 self->cbwidth_r + (!self->max_horz ? self->bwidth : 0),
386 (!self->max_horz || !self->max_vert ? self->bwidth : 0));
388 if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
389 self->size.top += ob_rr_theme->title_height + self->bwidth;
390 else if (self->max_horz && self->max_vert) {
391 /* A maximized and undecorated window needs a small border on the
392 top of the window to let the user still undecorate/unmaximize the
393 window via the client menu. */
394 /* XXX This size should probably be a theme option. */
398 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
399 ob_rr_theme->handle_height > 0)
401 self->size.bottom += ob_rr_theme->handle_height + self->bwidth;
404 /* position/size and map/unmap all the windows */
407 gint innercornerheight =
408 ob_rr_theme->grip_width - self->size.bottom;
410 if (self->cbwidth_l) {
411 XMoveResizeWindow(obt_display, self->innerleft,
412 self->size.left - self->cbwidth_l,
414 self->cbwidth_l, self->client->area.height);
416 XMapWindow(obt_display, self->innerleft);
418 XUnmapWindow(obt_display, self->innerleft);
420 if (self->cbwidth_l && innercornerheight > 0) {
421 XMoveResizeWindow(obt_display, self->innerbll,
423 self->client->area.height -
424 (ob_rr_theme->grip_width -
427 ob_rr_theme->grip_width - self->size.bottom);
429 XMapWindow(obt_display, self->innerbll);
431 XUnmapWindow(obt_display, self->innerbll);
433 if (self->cbwidth_r) {
434 XMoveResizeWindow(obt_display, self->innerright,
435 self->size.left + self->client->area.width,
437 self->cbwidth_r, self->client->area.height);
439 XMapWindow(obt_display, self->innerright);
441 XUnmapWindow(obt_display, self->innerright);
443 if (self->cbwidth_r && innercornerheight > 0) {
444 XMoveResizeWindow(obt_display, self->innerbrr,
446 self->client->area.height -
447 (ob_rr_theme->grip_width -
450 ob_rr_theme->grip_width - self->size.bottom);
452 XMapWindow(obt_display, self->innerbrr);
454 XUnmapWindow(obt_display, self->innerbrr);
456 if (self->cbwidth_t) {
457 XMoveResizeWindow(obt_display, self->innertop,
458 self->size.left - self->cbwidth_l,
459 self->size.top - self->cbwidth_t,
460 self->client->area.width +
461 self->cbwidth_l + self->cbwidth_r,
464 XMapWindow(obt_display, self->innertop);
466 XUnmapWindow(obt_display, self->innertop);
468 if (self->cbwidth_b) {
469 XMoveResizeWindow(obt_display, self->innerbottom,
470 self->size.left - self->cbwidth_l,
471 self->size.top + self->client->area.height,
472 self->client->area.width +
473 self->cbwidth_l + self->cbwidth_r,
476 XMoveResizeWindow(obt_display, self->innerblb,
478 ob_rr_theme->grip_width + self->bwidth,
480 XMoveResizeWindow(obt_display, self->innerbrb,
481 self->client->area.width +
482 self->cbwidth_l + self->cbwidth_r -
483 (ob_rr_theme->grip_width + self->bwidth),
485 ob_rr_theme->grip_width + self->bwidth,
488 XMapWindow(obt_display, self->innerbottom);
489 XMapWindow(obt_display, self->innerblb);
490 XMapWindow(obt_display, self->innerbrb);
492 XUnmapWindow(obt_display, self->innerbottom);
493 XUnmapWindow(obt_display, self->innerblb);
494 XUnmapWindow(obt_display, self->innerbrb);
500 /* height of titleleft and titleright */
501 titlesides = (!self->max_horz ? ob_rr_theme->grip_width : 0);
503 XMoveResizeWindow(obt_display, self->titletop,
504 ob_rr_theme->grip_width + self->bwidth, 0,
505 /* width + bwidth*2 - bwidth*2 - grips*2 */
506 self->width - ob_rr_theme->grip_width * 2,
508 XMoveResizeWindow(obt_display, self->titletopleft,
510 ob_rr_theme->grip_width + self->bwidth,
512 XMoveResizeWindow(obt_display, self->titletopright,
513 self->client->area.width +
514 self->size.left + self->size.right -
515 ob_rr_theme->grip_width - self->bwidth,
517 ob_rr_theme->grip_width + self->bwidth,
520 if (titlesides > 0) {
521 XMoveResizeWindow(obt_display, self->titleleft,
525 XMoveResizeWindow(obt_display, self->titleright,
526 self->client->area.width +
527 self->size.left + self->size.right -
533 XMapWindow(obt_display, self->titleleft);
534 XMapWindow(obt_display, self->titleright);
536 XUnmapWindow(obt_display, self->titleleft);
537 XUnmapWindow(obt_display, self->titleright);
540 XMapWindow(obt_display, self->titletop);
541 XMapWindow(obt_display, self->titletopleft);
542 XMapWindow(obt_display, self->titletopright);
544 if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
545 XMoveResizeWindow(obt_display, self->titlebottom,
546 (self->max_horz ? 0 : self->bwidth),
547 ob_rr_theme->title_height + self->bwidth,
551 XMapWindow(obt_display, self->titlebottom);
553 XUnmapWindow(obt_display, self->titlebottom);
555 XUnmapWindow(obt_display, self->titlebottom);
557 XUnmapWindow(obt_display, self->titletop);
558 XUnmapWindow(obt_display, self->titletopleft);
559 XUnmapWindow(obt_display, self->titletopright);
560 XUnmapWindow(obt_display, self->titleleft);
561 XUnmapWindow(obt_display, self->titleright);
564 if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
565 XMoveResizeWindow(obt_display, self->title,
566 (self->max_horz ? 0 : self->bwidth),
568 self->width, ob_rr_theme->title_height);
570 XMapWindow(obt_display, self->title);
572 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
573 XMoveResizeWindow(obt_display, self->topresize,
574 ob_rr_theme->grip_width,
576 self->width - ob_rr_theme->grip_width *2,
577 ob_rr_theme->paddingy + 1);
579 XMoveWindow(obt_display, self->tltresize, 0, 0);
580 XMoveWindow(obt_display, self->tllresize, 0, 0);
581 XMoveWindow(obt_display, self->trtresize,
582 self->width - ob_rr_theme->grip_width, 0);
583 XMoveWindow(obt_display, self->trrresize,
584 self->width - ob_rr_theme->paddingx - 1, 0);
586 XMapWindow(obt_display, self->topresize);
587 XMapWindow(obt_display, self->tltresize);
588 XMapWindow(obt_display, self->tllresize);
589 XMapWindow(obt_display, self->trtresize);
590 XMapWindow(obt_display, self->trrresize);
592 XUnmapWindow(obt_display, self->topresize);
593 XUnmapWindow(obt_display, self->tltresize);
594 XUnmapWindow(obt_display, self->tllresize);
595 XUnmapWindow(obt_display, self->trtresize);
596 XUnmapWindow(obt_display, self->trrresize);
599 XUnmapWindow(obt_display, self->title);
602 if ((self->decorations & OB_FRAME_DECOR_TITLEBAR))
603 /* layout the title bar elements */
607 gint sidebwidth = self->max_horz ? 0 : self->bwidth;
609 if (self->bwidth && self->size.bottom) {
610 XMoveResizeWindow(obt_display, self->handlebottom,
611 ob_rr_theme->grip_width +
612 self->bwidth + sidebwidth,
613 self->size.top + self->client->area.height +
614 self->size.bottom - self->bwidth,
615 self->width - (ob_rr_theme->grip_width +
621 XMoveResizeWindow(obt_display, self->lgripleft,
624 self->client->area.height +
627 ob_rr_theme->grip_width :
628 self->size.bottom - self->cbwidth_b),
631 ob_rr_theme->grip_width :
632 self->size.bottom - self->cbwidth_b));
633 XMoveResizeWindow(obt_display, self->rgripright,
635 self->client->area.width +
636 self->size.right - self->bwidth,
638 self->client->area.height +
641 ob_rr_theme->grip_width :
642 self->size.bottom - self->cbwidth_b),
645 ob_rr_theme->grip_width :
646 self->size.bottom - self->cbwidth_b));
648 XMapWindow(obt_display, self->lgripleft);
649 XMapWindow(obt_display, self->rgripright);
651 XUnmapWindow(obt_display, self->lgripleft);
652 XUnmapWindow(obt_display, self->rgripright);
655 XMoveResizeWindow(obt_display, self->lgripbottom,
657 self->size.top + self->client->area.height +
658 self->size.bottom - self->bwidth,
659 ob_rr_theme->grip_width + self->bwidth,
661 XMoveResizeWindow(obt_display, self->rgripbottom,
662 self->size.left + self->client->area.width +
663 self->size.right - self->bwidth - sidebwidth-
664 ob_rr_theme->grip_width,
665 self->size.top + self->client->area.height +
666 self->size.bottom - self->bwidth,
667 ob_rr_theme->grip_width + self->bwidth,
670 XMapWindow(obt_display, self->handlebottom);
671 XMapWindow(obt_display, self->lgripbottom);
672 XMapWindow(obt_display, self->rgripbottom);
674 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
675 ob_rr_theme->handle_height > 0)
677 XMoveResizeWindow(obt_display, self->handletop,
678 ob_rr_theme->grip_width +
679 self->bwidth + sidebwidth,
680 FRAME_HANDLE_Y(self),
681 self->width - (ob_rr_theme->grip_width +
684 XMapWindow(obt_display, self->handletop);
686 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
687 XMoveResizeWindow(obt_display, self->handleleft,
688 ob_rr_theme->grip_width,
691 ob_rr_theme->handle_height);
692 XMoveResizeWindow(obt_display, self->handleright,
694 ob_rr_theme->grip_width -
698 ob_rr_theme->handle_height);
700 XMoveResizeWindow(obt_display, self->lgriptop,
702 FRAME_HANDLE_Y(self),
703 ob_rr_theme->grip_width +
706 XMoveResizeWindow(obt_display, self->rgriptop,
708 self->client->area.width +
709 self->size.right - self->bwidth -
710 sidebwidth - ob_rr_theme->grip_width,
711 FRAME_HANDLE_Y(self),
712 ob_rr_theme->grip_width +
716 XMapWindow(obt_display, self->handleleft);
717 XMapWindow(obt_display, self->handleright);
718 XMapWindow(obt_display, self->lgriptop);
719 XMapWindow(obt_display, self->rgriptop);
721 XUnmapWindow(obt_display, self->handleleft);
722 XUnmapWindow(obt_display, self->handleright);
723 XUnmapWindow(obt_display, self->lgriptop);
724 XUnmapWindow(obt_display, self->rgriptop);
727 XUnmapWindow(obt_display, self->handleleft);
728 XUnmapWindow(obt_display, self->handleright);
729 XUnmapWindow(obt_display, self->lgriptop);
730 XUnmapWindow(obt_display, self->rgriptop);
732 XUnmapWindow(obt_display, self->handletop);
735 XUnmapWindow(obt_display, self->handleleft);
736 XUnmapWindow(obt_display, self->handleright);
737 XUnmapWindow(obt_display, self->lgriptop);
738 XUnmapWindow(obt_display, self->rgriptop);
740 XUnmapWindow(obt_display, self->handletop);
742 XUnmapWindow(obt_display, self->handlebottom);
743 XUnmapWindow(obt_display, self->lgripleft);
744 XUnmapWindow(obt_display, self->rgripright);
745 XUnmapWindow(obt_display, self->lgripbottom);
746 XUnmapWindow(obt_display, self->rgripbottom);
749 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
750 ob_rr_theme->handle_height > 0)
752 XMoveResizeWindow(obt_display, self->handle,
754 FRAME_HANDLE_Y(self) + self->bwidth,
755 self->width, ob_rr_theme->handle_height);
756 XMapWindow(obt_display, self->handle);
758 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
759 XMoveResizeWindow(obt_display, self->lgrip,
761 ob_rr_theme->grip_width,
762 ob_rr_theme->handle_height);
763 XMoveResizeWindow(obt_display, self->rgrip,
764 self->width - ob_rr_theme->grip_width,
766 ob_rr_theme->grip_width,
767 ob_rr_theme->handle_height);
769 XMapWindow(obt_display, self->lgrip);
770 XMapWindow(obt_display, self->rgrip);
772 XUnmapWindow(obt_display, self->lgrip);
773 XUnmapWindow(obt_display, self->rgrip);
776 XUnmapWindow(obt_display, self->lgrip);
777 XUnmapWindow(obt_display, self->rgrip);
779 XUnmapWindow(obt_display, self->handle);
782 if (self->bwidth && !self->max_horz &&
783 (self->client->area.height + self->size.top +
784 self->size.bottom) > ob_rr_theme->grip_width * 2)
786 XMoveResizeWindow(obt_display, self->left,
788 self->bwidth + ob_rr_theme->grip_width,
790 self->client->area.height +
791 self->size.top + self->size.bottom -
792 ob_rr_theme->grip_width * 2);
794 XMapWindow(obt_display, self->left);
796 XUnmapWindow(obt_display, self->left);
798 if (self->bwidth && !self->max_horz &&
799 (self->client->area.height + self->size.top +
800 self->size.bottom) > ob_rr_theme->grip_width * 2)
802 XMoveResizeWindow(obt_display, self->right,
803 self->client->area.width + self->cbwidth_l +
804 self->cbwidth_r + self->bwidth,
805 self->bwidth + ob_rr_theme->grip_width,
807 self->client->area.height +
808 self->size.top + self->size.bottom -
809 ob_rr_theme->grip_width * 2);
811 XMapWindow(obt_display, self->right);
813 XUnmapWindow(obt_display, self->right);
815 XMoveResizeWindow(obt_display, self->backback,
816 self->size.left, self->size.top,
817 self->client->area.width,
818 self->client->area.height);
822 /* shading can change without being moved or resized */
823 RECT_SET_SIZE(self->area,
824 self->client->area.width +
825 self->size.left + self->size.right,
826 (self->client->shaded ?
827 ob_rr_theme->title_height + self->bwidth * 2:
828 self->client->area.height +
829 self->size.top + self->size.bottom));
831 if ((moved || resized) && !fake) {
832 /* find the new coordinates, done after setting the frame.size, for
833 frame_client_gravity. */
834 self->area.x = self->client->area.x;
835 self->area.y = self->client->area.y;
836 frame_client_gravity(self, &self->area.x, &self->area.y);
840 if (!frame_iconify_animating(self))
841 /* move and resize the top level frame.
842 shading can change without being moved or resized.
844 but don't do this during an iconify animation. it will be
845 reflected afterwards.
847 XMoveResizeWindow(obt_display, self->window,
853 /* when the client has StaticGravity, it likes to move around.
854 also this correctly positions the client when it maps.
855 this also needs to be run when the frame's decorations sizes change!
857 XMoveWindow(obt_display, self->client->window,
858 self->size.left, self->size.top);
861 self->need_render = TRUE;
862 framerender_frame(self);
863 frame_adjust_shape(self);
866 if (!STRUT_EQUAL(self->size, self->oldsize)) {
868 vals[0] = self->size.left;
869 vals[1] = self->size.right;
870 vals[2] = self->size.top;
871 vals[3] = self->size.bottom;
872 OBT_PROP_SETA32(self->client->window, NET_FRAME_EXTENTS,
874 OBT_PROP_SETA32(self->client->window, KDE_NET_WM_FRAME_STRUT,
876 self->oldsize = self->size;
879 /* if this occurs while we are focus cycling, the indicator needs to
881 if (focus_cycle_target == self->client)
882 focus_cycle_update_indicator(self->client);
884 if (resized && (self->decorations & OB_FRAME_DECOR_TITLEBAR) &&
887 XResizeWindow(obt_display, self->label, self->label_width,
888 ob_rr_theme->label_height);
892 static void frame_adjust_cursors(ObFrame *self)
894 if ((self->functions & OB_CLIENT_FUNC_RESIZE) !=
895 (self->client->functions & OB_CLIENT_FUNC_RESIZE) ||
896 self->max_horz != self->client->max_horz ||
897 self->max_vert != self->client->max_vert ||
898 self->shaded != self->client->shaded)
900 gboolean r = (self->client->functions & OB_CLIENT_FUNC_RESIZE) &&
901 !(self->client->max_horz && self->client->max_vert);
902 gboolean topbot = !self->client->max_vert;
903 gboolean sh = self->client->shaded;
904 XSetWindowAttributes a;
906 /* these ones turn off when max vert, and some when shaded */
907 a.cursor = ob_cursor(r && topbot && !sh ?
908 OB_CURSOR_NORTH : OB_CURSOR_NONE);
909 XChangeWindowAttributes(obt_display, self->topresize, CWCursor, &a);
910 XChangeWindowAttributes(obt_display, self->titletop, CWCursor, &a);
911 a.cursor = ob_cursor(r && topbot ? OB_CURSOR_SOUTH : OB_CURSOR_NONE);
912 XChangeWindowAttributes(obt_display, self->handle, CWCursor, &a);
913 XChangeWindowAttributes(obt_display, self->handletop, CWCursor, &a);
914 XChangeWindowAttributes(obt_display, self->handlebottom, CWCursor, &a);
915 XChangeWindowAttributes(obt_display, self->innerbottom, CWCursor, &a);
917 /* these ones change when shaded */
918 a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_WEST : OB_CURSOR_NORTHWEST) :
920 XChangeWindowAttributes(obt_display, self->titleleft, CWCursor, &a);
921 XChangeWindowAttributes(obt_display, self->tltresize, CWCursor, &a);
922 XChangeWindowAttributes(obt_display, self->tllresize, CWCursor, &a);
923 XChangeWindowAttributes(obt_display, self->titletopleft, CWCursor, &a);
924 a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_EAST : OB_CURSOR_NORTHEAST) :
926 XChangeWindowAttributes(obt_display, self->titleright, CWCursor, &a);
927 XChangeWindowAttributes(obt_display, self->trtresize, CWCursor, &a);
928 XChangeWindowAttributes(obt_display, self->trrresize, CWCursor, &a);
929 XChangeWindowAttributes(obt_display, self->titletopright, CWCursor,&a);
931 /* these ones are pretty static */
932 a.cursor = ob_cursor(r ? OB_CURSOR_WEST : OB_CURSOR_NONE);
933 XChangeWindowAttributes(obt_display, self->left, CWCursor, &a);
934 XChangeWindowAttributes(obt_display, self->innerleft, CWCursor, &a);
935 a.cursor = ob_cursor(r ? OB_CURSOR_EAST : OB_CURSOR_NONE);
936 XChangeWindowAttributes(obt_display, self->right, CWCursor, &a);
937 XChangeWindowAttributes(obt_display, self->innerright, CWCursor, &a);
938 a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHWEST : OB_CURSOR_NONE);
939 XChangeWindowAttributes(obt_display, self->lgrip, CWCursor, &a);
940 XChangeWindowAttributes(obt_display, self->handleleft, CWCursor, &a);
941 XChangeWindowAttributes(obt_display, self->lgripleft, CWCursor, &a);
942 XChangeWindowAttributes(obt_display, self->lgriptop, CWCursor, &a);
943 XChangeWindowAttributes(obt_display, self->lgripbottom, CWCursor, &a);
944 XChangeWindowAttributes(obt_display, self->innerbll, CWCursor, &a);
945 XChangeWindowAttributes(obt_display, self->innerblb, CWCursor, &a);
946 a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHEAST : OB_CURSOR_NONE);
947 XChangeWindowAttributes(obt_display, self->rgrip, CWCursor, &a);
948 XChangeWindowAttributes(obt_display, self->handleright, CWCursor, &a);
949 XChangeWindowAttributes(obt_display, self->rgripright, CWCursor, &a);
950 XChangeWindowAttributes(obt_display, self->rgriptop, CWCursor, &a);
951 XChangeWindowAttributes(obt_display, self->rgripbottom, CWCursor, &a);
952 XChangeWindowAttributes(obt_display, self->innerbrr, CWCursor, &a);
953 XChangeWindowAttributes(obt_display, self->innerbrb, CWCursor, &a);
957 void frame_adjust_client_area(ObFrame *self)
959 /* adjust the window which is there to prevent flashing on unmap */
960 XMoveResizeWindow(obt_display, self->backfront, 0, 0,
961 self->client->area.width,
962 self->client->area.height);
965 void frame_adjust_state(ObFrame *self)
967 self->need_render = TRUE;
968 framerender_frame(self);
971 void frame_adjust_focus(ObFrame *self, gboolean hilite)
973 ob_debug_type(OB_DEBUG_FOCUS,
974 "Frame for 0x%x has focus: %d",
975 self->client->window, hilite);
976 self->focused = hilite;
977 self->need_render = TRUE;
978 framerender_frame(self);
982 void frame_adjust_title(ObFrame *self)
984 self->need_render = TRUE;
985 framerender_frame(self);
988 void frame_adjust_icon(ObFrame *self)
990 self->need_render = TRUE;
991 framerender_frame(self);
994 void frame_grab_client(ObFrame *self)
996 /* DO NOT map the client window here. we used to do that, but it is bogus.
997 we need to set up the client's dimensions and everything before we
998 send a mapnotify or we create race conditions.
1001 /* reparent the client to the frame */
1002 XReparentWindow(obt_display, self->client->window, self->window, 0, 0);
1005 When reparenting the client window, it is usually not mapped yet, since
1006 this occurs from a MapRequest. However, in the case where Openbox is
1007 starting up, the window is already mapped, so we'll see an unmap event
1010 if (ob_state() == OB_STATE_STARTING)
1011 ++self->client->ignore_unmaps;
1013 /* select the event mask on the client's parent (to receive config/map
1014 req's) the ButtonPress is to catch clicks on the client border */
1015 XSelectInput(obt_display, self->window, FRAME_EVENTMASK);
1017 /* set all the windows for the frame in the window_map */
1018 window_add(&self->window, CLIENT_AS_WINDOW(self->client));
1019 window_add(&self->backback, CLIENT_AS_WINDOW(self->client));
1020 window_add(&self->backfront, CLIENT_AS_WINDOW(self->client));
1021 window_add(&self->innerleft, CLIENT_AS_WINDOW(self->client));
1022 window_add(&self->innertop, CLIENT_AS_WINDOW(self->client));
1023 window_add(&self->innerright, CLIENT_AS_WINDOW(self->client));
1024 window_add(&self->innerbottom, CLIENT_AS_WINDOW(self->client));
1025 window_add(&self->innerblb, CLIENT_AS_WINDOW(self->client));
1026 window_add(&self->innerbll, CLIENT_AS_WINDOW(self->client));
1027 window_add(&self->innerbrb, CLIENT_AS_WINDOW(self->client));
1028 window_add(&self->innerbrr, CLIENT_AS_WINDOW(self->client));
1029 window_add(&self->title, CLIENT_AS_WINDOW(self->client));
1030 window_add(&self->label, CLIENT_AS_WINDOW(self->client));
1031 window_add(&self->max, CLIENT_AS_WINDOW(self->client));
1032 window_add(&self->close, CLIENT_AS_WINDOW(self->client));
1033 window_add(&self->desk, CLIENT_AS_WINDOW(self->client));
1034 window_add(&self->shade, CLIENT_AS_WINDOW(self->client));
1035 window_add(&self->icon, CLIENT_AS_WINDOW(self->client));
1036 window_add(&self->iconify, CLIENT_AS_WINDOW(self->client));
1037 window_add(&self->handle, CLIENT_AS_WINDOW(self->client));
1038 window_add(&self->lgrip, CLIENT_AS_WINDOW(self->client));
1039 window_add(&self->rgrip, CLIENT_AS_WINDOW(self->client));
1040 window_add(&self->topresize, CLIENT_AS_WINDOW(self->client));
1041 window_add(&self->tltresize, CLIENT_AS_WINDOW(self->client));
1042 window_add(&self->tllresize, CLIENT_AS_WINDOW(self->client));
1043 window_add(&self->trtresize, CLIENT_AS_WINDOW(self->client));
1044 window_add(&self->trrresize, CLIENT_AS_WINDOW(self->client));
1045 window_add(&self->left, CLIENT_AS_WINDOW(self->client));
1046 window_add(&self->right, CLIENT_AS_WINDOW(self->client));
1047 window_add(&self->titleleft, CLIENT_AS_WINDOW(self->client));
1048 window_add(&self->titletop, CLIENT_AS_WINDOW(self->client));
1049 window_add(&self->titletopleft, CLIENT_AS_WINDOW(self->client));
1050 window_add(&self->titletopright, CLIENT_AS_WINDOW(self->client));
1051 window_add(&self->titleright, CLIENT_AS_WINDOW(self->client));
1052 window_add(&self->titlebottom, CLIENT_AS_WINDOW(self->client));
1053 window_add(&self->handleleft, CLIENT_AS_WINDOW(self->client));
1054 window_add(&self->handletop, CLIENT_AS_WINDOW(self->client));
1055 window_add(&self->handleright, CLIENT_AS_WINDOW(self->client));
1056 window_add(&self->handlebottom, CLIENT_AS_WINDOW(self->client));
1057 window_add(&self->lgripleft, CLIENT_AS_WINDOW(self->client));
1058 window_add(&self->lgriptop, CLIENT_AS_WINDOW(self->client));
1059 window_add(&self->lgripbottom, CLIENT_AS_WINDOW(self->client));
1060 window_add(&self->rgripright, CLIENT_AS_WINDOW(self->client));
1061 window_add(&self->rgriptop, CLIENT_AS_WINDOW(self->client));
1062 window_add(&self->rgripbottom, CLIENT_AS_WINDOW(self->client));
1065 static gboolean find_reparent(XEvent *e, gpointer data)
1067 const ObFrame *self = data;
1069 /* Find ReparentNotify events for the window that aren't being reparented into the
1070 frame, thus the client reparenting itself off the frame. */
1071 return e->type == ReparentNotify && e->xreparent.window == self->client->window &&
1072 e->xreparent.parent != self->window;
1075 void frame_release_client(ObFrame *self)
1077 /* if there was any animation going on, kill it */
1078 if (self->iconify_animation_timer)
1079 g_source_remove(self->iconify_animation_timer);
1081 /* check if the app has already reparented its window away */
1082 if (!xqueue_exists_local(find_reparent, self)) {
1083 /* according to the ICCCM - if the client doesn't reparent itself,
1084 then we will reparent the window to root for them */
1085 XReparentWindow(obt_display, self->client->window, obt_root(ob_screen),
1086 self->client->area.x, self->client->area.y);
1089 /* remove all the windows for the frame from the window_map */
1090 window_remove(self->window);
1091 window_remove(self->backback);
1092 window_remove(self->backfront);
1093 window_remove(self->innerleft);
1094 window_remove(self->innertop);
1095 window_remove(self->innerright);
1096 window_remove(self->innerbottom);
1097 window_remove(self->innerblb);
1098 window_remove(self->innerbll);
1099 window_remove(self->innerbrb);
1100 window_remove(self->innerbrr);
1101 window_remove(self->title);
1102 window_remove(self->label);
1103 window_remove(self->max);
1104 window_remove(self->close);
1105 window_remove(self->desk);
1106 window_remove(self->shade);
1107 window_remove(self->icon);
1108 window_remove(self->iconify);
1109 window_remove(self->handle);
1110 window_remove(self->lgrip);
1111 window_remove(self->rgrip);
1112 window_remove(self->topresize);
1113 window_remove(self->tltresize);
1114 window_remove(self->tllresize);
1115 window_remove(self->trtresize);
1116 window_remove(self->trrresize);
1117 window_remove(self->left);
1118 window_remove(self->right);
1119 window_remove(self->titleleft);
1120 window_remove(self->titletop);
1121 window_remove(self->titletopleft);
1122 window_remove(self->titletopright);
1123 window_remove(self->titleright);
1124 window_remove(self->titlebottom);
1125 window_remove(self->handleleft);
1126 window_remove(self->handletop);
1127 window_remove(self->handleright);
1128 window_remove(self->handlebottom);
1129 window_remove(self->lgripleft);
1130 window_remove(self->lgriptop);
1131 window_remove(self->lgripbottom);
1132 window_remove(self->rgripright);
1133 window_remove(self->rgriptop);
1134 window_remove(self->rgripbottom);
1136 if (self->flash_timer) g_source_remove(self->flash_timer);
1139 /* is there anything present between us and the label? */
1140 static gboolean is_button_present(ObFrame *self, const gchar *lc, gint dir) {
1141 for (; *lc != '\0' && lc >= config_title_layout; lc += dir) {
1142 if (*lc == ' ') continue; /* it was invalid */
1143 if (*lc == 'N' && self->decorations & OB_FRAME_DECOR_ICON)
1145 if (*lc == 'D' && self->decorations & OB_FRAME_DECOR_ALLDESKTOPS)
1147 if (*lc == 'S' && self->decorations & OB_FRAME_DECOR_SHADE)
1149 if (*lc == 'I' && self->decorations & OB_FRAME_DECOR_ICONIFY)
1151 if (*lc == 'M' && self->decorations & OB_FRAME_DECOR_MAXIMIZE)
1153 if (*lc == 'C' && self->decorations & OB_FRAME_DECOR_CLOSE)
1155 if (*lc == 'L') return FALSE;
1160 static void place_button(ObFrame *self, const char *lc, gint bwidth,
1162 gint *x, gint *button_on, gint *button_x)
1164 if (!(*button_on = is_button_present(self, lc, i)))
1167 self->label_width -= bwidth;
1172 if (self->label_x <= left || *x > self->label_x) {
1175 /* the button would have been drawn on top of another button */
1177 self->label_width += bwidth;
1182 static void layout_title(ObFrame *self)
1187 const gint bwidth = ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
1188 /* position of the leftmost button */
1189 const gint left = ob_rr_theme->paddingx + 1;
1190 /* position of the rightmost button */
1191 const gint right = self->width;
1193 /* turn them all off */
1194 self->icon_on = self->desk_on = self->shade_on = self->iconify_on =
1195 self->max_on = self->close_on = self->label_on = FALSE;
1196 self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2;
1197 self->leftmost = self->rightmost = OB_FRAME_CONTEXT_NONE;
1199 /* figure out what's being shown, find each element's position, and the
1202 do the ones before the label, then after the label,
1203 i will be +1 the first time through when working to the left,
1204 and -1 the second time through when working to the right */
1205 for (i = 1; i >= -1; i-=2) {
1207 ObFrameContext *firstcon;
1211 lc = config_title_layout;
1212 firstcon = &self->leftmost;
1215 lc = config_title_layout + strlen(config_title_layout)-1;
1216 firstcon = &self->rightmost;
1219 /* stop at the end of the string (or the label, which calls break) */
1220 for (; *lc != '\0' && lc >= config_title_layout; lc+=i) {
1223 self->label_on = TRUE;
1226 break; /* break the for loop, do other side of label */
1227 } else if (*lc == 'N') {
1228 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICON;
1229 /* icon is bigger than buttons */
1230 place_button(self, lc, bwidth + 2, left, i, &x, &self->icon_on, &self->icon_x);
1231 } else if (*lc == 'D') {
1232 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ALLDESKTOPS;
1233 place_button(self, lc, bwidth, left, i, &x, &self->desk_on, &self->desk_x);
1234 } else if (*lc == 'S') {
1235 if (firstcon) *firstcon = OB_FRAME_CONTEXT_SHADE;
1236 place_button(self, lc, bwidth, left, i, &x, &self->shade_on, &self->shade_x);
1237 } else if (*lc == 'I') {
1238 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICONIFY;
1239 place_button(self, lc, bwidth, left, i, &x, &self->iconify_on, &self->iconify_x);
1240 } else if (*lc == 'M') {
1241 if (firstcon) *firstcon = OB_FRAME_CONTEXT_MAXIMIZE;
1242 place_button(self, lc, bwidth, left, i, &x, &self->max_on, &self->max_x);
1243 } else if (*lc == 'C') {
1244 if (firstcon) *firstcon = OB_FRAME_CONTEXT_CLOSE;
1245 place_button(self, lc, bwidth, left, i, &x, &self->close_on, &self->close_x);
1247 continue; /* don't set firstcon */
1252 /* position and map the elements */
1253 if (self->icon_on) {
1254 XMapWindow(obt_display, self->icon);
1255 XMoveWindow(obt_display, self->icon, self->icon_x,
1256 ob_rr_theme->paddingy);
1258 XUnmapWindow(obt_display, self->icon);
1260 if (self->desk_on) {
1261 XMapWindow(obt_display, self->desk);
1262 XMoveWindow(obt_display, self->desk, self->desk_x,
1263 ob_rr_theme->paddingy + 1);
1265 XUnmapWindow(obt_display, self->desk);
1267 if (self->shade_on) {
1268 XMapWindow(obt_display, self->shade);
1269 XMoveWindow(obt_display, self->shade, self->shade_x,
1270 ob_rr_theme->paddingy + 1);
1272 XUnmapWindow(obt_display, self->shade);
1274 if (self->iconify_on) {
1275 XMapWindow(obt_display, self->iconify);
1276 XMoveWindow(obt_display, self->iconify, self->iconify_x,
1277 ob_rr_theme->paddingy + 1);
1279 XUnmapWindow(obt_display, self->iconify);
1282 XMapWindow(obt_display, self->max);
1283 XMoveWindow(obt_display, self->max, self->max_x,
1284 ob_rr_theme->paddingy + 1);
1286 XUnmapWindow(obt_display, self->max);
1288 if (self->close_on) {
1289 XMapWindow(obt_display, self->close);
1290 XMoveWindow(obt_display, self->close, self->close_x,
1291 ob_rr_theme->paddingy + 1);
1293 XUnmapWindow(obt_display, self->close);
1295 if (self->label_on && self->label_width > 0) {
1296 XMapWindow(obt_display, self->label);
1297 XMoveWindow(obt_display, self->label, self->label_x,
1298 ob_rr_theme->paddingy);
1300 XUnmapWindow(obt_display, self->label);
1303 gboolean frame_next_context_from_string(gchar *names, ObFrameContext *cx)
1307 if (!*names) /* empty string */
1310 /* find the first space */
1311 for (p = names; *p; p = g_utf8_next_char(p)) {
1312 const gunichar c = g_utf8_get_char(p);
1313 if (g_unichar_isspace(c)) break;
1317 /* leading spaces in the string */
1318 n = g_utf8_next_char(names);
1319 if (!frame_next_context_from_string(n, cx))
1324 /* delete the space with null zero(s) */
1325 while (n < g_utf8_next_char(p))
1329 *cx = frame_context_from_string(names);
1331 /* find the next non-space */
1332 for (; *n; n = g_utf8_next_char(n)) {
1333 const gunichar c = g_utf8_get_char(n);
1334 if (!g_unichar_isspace(c)) break;
1338 /* delete everything we just read (copy everything at n to the start of
1340 for (p = names; *n; ++p, ++n)
1347 ObFrameContext frame_context_from_string(const gchar *name)
1349 if (!g_ascii_strcasecmp("Desktop", name))
1350 return OB_FRAME_CONTEXT_DESKTOP;
1351 else if (!g_ascii_strcasecmp("Root", name))
1352 return OB_FRAME_CONTEXT_ROOT;
1353 else if (!g_ascii_strcasecmp("Client", name))
1354 return OB_FRAME_CONTEXT_CLIENT;
1355 else if (!g_ascii_strcasecmp("Titlebar", name))
1356 return OB_FRAME_CONTEXT_TITLEBAR;
1357 else if (!g_ascii_strcasecmp("Frame", name))
1358 return OB_FRAME_CONTEXT_FRAME;
1359 else if (!g_ascii_strcasecmp("TLCorner", name))
1360 return OB_FRAME_CONTEXT_TLCORNER;
1361 else if (!g_ascii_strcasecmp("TRCorner", name))
1362 return OB_FRAME_CONTEXT_TRCORNER;
1363 else if (!g_ascii_strcasecmp("BLCorner", name))
1364 return OB_FRAME_CONTEXT_BLCORNER;
1365 else if (!g_ascii_strcasecmp("BRCorner", name))
1366 return OB_FRAME_CONTEXT_BRCORNER;
1367 else if (!g_ascii_strcasecmp("Top", name))
1368 return OB_FRAME_CONTEXT_TOP;
1369 else if (!g_ascii_strcasecmp("Bottom", name))
1370 return OB_FRAME_CONTEXT_BOTTOM;
1371 else if (!g_ascii_strcasecmp("Left", name))
1372 return OB_FRAME_CONTEXT_LEFT;
1373 else if (!g_ascii_strcasecmp("Right", name))
1374 return OB_FRAME_CONTEXT_RIGHT;
1375 else if (!g_ascii_strcasecmp("Maximize", name))
1376 return OB_FRAME_CONTEXT_MAXIMIZE;
1377 else if (!g_ascii_strcasecmp("AllDesktops", name))
1378 return OB_FRAME_CONTEXT_ALLDESKTOPS;
1379 else if (!g_ascii_strcasecmp("Shade", name))
1380 return OB_FRAME_CONTEXT_SHADE;
1381 else if (!g_ascii_strcasecmp("Iconify", name))
1382 return OB_FRAME_CONTEXT_ICONIFY;
1383 else if (!g_ascii_strcasecmp("Icon", name))
1384 return OB_FRAME_CONTEXT_ICON;
1385 else if (!g_ascii_strcasecmp("Close", name))
1386 return OB_FRAME_CONTEXT_CLOSE;
1387 else if (!g_ascii_strcasecmp("MoveResize", name))
1388 return OB_FRAME_CONTEXT_MOVE_RESIZE;
1389 else if (!g_ascii_strcasecmp("Dock", name))
1390 return OB_FRAME_CONTEXT_DOCK;
1391 else if (!g_ascii_strcasecmp("ScreenTop", name))
1392 return OB_FRAME_CONTEXT_EDGE_TOP;
1393 else if (!g_ascii_strcasecmp("ScreenTopRight", name))
1394 return OB_FRAME_CONTEXT_EDGE_TOPRIGHT;
1395 else if (!g_ascii_strcasecmp("ScreenRight", name))
1396 return OB_FRAME_CONTEXT_EDGE_RIGHT;
1397 else if (!g_ascii_strcasecmp("ScreenBottomRight", name))
1398 return OB_FRAME_CONTEXT_EDGE_BOTTOMRIGHT;
1399 else if (!g_ascii_strcasecmp("ScreenBottom", name))
1400 return OB_FRAME_CONTEXT_EDGE_BOTTOM;
1401 else if (!g_ascii_strcasecmp("ScreenBottomLeft", name))
1402 return OB_FRAME_CONTEXT_EDGE_BOTTOMLEFT;
1403 else if (!g_ascii_strcasecmp("ScreenLeft", name))
1404 return OB_FRAME_CONTEXT_EDGE_LEFT;
1405 else if (!g_ascii_strcasecmp("ScreenTopLeft", name))
1406 return OB_FRAME_CONTEXT_EDGE_TOPLEFT;
1408 return OB_FRAME_CONTEXT_NONE;
1411 ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y)
1416 if (moveresize_in_progress)
1417 return OB_FRAME_CONTEXT_MOVE_RESIZE;
1418 if (win == obt_root(ob_screen))
1419 return OB_FRAME_CONTEXT_ROOT;
1420 if ((obwin = window_find(win))) {
1421 if (WINDOW_IS_DOCK(obwin)) {
1422 return OB_FRAME_CONTEXT_DOCK;
1423 } else if (WINDOW_IS_EDGE(obwin)) {
1424 ObEdge *edge = (ObEdge *)obwin;
1425 return OB_FRAME_CONTEXT_EDGE_TOP + edge->location;
1428 if (client == NULL) return OB_FRAME_CONTEXT_NONE;
1429 if (win == client->window) {
1430 /* conceptually, this is the desktop, as far as users are
1432 if (client->type == OB_CLIENT_TYPE_DESKTOP)
1433 return OB_FRAME_CONTEXT_DESKTOP;
1434 return OB_FRAME_CONTEXT_CLIENT;
1437 self = client->frame;
1439 /* when the user clicks in the corners of the titlebar and the client
1440 is fully maximized, then treat it like they clicked in the
1441 button that is there */
1442 if (self->max_horz && self->max_vert &&
1443 (win == self->title || win == self->titletop ||
1444 win == self->titleleft || win == self->titletopleft ||
1445 win == self->titleright || win == self->titletopright))
1447 /* get the mouse coords in reference to the whole frame */
1451 /* these windows are down a border width from the top of the frame */
1452 if (win == self->title ||
1453 win == self->titleleft || win == self->titleright)
1456 /* title is a border width in from the edge */
1457 if (win == self->title)
1459 /* titletop is a bit to the right */
1460 else if (win == self->titletop)
1461 fx += ob_rr_theme->grip_width + self->bwidth;
1462 /* titletopright is way to the right edge */
1463 else if (win == self->titletopright)
1464 fx += self->area.width - (ob_rr_theme->grip_width + self->bwidth);
1465 /* titleright is even more way to the right edge */
1466 else if (win == self->titleright)
1467 fx += self->area.width - self->bwidth;
1469 /* figure out if we're over the area that should be considered a
1471 if (fy < self->bwidth + ob_rr_theme->paddingy + 1 +
1472 ob_rr_theme->button_size)
1474 if (fx < (self->bwidth + ob_rr_theme->paddingx + 1 +
1475 ob_rr_theme->button_size))
1477 if (self->leftmost != OB_FRAME_CONTEXT_NONE)
1478 return self->leftmost;
1480 else if (fx >= (self->area.width -
1481 (self->bwidth + ob_rr_theme->paddingx + 1 +
1482 ob_rr_theme->button_size)))
1484 if (self->rightmost != OB_FRAME_CONTEXT_NONE)
1485 return self->rightmost;
1489 /* there is no resizing maximized windows so make them the titlebar
1491 return OB_FRAME_CONTEXT_TITLEBAR;
1493 else if (self->max_vert &&
1494 (win == self->titletop || win == self->topresize))
1495 /* can't resize vertically when max vert */
1496 return OB_FRAME_CONTEXT_TITLEBAR;
1497 else if (self->shaded &&
1498 (win == self->titletop || win == self->topresize))
1499 /* can't resize vertically when shaded */
1500 return OB_FRAME_CONTEXT_TITLEBAR;
1502 if (win == self->window) return OB_FRAME_CONTEXT_FRAME;
1503 if (win == self->label) return OB_FRAME_CONTEXT_TITLEBAR;
1504 if (win == self->handle) return OB_FRAME_CONTEXT_BOTTOM;
1505 if (win == self->handletop) return OB_FRAME_CONTEXT_BOTTOM;
1506 if (win == self->handlebottom) return OB_FRAME_CONTEXT_BOTTOM;
1507 if (win == self->handleleft) return OB_FRAME_CONTEXT_BLCORNER;
1508 if (win == self->lgrip) return OB_FRAME_CONTEXT_BLCORNER;
1509 if (win == self->lgripleft) return OB_FRAME_CONTEXT_BLCORNER;
1510 if (win == self->lgriptop) return OB_FRAME_CONTEXT_BLCORNER;
1511 if (win == self->lgripbottom) return OB_FRAME_CONTEXT_BLCORNER;
1512 if (win == self->handleright) return OB_FRAME_CONTEXT_BRCORNER;
1513 if (win == self->rgrip) return OB_FRAME_CONTEXT_BRCORNER;
1514 if (win == self->rgripright) return OB_FRAME_CONTEXT_BRCORNER;
1515 if (win == self->rgriptop) return OB_FRAME_CONTEXT_BRCORNER;
1516 if (win == self->rgripbottom) return OB_FRAME_CONTEXT_BRCORNER;
1517 if (win == self->title) return OB_FRAME_CONTEXT_TITLEBAR;
1518 if (win == self->titlebottom) return OB_FRAME_CONTEXT_TITLEBAR;
1519 if (win == self->titleleft) return OB_FRAME_CONTEXT_TLCORNER;
1520 if (win == self->titletopleft) return OB_FRAME_CONTEXT_TLCORNER;
1521 if (win == self->titleright) return OB_FRAME_CONTEXT_TRCORNER;
1522 if (win == self->titletopright) return OB_FRAME_CONTEXT_TRCORNER;
1523 if (win == self->titletop) return OB_FRAME_CONTEXT_TOP;
1524 if (win == self->topresize) return OB_FRAME_CONTEXT_TOP;
1525 if (win == self->tltresize) return OB_FRAME_CONTEXT_TLCORNER;
1526 if (win == self->tllresize) return OB_FRAME_CONTEXT_TLCORNER;
1527 if (win == self->trtresize) return OB_FRAME_CONTEXT_TRCORNER;
1528 if (win == self->trrresize) return OB_FRAME_CONTEXT_TRCORNER;
1529 if (win == self->left) return OB_FRAME_CONTEXT_LEFT;
1530 if (win == self->right) return OB_FRAME_CONTEXT_RIGHT;
1531 if (win == self->innertop) return OB_FRAME_CONTEXT_TITLEBAR;
1532 if (win == self->innerleft) return OB_FRAME_CONTEXT_LEFT;
1533 if (win == self->innerbottom) return OB_FRAME_CONTEXT_BOTTOM;
1534 if (win == self->innerright) return OB_FRAME_CONTEXT_RIGHT;
1535 if (win == self->innerbll) return OB_FRAME_CONTEXT_BLCORNER;
1536 if (win == self->innerblb) return OB_FRAME_CONTEXT_BLCORNER;
1537 if (win == self->innerbrr) return OB_FRAME_CONTEXT_BRCORNER;
1538 if (win == self->innerbrb) return OB_FRAME_CONTEXT_BRCORNER;
1539 if (win == self->max) return OB_FRAME_CONTEXT_MAXIMIZE;
1540 if (win == self->iconify) return OB_FRAME_CONTEXT_ICONIFY;
1541 if (win == self->close) return OB_FRAME_CONTEXT_CLOSE;
1542 if (win == self->icon) return OB_FRAME_CONTEXT_ICON;
1543 if (win == self->desk) return OB_FRAME_CONTEXT_ALLDESKTOPS;
1544 if (win == self->shade) return OB_FRAME_CONTEXT_SHADE;
1546 return OB_FRAME_CONTEXT_NONE;
1549 void frame_client_gravity(ObFrame *self, gint *x, gint *y)
1552 switch (self->client->gravity) {
1554 case NorthWestGravity:
1555 case SouthWestGravity:
1562 /* the middle of the client will be the middle of the frame */
1563 *x -= (self->size.right - self->size.left) / 2;
1566 case NorthEastGravity:
1567 case SouthEastGravity:
1569 /* the right side of the client will be the right side of the frame */
1570 *x -= self->size.right + self->size.left -
1571 self->client->border_width * 2;
1576 /* the client's position won't move */
1577 *x -= self->size.left - self->client->border_width;
1582 switch (self->client->gravity) {
1584 case NorthWestGravity:
1585 case NorthEastGravity:
1592 /* the middle of the client will be the middle of the frame */
1593 *y -= (self->size.bottom - self->size.top) / 2;
1596 case SouthWestGravity:
1597 case SouthEastGravity:
1599 /* the bottom of the client will be the bottom of the frame */
1600 *y -= self->size.bottom + self->size.top -
1601 self->client->border_width * 2;
1606 /* the client's position won't move */
1607 *y -= self->size.top - self->client->border_width;
1612 void frame_frame_gravity(ObFrame *self, gint *x, gint *y)
1615 switch (self->client->gravity) {
1617 case NorthWestGravity:
1619 case SouthWestGravity:
1624 /* the middle of the client will be the middle of the frame */
1625 *x += (self->size.right - self->size.left) / 2;
1627 case NorthEastGravity:
1629 case SouthEastGravity:
1630 /* the right side of the client will be the right side of the frame */
1631 *x += self->size.right + self->size.left -
1632 self->client->border_width * 2;
1636 /* the client's position won't move */
1637 *x += self->size.left - self->client->border_width;
1642 switch (self->client->gravity) {
1644 case NorthWestGravity:
1646 case NorthEastGravity:
1651 /* the middle of the client will be the middle of the frame */
1652 *y += (self->size.bottom - self->size.top) / 2;
1654 case SouthWestGravity:
1656 case SouthEastGravity:
1657 /* the bottom of the client will be the bottom of the frame */
1658 *y += self->size.bottom + self->size.top -
1659 self->client->border_width * 2;
1663 /* the client's position won't move */
1664 *y += self->size.top - self->client->border_width;
1669 void frame_rect_to_frame(ObFrame *self, Rect *r)
1671 r->width += self->size.left + self->size.right;
1672 r->height += self->size.top + self->size.bottom;
1673 frame_client_gravity(self, &r->x, &r->y);
1676 void frame_rect_to_client(ObFrame *self, Rect *r)
1678 r->width -= self->size.left + self->size.right;
1679 r->height -= self->size.top + self->size.bottom;
1680 frame_frame_gravity(self, &r->x, &r->y);
1683 static void flash_done(gpointer data)
1685 ObFrame *self = data;
1687 if (self->focused != self->flash_on)
1688 frame_adjust_focus(self, self->focused);
1691 static gboolean flash_timeout(gpointer data)
1693 ObFrame *self = data;
1696 g_get_current_time(&now);
1697 if (now.tv_sec > self->flash_end.tv_sec ||
1698 (now.tv_sec == self->flash_end.tv_sec &&
1699 now.tv_usec >= self->flash_end.tv_usec))
1700 self->flashing = FALSE;
1702 if (!self->flashing)
1703 return FALSE; /* we are done */
1705 self->flash_on = !self->flash_on;
1706 if (!self->focused) {
1707 frame_adjust_focus(self, self->flash_on);
1708 self->focused = FALSE;
1711 return TRUE; /* go again */
1714 void frame_flash_start(ObFrame *self)
1716 self->flash_on = self->focused;
1718 if (!self->flashing)
1719 self->flash_timer = g_timeout_add_full(G_PRIORITY_DEFAULT,
1720 600, flash_timeout, self,
1722 g_get_current_time(&self->flash_end);
1723 g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
1725 self->flashing = TRUE;
1728 void frame_flash_stop(ObFrame *self)
1730 self->flashing = FALSE;
1733 static gulong frame_animate_iconify_time_left(ObFrame *self,
1734 const GTimeVal *now)
1737 sec = self->iconify_animation_end.tv_sec - now->tv_sec;
1738 usec = self->iconify_animation_end.tv_usec - now->tv_usec;
1740 usec += G_USEC_PER_SEC;
1743 /* no negative values */
1744 return MAX(sec * G_USEC_PER_SEC + usec, 0);
1747 static gboolean frame_animate_iconify(gpointer p)
1751 gint iconx, icony, iconw;
1754 gboolean iconifying;
1756 if (self->client->icon_geometry.width == 0) {
1757 /* there is no icon geometry set so just go straight down */
1760 a = screen_physical_area_monitor(screen_find_monitor(&self->area));
1761 iconx = self->area.x + self->area.width / 2 + 32;
1762 icony = a->y + a->width;
1765 iconx = self->client->icon_geometry.x;
1766 icony = self->client->icon_geometry.y;
1767 iconw = self->client->icon_geometry.width;
1770 iconifying = self->iconify_animation_going > 0;
1772 /* how far do we have left to go ? */
1773 g_get_current_time(&now);
1774 time = frame_animate_iconify_time_left(self, &now);
1776 if ((time > 0 && iconifying) || (time == 0 && !iconifying)) {
1777 /* start where the frame is supposed to be */
1780 w = self->area.width;
1781 h = self->area.height;
1783 /* start at the icon */
1787 h = self->size.top; /* just the titlebar */
1794 dx = self->area.x - iconx;
1795 dy = self->area.y - icony;
1796 dw = self->area.width - self->bwidth * 2 - iconw;
1797 /* if restoring, we move in the opposite direction */
1798 if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; }
1800 elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
1801 x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1802 y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1803 w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1804 h = self->size.top; /* just the titlebar */
1807 XMoveResizeWindow(obt_display, self->window, x, y, w, h);
1808 XFlush(obt_display);
1811 frame_end_iconify_animation(self);
1813 return time > 0; /* repeat until we're out of time */
1816 void frame_end_iconify_animation(ObFrame *self)
1818 /* see if there is an animation going */
1819 if (self->iconify_animation_going == 0) return;
1822 XUnmapWindow(obt_display, self->window);
1824 /* Send a ConfigureNotify when the animation is done, this fixes
1825 KDE's pager showing the window in the wrong place. since the
1826 window is mapped at a different location and is then moved, we
1827 need to send the synthetic configurenotify, since apps may have
1828 read the position when the client mapped, apparently. */
1829 client_reconfigure(self->client, TRUE);
1832 /* we're not animating any more ! */
1833 self->iconify_animation_going = 0;
1835 XMoveResizeWindow(obt_display, self->window,
1836 self->area.x, self->area.y,
1837 self->area.width, self->area.height);
1838 /* we delay re-rendering until after we're done animating */
1839 framerender_frame(self);
1840 XFlush(obt_display);
1843 void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying)
1846 gboolean new_anim = FALSE;
1847 gboolean set_end = TRUE;
1850 /* if there is no titlebar, just don't animate for now
1851 XXX it would be nice tho.. */
1852 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1855 /* get the current time */
1856 g_get_current_time(&now);
1858 /* get how long until the end */
1859 time = FRAME_ANIMATE_ICONIFY_TIME;
1860 if (self->iconify_animation_going) {
1861 if (!!iconifying != (self->iconify_animation_going > 0)) {
1862 /* animation was already going on in the opposite direction */
1863 time = time - frame_animate_iconify_time_left(self, &now);
1865 /* animation was already going in the same direction */
1869 self->iconify_animation_going = iconifying ? 1 : -1;
1871 /* set the ending time */
1873 self->iconify_animation_end.tv_sec = now.tv_sec;
1874 self->iconify_animation_end.tv_usec = now.tv_usec;
1875 g_time_val_add(&self->iconify_animation_end, time);
1879 if (self->iconify_animation_timer)
1880 g_source_remove(self->iconify_animation_timer);
1881 self->iconify_animation_timer =
1882 g_timeout_add_full(G_PRIORITY_DEFAULT,
1883 FRAME_ANIMATE_ICONIFY_STEP_TIME,
1884 frame_animate_iconify, self, NULL);
1887 /* do the first step */
1888 frame_animate_iconify(self);
1890 /* show it during the animation even if it is not "visible" */
1892 XMapWindow(obt_display, self->window);