]> Dogcows Code - chaz/openbox/blob - openbox/frame.c
construct the titlebar without borders
[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->leftresize = createWindow(self->window, NULL, mask, &attrib);
148 self->rightresize = 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->focused = FALSE;
163
164 /* the other stuff is shown based on decor settings */
165 XMapWindow(ob_display, self->plate);
166 XMapWindow(ob_display, self->inner);
167 XMapWindow(ob_display, self->lgrip);
168 XMapWindow(ob_display, self->rgrip);
169 XMapWindow(ob_display, self->label);
170
171 self->max_press = self->close_press = self->desk_press =
172 self->iconify_press = self->shade_press = FALSE;
173 self->max_hover = self->close_hover = self->desk_hover =
174 self->iconify_hover = self->shade_hover = FALSE;
175
176 set_theme_statics(self);
177
178 return (ObFrame*)self;
179 }
180
181 static void set_theme_statics(ObFrame *self)
182 {
183 gint handle_height;
184
185 if (ob_rr_theme->handle_height > 0)
186 handle_height = ob_rr_theme->handle_height;
187 else
188 handle_height = 1;
189
190
191 /* set colors/appearance/sizes for stuff that doesn't change */
192 XResizeWindow(ob_display, self->max,
193 ob_rr_theme->button_size, ob_rr_theme->button_size);
194 XResizeWindow(ob_display, self->iconify,
195 ob_rr_theme->button_size, ob_rr_theme->button_size);
196 XResizeWindow(ob_display, self->icon,
197 ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
198 XResizeWindow(ob_display, self->close,
199 ob_rr_theme->button_size, ob_rr_theme->button_size);
200 XResizeWindow(ob_display, self->desk,
201 ob_rr_theme->button_size, ob_rr_theme->button_size);
202 XResizeWindow(ob_display, self->shade,
203 ob_rr_theme->button_size, ob_rr_theme->button_size);
204 XResizeWindow(ob_display, self->lgrip,
205 ob_rr_theme->grip_width, handle_height);
206 XResizeWindow(ob_display, self->rgrip,
207 ob_rr_theme->grip_width, handle_height);
208 XResizeWindow(ob_display, self->tltresize,
209 ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
210 XResizeWindow(ob_display, self->trtresize,
211 ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
212 XResizeWindow(ob_display, self->tllresize,
213 ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
214 XResizeWindow(ob_display, self->trrresize,
215 ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
216
217 /* set up the dynamic appearances */
218 self->a_unfocused_title = RrAppearanceCopy(ob_rr_theme->a_unfocused_title);
219 self->a_focused_title = RrAppearanceCopy(ob_rr_theme->a_focused_title);
220 self->a_unfocused_label = RrAppearanceCopy(ob_rr_theme->a_unfocused_label);
221 self->a_focused_label = RrAppearanceCopy(ob_rr_theme->a_focused_label);
222 self->a_unfocused_handle =
223 RrAppearanceCopy(ob_rr_theme->a_unfocused_handle);
224 self->a_focused_handle = RrAppearanceCopy(ob_rr_theme->a_focused_handle);
225 self->a_icon = RrAppearanceCopy(ob_rr_theme->a_icon);
226 }
227
228 static void free_theme_statics(ObFrame *self)
229 {
230 RrAppearanceFree(self->a_unfocused_title);
231 RrAppearanceFree(self->a_focused_title);
232 RrAppearanceFree(self->a_unfocused_label);
233 RrAppearanceFree(self->a_focused_label);
234 RrAppearanceFree(self->a_unfocused_handle);
235 RrAppearanceFree(self->a_focused_handle);
236 RrAppearanceFree(self->a_icon);
237 }
238
239 void frame_free(ObFrame *self)
240 {
241 free_theme_statics(self);
242
243 XDestroyWindow(ob_display, self->window);
244 if (self->colormap)
245 XFreeColormap(ob_display, self->colormap);
246
247 g_free(self);
248 }
249
250 void frame_show(ObFrame *self)
251 {
252 if (!self->visible) {
253 self->visible = TRUE;
254 XMapWindow(ob_display, self->client->window);
255 XMapWindow(ob_display, self->window);
256 }
257 }
258
259 void frame_hide(ObFrame *self)
260 {
261 if (self->visible) {
262 self->visible = FALSE;
263 if (!frame_iconify_animating(self))
264 XUnmapWindow(ob_display, self->window);
265 /* we unmap the client itself so that we can get MapRequest
266 events, and because the ICCCM tells us to! */
267 XUnmapWindow(ob_display, self->client->window);
268 self->client->ignore_unmaps += 1;
269 }
270 }
271
272 void frame_adjust_theme(ObFrame *self)
273 {
274 free_theme_statics(self);
275 set_theme_statics(self);
276 }
277
278 void frame_adjust_shape(ObFrame *self)
279 {
280 #ifdef SHAPE
281 gint num;
282 XRectangle xrect[2];
283
284 if (!self->client->shaped) {
285 /* clear the shape on the frame window */
286 XShapeCombineMask(ob_display, self->window, ShapeBounding,
287 self->size.left,
288 self->size.top,
289 None, ShapeSet);
290 } else {
291 /* make the frame's shape match the clients */
292 XShapeCombineShape(ob_display, self->window, ShapeBounding,
293 self->size.left,
294 self->size.top,
295 self->client->window,
296 ShapeBounding, ShapeSet);
297
298 num = 0;
299 if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
300 xrect[0].x = -ob_rr_theme->fbwidth;
301 xrect[0].y = -ob_rr_theme->fbwidth;
302 xrect[0].width = self->width + self->bwidth * 2;
303 xrect[0].height = ob_rr_theme->title_height +
304 self->bwidth * 2;
305 ++num;
306 }
307
308 if (self->decorations & OB_FRAME_DECOR_HANDLE) {
309 xrect[1].x = -ob_rr_theme->fbwidth;
310 xrect[1].y = FRAME_HANDLE_Y(self);
311 xrect[1].width = self->width + self->bwidth * 2;
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
344 if (self->max_horz)
345 self->cbwidth_x = 0;
346
347 self->width = self->client->area.width + self->cbwidth_x * 2;
348 self->width = MAX(self->width, 1); /* no lower than 1 */
349
350 STRUT_SET(self->size,
351 self->cbwidth_x + self->bwidth,
352 self->cbwidth_y + self->bwidth,
353 self->cbwidth_x + self->bwidth,
354 self->cbwidth_y + self->bwidth);
355
356 /* set border widths */
357 if (!fake) {
358 XSetWindowBorderWidth(ob_display, self->handle, self->bwidth);
359 XSetWindowBorderWidth(ob_display, self->lgrip, self->bwidth);
360 XSetWindowBorderWidth(ob_display, self->rgrip, self->bwidth);
361 }
362
363 if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
364 self->size.top += ob_rr_theme->title_height + self->bwidth +
365 (self->bwidth - self->bwidth);
366 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
367 ob_rr_theme->handle_height > 0)
368 self->size.bottom += ob_rr_theme->handle_height +
369 self->bwidth + (self->bwidth - self->bwidth);
370
371 /* position/size and map/unmap all the windows */
372
373 if (!fake) {
374 if (self->bwidth) {
375 XMoveResizeWindow(ob_display, self->titletop,
376 ob_rr_theme->grip_width + self->bwidth, 0,
377 self->client->area.width +
378 self->cbwidth_x * 2 + self->bwidth * 2 -
379 (ob_rr_theme->grip_width + self->bwidth) * 2,
380 self->bwidth);
381 XMoveResizeWindow(ob_display, self->titletopleft,
382 0, 0,
383 ob_rr_theme->grip_width + self->bwidth,
384 self->bwidth);
385 XMoveResizeWindow(ob_display, self->titletopright,
386 self->client->area.width +
387 self->cbwidth_x * 2 + self->bwidth * 2 -
388 ob_rr_theme->grip_width - self->bwidth,
389 0,
390 ob_rr_theme->grip_width + self->bwidth,
391 self->bwidth);
392 XMoveResizeWindow(ob_display, self->titleleft,
393 0, self->bwidth,
394 self->bwidth,
395 ob_rr_theme->grip_width);
396 XMoveResizeWindow(ob_display, self->titleright,
397 self->client->area.width +
398 self->cbwidth_x * 2 + self->bwidth,
399 self->bwidth,
400 self->bwidth,
401 ob_rr_theme->grip_width);
402
403 XMapWindow(ob_display, self->titletop);
404 XMapWindow(ob_display, self->titletopleft);
405 XMapWindow(ob_display, self->titletopright);
406 XMapWindow(ob_display, self->titleleft);
407 XMapWindow(ob_display, self->titleright);
408
409 if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
410 XMoveResizeWindow(ob_display, self->titlebottom,
411 self->bwidth,
412 ob_rr_theme->title_height + self->bwidth,
413 self->client->area.width +
414 self->cbwidth_x * 2,
415 self->bwidth);
416
417 XMapWindow(ob_display, self->titlebottom);
418 } else
419 XUnmapWindow(ob_display, self->titlebottom);
420 } else {
421 XUnmapWindow(ob_display, self->titletop);
422 XUnmapWindow(ob_display, self->titletopleft);
423 XUnmapWindow(ob_display, self->titletopright);
424 XUnmapWindow(ob_display, self->titleleft);
425 XUnmapWindow(ob_display, self->titleright);
426 }
427
428 if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
429 XMoveResizeWindow(ob_display, self->title,
430 self->bwidth, self->bwidth,
431 self->width, ob_rr_theme->title_height);
432
433 XMapWindow(ob_display, self->title);
434
435 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
436 XMoveResizeWindow(ob_display, self->topresize,
437 ob_rr_theme->grip_width + self->bwidth,
438 0,
439 self->width - (ob_rr_theme->grip_width +
440 self->bwidth) * 2,
441 ob_rr_theme->paddingy + 1);
442
443 XMoveWindow(ob_display, self->tltresize, 0, 0);
444 XMoveWindow(ob_display, self->tllresize, 0, 0);
445 XMoveWindow(ob_display, self->trtresize,
446 self->width - ob_rr_theme->grip_width, 0);
447 XMoveWindow(ob_display, self->trrresize,
448 self->width - ob_rr_theme->paddingx - 1, 0);
449
450 XMapWindow(ob_display, self->topresize);
451 XMapWindow(ob_display, self->tltresize);
452 XMapWindow(ob_display, self->tllresize);
453 XMapWindow(ob_display, self->trtresize);
454 XMapWindow(ob_display, self->trrresize);
455 } else {
456 XUnmapWindow(ob_display, self->topresize);
457 XUnmapWindow(ob_display, self->tltresize);
458 XUnmapWindow(ob_display, self->tllresize);
459 XUnmapWindow(ob_display, self->trtresize);
460 XUnmapWindow(ob_display, self->trrresize);
461 }
462 } else
463 XUnmapWindow(ob_display, self->title);
464 }
465
466 if ((self->decorations & OB_FRAME_DECOR_TITLEBAR))
467 /* layout the title bar elements */
468 layout_title(self);
469
470 if (!fake) {
471 if (self->decorations & OB_FRAME_DECOR_HANDLE)
472 {
473 gint handle_height;
474
475 if (ob_rr_theme->handle_height > 0)
476 handle_height = ob_rr_theme->handle_height;
477 else
478 handle_height = 1;
479
480 XMoveResizeWindow(ob_display, self->handle,
481 0, FRAME_HANDLE_Y(self),
482 self->width, handle_height);
483 XMapWindow(ob_display, self->handle);
484
485 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
486 XMoveWindow(ob_display, self->lgrip,
487 -self->bwidth, -self->bwidth);
488 XMoveWindow(ob_display, self->rgrip,
489 -self->bwidth + self->width -
490 ob_rr_theme->grip_width, -self->bwidth);
491 XMapWindow(ob_display, self->lgrip);
492 XMapWindow(ob_display, self->rgrip);
493 } else {
494 XUnmapWindow(ob_display, self->lgrip);
495 XUnmapWindow(ob_display, self->rgrip);
496 }
497 } else
498 XUnmapWindow(ob_display, self->handle);
499
500 if (self->bwidth && !self->max_horz) {
501 XMoveResizeWindow(ob_display, self->leftresize,
502 0,
503 self->bwidth + ob_rr_theme->grip_width,
504 self->bwidth,
505 self->client->area.height +
506 self->cbwidth_y * 2);
507 XMoveResizeWindow(ob_display, self->rightresize,
508 self->client->area.width +
509 self->cbwidth_x * 2 + self->bwidth,
510 self->bwidth + ob_rr_theme->grip_width,
511 self->bwidth,
512 self->client->area.height +
513 self->cbwidth_y * 2);
514
515 XMapWindow(ob_display, self->leftresize);
516 XMapWindow(ob_display, self->rightresize);
517 } else {
518 XUnmapWindow(ob_display, self->leftresize);
519 XUnmapWindow(ob_display, self->rightresize);
520 }
521
522 /* move and resize the inner border window which contains the plate
523 */
524 XMoveResizeWindow(ob_display, self->inner,
525 0,
526 self->size.top - self->cbwidth_y,
527 self->client->area.width +
528 self->cbwidth_x * 2 + self->bwidth * 2,
529 self->client->area.height +
530 self->cbwidth_y * 2);
531
532 /* move the plate */
533 XMoveWindow(ob_display, self->plate,
534 self->bwidth + self->cbwidth_x, self->cbwidth_y);
535
536 /* when the client has StaticGravity, it likes to move around. */
537 XMoveWindow(ob_display, self->client->window, 0, 0);
538 }
539 }
540
541 /* shading can change without being moved or resized */
542 RECT_SET_SIZE(self->area,
543 self->client->area.width +
544 self->size.left + self->size.right,
545 (self->client->shaded ?
546 ob_rr_theme->title_height + self->bwidth * 2:
547 self->client->area.height +
548 self->size.top + self->size.bottom));
549
550 if (moved || resized) {
551 /* find the new coordinates, done after setting the frame.size, for
552 frame_client_gravity. */
553 self->area.x = self->client->area.x;
554 self->area.y = self->client->area.y;
555 frame_client_gravity(self, &self->area.x, &self->area.y,
556 self->client->area.width,
557 self->client->area.height);
558 }
559
560 if (!fake) {
561 if (!frame_iconify_animating(self))
562 /* move and resize the top level frame.
563 shading can change without being moved or resized.
564
565 but don't do this during an iconify animation. it will be
566 reflected afterwards.
567 */
568 XMoveResizeWindow(ob_display, self->window,
569 self->area.x,
570 self->area.y,
571 self->area.width,
572 self->area.height);
573
574 if (resized) {
575 framerender_frame(self);
576 frame_adjust_shape(self);
577 }
578
579 if (!STRUT_EQUAL(self->size, oldsize)) {
580 gulong vals[4];
581 vals[0] = self->size.left;
582 vals[1] = self->size.right;
583 vals[2] = self->size.top;
584 vals[3] = self->size.bottom;
585 PROP_SETA32(self->client->window, net_frame_extents,
586 cardinal, vals, 4);
587 PROP_SETA32(self->client->window, kde_net_wm_frame_strut,
588 cardinal, vals, 4);
589 }
590
591 /* if this occurs while we are focus cycling, the indicator needs to
592 match the changes */
593 if (focus_cycle_target == self->client)
594 focus_cycle_draw_indicator(self->client);
595 }
596 if (resized && (self->decorations & OB_FRAME_DECOR_TITLEBAR))
597 XResizeWindow(ob_display, self->label, self->label_width,
598 ob_rr_theme->label_height);
599
600 /* set up cursors */
601 if (!fake &&
602 (self->functions & OB_CLIENT_FUNC_RESIZE) !=
603 (self->client->functions & OB_CLIENT_FUNC_RESIZE))
604 {
605 gboolean r = self->client->functions & OB_CLIENT_FUNC_RESIZE;
606 XSetWindowAttributes a;
607
608 a.cursor = ob_cursor(r ? OB_CURSOR_NORTH : OB_CURSOR_NONE);
609 XChangeWindowAttributes(ob_display, self->topresize, CWCursor, &a);
610 XChangeWindowAttributes(ob_display, self->titletop, CWCursor, &a);
611 a.cursor = ob_cursor(r ? OB_CURSOR_NORTHWEST : OB_CURSOR_NONE);
612 XChangeWindowAttributes(ob_display, self->tltresize, CWCursor, &a);
613 XChangeWindowAttributes(ob_display, self->tllresize, CWCursor, &a);
614 XChangeWindowAttributes(ob_display, self->titletopleft, CWCursor, &a);
615 XChangeWindowAttributes(ob_display, self->titleleft, CWCursor, &a);
616 a.cursor = ob_cursor(r ? OB_CURSOR_NORTHEAST : OB_CURSOR_NONE);
617 XChangeWindowAttributes(ob_display, self->trtresize, CWCursor, &a);
618 XChangeWindowAttributes(ob_display, self->trrresize, CWCursor, &a);
619 XChangeWindowAttributes(ob_display, self->titletopright, CWCursor, &a);
620 XChangeWindowAttributes(ob_display, self->titleright, CWCursor, &a);
621 a.cursor = ob_cursor(r ? OB_CURSOR_WEST : OB_CURSOR_NONE);
622 XChangeWindowAttributes(ob_display, self->leftresize, CWCursor, &a);
623 a.cursor = ob_cursor(r ? OB_CURSOR_EAST : OB_CURSOR_NONE);
624 XChangeWindowAttributes(ob_display, self->rightresize, CWCursor, &a);
625 a.cursor = ob_cursor(r ? OB_CURSOR_SOUTH : OB_CURSOR_NONE);
626 XChangeWindowAttributes(ob_display, self->handle, CWCursor, &a);
627 a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHWEST : OB_CURSOR_NONE);
628 XChangeWindowAttributes(ob_display, self->lgrip, CWCursor, &a);
629 a.cursor = ob_cursor(r ? OB_CURSOR_SOUTHEAST : OB_CURSOR_NONE);
630 XChangeWindowAttributes(ob_display, self->rgrip, CWCursor, &a);
631
632 self->functions = self->client->functions;
633 }
634 }
635
636 void frame_adjust_client_area(ObFrame *self)
637 {
638 /* resize the plate */
639 XResizeWindow(ob_display, self->plate,
640 self->client->area.width, self->client->area.height);
641 }
642
643 void frame_adjust_state(ObFrame *self)
644 {
645 framerender_frame(self);
646 }
647
648 void frame_adjust_focus(ObFrame *self, gboolean hilite)
649 {
650 self->focused = hilite;
651 framerender_frame(self);
652 XFlush(ob_display);
653 }
654
655 void frame_adjust_title(ObFrame *self)
656 {
657 framerender_frame(self);
658 }
659
660 void frame_adjust_icon(ObFrame *self)
661 {
662 framerender_frame(self);
663 }
664
665 void frame_grab_client(ObFrame *self)
666 {
667 /* reparent the client to the frame */
668 XReparentWindow(ob_display, self->client->window, self->plate, 0, 0);
669
670 /*
671 When reparenting the client window, it is usually not mapped yet, since
672 this occurs from a MapRequest. However, in the case where Openbox is
673 starting up, the window is already mapped, so we'll see unmap events for
674 it. There are 2 unmap events generated that we see, one with the 'event'
675 member set the root window, and one set to the client, but both get
676 handled and need to be ignored.
677 */
678 if (ob_state() == OB_STATE_STARTING)
679 self->client->ignore_unmaps += 2;
680
681 /* select the event mask on the client's parent (to receive config/map
682 req's) the ButtonPress is to catch clicks on the client border */
683 XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
684
685 /* map the client so it maps when the frame does */
686 XMapWindow(ob_display, self->client->window);
687
688 /* set all the windows for the frame in the window_map */
689 g_hash_table_insert(window_map, &self->window, self->client);
690 g_hash_table_insert(window_map, &self->plate, self->client);
691 g_hash_table_insert(window_map, &self->inner, self->client);
692 g_hash_table_insert(window_map, &self->title, self->client);
693 g_hash_table_insert(window_map, &self->label, self->client);
694 g_hash_table_insert(window_map, &self->max, self->client);
695 g_hash_table_insert(window_map, &self->close, self->client);
696 g_hash_table_insert(window_map, &self->desk, self->client);
697 g_hash_table_insert(window_map, &self->shade, self->client);
698 g_hash_table_insert(window_map, &self->icon, self->client);
699 g_hash_table_insert(window_map, &self->iconify, self->client);
700 g_hash_table_insert(window_map, &self->handle, self->client);
701 g_hash_table_insert(window_map, &self->lgrip, self->client);
702 g_hash_table_insert(window_map, &self->rgrip, self->client);
703 g_hash_table_insert(window_map, &self->topresize, self->client);
704 g_hash_table_insert(window_map, &self->tltresize, self->client);
705 g_hash_table_insert(window_map, &self->tllresize, self->client);
706 g_hash_table_insert(window_map, &self->trtresize, self->client);
707 g_hash_table_insert(window_map, &self->trrresize, self->client);
708 g_hash_table_insert(window_map, &self->leftresize, self->client);
709 g_hash_table_insert(window_map, &self->rightresize, self->client);
710 g_hash_table_insert(window_map, &self->titleleft, self->client);
711 g_hash_table_insert(window_map, &self->titletop, self->client);
712 g_hash_table_insert(window_map, &self->titletopleft, self->client);
713 g_hash_table_insert(window_map, &self->titletopright, self->client);
714 g_hash_table_insert(window_map, &self->titleright, self->client);
715 g_hash_table_insert(window_map, &self->titlebottom, self->client);
716 }
717
718 void frame_release_client(ObFrame *self)
719 {
720 XEvent ev;
721 gboolean reparent = TRUE;
722
723 /* if there was any animation going on, kill it */
724 ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
725 self, FALSE);
726
727 /* check if the app has already reparented its window away */
728 while (XCheckTypedWindowEvent(ob_display, self->client->window,
729 ReparentNotify, &ev))
730 {
731 /* This check makes sure we don't catch our own reparent action to
732 our frame window. This doesn't count as the app reparenting itself
733 away of course.
734
735 Reparent events that are generated by us are just discarded here.
736 They are of no consequence to us anyhow.
737 */
738 if (ev.xreparent.parent != self->plate) {
739 reparent = FALSE;
740 XPutBackEvent(ob_display, &ev);
741 break;
742 }
743 }
744
745 if (reparent) {
746 /* according to the ICCCM - if the client doesn't reparent itself,
747 then we will reparent the window to root for them */
748 XReparentWindow(ob_display, self->client->window,
749 RootWindow(ob_display, ob_screen),
750 self->client->area.x,
751 self->client->area.y);
752 }
753
754 /* remove all the windows for the frame from the window_map */
755 g_hash_table_remove(window_map, &self->window);
756 g_hash_table_remove(window_map, &self->plate);
757 g_hash_table_remove(window_map, &self->inner);
758 g_hash_table_remove(window_map, &self->title);
759 g_hash_table_remove(window_map, &self->label);
760 g_hash_table_remove(window_map, &self->max);
761 g_hash_table_remove(window_map, &self->close);
762 g_hash_table_remove(window_map, &self->desk);
763 g_hash_table_remove(window_map, &self->shade);
764 g_hash_table_remove(window_map, &self->icon);
765 g_hash_table_remove(window_map, &self->iconify);
766 g_hash_table_remove(window_map, &self->handle);
767 g_hash_table_remove(window_map, &self->lgrip);
768 g_hash_table_remove(window_map, &self->rgrip);
769 g_hash_table_remove(window_map, &self->topresize);
770 g_hash_table_remove(window_map, &self->tltresize);
771 g_hash_table_remove(window_map, &self->tllresize);
772 g_hash_table_remove(window_map, &self->trtresize);
773 g_hash_table_remove(window_map, &self->trrresize);
774 g_hash_table_remove(window_map, &self->leftresize);
775 g_hash_table_remove(window_map, &self->rightresize);
776 g_hash_table_remove(window_map, &self->titleleft);
777 g_hash_table_remove(window_map, &self->titletop);
778 g_hash_table_remove(window_map, &self->titletopleft);
779 g_hash_table_remove(window_map, &self->titletopright);
780 g_hash_table_remove(window_map, &self->titleright);
781 g_hash_table_remove(window_map, &self->titlebottom);
782
783 ob_main_loop_timeout_remove_data(ob_main_loop, flash_timeout, self, TRUE);
784 }
785
786 /* is there anything present between us and the label? */
787 static gboolean is_button_present(ObFrame *self, const gchar *lc, gint dir) {
788 for (; *lc != '\0' && lc >= config_title_layout; lc += dir) {
789 if (*lc == ' ') continue; /* it was invalid */
790 if (*lc == 'N' && self->decorations & OB_FRAME_DECOR_ICON)
791 return TRUE;
792 if (*lc == 'D' && self->decorations & OB_FRAME_DECOR_ALLDESKTOPS)
793 return TRUE;
794 if (*lc == 'S' && self->decorations & OB_FRAME_DECOR_SHADE)
795 return TRUE;
796 if (*lc == 'I' && self->decorations & OB_FRAME_DECOR_ICONIFY)
797 return TRUE;
798 if (*lc == 'M' && self->decorations & OB_FRAME_DECOR_MAXIMIZE)
799 return TRUE;
800 if (*lc == 'C' && self->decorations & OB_FRAME_DECOR_CLOSE)
801 return TRUE;
802 if (*lc == 'L') return FALSE;
803 }
804 return FALSE;
805 }
806
807 static void layout_title(ObFrame *self)
808 {
809 gchar *lc;
810 gint i;
811
812 const gint bwidth = ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
813 /* position of the left most button */
814 const gint left = ob_rr_theme->paddingx + 1;
815 /* position of the right most button */
816 const gint right = self->width - bwidth;
817
818 /* turn them all off */
819 self->icon_on = self->desk_on = self->shade_on = self->iconify_on =
820 self->max_on = self->close_on = self->label_on = FALSE;
821 self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2;
822 self->leftmost = self->rightmost = OB_FRAME_CONTEXT_NONE;
823
824 /* figure out what's being show, find each element's position, and the
825 width of the label
826
827 do the ones before the label, then after the label,
828 i will be +1 the first time through when working to the left,
829 and -1 the second time through when working to the right */
830 for (i = 1; i >= -1; i-=2) {
831 gint x;
832 ObFrameContext *firstcon;
833
834 if (i > 0) {
835 x = left;
836 lc = config_title_layout;
837 firstcon = &self->leftmost;
838 } else {
839 x = right;
840 lc = config_title_layout + strlen(config_title_layout)-1;
841 firstcon = &self->rightmost;
842 }
843
844 /* stop at the end of the string (or the label, which calls break) */
845 for (; *lc != '\0' && lc >= config_title_layout; lc+=i) {
846 if (*lc == 'L') {
847 if (i > 0) {
848 self->label_on = TRUE;
849 self->label_x = x;
850 }
851 break; /* break the for loop, do other side of label */
852 } else if (*lc == 'N') {
853 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICON;
854 if ((self->icon_on = is_button_present(self, lc, i))) {
855 /* icon is bigger than buttons */
856 self->label_width -= bwidth + 2;
857 self->icon_x = x;
858 x += i * (bwidth + 2);
859 }
860 } else if (*lc == 'D') {
861 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ALLDESKTOPS;
862 if ((self->desk_on = is_button_present(self, lc, i))) {
863 self->label_width -= bwidth;
864 self->desk_x = x;
865 x += i * bwidth;
866 }
867 } else if (*lc == 'S') {
868 if (firstcon) *firstcon = OB_FRAME_CONTEXT_SHADE;
869 if ((self->shade_on = is_button_present(self, lc, i))) {
870 self->label_width -= bwidth;
871 self->shade_x = x;
872 x += i * bwidth;
873 }
874 } else if (*lc == 'I') {
875 if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICONIFY;
876 if ((self->iconify_on = is_button_present(self, lc, i))) {
877 self->label_width -= bwidth;
878 self->iconify_x = x;
879 x += i * bwidth;
880 }
881 } else if (*lc == 'M') {
882 if (firstcon) *firstcon = OB_FRAME_CONTEXT_MAXIMIZE;
883 if ((self->max_on = is_button_present(self, lc, i))) {
884 self->label_width -= bwidth;
885 self->max_x = x;
886 x += i * bwidth;
887 }
888 } else if (*lc == 'C') {
889 if (firstcon) *firstcon = OB_FRAME_CONTEXT_CLOSE;
890 if ((self->close_on = is_button_present(self, lc, i))) {
891 self->label_width -= bwidth;
892 self->close_x = x;
893 x += i * bwidth;
894 }
895 } else
896 continue; /* don't set firstcon */
897 firstcon = NULL;
898 }
899 }
900
901 /* position and map the elements */
902 if (self->icon_on) {
903 XMapWindow(ob_display, self->icon);
904 XMoveWindow(ob_display, self->icon, self->icon_x,
905 ob_rr_theme->paddingy);
906 } else
907 XUnmapWindow(ob_display, self->icon);
908
909 if (self->desk_on) {
910 XMapWindow(ob_display, self->desk);
911 XMoveWindow(ob_display, self->desk, self->desk_x,
912 ob_rr_theme->paddingy + 1);
913 } else
914 XUnmapWindow(ob_display, self->desk);
915
916 if (self->shade_on) {
917 XMapWindow(ob_display, self->shade);
918 XMoveWindow(ob_display, self->shade, self->shade_x,
919 ob_rr_theme->paddingy + 1);
920 } else
921 XUnmapWindow(ob_display, self->shade);
922
923 if (self->iconify_on) {
924 XMapWindow(ob_display, self->iconify);
925 XMoveWindow(ob_display, self->iconify, self->iconify_x,
926 ob_rr_theme->paddingy + 1);
927 } else
928 XUnmapWindow(ob_display, self->iconify);
929
930 if (self->max_on) {
931 XMapWindow(ob_display, self->max);
932 XMoveWindow(ob_display, self->max, self->max_x,
933 ob_rr_theme->paddingy + 1);
934 } else
935 XUnmapWindow(ob_display, self->max);
936
937 if (self->close_on) {
938 XMapWindow(ob_display, self->close);
939 XMoveWindow(ob_display, self->close, self->close_x,
940 ob_rr_theme->paddingy + 1);
941 } else
942 XUnmapWindow(ob_display, self->close);
943
944 if (self->label_on) {
945 self->label_width = MAX(1, self->label_width); /* no lower than 1 */
946 XMapWindow(ob_display, self->label);
947 XMoveWindow(ob_display, self->label, self->label_x,
948 ob_rr_theme->paddingy);
949 } else
950 XUnmapWindow(ob_display, self->label);
951 }
952
953 ObFrameContext frame_context_from_string(const gchar *name)
954 {
955 if (!g_ascii_strcasecmp("Desktop", name))
956 return OB_FRAME_CONTEXT_DESKTOP;
957 else if (!g_ascii_strcasecmp("Root", name))
958 return OB_FRAME_CONTEXT_ROOT;
959 else if (!g_ascii_strcasecmp("Client", name))
960 return OB_FRAME_CONTEXT_CLIENT;
961 else if (!g_ascii_strcasecmp("Titlebar", name))
962 return OB_FRAME_CONTEXT_TITLEBAR;
963 else if (!g_ascii_strcasecmp("Frame", name))
964 return OB_FRAME_CONTEXT_FRAME;
965 else if (!g_ascii_strcasecmp("TLCorner", name))
966 return OB_FRAME_CONTEXT_TLCORNER;
967 else if (!g_ascii_strcasecmp("TRCorner", name))
968 return OB_FRAME_CONTEXT_TRCORNER;
969 else if (!g_ascii_strcasecmp("BLCorner", name))
970 return OB_FRAME_CONTEXT_BLCORNER;
971 else if (!g_ascii_strcasecmp("BRCorner", name))
972 return OB_FRAME_CONTEXT_BRCORNER;
973 else if (!g_ascii_strcasecmp("Top", name))
974 return OB_FRAME_CONTEXT_TOP;
975 else if (!g_ascii_strcasecmp("Bottom", name))
976 return OB_FRAME_CONTEXT_BOTTOM;
977 else if (!g_ascii_strcasecmp("Left", name))
978 return OB_FRAME_CONTEXT_LEFT;
979 else if (!g_ascii_strcasecmp("Right", name))
980 return OB_FRAME_CONTEXT_RIGHT;
981 else if (!g_ascii_strcasecmp("Maximize", name))
982 return OB_FRAME_CONTEXT_MAXIMIZE;
983 else if (!g_ascii_strcasecmp("AllDesktops", name))
984 return OB_FRAME_CONTEXT_ALLDESKTOPS;
985 else if (!g_ascii_strcasecmp("Shade", name))
986 return OB_FRAME_CONTEXT_SHADE;
987 else if (!g_ascii_strcasecmp("Iconify", name))
988 return OB_FRAME_CONTEXT_ICONIFY;
989 else if (!g_ascii_strcasecmp("Icon", name))
990 return OB_FRAME_CONTEXT_ICON;
991 else if (!g_ascii_strcasecmp("Close", name))
992 return OB_FRAME_CONTEXT_CLOSE;
993 else if (!g_ascii_strcasecmp("MoveResize", name))
994 return OB_FRAME_CONTEXT_MOVE_RESIZE;
995 return OB_FRAME_CONTEXT_NONE;
996 }
997
998 ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y)
999 {
1000 ObFrame *self;
1001
1002 if (moveresize_in_progress)
1003 return OB_FRAME_CONTEXT_MOVE_RESIZE;
1004
1005 if (win == RootWindow(ob_display, ob_screen))
1006 return OB_FRAME_CONTEXT_ROOT ;
1007 if (client == NULL) return OB_FRAME_CONTEXT_NONE;
1008 if (win == client->window) {
1009 /* conceptually, this is the desktop, as far as users are
1010 concerned */
1011 if (client->type == OB_CLIENT_TYPE_DESKTOP)
1012 return OB_FRAME_CONTEXT_DESKTOP;
1013 return OB_FRAME_CONTEXT_CLIENT;
1014 }
1015
1016 self = client->frame;
1017 if (win == self->inner || win == self->plate) {
1018 /* conceptually, this is the desktop, as far as users are
1019 concerned */
1020 if (client->type == OB_CLIENT_TYPE_DESKTOP)
1021 return OB_FRAME_CONTEXT_DESKTOP;
1022 return OB_FRAME_CONTEXT_CLIENT;
1023 }
1024
1025 if (win == self->title) {
1026 /* when the user clicks in the corners of the titlebar and the client
1027 is fully maximized, then treat it like they clicked in the
1028 button that is there */
1029 if (self->client->max_horz && self->client->max_vert &&
1030 y < ob_rr_theme->paddingy + 1 + ob_rr_theme->button_size)
1031 {
1032 if (x < ((ob_rr_theme->paddingx + 1) * 2 +
1033 ob_rr_theme->button_size)) {
1034 if (self->leftmost != OB_FRAME_CONTEXT_NONE)
1035 return self->leftmost;
1036 }
1037 else if (x > (self->width -
1038 (ob_rr_theme->paddingx + 1 +
1039 ob_rr_theme->button_size)))
1040 {
1041 if (self->rightmost != OB_FRAME_CONTEXT_NONE)
1042 return self->rightmost;
1043 }
1044 }
1045 return OB_FRAME_CONTEXT_TITLEBAR;
1046 }
1047
1048 if (win == self->window) return OB_FRAME_CONTEXT_FRAME;
1049 if (win == self->label) return OB_FRAME_CONTEXT_TITLEBAR;
1050 if (win == self->handle) return OB_FRAME_CONTEXT_BOTTOM;
1051 if (win == self->lgrip) return OB_FRAME_CONTEXT_BLCORNER;
1052 if (win == self->rgrip) return OB_FRAME_CONTEXT_BRCORNER;
1053 if (win == self->titletop) return OB_FRAME_CONTEXT_TOP;
1054 if (win == self->topresize) return OB_FRAME_CONTEXT_TOP;
1055 if (win == self->tltresize) return OB_FRAME_CONTEXT_TLCORNER;
1056 if (win == self->tllresize) return OB_FRAME_CONTEXT_TLCORNER;
1057 if (win == self->titleleft) return OB_FRAME_CONTEXT_TLCORNER;
1058 if (win == self->titletopleft) return OB_FRAME_CONTEXT_TLCORNER;
1059 if (win == self->trtresize) return OB_FRAME_CONTEXT_TRCORNER;
1060 if (win == self->trrresize) return OB_FRAME_CONTEXT_TRCORNER;
1061 if (win == self->titleright) return OB_FRAME_CONTEXT_TRCORNER;
1062 if (win == self->titletopright) return OB_FRAME_CONTEXT_TRCORNER;
1063 if (win == self->leftresize) return OB_FRAME_CONTEXT_LEFT;
1064 if (win == self->rightresize) return OB_FRAME_CONTEXT_RIGHT;
1065 if (win == self->max) return OB_FRAME_CONTEXT_MAXIMIZE;
1066 if (win == self->iconify) return OB_FRAME_CONTEXT_ICONIFY;
1067 if (win == self->close) return OB_FRAME_CONTEXT_CLOSE;
1068 if (win == self->icon) return OB_FRAME_CONTEXT_ICON;
1069 if (win == self->desk) return OB_FRAME_CONTEXT_ALLDESKTOPS;
1070 if (win == self->shade) return OB_FRAME_CONTEXT_SHADE;
1071
1072 return OB_FRAME_CONTEXT_NONE;
1073 }
1074
1075 void frame_client_gravity(ObFrame *self, gint *x, gint *y, gint w, gint h)
1076 {
1077 /* horizontal */
1078 switch (self->client->gravity) {
1079 default:
1080 case NorthWestGravity:
1081 case SouthWestGravity:
1082 case WestGravity:
1083 break;
1084
1085 case NorthGravity:
1086 case SouthGravity:
1087 case CenterGravity:
1088 *x -= (self->size.left + w) / 2;
1089 break;
1090
1091 case NorthEastGravity:
1092 case SouthEastGravity:
1093 case EastGravity:
1094 *x -= (self->size.left + self->size.right + w) - 1;
1095 break;
1096
1097 case ForgetGravity:
1098 case StaticGravity:
1099 *x -= self->size.left;
1100 break;
1101 }
1102
1103 /* vertical */
1104 switch (self->client->gravity) {
1105 default:
1106 case NorthWestGravity:
1107 case NorthEastGravity:
1108 case NorthGravity:
1109 break;
1110
1111 case CenterGravity:
1112 case EastGravity:
1113 case WestGravity:
1114 *y -= (self->size.top + h) / 2;
1115 break;
1116
1117 case SouthWestGravity:
1118 case SouthEastGravity:
1119 case SouthGravity:
1120 *y -= (self->size.top + self->size.bottom + h) - 1;
1121 break;
1122
1123 case ForgetGravity:
1124 case StaticGravity:
1125 *y -= self->size.top;
1126 break;
1127 }
1128 }
1129
1130 void frame_frame_gravity(ObFrame *self, gint *x, gint *y, gint w, gint h)
1131 {
1132 /* horizontal */
1133 switch (self->client->gravity) {
1134 default:
1135 case NorthWestGravity:
1136 case WestGravity:
1137 case SouthWestGravity:
1138 break;
1139 case NorthGravity:
1140 case CenterGravity:
1141 case SouthGravity:
1142 *x += (self->size.left + w) / 2;
1143 break;
1144 case NorthEastGravity:
1145 case EastGravity:
1146 case SouthEastGravity:
1147 *x += (self->size.left + self->size.right + w) - 1;
1148 break;
1149 case StaticGravity:
1150 case ForgetGravity:
1151 *x += self->size.left;
1152 break;
1153 }
1154
1155 /* vertical */
1156 switch (self->client->gravity) {
1157 default:
1158 case NorthWestGravity:
1159 case NorthGravity:
1160 case NorthEastGravity:
1161 break;
1162 case WestGravity:
1163 case CenterGravity:
1164 case EastGravity:
1165 *y += (self->size.top + h) / 2;
1166 break;
1167 case SouthWestGravity:
1168 case SouthGravity:
1169 case SouthEastGravity:
1170 *y += (self->size.top + self->size.bottom + h) - 1;
1171 break;
1172 case StaticGravity:
1173 case ForgetGravity:
1174 *y += self->size.top;
1175 break;
1176 }
1177 }
1178
1179 static void flash_done(gpointer data)
1180 {
1181 ObFrame *self = data;
1182
1183 if (self->focused != self->flash_on)
1184 frame_adjust_focus(self, self->focused);
1185 }
1186
1187 static gboolean flash_timeout(gpointer data)
1188 {
1189 ObFrame *self = data;
1190 GTimeVal now;
1191
1192 g_get_current_time(&now);
1193 if (now.tv_sec > self->flash_end.tv_sec ||
1194 (now.tv_sec == self->flash_end.tv_sec &&
1195 now.tv_usec >= self->flash_end.tv_usec))
1196 self->flashing = FALSE;
1197
1198 if (!self->flashing)
1199 return FALSE; /* we are done */
1200
1201 self->flash_on = !self->flash_on;
1202 if (!self->focused) {
1203 frame_adjust_focus(self, self->flash_on);
1204 self->focused = FALSE;
1205 }
1206
1207 return TRUE; /* go again */
1208 }
1209
1210 void frame_flash_start(ObFrame *self)
1211 {
1212 self->flash_on = self->focused;
1213
1214 if (!self->flashing)
1215 ob_main_loop_timeout_add(ob_main_loop,
1216 G_USEC_PER_SEC * 0.6,
1217 flash_timeout,
1218 self,
1219 g_direct_equal,
1220 flash_done);
1221 g_get_current_time(&self->flash_end);
1222 g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
1223
1224 self->flashing = TRUE;
1225 }
1226
1227 void frame_flash_stop(ObFrame *self)
1228 {
1229 self->flashing = FALSE;
1230 }
1231
1232 static gulong frame_animate_iconify_time_left(ObFrame *self,
1233 const GTimeVal *now)
1234 {
1235 glong sec, usec;
1236 sec = self->iconify_animation_end.tv_sec - now->tv_sec;
1237 usec = self->iconify_animation_end.tv_usec - now->tv_usec;
1238 if (usec < 0) {
1239 usec += G_USEC_PER_SEC;
1240 sec--;
1241 }
1242 /* no negative values */
1243 return MAX(sec * G_USEC_PER_SEC + usec, 0);
1244 }
1245
1246 static gboolean frame_animate_iconify(gpointer p)
1247 {
1248 ObFrame *self = p;
1249 gint x, y, w, h;
1250 gint iconx, icony, iconw;
1251 GTimeVal now;
1252 gulong time;
1253 gboolean iconifying;
1254
1255 if (self->client->icon_geometry.width == 0) {
1256 /* there is no icon geometry set so just go straight down */
1257 Rect *a = screen_physical_area();
1258 iconx = self->area.x + self->area.width / 2 + 32;
1259 icony = a->y + a->width;
1260 iconw = 64;
1261 } else {
1262 iconx = self->client->icon_geometry.x;
1263 icony = self->client->icon_geometry.y;
1264 iconw = self->client->icon_geometry.width;
1265 }
1266
1267 iconifying = self->iconify_animation_going > 0;
1268
1269 /* how far do we have left to go ? */
1270 g_get_current_time(&now);
1271 time = frame_animate_iconify_time_left(self, &now);
1272
1273 if (time == 0 || iconifying) {
1274 /* start where the frame is supposed to be */
1275 x = self->area.x;
1276 y = self->area.y;
1277 w = self->area.width - self->bwidth * 2;
1278 h = self->area.height - self->bwidth * 2;
1279 } else {
1280 /* start at the icon */
1281 x = iconx;
1282 y = icony;
1283 w = iconw;
1284 h = self->size.top; /* just the titlebar */
1285 }
1286
1287 if (time > 0) {
1288 glong dx, dy, dw;
1289 glong elapsed;
1290
1291 dx = self->area.x - iconx;
1292 dy = self->area.y - icony;
1293 dw = self->area.width - self->bwidth * 2 - iconw;
1294 /* if restoring, we move in the opposite direction */
1295 if (!iconifying) { dx = -dx; dy = -dy; dw = -dw; }
1296
1297 elapsed = FRAME_ANIMATE_ICONIFY_TIME - time;
1298 x = x - (dx * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1299 y = y - (dy * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1300 w = w - (dw * elapsed) / FRAME_ANIMATE_ICONIFY_TIME;
1301 h = self->size.top; /* just the titlebar */
1302 }
1303
1304 if (time == 0)
1305 frame_end_iconify_animation(self);
1306 else {
1307 XMoveResizeWindow(ob_display, self->window, x, y, w, h);
1308 XFlush(ob_display);
1309 }
1310
1311 return time > 0; /* repeat until we're out of time */
1312 }
1313
1314 void frame_end_iconify_animation(ObFrame *self)
1315 {
1316 /* see if there is an animation going */
1317 if (self->iconify_animation_going == 0) return;
1318
1319 if (!self->visible)
1320 XUnmapWindow(ob_display, self->window);
1321 else
1322 /* Send a ConfigureNotify when the animation is done, this fixes
1323 KDE's pager showing the window in the wrong place. */
1324 client_reconfigure(self->client);
1325
1326 /* we're not animating any more ! */
1327 self->iconify_animation_going = 0;
1328
1329 XMoveResizeWindow(ob_display, self->window,
1330 self->area.x, self->area.y,
1331 self->area.width - self->bwidth * 2,
1332 self->area.height - self->bwidth * 2);
1333 XFlush(ob_display);
1334 }
1335
1336 void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying)
1337 {
1338 gulong time;
1339 gboolean new_anim = FALSE;
1340 gboolean set_end = TRUE;
1341 GTimeVal now;
1342
1343 /* if there is no titlebar, just don't animate for now
1344 XXX it would be nice tho.. */
1345 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1346 return;
1347
1348 /* get the current time */
1349 g_get_current_time(&now);
1350
1351 /* get how long until the end */
1352 time = FRAME_ANIMATE_ICONIFY_TIME;
1353 if (self->iconify_animation_going) {
1354 if (!!iconifying != (self->iconify_animation_going > 0)) {
1355 /* animation was already going on in the opposite direction */
1356 time = time - frame_animate_iconify_time_left(self, &now);
1357 } else
1358 /* animation was already going in the same direction */
1359 set_end = FALSE;
1360 } else
1361 new_anim = TRUE;
1362 self->iconify_animation_going = iconifying ? 1 : -1;
1363
1364 /* set the ending time */
1365 if (set_end) {
1366 self->iconify_animation_end.tv_sec = now.tv_sec;
1367 self->iconify_animation_end.tv_usec = now.tv_usec;
1368 g_time_val_add(&self->iconify_animation_end, time);
1369 }
1370
1371 if (new_anim) {
1372 ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify,
1373 self, FALSE);
1374 ob_main_loop_timeout_add(ob_main_loop,
1375 FRAME_ANIMATE_ICONIFY_STEP_TIME,
1376 frame_animate_iconify, self,
1377 g_direct_equal, NULL);
1378
1379 /* do the first step */
1380 frame_animate_iconify(self);
1381
1382 /* show it during the animation even if it is not "visible" */
1383 if (!self->visible)
1384 XMapWindow(ob_display, self->window);
1385 }
1386 }
This page took 0.109174 seconds and 5 git commands to generate.