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