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