]> Dogcows Code - chaz/openbox/blob - openbox/frame.c
make move and resize resist care about the dock
[chaz/openbox] / openbox / frame.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 frame.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
6
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.
11
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.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "frame.h"
21 #include "client.h"
22 #include "openbox.h"
23 #include "extensions.h"
24 #include "prop.h"
25 #include "grab.h"
26 #include "config.h"
27 #include "framerender.h"
28 #include "mainloop.h"
29 #include "focus_cycle.h"
30 #include "focus_cycle_indicator.h"
31 #include "moveresize.h"
32 #include "screen.h"
33 #include "render/theme.h"
34
35 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
36 ButtonPressMask | ButtonReleaseMask | \
37 SubstructureRedirectMask | FocusChangeMask)
38 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
39 ButtonMotionMask | PointerMotionMask | \
40 EnterWindowMask | LeaveWindowMask)
41
42 #define FRAME_ANIMATE_ICONIFY_TIME 150000 /* .15 seconds */
43 #define FRAME_ANIMATE_ICONIFY_STEP_TIME (G_USEC_PER_SEC / 60) /* 60 Hz */
44
45 #define FRAME_HANDLE_Y(f) (f->size.top + f->client->area.height + f->cbwidth_b)
46
47 static void flash_done(gpointer data);
48 static gboolean flash_timeout(gpointer data);
49
50 static void layout_title(ObFrame *self);
51 static void set_theme_statics(ObFrame *self);
52 static void free_theme_statics(ObFrame *self);
53 static gboolean frame_animate_iconify(gpointer self);
54 static void frame_adjust_cursors(ObFrame *self);
55
56 static Window createWindow(Window parent, Visual *visual,
57 gulong mask, XSetWindowAttributes *attrib)
58 {
59 return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
60 (visual ? 32 : RrDepth(ob_rr_inst)), InputOutput,
61 (visual ? visual : RrVisual(ob_rr_inst)),
62 mask, attrib);
63
64 }
65
66 static Visual *check_32bit_client(ObClient *c)
67 {
68 XWindowAttributes wattrib;
69 Status ret;
70
71 /* we're already running at 32 bit depth, yay. we don't need to use their
72 visual */
73 if (RrDepth(ob_rr_inst) == 32)
74 return NULL;
75
76 ret = XGetWindowAttributes(ob_display, c->window, &wattrib);
77 g_assert(ret != BadDrawable);
78 g_assert(ret != BadWindow);
79
80 if (wattrib.depth == 32)
81 return wattrib.visual;
82 return NULL;
83 }
84
85 ObFrame *frame_new(ObClient *client)
86 {
87 XSetWindowAttributes attrib;
88 gulong mask;
89 ObFrame *self;
90 Visual *visual;
91
92 self = g_new0(ObFrame, 1);
93 self->client = client;
94
95 visual = check_32bit_client(client);
96
97 /* create the non-visible decor windows */
98
99 mask = 0;
100 if (visual) {
101 /* client has a 32-bit visual */
102 mask |= CWColormap | CWBackPixel | CWBorderPixel;
103 /* create a colormap with the visual */
104 self->colormap = attrib.colormap =
105 XCreateColormap(ob_display,
106 RootWindow(ob_display, ob_screen),
107 visual, AllocNone);
108 attrib.background_pixel = BlackPixel(ob_display, ob_screen);
109 attrib.border_pixel = BlackPixel(ob_display, ob_screen);
110 }
111 self->window = createWindow(RootWindow(ob_display, ob_screen), visual,
112 mask, &attrib);
113
114 /* create the visible decor windows */
115
116 mask = 0;
117 if (visual) {
118 /* client has a 32-bit visual */
119 mask |= CWColormap | CWBackPixel | CWBorderPixel;
120 attrib.colormap = RrColormap(ob_rr_inst);
121 }
122
123 self->backback = createWindow(self->window, NULL, mask, &attrib);
124 self->backfront = createWindow(self->backback, NULL, mask, &attrib);
125
126 mask |= CWEventMask;
127 attrib.event_mask = ELEMENT_EVENTMASK;
128 self->innerleft = createWindow(self->window, NULL, mask, &attrib);
129 self->innertop = createWindow(self->window, NULL, mask, &attrib);
130 self->innerright = createWindow(self->window, NULL, mask, &attrib);
131 self->innerbottom = createWindow(self->window, NULL, mask, &attrib);
132
133 self->title = createWindow(self->window, NULL, mask, &attrib);
134 self->titleleft = createWindow(self->window, NULL, mask, &attrib);
135 self->titletop = createWindow(self->window, NULL, mask, &attrib);
136 self->titletopleft = createWindow(self->window, NULL, mask, &attrib);
137 self->titletopright = createWindow(self->window, NULL, mask, &attrib);
138 self->titleright = createWindow(self->window, NULL, mask, &attrib);
139 self->titlebottom = createWindow(self->window, NULL, mask, &attrib);
140
141 self->topresize = createWindow(self->title, NULL, mask, &attrib);
142 self->tltresize = createWindow(self->title, NULL, mask, &attrib);
143 self->tllresize = createWindow(self->title, NULL, mask, &attrib);
144 self->trtresize = createWindow(self->title, NULL, mask, &attrib);
145 self->trrresize = createWindow(self->title, NULL, mask, &attrib);
146
147 self->left = createWindow(self->window, NULL, mask, &attrib);
148 self->right = createWindow(self->window, NULL, mask, &attrib);
149
150 self->label = createWindow(self->title, NULL, mask, &attrib);
151 self->max = createWindow(self->title, NULL, mask, &attrib);
152 self->close = createWindow(self->title, NULL, mask, &attrib);
153 self->desk = createWindow(self->title, NULL, mask, &attrib);
154 self->shade = createWindow(self->title, NULL, mask, &attrib);
155 self->icon = createWindow(self->title, NULL, mask, &attrib);
156 self->iconify = createWindow(self->title, NULL, mask, &attrib);
157
158 self->handle = createWindow(self->window, NULL, mask, &attrib);
159 self->lgrip = createWindow(self->handle, NULL, mask, &attrib);
160 self->rgrip = createWindow(self->handle, NULL, mask, &attrib);
161
162 self->handleleft = createWindow(self->handle, NULL, mask, &attrib);
163 self->handleright = createWindow(self->handle, NULL, mask, &attrib);
164
165 self->handletop = createWindow(self->window, NULL, mask, &attrib);
166 self->handlebottom = createWindow(self->window, NULL, mask, &attrib);
167 self->lgripleft = createWindow(self->window, NULL, mask, &attrib);
168 self->lgriptop = createWindow(self->window, NULL, mask, &attrib);
169 self->lgripbottom = createWindow(self->window, NULL, mask, &attrib);
170 self->rgripright = createWindow(self->window, NULL, mask, &attrib);
171 self->rgriptop = createWindow(self->window, NULL, mask, &attrib);
172 self->rgripbottom = createWindow(self->window, NULL, mask, &attrib);
173
174 self->focused = FALSE;
175
176 /* the other stuff is shown based on decor settings */
177 XMapWindow(ob_display, self->label);
178 XMapWindow(ob_display, self->backback);
179 XMapWindow(ob_display, self->backfront);
180
181 self->max_press = self->close_press = self->desk_press =
182 self->iconify_press = self->shade_press = FALSE;
183 self->max_hover = self->close_hover = self->desk_hover =
184 self->iconify_hover = self->shade_hover = FALSE;
185
186 set_theme_statics(self);
187
188 return (ObFrame*)self;
189 }
190
191 static void set_theme_statics(ObFrame *self)
192 {
193 /* set colors/appearance/sizes for stuff that doesn't change */
194 XResizeWindow(ob_display, self->max,
195 ob_rr_theme->button_size, ob_rr_theme->button_size);
196 XResizeWindow(ob_display, self->iconify,
197 ob_rr_theme->button_size, ob_rr_theme->button_size);
198 XResizeWindow(ob_display, self->icon,
199 ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
200 XResizeWindow(ob_display, self->close,
201 ob_rr_theme->button_size, ob_rr_theme->button_size);
202 XResizeWindow(ob_display, self->desk,
203 ob_rr_theme->button_size, ob_rr_theme->button_size);
204 XResizeWindow(ob_display, self->shade,
205 ob_rr_theme->button_size, ob_rr_theme->button_size);
206 XResizeWindow(ob_display, self->tltresize,
207 ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
208 XResizeWindow(ob_display, self->trtresize,
209 ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
210 XResizeWindow(ob_display, self->tllresize,
211 ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
212 XResizeWindow(ob_display, self->trrresize,
213 ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
214
215 /* set up the dynamic appearances */
216 self->a_unfocused_title = RrAppearanceCopy(ob_rr_theme->a_unfocused_title);
217 self->a_focused_title = RrAppearanceCopy(ob_rr_theme->a_focused_title);
218 self->a_unfocused_label = RrAppearanceCopy(ob_rr_theme->a_unfocused_label);
219 self->a_focused_label = RrAppearanceCopy(ob_rr_theme->a_focused_label);
220 self->a_unfocused_handle =
221 RrAppearanceCopy(ob_rr_theme->a_unfocused_handle);
222 self->a_focused_handle = RrAppearanceCopy(ob_rr_theme->a_focused_handle);
223 self->a_icon = RrAppearanceCopy(ob_rr_theme->a_icon);
224 }
225
226 static void free_theme_statics(ObFrame *self)
227 {
228 RrAppearanceFree(self->a_unfocused_title);
229 RrAppearanceFree(self->a_focused_title);
230 RrAppearanceFree(self->a_unfocused_label);
231 RrAppearanceFree(self->a_focused_label);
232 RrAppearanceFree(self->a_unfocused_handle);
233 RrAppearanceFree(self->a_focused_handle);
234 RrAppearanceFree(self->a_icon);
235 }
236
237 void frame_free(ObFrame *self)
238 {
239 free_theme_statics(self);
240
241 XDestroyWindow(ob_display, self->window);
242 if (self->colormap)
243 XFreeColormap(ob_display, self->colormap);
244
245 g_free(self);
246 }
247
248 void frame_show(ObFrame *self)
249 {
250 if (!self->visible) {
251 self->visible = TRUE;
252 framerender_frame(self);
253 /* Grab the server to make sure that the frame window is mapped before
254 the client gets its MapNotify, i.e. to make sure the client is
255 _visible_ when it gets MapNotify. */
256 grab_server(TRUE);
257 XMapWindow(ob_display, self->client->window);
258 XMapWindow(ob_display, self->window);
259 grab_server(FALSE);
260 }
261 }
262
263 void frame_hide(ObFrame *self)
264 {
265 if (self->visible) {
266 self->visible = FALSE;
267 if (!frame_iconify_animating(self))
268 XUnmapWindow(ob_display, self->window);
269 /* we unmap the client itself so that we can get MapRequest
270 events, and because the ICCCM tells us to! */
271 XUnmapWindow(ob_display, self->client->window);
272 self->client->ignore_unmaps += 1;
273 }
274 }
275
276 void frame_adjust_theme(ObFrame *self)
277 {
278 free_theme_statics(self);
279 set_theme_statics(self);
280 }
281
282 void frame_adjust_shape(ObFrame *self)
283 {
284 #ifdef SHAPE
285 gint num;
286 XRectangle xrect[2];
287
288 if (!self->client->shaped) {
289 /* clear the shape on the frame window */
290 XShapeCombineMask(ob_display, self->window, ShapeBounding,
291 self->size.left,
292 self->size.top,
293 None, ShapeSet);
294 } else {
295 /* make the frame's shape match the clients */
296 XShapeCombineShape(ob_display, self->window, ShapeBounding,
297 self->size.left,
298 self->size.top,
299 self->client->window,
300 ShapeBounding, ShapeSet);
301
302 num = 0;
303 if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
304 xrect[0].x = 0;
305 xrect[0].y = 0;
306 xrect[0].width = self->area.width;
307 xrect[0].height = self->size.top;
308 ++num;
309 }
310
311 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
312 ob_rr_theme->handle_height > 0)
313 {
314 xrect[1].x = 0;
315 xrect[1].y = FRAME_HANDLE_Y(self);
316 xrect[1].width = self->area.width;
317 xrect[1].height = ob_rr_theme->handle_height +
318 self->bwidth * 2;
319 ++num;
320 }
321
322 XShapeCombineRectangles(ob_display, self->window,
323 ShapeBounding, 0, 0, xrect, num,
324 ShapeUnion, Unsorted);
325 }
326 #endif
327 }
328
329 void frame_adjust_area(ObFrame *self, gboolean moved,
330 gboolean resized, gboolean fake)
331 {
332 Strut oldsize;
333
334 oldsize = self->size;
335
336 if (resized) {
337 /* do this before changing the frame's status like max_horz max_vert */
338 frame_adjust_cursors(self);
339
340 self->functions = self->client->functions;
341 self->decorations = self->client->decorations;
342 self->max_horz = self->client->max_horz;
343 self->max_vert = self->client->max_vert;
344 self->shaded = self->client->shaded;
345
346 if (self->decorations & OB_FRAME_DECOR_BORDER ||
347 (self->client->undecorated && config_theme_keepborder))
348 self->bwidth = ob_rr_theme->fbwidth;
349 else
350 self->bwidth = 0;
351
352 if (self->decorations & OB_FRAME_DECOR_BORDER) {
353 self->cbwidth_l = self->cbwidth_r = ob_rr_theme->cbwidthx;
354 self->cbwidth_t = self->cbwidth_b = ob_rr_theme->cbwidthy;
355 } else
356 self->cbwidth_l = self->cbwidth_t =
357 self->cbwidth_r = self->cbwidth_b = 0;
358
359 if (self->max_horz) {
360 self->cbwidth_l = self->cbwidth_r = 0;
361 self->width = self->client->area.width;
362 if (self->max_vert)
363 self->cbwidth_b = 0;
364 } else
365 self->width = self->client->area.width +
366 self->cbwidth_l + self->cbwidth_r;
367
368 /* some elements are sized based of the width, so don't let them have
369 negative values */
370 self->width = MAX(self->width,
371 (ob_rr_theme->grip_width + self->bwidth) * 2 + 1);
372
373 STRUT_SET(self->size,
374 self->cbwidth_l + (!self->max_horz ? self->bwidth : 0),
375 self->cbwidth_t + self->bwidth,
376 self->cbwidth_r + (!self->max_horz ? self->bwidth : 0),
377 self->cbwidth_b +
378 (!self->max_horz || !self->max_vert ? self->bwidth : 0));
379
380 if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
381 self->size.top += ob_rr_theme->title_height + self->bwidth;
382 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
383 ob_rr_theme->handle_height > 0)
384 {
385 self->size.bottom += ob_rr_theme->handle_height + self->bwidth;
386 }
387
388 /* position/size and map/unmap all the windows */
389
390 if (!fake) {
391 if (self->cbwidth_l) {
392 XMoveResizeWindow(ob_display, self->innerleft,
393 self->size.left - self->cbwidth_l,
394 self->size.top,
395 self->cbwidth_l, self->client->area.height);
396
397 XMapWindow(ob_display, self->innerleft);
398 } else
399 XUnmapWindow(ob_display, self->innerleft);
400
401 if (self->cbwidth_r) {
402 XMoveResizeWindow(ob_display, self->innerright,
403 self->size.left + self->client->area.width,
404 self->size.top,
405 self->cbwidth_r, self->client->area.height);
406
407 XMapWindow(ob_display, self->innerright);
408 } else
409 XUnmapWindow(ob_display, self->innerright);
410
411 if (self->cbwidth_t) {
412 XMoveResizeWindow(ob_display, self->innertop,
413 self->size.left - self->cbwidth_l,
414 self->size.top - self->cbwidth_t,
415 self->client->area.width +
416 self->cbwidth_l + self->cbwidth_r,
417 self->cbwidth_t);
418
419 XMapWindow(ob_display, self->innertop);
420 } else
421 XUnmapWindow(ob_display, self->innertop);
422
423 if (self->cbwidth_b) {
424 XMoveResizeWindow(ob_display, self->innerbottom,
425 self->size.left - self->cbwidth_l,
426 self->size.top + self->client->area.height,
427 self->client->area.width +
428 self->cbwidth_l + self->cbwidth_r,
429 self->cbwidth_b);
430
431 XMapWindow(ob_display, self->innerbottom);
432 } else
433 XUnmapWindow(ob_display, self->innerbottom);
434
435 if (self->bwidth) {
436 gint titlesides;
437
438 /* height of titleleft and titleright */
439 titlesides = (!self->max_horz ? ob_rr_theme->grip_width : 0);
440
441 XMoveResizeWindow(ob_display, self->titletop,
442 ob_rr_theme->grip_width + self->bwidth, 0,
443 /* width + bwidth*2 - bwidth*2 - grips*2 */
444 self->width - ob_rr_theme->grip_width * 2,
445 self->bwidth);
446 XMoveResizeWindow(ob_display, self->titletopleft,
447 0, 0,
448 ob_rr_theme->grip_width + self->bwidth,
449 self->bwidth);
450 XMoveResizeWindow(ob_display, self->titletopright,
451 self->client->area.width +
452 self->size.left + self->size.right -
453 ob_rr_theme->grip_width - self->bwidth,
454 0,
455 ob_rr_theme->grip_width + self->bwidth,
456 self->bwidth);
457
458 if (titlesides > 0) {
459 XMoveResizeWindow(ob_display, self->titleleft,
460 0, self->bwidth,
461 self->bwidth,
462 titlesides);
463 XMoveResizeWindow(ob_display, self->titleright,
464 self->client->area.width +
465 self->size.left + self->size.right -
466 self->bwidth,
467 self->bwidth,
468 self->bwidth,
469 titlesides);
470
471 XMapWindow(ob_display, self->titleleft);
472 XMapWindow(ob_display, self->titleright);
473 } else {
474 XUnmapWindow(ob_display, self->titleleft);
475 XUnmapWindow(ob_display, self->titleright);
476 }
477
478 XMapWindow(ob_display, self->titletop);
479 XMapWindow(ob_display, self->titletopleft);
480 XMapWindow(ob_display, self->titletopright);
481
482 if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
483 XMoveResizeWindow(ob_display, self->titlebottom,
484 (self->max_horz ? 0 : self->bwidth),
485 ob_rr_theme->title_height + self->bwidth,
486 self->width,
487 self->bwidth);
488
489 XMapWindow(ob_display, self->titlebottom);
490 } else
491 XUnmapWindow(ob_display, self->titlebottom);
492 } else {
493 XUnmapWindow(ob_display, self->titlebottom);
494
495 XUnmapWindow(ob_display, self->titletop);
496 XUnmapWindow(ob_display, self->titletopleft);
497 XUnmapWindow(ob_display, self->titletopright);
498 XUnmapWindow(ob_display, self->titleleft);
499 XUnmapWindow(ob_display, self->titleright);
500 }
501
502 if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
503 XMoveResizeWindow(ob_display, self->title,
504 (self->max_horz ? 0 : self->bwidth),
505 self->bwidth,
506 self->width, ob_rr_theme->title_height);
507
508 XMapWindow(ob_display, self->title);
509
510 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
511 XMoveResizeWindow(ob_display, self->topresize,
512 ob_rr_theme->grip_width,
513 0,
514 self->width - ob_rr_theme->grip_width *2,
515 ob_rr_theme->paddingy + 1);
516
517 XMoveWindow(ob_display, self->tltresize, 0, 0);
518 XMoveWindow(ob_display, self->tllresize, 0, 0);
519 XMoveWindow(ob_display, self->trtresize,
520 self->width - ob_rr_theme->grip_width, 0);
521 XMoveWindow(ob_display, self->trrresize,
522 self->width - ob_rr_theme->paddingx - 1, 0);
523
524 XMapWindow(ob_display, self->topresize);
525 XMapWindow(ob_display, self->tltresize);
526 XMapWindow(ob_display, self->tllresize);
527 XMapWindow(ob_display, self->trtresize);
528 XMapWindow(ob_display, self->trrresize);
529 } else {
530 XUnmapWindow(ob_display, self->topresize);
531 XUnmapWindow(ob_display, self->tltresize);
532 XUnmapWindow(ob_display, self->tllresize);
533 XUnmapWindow(ob_display, self->trtresize);
534 XUnmapWindow(ob_display, self->trrresize);
535 }
536 } else
537 XUnmapWindow(ob_display, self->title);
538 }
539
540 if ((self->decorations & OB_FRAME_DECOR_TITLEBAR))
541 /* layout the title bar elements */
542 layout_title(self);
543
544 if (!fake) {
545 gint sidebwidth = self->max_horz ? 0 : self->bwidth;
546
547 if (self->bwidth && self->size.bottom) {
548 XMoveResizeWindow(ob_display, self->handlebottom,
549 ob_rr_theme->grip_width +
550 self->bwidth + sidebwidth,
551 self->size.top + self->client->area.height +
552 self->size.bottom - self->bwidth,
553 self->width - (ob_rr_theme->grip_width +
554 sidebwidth) * 2,
555 self->bwidth);
556
557
558 if (sidebwidth) {
559 XMoveResizeWindow(ob_display, self->lgripleft,
560 0,
561 self->size.top +
562 self->client->area.height +
563 self->size.bottom -
564 (!self->max_horz ?
565 ob_rr_theme->grip_width :
566 self->size.bottom - self->cbwidth_b),
567 self->bwidth,
568 (!self->max_horz ?
569 ob_rr_theme->grip_width :
570 self->size.bottom - self->cbwidth_b));
571 XMoveResizeWindow(ob_display, self->rgripright,
572 self->size.left +
573 self->client->area.width +
574 self->size.right - self->bwidth,
575 self->size.top +
576 self->client->area.height +
577 self->size.bottom -
578 (!self->max_horz ?
579 ob_rr_theme->grip_width :
580 self->size.bottom - self->cbwidth_b),
581 self->bwidth,
582 (!self->max_horz ?
583 ob_rr_theme->grip_width :
584 self->size.bottom - self->cbwidth_b));
585
586 XMapWindow(ob_display, self->lgripleft);
587 XMapWindow(ob_display, self->rgripright);
588 } else {
589 XUnmapWindow(ob_display, self->lgripleft);
590 XUnmapWindow(ob_display, self->rgripright);
591 }
592
593 XMoveResizeWindow(ob_display, self->lgripbottom,
594 sidebwidth,
595 self->size.top + self->client->area.height +
596 self->size.bottom - self->bwidth,
597 ob_rr_theme->grip_width + self->bwidth,
598 self->bwidth);
599 XMoveResizeWindow(ob_display, self->rgripbottom,
600 self->size.left + self->client->area.width +
601 self->size.right - self->bwidth - sidebwidth -
602 ob_rr_theme->grip_width,
603 self->size.top + self->client->area.height +
604 self->size.bottom - self->bwidth,
605 ob_rr_theme->grip_width + self->bwidth,
606 self->bwidth);
607
608 XMapWindow(ob_display, self->handlebottom);
609 XMapWindow(ob_display, self->lgripbottom);
610 XMapWindow(ob_display, self->rgripbottom);
611
612 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
613 ob_rr_theme->handle_height > 0)
614 {
615 XMoveResizeWindow(ob_display, self->handletop,
616 ob_rr_theme->grip_width +
617 self->bwidth + sidebwidth,
618 FRAME_HANDLE_Y(self),
619 self->width - (ob_rr_theme->grip_width +
620 sidebwidth) * 2,
621 self->bwidth);
622 XMapWindow(ob_display, self->handletop);
623
624 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
625 XMoveResizeWindow(ob_display, self->handleleft,
626 ob_rr_theme->grip_width,
627 0,
628 self->bwidth,
629 ob_rr_theme->handle_height);
630 XMoveResizeWindow(ob_display, self->handleright,
631 self->width -
632 ob_rr_theme->grip_width -
633 self->bwidth,
634 0,
635 self->bwidth,
636 ob_rr_theme->handle_height);
637
638 XMoveResizeWindow(ob_display, self->lgriptop,
639 sidebwidth,
640 FRAME_HANDLE_Y(self),
641 ob_rr_theme->grip_width +
642 self->bwidth,
643 self->bwidth);
644 XMoveResizeWindow(ob_display, self->rgriptop,
645 self->size.left +
646 self->client->area.width +
647 self->size.right - self->bwidth -
648 sidebwidth - ob_rr_theme->grip_width,
649 FRAME_HANDLE_Y(self),
650 ob_rr_theme->grip_width +
651 self->bwidth,
652 self->bwidth);
653
654 XMapWindow(ob_display, self->handleleft);
655 XMapWindow(ob_display, self->handleright);
656 XMapWindow(ob_display, self->lgriptop);
657 XMapWindow(ob_display, self->rgriptop);
658 } else {
659 XUnmapWindow(ob_display, self->handleleft);
660 XUnmapWindow(ob_display, self->handleright);
661 XUnmapWindow(ob_display, self->lgriptop);
662 XUnmapWindow(ob_display, self->rgriptop);
663 }
664 } else {
665 XUnmapWindow(ob_display, self->handleleft);
666 XUnmapWindow(ob_display, self->handleright);
667 XUnmapWindow(ob_display, self->lgriptop);
668 XUnmapWindow(ob_display, self->rgriptop);
669
670 XUnmapWindow(ob_display, self->handletop);
671 }
672 } else {
673 XUnmapWindow(ob_display, self->handleleft);
674 XUnmapWindow(ob_display, self->handleright);
675 XUnmapWindow(ob_display, self->lgriptop);
676 XUnmapWindow(ob_display, self->rgriptop);
677
678 XUnmapWindow(ob_display, self->handletop);
679
680 XUnmapWindow(ob_display, self->handlebottom);
681 XUnmapWindow(ob_display, self->lgripleft);
682 XUnmapWindow(ob_display, self->rgripright);
683 XUnmapWindow(ob_display, self->lgripbottom);
684 XUnmapWindow(ob_display, self->rgripbottom);
685 }
686
687 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
688 ob_rr_theme->handle_height > 0)
689 {
690 XMoveResizeWindow(ob_display, self->handle,
691 sidebwidth,
692 FRAME_HANDLE_Y(self) + self->bwidth,
693 self->width, ob_rr_theme->handle_height);
694 XMapWindow(ob_display, self->handle);
695
696 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
697 XMoveResizeWindow(ob_display, self->lgrip,
698 0, 0,
699 ob_rr_theme->grip_width,
700 ob_rr_theme->handle_height);
701 XMoveResizeWindow(ob_display, self->rgrip,
702 self->width - ob_rr_theme->grip_width,
703 0,
704 ob_rr_theme->grip_width,
705 ob_rr_theme->handle_height);
706
707 XMapWindow(ob_display, self->lgrip);
708 XMapWindow(ob_display, self->rgrip);
709 } else {
710 XUnmapWindow(ob_display, self->lgrip);
711 XUnmapWindow(ob_display, self->rgrip);
712 }
713 } else {
714 XUnmapWindow(ob_display, self->lgrip);
715 XUnmapWindow(ob_display, self->rgrip);
716
717 XUnmapWindow(ob_display, self->handle);
718 }
719
720 if (self->bwidth && !self->max_horz &&
721 (self->client->area.height + self->size.top +
722 self->size.bottom) > ob_rr_theme->grip_width * 2)
723 {
724 XMoveResizeWindow(ob_display, self->left,
725 0,
726 self->bwidth + ob_rr_theme->grip_width,
727 self->bwidth,
728 self->client->area.height +
729 self->size.top + self->size.bottom -
730 ob_rr_theme->grip_width * 2);
731
732 XMapWindow(ob_display, self->left);
733 } else
734 XUnmapWindow(ob_display, self->left);
735
736 if (self->bwidth && !self->max_horz &&
737 (self->client->area.height + self->size.top +
738 self->size.bottom) > ob_rr_theme->grip_width * 2)
739 {
740 XMoveResizeWindow(ob_display, self->right,
741 self->client->area.width + self->cbwidth_l +
742 self->cbwidth_r + self->bwidth,
743 self->bwidth + ob_rr_theme->grip_width,
744 self->bwidth,
745 self->client->area.height +
746 self->size.top + self->size.bottom -
747 ob_rr_theme->grip_width * 2);
748
749 XMapWindow(ob_display, self->right);
750 } else
751 XUnmapWindow(ob_display, self->right);
752
753 XMoveResizeWindow(ob_display, self->backback,
754 self->size.left, self->size.top,
755 self->client->area.width,
756 self->client->area.height);
757 }
758 }
759
760 /* shading can change without being moved or resized */
761 RECT_SET_SIZE(self->area,
762 self->client->area.width +
763 self->size.left + self->size.right,
764 (self->client->shaded ?
765 ob_rr_theme->title_height + self->bwidth * 2:
766 self->client->area.height +
767 self->size.top + self->size.bottom));
768
769 if ((moved || resized) && !fake) {
770 /* find the new coordinates, done after setting the frame.size, for
771 frame_client_gravity. */
772 self->area.x = self->client->area.x;
773 self->area.y = self->client->area.y;
774 frame_client_gravity(self, &self->area.x, &self->area.y);
775 }
776
777 if (!fake) {
778 if (!frame_iconify_animating(self))
779 /* move and resize the top level frame.
780 shading can change without being moved or resized.
781
782 but don't do this during an iconify animation. it will be
783 reflected afterwards.
784 */
785 XMoveResizeWindow(ob_display, self->window,
786 self->area.x,
787 self->area.y,
788 self->area.width,
789 self->area.height);
790
791 /* when the client has StaticGravity, it likes to move around.
792 also this correctly positions the client when it maps.
793 this also needs to be run when the frame's decorations sizes change!
794 */
795 XMoveWindow(ob_display, self->client->window,
796 self->size.left, self->size.top);
797
798 if (resized) {
799 self->need_render = TRUE;
800 framerender_frame(self);
801 frame_adjust_shape(self);
802 }
803
804 if (!STRUT_EQUAL(self->size, oldsize)) {
805 gulong vals[4];
806 vals[0] = self->size.left;
807 vals[1] = self->size.right;
808 vals[2] = self->size.top;
809 vals[3] = self->size.bottom;
810 PROP_SETA32(self->client->window, net_frame_extents,
811 cardinal, vals, 4);
812 PROP_SETA32(self->client->window, kde_net_wm_frame_strut,
813 cardinal, vals, 4);
814 }
815
816 /* if this occurs while we are focus cycling, the indicator needs to
817 match the changes */
818 if (focus_cycle_target == self->client)
819 focus_cycle_draw_indicator(self->client);
820 }
821 if (resized && (self->decorations & OB_FRAME_DECOR_TITLEBAR))
822 XResizeWindow(ob_display, self->label, self->label_width,
823 ob_rr_theme->label_height);
824
825 }
826
827 static void frame_adjust_cursors(ObFrame *self)
828 {
829 if ((self->functions & OB_CLIENT_FUNC_RESIZE) !=
830 (self->client->functions & OB_CLIENT_FUNC_RESIZE) ||
831 self->max_horz != self->client->max_horz ||
832 self->max_vert != self->client->max_vert ||
833 self->shaded != self->client->shaded)
834 {
835 gboolean r = (self->client->functions & OB_CLIENT_FUNC_RESIZE) &&
836 !(self->client->max_horz && self->client->max_vert);
837 gboolean topbot = !self->client->max_vert;
838 gboolean sh = self->client->shaded;
839 XSetWindowAttributes a;
840
841 /* these ones turn off when max vert, and some when shaded */
842 a.cursor = ob_cursor(r && topbot && !sh ?
843 OB_CURSOR_NORTH : OB_CURSOR_NONE);
844 XChangeWindowAttributes(ob_display, self->topresize, CWCursor, &a);
845 XChangeWindowAttributes(ob_display, self->titletop, CWCursor, &a);
846 a.cursor = ob_cursor(r && topbot ? OB_CURSOR_SOUTH : OB_CURSOR_NONE);
847 XChangeWindowAttributes(ob_display, self->handle, CWCursor, &a);
848 XChangeWindowAttributes(ob_display, self->handletop, CWCursor, &a);
849 XChangeWindowAttributes(ob_display, self->handlebottom, CWCursor, &a);
850 XChangeWindowAttributes(ob_display, self->innerbottom, CWCursor, &a);
851
852 /* these ones change when shaded */
853 a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_WEST : OB_CURSOR_NORTHWEST) :
854 OB_CURSOR_NONE);
855 XChangeWindowAttributes(ob_display, self->titleleft, CWCursor, &a);
856 XChangeWindowAttributes(ob_display, self->tltresize, CWCursor, &a);
857 XChangeWindowAttributes(ob_display, self->tllresize, CWCursor, &a);
858 XChangeWindowAttributes(ob_display, self->titletopleft, CWCursor, &a);
859 a.cursor = ob_cursor(r ? (sh ? OB_CURSOR_EAST : OB_CURSOR_NORTHEAST) :
860 OB_CURSOR_NONE);
861 XChangeWindowAttributes(ob_display, self->titleright, CWCursor, &a);
862 XChangeWindowAttributes(ob_display, self->trtresize, CWCursor, &a);
863 XChangeWindowAttributes(ob_display, self->trrresize, CWCursor, &a);
864 XChangeWindowAttributes(ob_display, self->titletopright, CWCursor, &a);
865
866 /* these ones are pretty static */
867 a.cursor = ob_cursor(r ? OB_CURSOR_WEST : OB_CURSOR_NONE);
868 XChangeWindowAttributes(ob_display, self->left, CWCursor, &a);
869 XChangeWindowAttributes(ob_display, self->innerleft, CWCursor, &a);
870 a.cursor = ob_cursor(r ? OB_CURSOR_EAST : OB_CURSOR_NONE);
871 XChangeWindowAttributes(ob_display, self->right, CWCursor, &a);
872 XChangeWindowAttributes(ob_display, self->innerright, CWCursor, &a);
873 a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHWEST : OB_CURSOR_NONE);
874 XChangeWindowAttributes(ob_display, self->lgrip, CWCursor, &a);
875 XChangeWindowAttributes(ob_display, self->handleleft, CWCursor, &a);
876 XChangeWindowAttributes(ob_display, self->lgripleft, CWCursor, &a);
877 XChangeWindowAttributes(ob_display, self->lgriptop, CWCursor, &a);
878 XChangeWindowAttributes(ob_display, self->lgripbottom, CWCursor, &a);
879 a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHEAST : OB_CURSOR_NONE);
880 XChangeWindowAttributes(ob_display, self->rgrip, CWCursor, &a);
881 XChangeWindowAttributes(ob_display, self->handleright, CWCursor, &a);
882 XChangeWindowAttributes(ob_display, self->rgripright, CWCursor, &a);
883 XChangeWindowAttributes(ob_display, self->rgriptop, CWCursor, &a);
884 XChangeWindowAttributes(ob_display, self->rgripbottom, CWCursor, &a);
885 }
886 }
887
888 void frame_adjust_client_area(ObFrame *self)
889 {
890 /* adjust the window which is there to prevent flashing on unmap */
891 XMoveResizeWindow(ob_display, self->backfront, 0, 0,
892 self->client->area.width,
893 self->client->area.height);
894 }
895
896 void frame_adjust_state(ObFrame *self)
897 {
898 self->need_render = TRUE;
899 framerender_frame(self);
900 }
901
902 void frame_adjust_focus(ObFrame *self, gboolean hilite)
903 {
904 self->focused = hilite;
905 self->need_render = TRUE;
906 framerender_frame(self);
907 XFlush(ob_display);
908 }
909
910 void frame_adjust_title(ObFrame *self)
911 {
912 self->need_render = TRUE;
913 framerender_frame(self);
914 }
915
916 void frame_adjust_icon(ObFrame *self)
917 {
918 self->need_render = TRUE;
919 framerender_frame(self);
920 }
921
922 void frame_grab_client(ObFrame *self)
923 {
924 /* DO NOT map the client window here. we used to do that, but it is bogus.
925 we need to set up the client's dimensions and everything before we
926 send a mapnotify or we create race conditions.
927 */
928
929 /* reparent the client to the frame */
930 XReparentWindow(ob_display, self->client->window, self->window, 0, 0);
931
932 /*
933 When reparenting the client window, it is usually not mapped yet, since
934 this occurs from a MapRequest. However, in the case where Openbox is
935 starting up, the window is already mapped, so we'll see an unmap event
936 for it.
937 */
938 if (ob_state() == OB_STATE_STARTING)
939 ++self->client->ignore_unmaps;
940
941 /* select the event mask on the client's parent (to receive config/map
942 req's) the ButtonPress is to catch clicks on the client border */
943 XSelectInput(ob_display, self->window, FRAME_EVENTMASK);
944
945 /* set all the windows for the frame in the window_map */
946 g_hash_table_insert(window_map, &self->window, self->client);
947 g_hash_table_insert(window_map, &self->backback, self->client);
948 g_hash_table_insert(window_map, &self->backfront, self->client);
949 g_hash_table_insert(window_map, &self->innerleft, self->client);
950 g_hash_table_insert(window_map, &self->innertop, self->client);
951 g_hash_table_insert(window_map, &self->innerright, self->client);
952 g_hash_table_insert(window_map, &self->innerbottom, self->client);
953 g_hash_table_insert(window_map, &self->title, self->client);
954 g_hash_table_insert(window_map, &self->label, self->client);
955 g_hash_table_insert(window_map, &self->max, self->client);
956 g_hash_table_insert(window_map, &self->close, self->client);
957 g_hash_table_insert(window_map, &self->desk, self->client);
958 g_hash_table_insert(window_map, &self->shade, self->client);
959 g_hash_table_insert(window_map, &self->icon, self->client);
960 g_hash_table_insert(window_map, &self->iconify, self->client);
961 g_hash_table_insert(window_map, &self->handle, self->client);
962 g_hash_table_insert(window_map, &self->lgrip, self->client);
963 g_hash_table_insert(window_map, &self->rgrip, self->client);
964 g_hash_table_insert(window_map, &self->topresize, self->client);
965 g_hash_table_insert(window_map, &self->tltresize, self->client);
966 g_hash_table_insert(window_map, &self->tllresize, self->client);
967 g_hash_table_insert(window_map, &self->trtresize, self->client);
968 g_hash_table_insert(window_map, &self->trrresize, self->client);
969 g_hash_table_insert(window_map, &self->left, self->client);
970 g_hash_table_insert(window_map, &self->right, self->client);
971 g_hash_table_insert(window_map, &self->titleleft, self->client);
972 g_hash_table_insert(window_map, &self->titletop, self->client);
973 g_hash_table_insert(window_map, &self->titletopleft, self->client);
974 g_hash_table_insert(window_map, &self->titletopright, self->client);
975 g_hash_table_insert(window_map, &self->titleright, self->client);
976 g_hash_table_insert(window_map, &self->titlebottom, self->client);
977 g_hash_table_insert(window_map, &self->handleleft, self->client);
978 g_hash_table_insert(window_map, &self->handletop, self->client);
979 g_hash_table_insert(window_map, &self->handleright, self->client);
980 g_hash_table_insert(window_map, &self->handlebottom, self->client);
981 g_hash_table_insert(window_map, &self->lgripleft, self->client);
982 g_hash_table_insert(window_map, &self->lgriptop, self->client);
983 g_hash_table_insert(window_map, &self->lgripbottom, self->client);
984 g_hash_table_insert(window_map, &self->rgripright, self->client);
985 g_hash_table_insert(window_map, &self->rgriptop, self->client);
986 g_hash_table_insert(window_map, &self->rgripbottom, self->client);
987 }
988
989 void frame_release_client(ObFrame *self)
990 {
991 XEvent ev;
992 gboolean reparent = TRUE;
993
994 /* if there was any animation going on, kill it */
995 ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
996 self, FALSE);
997
998 /* check if the app has already reparented its window away */
999 while (XCheckTypedWindowEvent(ob_display, self->client->window,
1000 ReparentNotify, &ev))
1001 {
1002 /* This check makes sure we don't catch our own reparent action to
1003 our frame window. This doesn't count as the app reparenting itself
1004 away of course.
1005
1006 Reparent events that are generated by us are just discarded here.
1007 They are of no consequence to us anyhow.
1008 */
1009 if (ev.xreparent.parent != self->window) {
1010 reparent = FALSE;
1011 XPutBackEvent(ob_display, &ev);
1012 break;
1013 }
1014 }
1015
1016 if (reparent) {
1017 /* according to the ICCCM - if the client doesn't reparent itself,
1018 then we will reparent the window to root for them */
1019 XReparentWindow(ob_display, self->client->window,
1020 RootWindow(ob_display, ob_screen),
1021 self->client->area.x,
1022 self->client->area.y);
1023 }
1024
1025 /* remove all the windows for the frame from the window_map */
1026 g_hash_table_remove(window_map, &self->window);
1027 g_hash_table_remove(window_map, &self->backback);
1028 g_hash_table_remove(window_map, &self->backfront);
1029 g_hash_table_remove(window_map, &self->innerleft);
1030 g_hash_table_remove(window_map, &self->innertop);
1031 g_hash_table_remove(window_map, &self->innerright);
1032 g_hash_table_remove(window_map, &self->innerbottom);
1033 g_hash_table_remove(window_map, &self->title);
1034 g_hash_table_remove(window_map, &self->label);
1035 g_hash_table_remove(window_map, &self->max);
1036 g_hash_table_remove(window_map, &self->close);
1037 g_hash_table_remove(window_map, &self->desk);
1038 g_hash_table_remove(window_map, &self->shade);
1039 g_hash_table_remove(window_map, &self->icon);
1040 g_hash_table_remove(window_map, &self->iconify);
1041 g_hash_table_remove(window_map, &self->handle);
1042 g_hash_table_remove(window_map, &self->lgrip);
1043 g_hash_table_remove(window_map, &self->rgrip);
1044 g_hash_table_remove(window_map, &self->topresize);
1045 g_hash_table_remove(window_map, &self->tltresize);
1046 g_hash_table_remove(window_map, &self->tllresize);
1047 g_hash_table_remove(window_map, &self->trtresize);
1048 g_hash_table_remove(window_map, &self->trrresize);
1049 g_hash_table_remove(window_map, &self->left);
1050 g_hash_table_remove(window_map, &self->right);
1051 g_hash_table_remove(window_map, &self->titleleft);
1052 g_hash_table_remove(window_map, &self->titletop);
1053 g_hash_table_remove(window_map, &self->titletopleft);
1054 g_hash_table_remove(window_map, &self->titletopright);
1055 g_hash_table_remove(window_map, &self->titleright);
1056 g_hash_table_remove(window_map, &self->titlebottom);
1057 g_hash_table_remove(window_map, &self->handleleft);
1058 g_hash_table_remove(window_map, &self->handletop);
1059 g_hash_table_remove(window_map, &self->handleright);
1060 g_hash_table_remove(window_map, &self->handlebottom);
1061 g_hash_table_remove(window_map, &self->lgripleft);
1062 g_hash_table_remove(window_map, &self->lgriptop);
1063 g_hash_table_remove(window_map, &self->lgripbottom);
1064 g_hash_table_remove(window_map, &self->rgripright);
1065 g_hash_table_remove(window_map, &self->rgriptop);
1066 g_hash_table_remove(window_map, &self->rgripbottom);
1067
1068 ob_main_loop_timeout_remove_data(ob_main_loop, flash_timeout, self, TRUE);
1069 }
1070
1071 /* is there anything present between us and the label? */
1072 static gboolean is_button_present(ObFrame *self, const gchar *lc, gint dir) {
1073 for (; *lc != '\0' && lc >= config_title_layout; lc += dir) {
1074 if (*lc == ' ') continue; /* it was invalid */
1075 if (*lc == 'N' && self->decorations & OB_FRAME_DECOR_ICON)
1076 return TRUE;
1077 if (*lc == 'D' && self->decorations & OB_FRAME_DECOR_ALLDESKTOPS)
1078 return TRUE;
1079 if (*lc == 'S' && self->decorations & OB_FRAME_DECOR_SHADE)
1080 return TRUE;
1081 if (*lc == 'I' && self->decorations & OB_FRAME_DECOR_ICONIFY)
1082 return TRUE;
1083 if (*lc == 'M' && self->decorations & OB_FRAME_DECOR_MAXIMIZE)
1084 return TRUE;
1085 if (*lc == 'C' && self->decorations & OB_FRAME_DECOR_CLOSE)
1086 return TRUE;
1087 if (*lc == 'L') return FALSE;
1088 }
1089 return FALSE;
1090 }
1091
1092 static void layout_title(ObFrame *self)
1093 {
1094 gchar *lc;
1095 gint i;
1096
1097 const gint bwidth = ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
1098 /* position of the left most button */
1099 const gint left = ob_rr_theme->paddingx + 1;
1100 /* position of the right most button */
1101 const gint right = self->width;
1102
1103 /* turn them all off */
1104 self->icon_on = self->desk_on = self->shade_on = self->iconify_on =
1105 self->max_on = self->close_on = self->label_on = FALSE;
1106 self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2;
1107 self->leftmost = self->rightmost = OB_FRAME_CONTEXT_NONE;
1108
1109 /* figure out what's being show, find each element's position, and the
1110 width of the label
1111
1112 do the ones before the label, then after the label,
1113 i will be +1 the first time through when working to the left,
1114 and -1 the second time through when working to the right */
1115 for (i = 1; i >= -1; i-=2) {
1116 gint x;
1117 ObFrameContext *firstcon;
1118
1119 if (i > 0) {
1120 x = left;
1121 lc = config_title_layout;
1122 firstcon = &self->leftmost;
1123 } else {
1124 x = right;
1125 lc = config_title_layout + strlen(config_title_layout)-1;
1126 firstcon = &self->rightmost;
1127 }
1128
1129 /* stop at the end of the string (or the label, which calls break) */
1130 for (; *lc != '\0' && lc >= config_title_layout; lc+=i) {
1131 if (*lc == 'L') {
1132 if (i > 0) {
1133 self->label_on = TRUE;
1134 self->label_x = x;
1135 }
1136 break; /* break the for loop, do other side of label */
1137 } else if (*lc == 'N') {
1138 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICON;
1139 if ((self->icon_on = is_button_present(self, lc, i))) {
1140 /* icon is bigger than buttons */
1141 self->label_width -= bwidth + 2;
1142 if (i > 0) self->icon_x = x;
1143 x += i * (bwidth + 2);
1144 if (i < 0) self->icon_x = x;
1145 }
1146 } else if (*lc == 'D') {
1147 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ALLDESKTOPS;
1148 if ((self->desk_on = is_button_present(self, lc, i))) {
1149 self->label_width -= bwidth;
1150 if (i > 0) self->desk_x = x;
1151 x += i * bwidth;
1152 if (i < 0) self->desk_x = x;
1153 }
1154 } else if (*lc == 'S') {
1155 if (firstcon) *firstcon = OB_FRAME_CONTEXT_SHADE;
1156 if ((self->shade_on = is_button_present(self, lc, i))) {
1157 self->label_width -= bwidth;
1158 if (i > 0) self->shade_x = x;
1159 x += i * bwidth;
1160 if (i < 0) self->shade_x = x;
1161 }
1162 } else if (*lc == 'I') {
1163 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICONIFY;
1164 if ((self->iconify_on = is_button_present(self, lc, i))) {
1165 self->label_width -= bwidth;
1166 if (i > 0) self->iconify_x = x;
1167 x += i * bwidth;
1168 if (i < 0) self->iconify_x = x;
1169 }
1170 } else if (*lc == 'M') {
1171 if (firstcon) *firstcon = OB_FRAME_CONTEXT_MAXIMIZE;
1172 if ((self->max_on = is_button_present(self, lc, i))) {
1173 self->label_width -= bwidth;
1174 if (i > 0) self->max_x = x;
1175 x += i * bwidth;
1176 if (i < 0) self->max_x = x;
1177 }
1178 } else if (*lc == 'C') {
1179 if (firstcon) *firstcon = OB_FRAME_CONTEXT_CLOSE;
1180 if ((self->close_on = is_button_present(self, lc, i))) {
1181 self->label_width -= bwidth;
1182 if (i > 0) self->close_x = x;
1183 x += i * bwidth;
1184 if (i < 0) self->close_x = x;
1185 }
1186 } else
1187 continue; /* don't set firstcon */
1188 firstcon = NULL;
1189 }
1190 }
1191
1192 /* position and map the elements */
1193 if (self->icon_on) {
1194 XMapWindow(ob_display, self->icon);
1195 XMoveWindow(ob_display, self->icon, self->icon_x,
1196 ob_rr_theme->paddingy);
1197 } else
1198 XUnmapWindow(ob_display, self->icon);
1199
1200 if (self->desk_on) {
1201 XMapWindow(ob_display, self->desk);
1202 XMoveWindow(ob_display, self->desk, self->desk_x,
1203 ob_rr_theme->paddingy + 1);
1204 } else
1205 XUnmapWindow(ob_display, self->desk);
1206
1207 if (self->shade_on) {
1208 XMapWindow(ob_display, self->shade);
1209 XMoveWindow(ob_display, self->shade, self->shade_x,
1210 ob_rr_theme->paddingy + 1);
1211 } else
1212 XUnmapWindow(ob_display, self->shade);
1213
1214 if (self->iconify_on) {
1215 XMapWindow(ob_display, self->iconify);
1216 XMoveWindow(ob_display, self->iconify, self->iconify_x,
1217 ob_rr_theme->paddingy + 1);
1218 } else
1219 XUnmapWindow(ob_display, self->iconify);
1220
1221 if (self->max_on) {
1222 XMapWindow(ob_display, self->max);
1223 XMoveWindow(ob_display, self->max, self->max_x,
1224 ob_rr_theme->paddingy + 1);
1225 } else
1226 XUnmapWindow(ob_display, self->max);
1227
1228 if (self->close_on) {
1229 XMapWindow(ob_display, self->close);
1230 XMoveWindow(ob_display, self->close, self->close_x,
1231 ob_rr_theme->paddingy + 1);
1232 } else
1233 XUnmapWindow(ob_display, self->close);
1234
1235 if (self->label_on) {
1236 self->label_width = MAX(1, self->label_width); /* no lower than 1 */
1237 XMapWindow(ob_display, self->label);
1238 XMoveWindow(ob_display, self->label, self->label_x,
1239 ob_rr_theme->paddingy);
1240 } else
1241 XUnmapWindow(ob_display, self->label);
1242 }
1243
1244 ObFrameContext frame_context_from_string(const gchar *name)
1245 {
1246 if (!g_ascii_strcasecmp("Desktop", name))
1247 return OB_FRAME_CONTEXT_DESKTOP;
1248 else if (!g_ascii_strcasecmp("Root", name))
1249 return OB_FRAME_CONTEXT_ROOT;
1250 else if (!g_ascii_strcasecmp("Client", name))
1251 return OB_FRAME_CONTEXT_CLIENT;
1252 else if (!g_ascii_strcasecmp("Titlebar", name))
1253 return OB_FRAME_CONTEXT_TITLEBAR;
1254 else if (!g_ascii_strcasecmp("Frame", name))
1255 return OB_FRAME_CONTEXT_FRAME;
1256 else if (!g_ascii_strcasecmp("TLCorner", name))
1257 return OB_FRAME_CONTEXT_TLCORNER;
1258 else if (!g_ascii_strcasecmp("TRCorner", name))
1259 return OB_FRAME_CONTEXT_TRCORNER;
1260 else if (!g_ascii_strcasecmp("BLCorner", name))
1261 return OB_FRAME_CONTEXT_BLCORNER;
1262 else if (!g_ascii_strcasecmp("BRCorner", name))
1263 return OB_FRAME_CONTEXT_BRCORNER;
1264 else if (!g_ascii_strcasecmp("Top", name))
1265 return OB_FRAME_CONTEXT_TOP;
1266 else if (!g_ascii_strcasecmp("Bottom", name))
1267 return OB_FRAME_CONTEXT_BOTTOM;
1268 else if (!g_ascii_strcasecmp("Left", name))
1269 return OB_FRAME_CONTEXT_LEFT;
1270 else if (!g_ascii_strcasecmp("Right", name))
1271 return OB_FRAME_CONTEXT_RIGHT;
1272 else if (!g_ascii_strcasecmp("Maximize", name))
1273 return OB_FRAME_CONTEXT_MAXIMIZE;
1274 else if (!g_ascii_strcasecmp("AllDesktops", name))
1275 return OB_FRAME_CONTEXT_ALLDESKTOPS;
1276 else if (!g_ascii_strcasecmp("Shade", name))
1277 return OB_FRAME_CONTEXT_SHADE;
1278 else if (!g_ascii_strcasecmp("Iconify", name))
1279 return OB_FRAME_CONTEXT_ICONIFY;
1280 else if (!g_ascii_strcasecmp("Icon", name))
1281 return OB_FRAME_CONTEXT_ICON;
1282 else if (!g_ascii_strcasecmp("Close", name))
1283 return OB_FRAME_CONTEXT_CLOSE;
1284 else if (!g_ascii_strcasecmp("MoveResize", name))
1285 return OB_FRAME_CONTEXT_MOVE_RESIZE;
1286 return OB_FRAME_CONTEXT_NONE;
1287 }
1288
1289 ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y)
1290 {
1291 ObFrame *self;
1292
1293 if (moveresize_in_progress)
1294 return OB_FRAME_CONTEXT_MOVE_RESIZE;
1295
1296 if (win == RootWindow(ob_display, ob_screen))
1297 return OB_FRAME_CONTEXT_ROOT ;
1298 if (client == NULL) return OB_FRAME_CONTEXT_NONE;
1299 if (win == client->window) {
1300 /* conceptually, this is the desktop, as far as users are
1301 concerned */
1302 if (client->type == OB_CLIENT_TYPE_DESKTOP)
1303 return OB_FRAME_CONTEXT_DESKTOP;
1304 return OB_FRAME_CONTEXT_CLIENT;
1305 }
1306
1307 self = client->frame;
1308
1309 /* when the user clicks in the corners of the titlebar and the client
1310 is fully maximized, then treat it like they clicked in the
1311 button that is there */
1312 if (self->max_horz && self->max_vert &&
1313 (win == self->title || win == self->titletop ||
1314 win == self->titleleft || win == self->titletopleft ||
1315 win == self->titleright || win == self->titletopright))
1316 {
1317 /* get the mouse coords in reference to the whole frame */
1318 gint fx = x;
1319 gint fy = y;
1320
1321 /* these windows are down a border width from the top of the frame */
1322 if (win == self->title ||
1323 win == self->titleleft || win == self->titleright)
1324 fy += self->bwidth;
1325
1326 /* title is a border width in from the edge */
1327 if (win == self->title)
1328 fx += self->bwidth;
1329 /* titletop is a bit to the right */
1330 else if (win == self->titletop)
1331 fx += ob_rr_theme->grip_width + self->bwidth;
1332 /* titletopright is way to the right edge */
1333 else if (win == self->titletopright)
1334 fx += self->area.width - (ob_rr_theme->grip_width + self->bwidth);
1335 /* titleright is even more way to the right edge */
1336 else if (win == self->titleright)
1337 fx += self->area.width - self->bwidth;
1338
1339 /* figure out if we're over the area that should be considered a
1340 button */
1341 if (fy < self->bwidth + ob_rr_theme->paddingy + 1 +
1342 ob_rr_theme->button_size)
1343 {
1344 if (fx < (self->bwidth + ob_rr_theme->paddingx + 1 +
1345 ob_rr_theme->button_size))
1346 {
1347 if (self->leftmost != OB_FRAME_CONTEXT_NONE)
1348 return self->leftmost;
1349 }
1350 else if (fx >= (self->area.width -
1351 (self->bwidth + ob_rr_theme->paddingx + 1 +
1352 ob_rr_theme->button_size)))
1353 {
1354 if (self->rightmost != OB_FRAME_CONTEXT_NONE)
1355 return self->rightmost;
1356 }
1357 }
1358
1359 /* there is no resizing maximized windows so make them the titlebar
1360 context */
1361 return OB_FRAME_CONTEXT_TITLEBAR;
1362 }
1363 else if (self->max_vert &&
1364 (win == self->titletop || win == self->topresize))
1365 /* can't resize vertically when max vert */
1366 return OB_FRAME_CONTEXT_TITLEBAR;
1367 else if (self->shaded &&
1368 (win == self->titletop || win == self->topresize))
1369 /* can't resize vertically when shaded */
1370 return OB_FRAME_CONTEXT_TITLEBAR;
1371
1372 if (win == self->window) return OB_FRAME_CONTEXT_FRAME;
1373 if (win == self->label) return OB_FRAME_CONTEXT_TITLEBAR;
1374 if (win == self->handle) return OB_FRAME_CONTEXT_BOTTOM;
1375 if (win == self->handletop) return OB_FRAME_CONTEXT_BOTTOM;
1376 if (win == self->handlebottom) return OB_FRAME_CONTEXT_BOTTOM;
1377 if (win == self->handleleft) return OB_FRAME_CONTEXT_BLCORNER;
1378 if (win == self->lgrip) return OB_FRAME_CONTEXT_BLCORNER;
1379 if (win == self->lgripleft) return OB_FRAME_CONTEXT_BLCORNER;
1380 if (win == self->lgriptop) return OB_FRAME_CONTEXT_BLCORNER;
1381 if (win == self->lgripbottom) return OB_FRAME_CONTEXT_BLCORNER;
1382 if (win == self->handleright) return OB_FRAME_CONTEXT_BRCORNER;
1383 if (win == self->rgrip) return OB_FRAME_CONTEXT_BRCORNER;
1384 if (win == self->rgripright) return OB_FRAME_CONTEXT_BLCORNER;
1385 if (win == self->rgriptop) return OB_FRAME_CONTEXT_BLCORNER;
1386 if (win == self->rgripbottom) return OB_FRAME_CONTEXT_BLCORNER;
1387 if (win == self->title) return OB_FRAME_CONTEXT_TITLEBAR;
1388 if (win == self->titlebottom) return OB_FRAME_CONTEXT_TITLEBAR;
1389 if (win == self->titleleft) return OB_FRAME_CONTEXT_TLCORNER;
1390 if (win == self->titletopleft) return OB_FRAME_CONTEXT_TLCORNER;
1391 if (win == self->titleright) return OB_FRAME_CONTEXT_TRCORNER;
1392 if (win == self->titletopright) return OB_FRAME_CONTEXT_TRCORNER;
1393 if (win == self->titletop) return OB_FRAME_CONTEXT_TOP;
1394 if (win == self->topresize) return OB_FRAME_CONTEXT_TOP;
1395 if (win == self->tltresize) return OB_FRAME_CONTEXT_TLCORNER;
1396 if (win == self->tllresize) return OB_FRAME_CONTEXT_TLCORNER;
1397 if (win == self->trtresize) return OB_FRAME_CONTEXT_TRCORNER;
1398 if (win == self->trrresize) return OB_FRAME_CONTEXT_TRCORNER;
1399 if (win == self->left) return OB_FRAME_CONTEXT_LEFT;
1400 if (win == self->right) return OB_FRAME_CONTEXT_RIGHT;
1401 if (win == self->innertop) return OB_FRAME_CONTEXT_TITLEBAR;
1402 if (win == self->innerleft) return OB_FRAME_CONTEXT_LEFT;
1403 if (win == self->innerbottom) return OB_FRAME_CONTEXT_BOTTOM;
1404 if (win == self->innerright) return OB_FRAME_CONTEXT_RIGHT;
1405 if (win == self->max) return OB_FRAME_CONTEXT_MAXIMIZE;
1406 if (win == self->iconify) return OB_FRAME_CONTEXT_ICONIFY;
1407 if (win == self->close) return OB_FRAME_CONTEXT_CLOSE;
1408 if (win == self->icon) return OB_FRAME_CONTEXT_ICON;
1409 if (win == self->desk) return OB_FRAME_CONTEXT_ALLDESKTOPS;
1410 if (win == self->shade) return OB_FRAME_CONTEXT_SHADE;
1411
1412 return OB_FRAME_CONTEXT_NONE;
1413 }
1414
1415 void frame_client_gravity(ObFrame *self, gint *x, gint *y)
1416 {
1417 /* horizontal */
1418 switch (self->client->gravity) {
1419 default:
1420 case NorthWestGravity:
1421 case SouthWestGravity:
1422 case WestGravity:
1423 break;
1424
1425 case NorthGravity:
1426 case SouthGravity:
1427 case CenterGravity:
1428 /* the middle of the client will be the middle of the frame */
1429 *x -= (self->size.right - self->size.left) / 2;
1430 break;
1431
1432 case NorthEastGravity:
1433 case SouthEastGravity:
1434 case EastGravity:
1435 /* the right side of the client will be the right side of the frame */
1436 *x -= self->size.right + self->size.left -
1437 self->client->border_width * 2;
1438 break;
1439
1440 case ForgetGravity:
1441 case StaticGravity:
1442 /* the client's position won't move */
1443 *x -= self->size.left - self->client->border_width;
1444 break;
1445 }
1446
1447 /* vertical */
1448 switch (self->client->gravity) {
1449 default:
1450 case NorthWestGravity:
1451 case NorthEastGravity:
1452 case NorthGravity:
1453 break;
1454
1455 case CenterGravity:
1456 case EastGravity:
1457 case WestGravity:
1458 /* the middle of the client will be the middle of the frame */
1459 *y -= (self->size.bottom - self->size.top) / 2;
1460 break;
1461
1462 case SouthWestGravity:
1463 case SouthEastGravity:
1464 case SouthGravity:
1465 /* the bottom of the client will be the bottom of the frame */
1466 *y -= self->size.bottom + self->size.top -
1467 self->client->border_width * 2;
1468 break;
1469
1470 case ForgetGravity:
1471 case StaticGravity:
1472 /* the client's position won't move */
1473 *y -= self->size.top - self->client->border_width;
1474 break;
1475 }
1476 }
1477
1478 void frame_frame_gravity(ObFrame *self, gint *x, gint *y)
1479 {
1480 /* horizontal */
1481 switch (self->client->gravity) {
1482 default:
1483 case NorthWestGravity:
1484 case WestGravity:
1485 case SouthWestGravity:
1486 break;
1487 case NorthGravity:
1488 case CenterGravity:
1489 case SouthGravity:
1490 /* the middle of the client will be the middle of the frame */
1491 *x += (self->size.right - self->size.left) / 2;
1492 break;
1493 case NorthEastGravity:
1494 case EastGravity:
1495 case SouthEastGravity:
1496 /* the right side of the client will be the right side of the frame */
1497 *x += self->size.right + self->size.left -
1498 self->client->border_width * 2;
1499 break;
1500 case StaticGravity:
1501 case ForgetGravity:
1502 /* the client's position won't move */
1503 *x += self->size.left - self->client->border_width;
1504 break;
1505 }
1506
1507 /* vertical */
1508 switch (self->client->gravity) {
1509 default:
1510 case NorthWestGravity:
1511 case NorthGravity:
1512 case NorthEastGravity:
1513 break;
1514 case WestGravity:
1515 case CenterGravity:
1516 case EastGravity:
1517 /* the middle of the client will be the middle of the frame */
1518 *y += (self->size.bottom - self->size.top) / 2;
1519 break;
1520 case SouthWestGravity:
1521 case SouthGravity:
1522 case SouthEastGravity:
1523 /* the bottom of the client will be the bottom of the frame */
1524 *y += self->size.bottom + self->size.top -
1525 self->client->border_width * 2;
1526 break;
1527 case StaticGravity:
1528 case ForgetGravity:
1529 /* the client's position won't move */
1530 *y += self->size.top - self->client->border_width;
1531 break;
1532 }
1533 }
1534
1535 void frame_rect_to_frame(ObFrame *self, Rect *r)
1536 {
1537 r->width += self->size.left + self->size.right;
1538 r->height += self->size.top + self->size.bottom;
1539 frame_client_gravity(self, &r->x, &r->y);
1540 }
1541
1542 static void flash_done(gpointer data)
1543 {
1544 ObFrame *self = data;
1545
1546 if (self->focused != self->flash_on)
1547 frame_adjust_focus(self, self->focused);
1548 }
1549
1550 static gboolean flash_timeout(gpointer data)
1551 {
1552 ObFrame *self = data;
1553 GTimeVal now;
1554
1555 g_get_current_time(&now);
1556 if (now.tv_sec > self->flash_end.tv_sec ||
1557 (now.tv_sec == self->flash_end.tv_sec &&
1558 now.tv_usec >= self->flash_end.tv_usec))
1559 self->flashing = FALSE;
1560
1561 if (!self->flashing)
1562 return FALSE; /* we are done */
1563
1564 self->flash_on = !self->flash_on;
1565 if (!self->focused) {
1566 frame_adjust_focus(self, self->flash_on);
1567 self->focused = FALSE;
1568 }
1569
1570 return TRUE; /* go again */
1571 }
1572
1573 void frame_flash_start(ObFrame *self)
1574 {
1575 self->flash_on = self->focused;
1576
1577 if (!self->flashing)
1578 ob_main_loop_timeout_add(ob_main_loop,
1579 G_USEC_PER_SEC * 0.6,
1580 flash_timeout,
1581 self,
1582 g_direct_equal,
1583 flash_done);
1584 g_get_current_time(&self->flash_end);
1585 g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
1586
1587 self->flashing = TRUE;
1588 }
1589
1590 void frame_flash_stop(ObFrame *self)
1591 {
1592 self->flashing = FALSE;
1593 }
1594
1595 static gulong frame_animate_iconify_time_left(ObFrame *self,
1596 const GTimeVal *now)
1597 {
1598 glong sec, usec;
1599 sec = self->iconify_animation_end.tv_sec - now->tv_sec;
1600 usec = self->iconify_animation_end.tv_usec - now->tv_usec;
1601 if (usec < 0) {
1602 usec += G_USEC_PER_SEC;
1603 sec--;
1604 }
1605 /* no negative values */
1606 return MAX(sec * G_USEC_PER_SEC + usec, 0);
1607 }
1608
1609 static gboolean frame_animate_iconify(gpointer p)
1610 {
1611 ObFrame *self = p;
1612 gint x, y, w, h;
1613 gint iconx, icony, iconw;
1614 GTimeVal now;
1615 gulong time;
1616 gboolean iconifying;
1617
1618 if (self->client->icon_geometry.width == 0) {
1619 /* there is no icon geometry set so just go straight down */
1620 Rect *a = screen_physical_area_monitor
1621 (screen_find_monitor(&self->area));
1622 iconx = self->area.x + self->area.width / 2 + 32;
1623 icony = a->y + a->width;
1624 iconw = 64;
1625 g_free(a);
1626 } else {
1627 iconx = self->client->icon_geometry.x;
1628 icony = self->client->icon_geometry.y;
1629 iconw = self->client->icon_geometry.width;
1630 }
1631
1632 iconifying = self->iconify_animation_going > 0;
1633
1634 /* how far do we have left to go ? */
1635 g_get_current_time(&now);
1636 time = frame_animate_iconify_time_left(self, &now);
1637
1638 if (time == 0 || iconifying) {
1639 /* start where the frame is supposed to be */
1640 x = self->area.x;
1641 y = self->area.y;
1642 w = self->area.width;
1643 h = self->area.height;
1644 } else {
1645 /* start at the icon */
1646 x = iconx;
1647 y = icony;
1648 w = iconw;
1649 h = self->size.top; /* just the titlebar */
1650 }
1651
1652 if (time > 0) {
1653 glong dx, dy, dw;
1654 glong elapsed;
1655
1656 dx = self->area.x - iconx;
1657 dy = self->area.y - icony;
1658 dw = self->area.width - self->bwidth * 2 - iconw;
1659 /* if restoring, we move in the opposite direction */
1660 if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; }
1661
1662 elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
1663 x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1664 y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1665 w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1666 h = self->size.top; /* just the titlebar */
1667 }
1668
1669 if (time == 0)
1670 frame_end_iconify_animation(self);
1671 else {
1672 XMoveResizeWindow(ob_display, self->window, x, y, w, h);
1673 XFlush(ob_display);
1674 }
1675
1676 return time > 0; /* repeat until we're out of time */
1677 }
1678
1679 void frame_end_iconify_animation(ObFrame *self)
1680 {
1681 /* see if there is an animation going */
1682 if (self->iconify_animation_going == 0) return;
1683
1684 if (!self->visible)
1685 XUnmapWindow(ob_display, self->window);
1686 else {
1687 /* Send a ConfigureNotify when the animation is done, this fixes
1688 KDE's pager showing the window in the wrong place. since the
1689 window is mapped at a different location and is then moved, we
1690 need to send the synthetic configurenotify, since apps may have
1691 read the position when the client mapped, apparently. */
1692 client_reconfigure(self->client, TRUE);
1693 }
1694
1695 /* we're not animating any more ! */
1696 self->iconify_animation_going = 0;
1697
1698 XMoveResizeWindow(ob_display, self->window,
1699 self->area.x, self->area.y,
1700 self->area.width, self->area.height);
1701 /* we delay re-rendering until after we're done animating */
1702 framerender_frame(self);
1703 XFlush(ob_display);
1704 }
1705
1706 void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying)
1707 {
1708 gulong time;
1709 gboolean new_anim = FALSE;
1710 gboolean set_end = TRUE;
1711 GTimeVal now;
1712
1713 /* if there is no titlebar, just don't animate for now
1714 XXX it would be nice tho.. */
1715 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1716 return;
1717
1718 /* get the current time */
1719 g_get_current_time(&now);
1720
1721 /* get how long until the end */
1722 time = FRAME_ANIMATE_ICONIFY_TIME;
1723 if (self->iconify_animation_going) {
1724 if (!!iconifying != (self->iconify_animation_going > 0)) {
1725 /* animation was already going on in the opposite direction */
1726 time = time - frame_animate_iconify_time_left(self, &now);
1727 } else
1728 /* animation was already going in the same direction */
1729 set_end = FALSE;
1730 } else
1731 new_anim = TRUE;
1732 self->iconify_animation_going = iconifying ? 1 : -1;
1733
1734 /* set the ending time */
1735 if (set_end) {
1736 self->iconify_animation_end.tv_sec = now.tv_sec;
1737 self->iconify_animation_end.tv_usec = now.tv_usec;
1738 g_time_val_add(&self->iconify_animation_end, time);
1739 }
1740
1741 if (new_anim) {
1742 ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
1743 self, FALSE);
1744 ob_main_loop_timeout_add(ob_main_loop,
1745 FRAME_ANIMATE_ICONIFY_STEP_TIME,
1746 frame_animate_iconify, self,
1747 g_direct_equal, NULL);
1748
1749 /* do the first step */
1750 frame_animate_iconify(self);
1751
1752 /* show it during the animation even if it is not "visible" */
1753 if (!self->visible)
1754 XMapWindow(ob_display, self->window);
1755 }
1756 }
This page took 0.120821 seconds and 4 git commands to generate.