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