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