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