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