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