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