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