]> Dogcows Code - chaz/openbox/blob - openbox/frame.c
refix for managing iconic windows, without having the frame map which caused flashing.
[chaz/openbox] / openbox / frame.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 frame.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "frame.h"
21 #include "client.h"
22 #include "openbox.h"
23 #include "extensions.h"
24 #include "prop.h"
25 #include "config.h"
26 #include "framerender.h"
27 #include "mainloop.h"
28 #include "focus.h"
29 #include "moveresize.h"
30 #include "render/theme.h"
31
32 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask | \
33 FocusChangeMask)
34 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
35 ButtonPressMask | ButtonReleaseMask | \
36 VisibilityChangeMask)
37 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
38 ButtonMotionMask | ExposureMask | \
39 EnterWindowMask | LeaveWindowMask)
40
41 #define FRAME_HANDLE_Y(f) (f->innersize.top + f->client->area.height + \
42 f->cbwidth_y)
43
44 static void layout_title(ObFrame *self);
45 static void flash_done(gpointer data);
46 static gboolean flash_timeout(gpointer data);
47
48 static void set_theme_statics(ObFrame *self);
49 static void free_theme_statics(ObFrame *self);
50
51 static Window createWindow(Window parent, Visual *visual,
52 gulong mask, XSetWindowAttributes *attrib)
53 {
54 return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
55 (visual ? 32 : RrDepth(ob_rr_inst)), InputOutput,
56 (visual ? visual : RrVisual(ob_rr_inst)),
57 mask, attrib);
58
59 }
60
61 static Visual *check_32bit_client(ObClient *c)
62 {
63 XWindowAttributes wattrib;
64 Status ret;
65
66 ret = XGetWindowAttributes(ob_display, c->window, &wattrib);
67 g_assert(ret != BadDrawable);
68 g_assert(ret != BadWindow);
69
70 if (wattrib.depth == 32)
71 return wattrib.visual;
72 return NULL;
73 }
74
75 ObFrame *frame_new(ObClient *client)
76 {
77 XSetWindowAttributes attrib;
78 gulong mask;
79 ObFrame *self;
80 Visual *visual;
81
82 self = g_new0(ObFrame, 1);
83
84 self->obscured = TRUE;
85
86 visual = check_32bit_client(client);
87
88 /* create the non-visible decor windows */
89
90 mask = CWEventMask;
91 if (visual) {
92 /* client has a 32-bit visual */
93 mask |= CWColormap | CWBackPixel | CWBorderPixel;
94 /* create a colormap with the visual */
95 self->colormap = attrib.colormap =
96 XCreateColormap(ob_display,
97 RootWindow(ob_display, ob_screen),
98 visual, AllocNone);
99 attrib.background_pixel = BlackPixel(ob_display, 0);
100 attrib.border_pixel = BlackPixel(ob_display, 0);
101 }
102 attrib.event_mask = FRAME_EVENTMASK;
103 self->window = createWindow(RootWindow(ob_display, ob_screen), visual,
104 mask, &attrib);
105 mask &= ~CWEventMask;
106 self->plate = createWindow(self->window, visual, mask, &attrib);
107
108 /* create the visible decor windows */
109
110 mask = CWEventMask;
111 if (visual) {
112 /* client has a 32-bit visual */
113 mask |= CWColormap | CWBackPixel | CWBorderPixel;
114 attrib.colormap = RrColormap(ob_rr_inst);
115 }
116 attrib.event_mask = ELEMENT_EVENTMASK;
117 self->title = createWindow(self->window, NULL, mask, &attrib);
118
119 mask |= CWCursor;
120 attrib.cursor = ob_cursor(OB_CURSOR_NORTHWEST);
121 self->tltresize = createWindow(self->title, NULL, mask, &attrib);
122 self->tllresize = createWindow(self->title, NULL, mask, &attrib);
123 attrib.cursor = ob_cursor(OB_CURSOR_NORTHEAST);
124 self->trtresize = createWindow(self->title, NULL, mask, &attrib);
125 self->trrresize = createWindow(self->title, NULL, mask, &attrib);
126
127 mask &= ~CWCursor;
128 self->label = createWindow(self->title, NULL, mask, &attrib);
129 self->max = createWindow(self->title, NULL, mask, &attrib);
130 self->close = createWindow(self->title, NULL, mask, &attrib);
131 self->desk = createWindow(self->title, NULL, mask, &attrib);
132 self->shade = createWindow(self->title, NULL, mask, &attrib);
133 self->icon = createWindow(self->title, NULL, mask, &attrib);
134 self->iconify = createWindow(self->title, NULL, mask, &attrib);
135 self->handle = createWindow(self->window, NULL, mask, &attrib);
136
137 mask |= CWCursor;
138 attrib.cursor = ob_cursor(OB_CURSOR_SOUTHWEST);
139 self->lgrip = createWindow(self->handle, NULL, mask, &attrib);
140 attrib.cursor = ob_cursor(OB_CURSOR_SOUTHEAST);
141 self->rgrip = createWindow(self->handle, NULL, mask, &attrib);
142
143 self->focused = FALSE;
144
145 /* the other stuff is shown based on decor settings */
146 XMapWindow(ob_display, self->plate);
147 XMapWindow(ob_display, self->lgrip);
148 XMapWindow(ob_display, self->rgrip);
149 XMapWindow(ob_display, self->label);
150
151 self->max_press = self->close_press = self->desk_press =
152 self->iconify_press = self->shade_press = FALSE;
153 self->max_hover = self->close_hover = self->desk_hover =
154 self->iconify_hover = self->shade_hover = FALSE;
155
156 set_theme_statics(self);
157
158 return (ObFrame*)self;
159 }
160
161 static void set_theme_statics(ObFrame *self)
162 {
163 /* set colors/appearance/sizes for stuff that doesn't change */
164 XSetWindowBorder(ob_display, self->window,
165 RrColorPixel(ob_rr_theme->frame_b_color));
166 XSetWindowBorder(ob_display, self->title,
167 RrColorPixel(ob_rr_theme->frame_b_color));
168 XSetWindowBorder(ob_display, self->handle,
169 RrColorPixel(ob_rr_theme->frame_b_color));
170 XSetWindowBorder(ob_display, self->rgrip,
171 RrColorPixel(ob_rr_theme->frame_b_color));
172 XSetWindowBorder(ob_display, self->lgrip,
173 RrColorPixel(ob_rr_theme->frame_b_color));
174
175 XResizeWindow(ob_display, self->max,
176 ob_rr_theme->button_size, ob_rr_theme->button_size);
177 XResizeWindow(ob_display, self->iconify,
178 ob_rr_theme->button_size, ob_rr_theme->button_size);
179 XResizeWindow(ob_display, self->icon,
180 ob_rr_theme->button_size + 2, ob_rr_theme->button_size + 2);
181 XResizeWindow(ob_display, self->close,
182 ob_rr_theme->button_size, ob_rr_theme->button_size);
183 XResizeWindow(ob_display, self->desk,
184 ob_rr_theme->button_size, ob_rr_theme->button_size);
185 XResizeWindow(ob_display, self->shade,
186 ob_rr_theme->button_size, ob_rr_theme->button_size);
187 if (ob_rr_theme->handle_height > 0) {
188 XResizeWindow(ob_display, self->lgrip,
189 ob_rr_theme->grip_width, ob_rr_theme->handle_height);
190 XResizeWindow(ob_display, self->rgrip,
191 ob_rr_theme->grip_width, ob_rr_theme->handle_height);
192 }
193 XResizeWindow(ob_display, self->tltresize,
194 ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
195 XResizeWindow(ob_display, self->trtresize,
196 ob_rr_theme->grip_width, ob_rr_theme->paddingy + 1);
197 XResizeWindow(ob_display, self->tllresize,
198 ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
199 XResizeWindow(ob_display, self->trrresize,
200 ob_rr_theme->paddingx + 1, ob_rr_theme->title_height);
201
202 /* set up the dynamic appearances */
203 self->a_unfocused_title = RrAppearanceCopy(ob_rr_theme->a_unfocused_title);
204 self->a_focused_title = RrAppearanceCopy(ob_rr_theme->a_focused_title);
205 self->a_unfocused_label = RrAppearanceCopy(ob_rr_theme->a_unfocused_label);
206 self->a_focused_label = RrAppearanceCopy(ob_rr_theme->a_focused_label);
207 self->a_unfocused_handle =
208 RrAppearanceCopy(ob_rr_theme->a_unfocused_handle);
209 self->a_focused_handle = RrAppearanceCopy(ob_rr_theme->a_focused_handle);
210 self->a_icon = RrAppearanceCopy(ob_rr_theme->a_icon);
211 }
212
213 static void free_theme_statics(ObFrame *self)
214 {
215 RrAppearanceFree(self->a_unfocused_title);
216 RrAppearanceFree(self->a_focused_title);
217 RrAppearanceFree(self->a_unfocused_label);
218 RrAppearanceFree(self->a_focused_label);
219 RrAppearanceFree(self->a_unfocused_handle);
220 RrAppearanceFree(self->a_focused_handle);
221 RrAppearanceFree(self->a_icon);
222 }
223
224 static void frame_free(ObFrame *self)
225 {
226 free_theme_statics(self);
227
228 XDestroyWindow(ob_display, self->window);
229 if (self->colormap)
230 XFreeColormap(ob_display, self->colormap);
231
232 g_free(self);
233 }
234
235 void frame_show(ObFrame *self)
236 {
237 if (!self->visible) {
238 self->visible = TRUE;
239 XMapWindow(ob_display, self->client->window);
240 XMapWindow(ob_display, self->window);
241 self->firstmap = TRUE;
242 }
243 }
244
245 void frame_hide(ObFrame *self)
246 {
247 if (self->visible || self->firstmap == FALSE) {
248 if (self->visible) {
249 self->visible = FALSE;
250 self->client->ignore_unmaps += 2;
251 /* we unmap the client itself so that we can get MapRequest
252 events, and because the ICCCM tells us to! */
253 XUnmapWindow(ob_display, self->window);
254 XUnmapWindow(ob_display, self->client->window);
255 } else {
256 /* the frame wasn't visible, but the frame is being hidden now.
257 so we don't need to unmap the frame, but we do need to unmap
258 the client. */
259 self->client->ignore_unmaps += 1;
260 XUnmapWindow(ob_display, self->client->window);
261 }
262 self->firstmap = TRUE;
263 }
264 }
265
266 void frame_adjust_theme(ObFrame *self)
267 {
268 free_theme_statics(self);
269 set_theme_statics(self);
270 }
271
272 void frame_adjust_shape(ObFrame *self)
273 {
274 #ifdef SHAPE
275 gint num;
276 XRectangle xrect[2];
277
278 if (!self->client->shaped) {
279 /* clear the shape on the frame window */
280 XShapeCombineMask(ob_display, self->window, ShapeBounding,
281 self->innersize.left,
282 self->innersize.top,
283 None, ShapeSet);
284 } else {
285 /* make the frame's shape match the clients */
286 XShapeCombineShape(ob_display, self->window, ShapeBounding,
287 self->innersize.left,
288 self->innersize.top,
289 self->client->window,
290 ShapeBounding, ShapeSet);
291
292 num = 0;
293 if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
294 xrect[0].x = -ob_rr_theme->fbwidth;
295 xrect[0].y = -ob_rr_theme->fbwidth;
296 xrect[0].width = self->width + self->rbwidth * 2;
297 xrect[0].height = ob_rr_theme->title_height +
298 self->bwidth * 2;
299 ++num;
300 }
301
302 if (self->decorations & OB_FRAME_DECOR_HANDLE) {
303 xrect[1].x = -ob_rr_theme->fbwidth;
304 xrect[1].y = FRAME_HANDLE_Y(self);
305 xrect[1].width = self->width + self->rbwidth * 2;
306 xrect[1].height = ob_rr_theme->handle_height +
307 self->bwidth * 2;
308 ++num;
309 }
310
311 XShapeCombineRectangles(ob_display, self->window,
312 ShapeBounding, 0, 0, xrect, num,
313 ShapeUnion, Unsorted);
314 }
315 #endif
316 }
317
318 void frame_adjust_area(ObFrame *self, gboolean moved,
319 gboolean resized, gboolean fake)
320 {
321 Strut oldsize;
322
323 oldsize = self->size;
324
325 if (resized) {
326 self->decorations = self->client->decorations;
327 self->max_horz = self->client->max_horz;
328
329 if (self->decorations & OB_FRAME_DECOR_BORDER) {
330 self->bwidth = ob_rr_theme->fbwidth;
331 self->cbwidth_x = ob_rr_theme->cbwidthx;
332 self->cbwidth_y = ob_rr_theme->cbwidthy;
333 } else {
334 self->bwidth = self->cbwidth_x = self->cbwidth_y = 0;
335 }
336 self->rbwidth = self->bwidth;
337
338 if (self->max_horz)
339 self->bwidth = self->cbwidth_x = 0;
340
341 STRUT_SET(self->innersize,
342 self->cbwidth_x,
343 self->cbwidth_y,
344 self->cbwidth_x,
345 self->cbwidth_y);
346 self->width = self->client->area.width + self->cbwidth_x * 2 -
347 (self->max_horz ? self->rbwidth * 2 : 0);
348 self->width = MAX(self->width, 1); /* no lower than 1 */
349
350 /* set border widths */
351 if (!fake) {
352 XSetWindowBorderWidth(ob_display, self->window, self->bwidth);
353 XSetWindowBorderWidth(ob_display, self->title, self->rbwidth);
354 XSetWindowBorderWidth(ob_display, self->handle, self->rbwidth);
355 XSetWindowBorderWidth(ob_display, self->lgrip, self->rbwidth);
356 XSetWindowBorderWidth(ob_display, self->rgrip, self->rbwidth);
357 }
358
359 if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
360 self->innersize.top += ob_rr_theme->title_height + self->rbwidth +
361 (self->rbwidth - self->bwidth);
362 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
363 ob_rr_theme->handle_height > 0)
364 self->innersize.bottom += ob_rr_theme->handle_height +
365 self->rbwidth + (self->rbwidth - self->bwidth);
366
367 /* they all default off, they're turned on in layout_title */
368 self->icon_x = -1;
369 self->desk_x = -1;
370 self->shade_x = -1;
371 self->iconify_x = -1;
372 self->label_x = -1;
373 self->max_x = -1;
374 self->close_x = -1;
375
376 /* position/size and map/unmap all the windows */
377
378 if (!fake) {
379 if (self->decorations & OB_FRAME_DECOR_TITLEBAR) {
380 XMoveResizeWindow(ob_display, self->title,
381 -self->bwidth, -self->bwidth,
382 self->width, ob_rr_theme->title_height);
383 XMapWindow(ob_display, self->title);
384
385 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
386 XMoveWindow(ob_display, self->tltresize, 0, 0);
387 XMoveWindow(ob_display, self->tllresize, 0, 0);
388 XMoveWindow(ob_display, self->trtresize,
389 self->width - ob_rr_theme->grip_width, 0);
390 XMoveWindow(ob_display, self->trrresize,
391 self->width - ob_rr_theme->paddingx - 1, 0);
392 XMapWindow(ob_display, self->tltresize);
393 XMapWindow(ob_display, self->tllresize);
394 XMapWindow(ob_display, self->trtresize);
395 XMapWindow(ob_display, self->trrresize);
396 } else {
397 XUnmapWindow(ob_display, self->tltresize);
398 XUnmapWindow(ob_display, self->tllresize);
399 XUnmapWindow(ob_display, self->trtresize);
400 XUnmapWindow(ob_display, self->trrresize);
401 }
402 } else
403 XUnmapWindow(ob_display, self->title);
404 }
405
406 if (self->decorations & OB_FRAME_DECOR_TITLEBAR)
407 /* layout the title bar elements */
408 layout_title(self);
409
410 if (!fake) {
411 if (self->decorations & OB_FRAME_DECOR_HANDLE &&
412 ob_rr_theme->handle_height > 0)
413 {
414 XMoveResizeWindow(ob_display, self->handle,
415 -self->bwidth, FRAME_HANDLE_Y(self),
416 self->width, ob_rr_theme->handle_height);
417 XMapWindow(ob_display, self->handle);
418
419 if (self->decorations & OB_FRAME_DECOR_GRIPS) {
420 XMoveWindow(ob_display, self->lgrip,
421 -self->rbwidth, -self->rbwidth);
422 XMoveWindow(ob_display, self->rgrip,
423 -self->rbwidth + self->width -
424 ob_rr_theme->grip_width, -self->rbwidth);
425 XMapWindow(ob_display, self->lgrip);
426 XMapWindow(ob_display, self->rgrip);
427 } else {
428 XUnmapWindow(ob_display, self->lgrip);
429 XUnmapWindow(ob_display, self->rgrip);
430 }
431 } else
432 XUnmapWindow(ob_display, self->handle);
433
434 /* move and resize the plate */
435 XMoveResizeWindow(ob_display, self->plate,
436 self->innersize.left - self->cbwidth_x,
437 self->innersize.top - self->cbwidth_y,
438 self->client->area.width + self->cbwidth_x * 2,
439 self->client->area.height + self->cbwidth_y * 2);
440 /* when the client has StaticGravity, it likes to move around. */
441 XMoveWindow(ob_display, self->client->window,
442 self->cbwidth_x, self->cbwidth_y);
443 }
444
445 STRUT_SET(self->size,
446 self->innersize.left + self->bwidth,
447 self->innersize.top + self->bwidth,
448 self->innersize.right + self->bwidth,
449 self->innersize.bottom + self->bwidth);
450 }
451
452 /* shading can change without being moved or resized */
453 RECT_SET_SIZE(self->area,
454 self->client->area.width +
455 self->size.left + self->size.right,
456 (self->client->shaded ?
457 ob_rr_theme->title_height + self->rbwidth * 2:
458 self->client->area.height +
459 self->size.top + self->size.bottom));
460
461 if (moved) {
462 /* find the new coordinates, done after setting the frame.size, for
463 frame_client_gravity. */
464 self->area.x = self->client->area.x;
465 self->area.y = self->client->area.y;
466 frame_client_gravity(self, &self->area.x, &self->area.y);
467 }
468
469 if (!fake) {
470 /* move and resize the top level frame.
471 shading can change without being moved or resized */
472 XMoveResizeWindow(ob_display, self->window,
473 self->area.x, self->area.y,
474 self->area.width - self->bwidth * 2,
475 self->area.height - self->bwidth * 2);
476
477 if (resized) {
478 framerender_frame(self);
479 frame_adjust_shape(self);
480 }
481
482 if (!STRUT_EQUAL(self->size, oldsize)) {
483 gulong vals[4];
484 vals[0] = self->size.left;
485 vals[1] = self->size.right;
486 vals[2] = self->size.top;
487 vals[3] = self->size.bottom;
488 PROP_SETA32(self->client->window, net_frame_extents,
489 cardinal, vals, 4);
490 }
491
492 /* if this occurs while we are focus cycling, the indicator needs to
493 match the changes */
494 if (focus_cycle_target == self->client)
495 focus_cycle_draw_indicator();
496 }
497 if (resized && (self->decorations & OB_FRAME_DECOR_TITLEBAR))
498 XResizeWindow(ob_display, self->label, self->label_width,
499 ob_rr_theme->label_height);
500 }
501
502 void frame_adjust_state(ObFrame *self)
503 {
504 framerender_frame(self);
505 }
506
507 void frame_adjust_focus(ObFrame *self, gboolean hilite)
508 {
509 self->focused = hilite;
510 framerender_frame(self);
511 }
512
513 void frame_adjust_title(ObFrame *self)
514 {
515 framerender_frame(self);
516 }
517
518 void frame_adjust_icon(ObFrame *self)
519 {
520 framerender_frame(self);
521 }
522
523 void frame_grab_client(ObFrame *self, ObClient *client)
524 {
525 self->client = client;
526
527 /* reparent the client to the frame */
528 XReparentWindow(ob_display, client->window, self->plate, 0, 0);
529 /*
530 When reparenting the client window, it is usually not mapped yet, since
531 this occurs from a MapRequest. However, in the case where Openbox is
532 starting up, the window is already mapped, so we'll see unmap events for
533 it. There are 2 unmap events generated that we see, one with the 'event'
534 member set the root window, and one set to the client, but both get
535 handled and need to be ignored.
536 */
537 if (ob_state() == OB_STATE_STARTING)
538 client->ignore_unmaps += 2;
539
540 /* select the event mask on the client's parent (to receive config/map
541 req's) the ButtonPress is to catch clicks on the client border */
542 XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
543
544 frame_adjust_area(self, TRUE, TRUE, FALSE);
545
546 /* map the client so it maps when the frame does */
547 XMapWindow(ob_display, client->window);
548
549 /* set all the windows for the frame in the window_map */
550 g_hash_table_insert(window_map, &self->window, client);
551 g_hash_table_insert(window_map, &self->plate, client);
552 g_hash_table_insert(window_map, &self->title, client);
553 g_hash_table_insert(window_map, &self->label, client);
554 g_hash_table_insert(window_map, &self->max, client);
555 g_hash_table_insert(window_map, &self->close, client);
556 g_hash_table_insert(window_map, &self->desk, client);
557 g_hash_table_insert(window_map, &self->shade, client);
558 g_hash_table_insert(window_map, &self->icon, client);
559 g_hash_table_insert(window_map, &self->iconify, client);
560 g_hash_table_insert(window_map, &self->handle, client);
561 g_hash_table_insert(window_map, &self->lgrip, client);
562 g_hash_table_insert(window_map, &self->rgrip, client);
563 g_hash_table_insert(window_map, &self->tltresize, client);
564 g_hash_table_insert(window_map, &self->tllresize, client);
565 g_hash_table_insert(window_map, &self->trtresize, client);
566 g_hash_table_insert(window_map, &self->trrresize, client);
567 }
568
569 void frame_release_client(ObFrame *self, ObClient *client)
570 {
571 XEvent ev;
572 gboolean reparent = TRUE;
573
574 g_assert(self->client == client);
575
576 /* check if the app has already reparented its window away */
577 while (XCheckTypedWindowEvent(ob_display, client->window,
578 ReparentNotify, &ev))
579 {
580 /* This check makes sure we don't catch our own reparent action to
581 our frame window. This doesn't count as the app reparenting itself
582 away of course.
583
584 Reparent events that are generated by us are just discarded here.
585 They are of no consequence to us anyhow.
586 */
587 if (ev.xreparent.parent != self->plate) {
588 reparent = FALSE;
589 XPutBackEvent(ob_display, &ev);
590 break;
591 }
592 }
593
594 if (reparent) {
595 /* according to the ICCCM - if the client doesn't reparent itself,
596 then we will reparent the window to root for them */
597 XReparentWindow(ob_display, client->window,
598 RootWindow(ob_display, ob_screen),
599 client->area.x,
600 client->area.y);
601 }
602
603 /* remove all the windows for the frame from the window_map */
604 g_hash_table_remove(window_map, &self->window);
605 g_hash_table_remove(window_map, &self->plate);
606 g_hash_table_remove(window_map, &self->title);
607 g_hash_table_remove(window_map, &self->label);
608 g_hash_table_remove(window_map, &self->max);
609 g_hash_table_remove(window_map, &self->close);
610 g_hash_table_remove(window_map, &self->desk);
611 g_hash_table_remove(window_map, &self->shade);
612 g_hash_table_remove(window_map, &self->icon);
613 g_hash_table_remove(window_map, &self->iconify);
614 g_hash_table_remove(window_map, &self->handle);
615 g_hash_table_remove(window_map, &self->lgrip);
616 g_hash_table_remove(window_map, &self->rgrip);
617 g_hash_table_remove(window_map, &self->tltresize);
618 g_hash_table_remove(window_map, &self->tllresize);
619 g_hash_table_remove(window_map, &self->trtresize);
620 g_hash_table_remove(window_map, &self->trrresize);
621
622 ob_main_loop_timeout_remove_data(ob_main_loop, flash_timeout, self, TRUE);
623
624 frame_free(self);
625 }
626
627 static void layout_title(ObFrame *self)
628 {
629 gchar *lc;
630 gint x;
631 gboolean n, d, i, l, m, c, s;
632
633 n = d = i = l = m = c = s = FALSE;
634
635 /* figure out whats being shown, and the width of the label */
636 self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2;
637 for (lc = config_title_layout; *lc != '\0'; ++lc) {
638 switch (*lc) {
639 case 'N':
640 if (n) { *lc = ' '; break; } /* rm duplicates */
641 n = TRUE;
642 self->label_width -= (ob_rr_theme->button_size + 2 +
643 ob_rr_theme->paddingx + 1);
644 break;
645 case 'D':
646 if (d) { *lc = ' '; break; }
647 if (!(self->decorations & OB_FRAME_DECOR_ALLDESKTOPS)
648 && config_theme_hidedisabled)
649 break;
650 d = TRUE;
651 self->label_width -= (ob_rr_theme->button_size +
652 ob_rr_theme->paddingx + 1);
653 break;
654 case 'S':
655 if (s) { *lc = ' '; break; }
656 if (!(self->decorations & OB_FRAME_DECOR_SHADE)
657 && config_theme_hidedisabled)
658 break;
659 s = TRUE;
660 self->label_width -= (ob_rr_theme->button_size +
661 ob_rr_theme->paddingx + 1);
662 break;
663 case 'I':
664 if (i) { *lc = ' '; break; }
665 if (!(self->decorations & OB_FRAME_DECOR_ICONIFY)
666 && config_theme_hidedisabled)
667 break;
668 i = TRUE;
669 self->label_width -= (ob_rr_theme->button_size +
670 ob_rr_theme->paddingx + 1);
671 break;
672 case 'L':
673 if (l) { *lc = ' '; break; }
674 l = TRUE;
675 break;
676 case 'M':
677 if (m) { *lc = ' '; break; }
678 if (!(self->decorations & OB_FRAME_DECOR_MAXIMIZE)
679 && config_theme_hidedisabled)
680 break;
681 m = TRUE;
682 self->label_width -= (ob_rr_theme->button_size +
683 ob_rr_theme->paddingx + 1);
684 break;
685 case 'C':
686 if (c) { *lc = ' '; break; }
687 if (!(self->decorations & OB_FRAME_DECOR_CLOSE)
688 && config_theme_hidedisabled)
689 break;
690 c = TRUE;
691 self->label_width -= (ob_rr_theme->button_size +
692 ob_rr_theme->paddingx + 1);
693 break;
694 }
695 }
696 if (self->label_width < 1) self->label_width = 1;
697
698 if (!n) XUnmapWindow(ob_display, self->icon);
699 if (!d) XUnmapWindow(ob_display, self->desk);
700 if (!s) XUnmapWindow(ob_display, self->shade);
701 if (!i) XUnmapWindow(ob_display, self->iconify);
702 if (!l) XUnmapWindow(ob_display, self->label);
703 if (!m) XUnmapWindow(ob_display, self->max);
704 if (!c) XUnmapWindow(ob_display, self->close);
705
706 x = ob_rr_theme->paddingx + 1;
707 for (lc = config_title_layout; *lc != '\0'; ++lc) {
708 switch (*lc) {
709 case 'N':
710 if (!n) break;
711 self->icon_x = x;
712 XMapWindow(ob_display, self->icon);
713 XMoveWindow(ob_display, self->icon, x, ob_rr_theme->paddingy);
714 x += ob_rr_theme->button_size + 2 + ob_rr_theme->paddingx + 1;
715 break;
716 case 'D':
717 if (!d) break;
718 self->desk_x = x;
719 XMapWindow(ob_display, self->desk);
720 XMoveWindow(ob_display, self->desk, x, ob_rr_theme->paddingy + 1);
721 x += ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
722 break;
723 case 'S':
724 if (!s) break;
725 self->shade_x = x;
726 XMapWindow(ob_display, self->shade);
727 XMoveWindow(ob_display, self->shade, x, ob_rr_theme->paddingy + 1);
728 x += ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
729 break;
730 case 'I':
731 if (!i) break;
732 self->iconify_x = x;
733 XMapWindow(ob_display, self->iconify);
734 XMoveWindow(ob_display,self->iconify, x, ob_rr_theme->paddingy + 1);
735 x += ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
736 break;
737 case 'L':
738 if (!l) break;
739 self->label_x = x;
740 XMapWindow(ob_display, self->label);
741 XMoveWindow(ob_display, self->label, x, ob_rr_theme->paddingy);
742 x += self->label_width + ob_rr_theme->paddingx + 1;
743 break;
744 case 'M':
745 if (!m) break;
746 self->max_x = x;
747 XMapWindow(ob_display, self->max);
748 XMoveWindow(ob_display, self->max, x, ob_rr_theme->paddingy + 1);
749 x += ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
750 break;
751 case 'C':
752 if (!c) break;
753 self->close_x = x;
754 XMapWindow(ob_display, self->close);
755 XMoveWindow(ob_display, self->close, x, ob_rr_theme->paddingy + 1);
756 x += ob_rr_theme->button_size + ob_rr_theme->paddingx + 1;
757 break;
758 }
759 }
760 }
761
762 ObFrameContext frame_context_from_string(const gchar *name)
763 {
764 if (!g_ascii_strcasecmp("Desktop", name))
765 return OB_FRAME_CONTEXT_DESKTOP;
766 else if (!g_ascii_strcasecmp("Client", name))
767 return OB_FRAME_CONTEXT_CLIENT;
768 else if (!g_ascii_strcasecmp("Titlebar", name))
769 return OB_FRAME_CONTEXT_TITLEBAR;
770 else if (!g_ascii_strcasecmp("Handle", name))
771 return OB_FRAME_CONTEXT_HANDLE;
772 else if (!g_ascii_strcasecmp("Frame", name))
773 return OB_FRAME_CONTEXT_FRAME;
774 else if (!g_ascii_strcasecmp("TLCorner", name))
775 return OB_FRAME_CONTEXT_TLCORNER;
776 else if (!g_ascii_strcasecmp("TRCorner", name))
777 return OB_FRAME_CONTEXT_TRCORNER;
778 else if (!g_ascii_strcasecmp("BLCorner", name))
779 return OB_FRAME_CONTEXT_BLCORNER;
780 else if (!g_ascii_strcasecmp("BRCorner", name))
781 return OB_FRAME_CONTEXT_BRCORNER;
782 else if (!g_ascii_strcasecmp("Maximize", name))
783 return OB_FRAME_CONTEXT_MAXIMIZE;
784 else if (!g_ascii_strcasecmp("AllDesktops", name))
785 return OB_FRAME_CONTEXT_ALLDESKTOPS;
786 else if (!g_ascii_strcasecmp("Shade", name))
787 return OB_FRAME_CONTEXT_SHADE;
788 else if (!g_ascii_strcasecmp("Iconify", name))
789 return OB_FRAME_CONTEXT_ICONIFY;
790 else if (!g_ascii_strcasecmp("Icon", name))
791 return OB_FRAME_CONTEXT_ICON;
792 else if (!g_ascii_strcasecmp("Close", name))
793 return OB_FRAME_CONTEXT_CLOSE;
794 else if (!g_ascii_strcasecmp("MoveResize", name))
795 return OB_FRAME_CONTEXT_MOVE_RESIZE;
796 return OB_FRAME_CONTEXT_NONE;
797 }
798
799 ObFrameContext frame_context(ObClient *client, Window win)
800 {
801 ObFrame *self;
802
803 if (moveresize_in_progress)
804 return OB_FRAME_CONTEXT_MOVE_RESIZE;
805
806 if (win == RootWindow(ob_display, ob_screen))
807 return OB_FRAME_CONTEXT_DESKTOP;
808 if (client == NULL) return OB_FRAME_CONTEXT_NONE;
809 if (win == client->window) {
810 /* conceptually, this is the desktop, as far as users are
811 concerned */
812 if (client->type == OB_CLIENT_TYPE_DESKTOP)
813 return OB_FRAME_CONTEXT_DESKTOP;
814 return OB_FRAME_CONTEXT_CLIENT;
815 }
816
817 self = client->frame;
818 if (win == self->plate) {
819 /* conceptually, this is the desktop, as far as users are
820 concerned */
821 if (client->type == OB_CLIENT_TYPE_DESKTOP)
822 return OB_FRAME_CONTEXT_DESKTOP;
823 return OB_FRAME_CONTEXT_CLIENT;
824 }
825
826 if (win == self->window) return OB_FRAME_CONTEXT_FRAME;
827 if (win == self->title) return OB_FRAME_CONTEXT_TITLEBAR;
828 if (win == self->label) return OB_FRAME_CONTEXT_TITLEBAR;
829 if (win == self->handle) return OB_FRAME_CONTEXT_HANDLE;
830 if (win == self->lgrip) return OB_FRAME_CONTEXT_BLCORNER;
831 if (win == self->rgrip) return OB_FRAME_CONTEXT_BRCORNER;
832 if (win == self->tltresize) return OB_FRAME_CONTEXT_TLCORNER;
833 if (win == self->tllresize) return OB_FRAME_CONTEXT_TLCORNER;
834 if (win == self->trtresize) return OB_FRAME_CONTEXT_TRCORNER;
835 if (win == self->trrresize) return OB_FRAME_CONTEXT_TRCORNER;
836 if (win == self->max) return OB_FRAME_CONTEXT_MAXIMIZE;
837 if (win == self->iconify) return OB_FRAME_CONTEXT_ICONIFY;
838 if (win == self->close) return OB_FRAME_CONTEXT_CLOSE;
839 if (win == self->icon) return OB_FRAME_CONTEXT_ICON;
840 if (win == self->desk) return OB_FRAME_CONTEXT_ALLDESKTOPS;
841 if (win == self->shade) return OB_FRAME_CONTEXT_SHADE;
842
843 return OB_FRAME_CONTEXT_NONE;
844 }
845
846 void frame_client_gravity(ObFrame *self, gint *x, gint *y)
847 {
848 /* horizontal */
849 switch (self->client->gravity) {
850 default:
851 case NorthWestGravity:
852 case SouthWestGravity:
853 case WestGravity:
854 break;
855
856 case NorthGravity:
857 case SouthGravity:
858 case CenterGravity:
859 *x -= (self->size.left + self->size.right) / 2;
860 break;
861
862 case NorthEastGravity:
863 case SouthEastGravity:
864 case EastGravity:
865 *x -= self->size.left + self->size.right;
866 break;
867
868 case ForgetGravity:
869 case StaticGravity:
870 *x -= self->size.left;
871 break;
872 }
873
874 /* vertical */
875 switch (self->client->gravity) {
876 default:
877 case NorthWestGravity:
878 case NorthEastGravity:
879 case NorthGravity:
880 break;
881
882 case CenterGravity:
883 case EastGravity:
884 case WestGravity:
885 *y -= (self->size.top + self->size.bottom) / 2;
886 break;
887
888 case SouthWestGravity:
889 case SouthEastGravity:
890 case SouthGravity:
891 *y -= self->size.top + self->size.bottom;
892 break;
893
894 case ForgetGravity:
895 case StaticGravity:
896 *y -= self->size.top;
897 break;
898 }
899 }
900
901 void frame_frame_gravity(ObFrame *self, gint *x, gint *y)
902 {
903 /* horizontal */
904 switch (self->client->gravity) {
905 default:
906 case NorthWestGravity:
907 case WestGravity:
908 case SouthWestGravity:
909 break;
910 case NorthGravity:
911 case CenterGravity:
912 case SouthGravity:
913 *x += (self->size.left + self->size.right) / 2;
914 break;
915 case NorthEastGravity:
916 case EastGravity:
917 case SouthEastGravity:
918 *x += self->size.left + self->size.right;
919 break;
920 case StaticGravity:
921 case ForgetGravity:
922 *x += self->size.left;
923 break;
924 }
925
926 /* vertical */
927 switch (self->client->gravity) {
928 default:
929 case NorthWestGravity:
930 case NorthGravity:
931 case NorthEastGravity:
932 break;
933 case WestGravity:
934 case CenterGravity:
935 case EastGravity:
936 *y += (self->size.top + self->size.bottom) / 2;
937 break;
938 case SouthWestGravity:
939 case SouthGravity:
940 case SouthEastGravity:
941 *y += self->size.top + self->size.bottom;
942 break;
943 case StaticGravity:
944 case ForgetGravity:
945 *y += self->size.top;
946 break;
947 }
948 }
949
950 static void flash_done(gpointer data)
951 {
952 ObFrame *self = data;
953
954 if (self->focused != self->flash_on)
955 frame_adjust_focus(self, self->focused);
956 }
957
958 static gboolean flash_timeout(gpointer data)
959 {
960 ObFrame *self = data;
961 GTimeVal now;
962
963 g_get_current_time(&now);
964 if (now.tv_sec > self->flash_end.tv_sec ||
965 (now.tv_sec == self->flash_end.tv_sec &&
966 now.tv_usec >= self->flash_end.tv_usec))
967 self->flashing = FALSE;
968
969 if (!self->flashing)
970 return FALSE; /* we are done */
971
972 self->flash_on = !self->flash_on;
973 if (!self->focused) {
974 frame_adjust_focus(self, self->flash_on);
975 self->focused = FALSE;
976 }
977
978 return TRUE; /* go again */
979 }
980
981 void frame_flash_start(ObFrame *self)
982 {
983 self->flash_on = self->focused;
984
985 if (!self->flashing)
986 ob_main_loop_timeout_add(ob_main_loop,
987 G_USEC_PER_SEC * 0.6,
988 flash_timeout,
989 self,
990 flash_done);
991 g_get_current_time(&self->flash_end);
992 g_time_val_add(&self->flash_end, G_USEC_PER_SEC * 5);
993
994 self->flashing = TRUE;
995 }
996
997 void frame_flash_stop(ObFrame *self)
998 {
999 self->flashing = FALSE;
1000 }
This page took 0.083576 seconds and 4 git commands to generate.