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