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