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