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