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