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