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