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