]> Dogcows Code - chaz/openbox/blob - openbox/client.c
6aec7b295254b730732829ed2e4ab0b390af2307
[chaz/openbox] / openbox / client.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 client.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 "client.h"
21 #include "debug.h"
22 #include "startupnotify.h"
23 #include "dock.h"
24 #include "xerror.h"
25 #include "screen.h"
26 #include "moveresize.h"
27 #include "place.h"
28 #include "prop.h"
29 #include "extensions.h"
30 #include "frame.h"
31 #include "session.h"
32 #include "event.h"
33 #include "grab.h"
34 #include "focus.h"
35 #include "stacking.h"
36 #include "openbox.h"
37 #include "group.h"
38 #include "config.h"
39 #include "menuframe.h"
40 #include "keyboard.h"
41 #include "mouse.h"
42 #include "render/render.h"
43
44 #include <glib.h>
45 #include <X11/Xutil.h>
46
47 /*! The event mask to grab on client windows */
48 #define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
49 StructureNotifyMask)
50
51 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
52 ButtonMotionMask)
53
54 typedef struct
55 {
56 ObClientDestructor func;
57 gpointer data;
58 } Destructor;
59
60 GList *client_list = NULL;
61 GSList *client_destructors = NULL;
62
63 static void client_get_all(ObClient *self);
64 static void client_toggle_border(ObClient *self, gboolean show);
65 static void client_get_startup_id(ObClient *self);
66 static void client_get_area(ObClient *self);
67 static void client_get_desktop(ObClient *self);
68 static void client_get_state(ObClient *self);
69 static void client_get_shaped(ObClient *self);
70 static void client_get_mwm_hints(ObClient *self);
71 static void client_get_gravity(ObClient *self);
72 static void client_showhide(ObClient *self);
73 static void client_change_allowed_actions(ObClient *self);
74 static void client_change_state(ObClient *self);
75 static void client_apply_startup_state(ObClient *self);
76 static void client_restore_session_state(ObClient *self);
77 static void client_restore_session_stacking(ObClient *self);
78 static void client_urgent_notify(ObClient *self);
79
80 void client_startup(gboolean reconfig)
81 {
82 if (reconfig) return;
83
84 client_set_list();
85 }
86
87 void client_shutdown(gboolean reconfig)
88 {
89 }
90
91 void client_add_destructor(ObClientDestructor func, gpointer data)
92 {
93 Destructor *d = g_new(Destructor, 1);
94 d->func = func;
95 d->data = data;
96 client_destructors = g_slist_prepend(client_destructors, d);
97 }
98
99 void client_remove_destructor(ObClientDestructor func)
100 {
101 GSList *it;
102
103 for (it = client_destructors; it; it = g_slist_next(it)) {
104 Destructor *d = it->data;
105 if (d->func == func) {
106 g_free(d);
107 client_destructors = g_slist_delete_link(client_destructors, it);
108 break;
109 }
110 }
111 }
112
113 void client_set_list()
114 {
115 Window *windows, *win_it;
116 GList *it;
117 guint size = g_list_length(client_list);
118
119 /* create an array of the window ids */
120 if (size > 0) {
121 windows = g_new(Window, size);
122 win_it = windows;
123 for (it = client_list; it; it = g_list_next(it), ++win_it)
124 *win_it = ((ObClient*)it->data)->window;
125 } else
126 windows = NULL;
127
128 PROP_SETA32(RootWindow(ob_display, ob_screen),
129 net_client_list, window, (guint32*)windows, size);
130
131 if (windows)
132 g_free(windows);
133
134 stacking_set_list();
135 }
136
137 /*
138 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
139 {
140 GSList *it;
141
142 for (it = self->transients; it; it = g_slist_next(it)) {
143 if (!func(it->data, data)) return;
144 client_foreach_transient(it->data, func, data);
145 }
146 }
147
148 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
149 {
150 if (self->transient_for) {
151 if (self->transient_for != OB_TRAN_GROUP) {
152 if (!func(self->transient_for, data)) return;
153 client_foreach_ancestor(self->transient_for, func, data);
154 } else {
155 GSList *it;
156
157 for (it = self->group->members; it; it = g_slist_next(it))
158 if (it->data != self &&
159 !((ObClient*)it->data)->transient_for) {
160 if (!func(it->data, data)) return;
161 client_foreach_ancestor(it->data, func, data);
162 }
163 }
164 }
165 }
166 */
167
168 void client_manage_all()
169 {
170 guint i, j, nchild;
171 Window w, *children;
172 XWMHints *wmhints;
173 XWindowAttributes attrib;
174
175 XQueryTree(ob_display, RootWindow(ob_display, ob_screen),
176 &w, &w, &children, &nchild);
177
178 /* remove all icon windows from the list */
179 for (i = 0; i < nchild; i++) {
180 if (children[i] == None) continue;
181 wmhints = XGetWMHints(ob_display, children[i]);
182 if (wmhints) {
183 if ((wmhints->flags & IconWindowHint) &&
184 (wmhints->icon_window != children[i]))
185 for (j = 0; j < nchild; j++)
186 if (children[j] == wmhints->icon_window) {
187 children[j] = None;
188 break;
189 }
190 XFree(wmhints);
191 }
192 }
193
194 for (i = 0; i < nchild; ++i) {
195 if (children[i] == None)
196 continue;
197 if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
198 if (attrib.override_redirect) continue;
199
200 if (attrib.map_state != IsUnmapped)
201 client_manage(children[i]);
202 }
203 }
204 XFree(children);
205 }
206
207 void client_manage(Window window)
208 {
209 ObClient *self;
210 XEvent e;
211 XWindowAttributes attrib;
212 XSetWindowAttributes attrib_set;
213 XWMHints *wmhint;
214 gboolean activate = FALSE;
215
216 grab_server(TRUE);
217
218 /* check if it has already been unmapped by the time we started mapping
219 the grab does a sync so we don't have to here */
220 if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
221 XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e)) {
222 XPutBackEvent(ob_display, &e);
223
224 grab_server(FALSE);
225 return; /* don't manage it */
226 }
227
228 /* make sure it isn't an override-redirect window */
229 if (!XGetWindowAttributes(ob_display, window, &attrib) ||
230 attrib.override_redirect) {
231 grab_server(FALSE);
232 return; /* don't manage it */
233 }
234
235 /* is the window a docking app */
236 if ((wmhint = XGetWMHints(ob_display, window))) {
237 if ((wmhint->flags & StateHint) &&
238 wmhint->initial_state == WithdrawnState) {
239 dock_add(window, wmhint);
240 grab_server(FALSE);
241 XFree(wmhint);
242 return;
243 }
244 XFree(wmhint);
245 }
246
247 ob_debug("Managing window: %lx\n", window);
248
249 /* choose the events we want to receive on the CLIENT window */
250 attrib_set.event_mask = CLIENT_EVENTMASK;
251 attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
252 XChangeWindowAttributes(ob_display, window,
253 CWEventMask|CWDontPropagate, &attrib_set);
254
255
256 /* create the ObClient struct, and populate it from the hints on the
257 window */
258 self = g_new0(ObClient, 1);
259 self->obwin.type = Window_Client;
260 self->window = window;
261
262 /* non-zero defaults */
263 self->title_count = 1;
264 self->wmstate = NormalState;
265 self->layer = -1;
266 self->desktop = screen_num_desktops; /* always an invalid value */
267
268 client_get_all(self);
269 client_restore_session_state(self);
270
271 sn_app_started(self->class);
272
273 /* update the focus lists, do this before the call to change_state or
274 it can end up in the list twice! */
275 focus_order_add_new(self);
276
277 client_change_state(self);
278
279 /* remove the client's border (and adjust re gravity) */
280 client_toggle_border(self, FALSE);
281
282 /* specify that if we exit, the window should not be destroyed and should
283 be reparented back to root automatically */
284 XChangeSaveSet(ob_display, window, SetModeInsert);
285
286 /* create the decoration frame for the client window */
287 self->frame = frame_new();
288
289 frame_grab_client(self->frame, self);
290
291 grab_server(FALSE);
292
293 client_apply_startup_state(self);
294
295 stacking_add(CLIENT_AS_WINDOW(self));
296 client_restore_session_stacking(self);
297
298 /* focus the new window? */
299 if (ob_state() != OB_STATE_STARTING &&
300 (config_focus_new || client_search_focus_parent(self)) &&
301 /* note the check against Type_Normal/Dialog, not client_normal(self),
302 which would also include other types. in this case we want more
303 strict rules for focus */
304 (self->type == OB_CLIENT_TYPE_NORMAL ||
305 self->type == OB_CLIENT_TYPE_DIALOG))
306 {
307 activate = TRUE;
308 #if 0
309 if (self->desktop != screen_desktop) {
310 /* activate the window */
311 activate = TRUE;
312 } else {
313 gboolean group_foc = FALSE;
314
315 if (self->group) {
316 GSList *it;
317
318 for (it = self->group->members; it; it = g_slist_next(it))
319 {
320 if (client_focused(it->data))
321 {
322 group_foc = TRUE;
323 break;
324 }
325 }
326 }
327 if ((group_foc ||
328 (!self->transient_for && (!self->group ||
329 !self->group->members->next))) ||
330 client_search_focus_tree_full(self) ||
331 !focus_client ||
332 !client_normal(focus_client))
333 {
334 /* activate the window */
335 activate = TRUE;
336 }
337 }
338 #endif
339 }
340
341 if (ob_state() == OB_STATE_RUNNING) {
342 gint x = self->area.x, ox = x;
343 gint y = self->area.y, oy = y;
344
345 place_client(self, &x, &y);
346
347 /* make sure the window is visible. */
348 client_find_onscreen(self, &x, &y,
349 self->frame->area.width,
350 self->frame->area.height,
351 /* non-normal clients has less rules, and
352 windows that are being restored from a session
353 do also. we can assume you want it back where
354 you saved it */
355 client_normal(self) && !self->session);
356 if (x != ox || y != oy)
357 client_move(self, x, y);
358 }
359
360 keyboard_grab_for_client(self, TRUE);
361 mouse_grab_for_client(self, TRUE);
362
363 client_showhide(self);
364
365 /* use client_focus instead of client_activate cuz client_activate does
366 stuff like switch desktops etc and I'm not interested in all that when
367 a window maps since its not based on an action from the user like
368 clicking a window to activate is. so keep the new window out of the way
369 but do focus it. */
370 if (activate) {
371 /* if using focus_delay, stop the timer now so that focus doesn't go
372 moving on us */
373 event_halt_focus_delay();
374
375 client_focus(self);
376 /* since focus can change the stacking orders, if we focus the window
377 then the standard raise it gets is not enough, we need to queue one
378 for after the focus change takes place */
379 client_raise(self);
380 }
381
382 /* client_activate does this but we aret using it so we have to do it
383 here as well */
384 if (screen_showing_desktop)
385 screen_show_desktop(FALSE);
386
387 /* add to client list/map */
388 client_list = g_list_append(client_list, self);
389 g_hash_table_insert(window_map, &self->window, self);
390
391 /* this has to happen after we're in the client_list */
392 screen_update_areas();
393
394 /* update the list hints */
395 client_set_list();
396
397 ob_debug("Managed window 0x%lx (%s)\n", window, self->class);
398 }
399
400 void client_unmanage_all()
401 {
402 while (client_list != NULL)
403 client_unmanage(client_list->data);
404 }
405
406 void client_unmanage(ObClient *self)
407 {
408 guint j;
409 GSList *it;
410
411 ob_debug("Unmanaging window: %lx (%s)\n", self->window, self->class);
412
413 g_assert(self != NULL);
414
415 keyboard_grab_for_client(self, FALSE);
416 mouse_grab_for_client(self, FALSE);
417
418 /* potentially fix focusLast */
419 if (config_focus_last)
420 grab_pointer(TRUE, OB_CURSOR_NONE);
421
422 /* remove the window from our save set */
423 XChangeSaveSet(ob_display, self->window, SetModeDelete);
424
425 /* we dont want events no more */
426 XSelectInput(ob_display, self->window, NoEventMask);
427
428 frame_hide(self->frame);
429
430 client_list = g_list_remove(client_list, self);
431 stacking_remove(self);
432 g_hash_table_remove(window_map, &self->window);
433
434 /* update the focus lists */
435 focus_order_remove(self);
436
437 /* once the client is out of the list, update the struts to remove it's
438 influence */
439 screen_update_areas();
440
441 for (it = client_destructors; it; it = g_slist_next(it)) {
442 Destructor *d = it->data;
443 d->func(self, d->data);
444 }
445
446 if (focus_client == self) {
447 XEvent e;
448
449 /* focus the last focused window on the desktop, and ignore enter
450 events from the unmap so it doesnt mess with the focus */
451 while (XCheckTypedEvent(ob_display, EnterNotify, &e));
452 /* remove these flags so we don't end up getting focused in the
453 fallback! */
454 self->can_focus = FALSE;
455 self->focus_notify = FALSE;
456 self->modal = FALSE;
457 client_unfocus(self);
458 }
459
460 /* tell our parent(s) that we're gone */
461 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
462 for (it = self->group->members; it; it = g_slist_next(it))
463 if (it->data != self)
464 ((ObClient*)it->data)->transients =
465 g_slist_remove(((ObClient*)it->data)->transients, self);
466 } else if (self->transient_for) { /* transient of window */
467 self->transient_for->transients =
468 g_slist_remove(self->transient_for->transients, self);
469 }
470
471 /* tell our transients that we're gone */
472 for (it = self->transients; it; it = g_slist_next(it)) {
473 if (((ObClient*)it->data)->transient_for != OB_TRAN_GROUP) {
474 ((ObClient*)it->data)->transient_for = NULL;
475 client_calc_layer(it->data);
476 }
477 }
478
479 /* remove from its group */
480 if (self->group) {
481 group_remove(self->group, self);
482 self->group = NULL;
483 }
484
485 /* give the client its border back */
486 client_toggle_border(self, TRUE);
487
488 /* reparent the window out of the frame, and free the frame */
489 frame_release_client(self->frame, self);
490 self->frame = NULL;
491
492 if (ob_state() != OB_STATE_EXITING) {
493 /* these values should not be persisted across a window
494 unmapping/mapping */
495 PROP_ERASE(self->window, net_wm_desktop);
496 PROP_ERASE(self->window, net_wm_state);
497 PROP_ERASE(self->window, wm_state);
498 } else {
499 /* if we're left in an unmapped state, the client wont be mapped. this
500 is bad, since we will no longer be managing the window on restart */
501 XMapWindow(ob_display, self->window);
502 }
503
504
505 ob_debug("Unmanaged window 0x%lx\n", self->window);
506
507 /* free all data allocated in the client struct */
508 g_slist_free(self->transients);
509 for (j = 0; j < self->nicons; ++j)
510 g_free(self->icons[j].data);
511 if (self->nicons > 0)
512 g_free(self->icons);
513 g_free(self->title);
514 g_free(self->icon_title);
515 g_free(self->name);
516 g_free(self->class);
517 g_free(self->role);
518 g_free(self->sm_client_id);
519 g_free(self);
520
521 /* update the list hints */
522 client_set_list();
523
524 if (config_focus_last)
525 grab_pointer(FALSE, OB_CURSOR_NONE);
526 }
527
528 static void client_urgent_notify(ObClient *self)
529 {
530 if (self->urgent)
531 frame_flash_start(self->frame);
532 else
533 frame_flash_stop(self->frame);
534 }
535
536 static void client_restore_session_state(ObClient *self)
537 {
538 GList *it;
539
540 if (!(it = session_state_find(self)))
541 return;
542
543 self->session = it->data;
544
545 RECT_SET_POINT(self->area, self->session->x, self->session->y);
546 self->positioned = TRUE;
547 if (self->session->w > 0)
548 self->area.width = self->session->w;
549 if (self->session->h > 0)
550 self->area.height = self->session->h;
551 XResizeWindow(ob_display, self->window,
552 self->area.width, self->area.height);
553
554 self->desktop = (self->session->desktop == DESKTOP_ALL ?
555 self->session->desktop :
556 MIN(screen_num_desktops - 1, self->session->desktop));
557 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
558
559 self->shaded = self->session->shaded;
560 self->iconic = self->session->iconic;
561 self->skip_pager = self->session->skip_pager;
562 self->skip_taskbar = self->session->skip_taskbar;
563 self->fullscreen = self->session->fullscreen;
564 self->above = self->session->above;
565 self->below = self->session->below;
566 self->max_horz = self->session->max_horz;
567 self->max_vert = self->session->max_vert;
568 }
569
570 static void client_restore_session_stacking(ObClient *self)
571 {
572 GList *it;
573
574 if (!self->session) return;
575
576 it = g_list_find(session_saved_state, self->session);
577 for (it = g_list_previous(it); it; it = g_list_previous(it)) {
578 GList *cit;
579
580 for (cit = client_list; cit; cit = g_list_next(cit))
581 if (session_state_cmp(it->data, cit->data))
582 break;
583 if (cit) {
584 client_calc_layer(self);
585 stacking_below(CLIENT_AS_WINDOW(self),
586 CLIENT_AS_WINDOW(cit->data));
587 break;
588 }
589 }
590 }
591
592 void client_move_onscreen(ObClient *self, gboolean rude)
593 {
594 gint x = self->area.x;
595 gint y = self->area.y;
596 if (client_find_onscreen(self, &x, &y,
597 self->frame->area.width,
598 self->frame->area.height, rude)) {
599 client_move(self, x, y);
600 }
601 }
602
603 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
604 gboolean rude)
605 {
606 Rect *a;
607 gint ox = *x, oy = *y;
608
609 frame_client_gravity(self->frame, x, y); /* get where the frame
610 would be */
611
612 /* XXX watch for xinerama dead areas */
613 /* This makes sure windows aren't entirely outside of the screen so you
614 * can't see them at all */
615 a = screen_area(self->desktop);
616 if (client_normal(self)) {
617 if (!self->strut.right && *x >= a->x + a->width - 1)
618 *x = a->x + a->width - self->frame->area.width;
619 if (!self->strut.bottom && *y >= a->y + a->height - 1)
620 *y = a->y + a->height - self->frame->area.height;
621 if (!self->strut.left && *x + self->frame->area.width - 1 < a->x)
622 *x = a->x;
623 if (!self->strut.top && *y + self->frame->area.height - 1 < a->y)
624 *y = a->y;
625 }
626
627 /* This here doesn't let windows even a pixel outside the screen,
628 * not applied to all windows. Not sure if it's going to stay at all.
629 * I wonder if disabling this will break struts somehow? Let's find out. */
630 if (0 && rude) {
631 /* avoid the xinerama monitor divide while we're at it,
632 * remember to fix the placement stuff to avoid it also and
633 * then remove this XXX */
634 a = screen_physical_area_monitor(client_monitor(self));
635 /* this is ben's MOZILLA BITCHSLAP. "oh ya it fucking feels good.
636 Java can suck it too." */
637
638 /* dont let windows map/move into the strut unless they
639 are bigger than the available area */
640 if (w <= a->width) {
641 if (!self->strut.left && *x < a->x) *x = a->x;
642 if (!self->strut.right && *x + w > a->x + a->width)
643 *x = a->x + a->width - w;
644 }
645 if (h <= a->height) {
646 if (!self->strut.top && *y < a->y) *y = a->y;
647 if (!self->strut.bottom && *y + h > a->y + a->height)
648 *y = a->y + a->height - h;
649 }
650 }
651
652 frame_frame_gravity(self->frame, x, y); /* get where the client
653 should be */
654
655 return ox != *x || oy != *y;
656 }
657
658 static void client_toggle_border(ObClient *self, gboolean show)
659 {
660 /* adjust our idea of where the client is, based on its border. When the
661 border is removed, the client should now be considered to be in a
662 different position.
663 when re-adding the border to the client, the same operation needs to be
664 reversed. */
665 gint oldx = self->area.x, oldy = self->area.y;
666 gint x = oldx, y = oldy;
667 switch(self->gravity) {
668 default:
669 case NorthWestGravity:
670 case WestGravity:
671 case SouthWestGravity:
672 break;
673 case NorthEastGravity:
674 case EastGravity:
675 case SouthEastGravity:
676 if (show) x -= self->border_width * 2;
677 else x += self->border_width * 2;
678 break;
679 case NorthGravity:
680 case SouthGravity:
681 case CenterGravity:
682 case ForgetGravity:
683 case StaticGravity:
684 if (show) x -= self->border_width;
685 else x += self->border_width;
686 break;
687 }
688 switch(self->gravity) {
689 default:
690 case NorthWestGravity:
691 case NorthGravity:
692 case NorthEastGravity:
693 break;
694 case SouthWestGravity:
695 case SouthGravity:
696 case SouthEastGravity:
697 if (show) y -= self->border_width * 2;
698 else y += self->border_width * 2;
699 break;
700 case WestGravity:
701 case EastGravity:
702 case CenterGravity:
703 case ForgetGravity:
704 case StaticGravity:
705 if (show) y -= self->border_width;
706 else y += self->border_width;
707 break;
708 }
709 self->area.x = x;
710 self->area.y = y;
711
712 if (show) {
713 XSetWindowBorderWidth(ob_display, self->window, self->border_width);
714
715 /* move the client so it is back it the right spot _with_ its
716 border! */
717 if (x != oldx || y != oldy)
718 XMoveWindow(ob_display, self->window, x, y);
719 } else
720 XSetWindowBorderWidth(ob_display, self->window, 0);
721 }
722
723
724 static void client_get_all(ObClient *self)
725 {
726 client_get_area(self);
727 client_update_transient_for(self);
728 client_update_wmhints(self);
729 client_get_startup_id(self);
730 client_get_desktop(self);
731 client_get_shaped(self);
732
733 client_get_mwm_hints(self);
734 client_get_type(self);/* this can change the mwmhints for special cases */
735
736 /* The transient hint is used to pick a type, but the type can also affect
737 transiency (dialogs are always made transients). This is Havoc's idea,
738 but it is needed to make some apps work right (eg tsclient). */
739 client_update_transient_for(self);
740
741 client_get_state(self);
742
743 {
744 /* a couple type-based defaults for new windows */
745
746 /* this makes sure that these windows appear on all desktops */
747 if (self->type == OB_CLIENT_TYPE_DESKTOP)
748 self->desktop = DESKTOP_ALL;
749 }
750
751 client_update_protocols(self);
752
753 client_get_gravity(self); /* get the attribute gravity */
754 client_update_normal_hints(self); /* this may override the attribute
755 gravity */
756
757 /* got the type, the mwmhints, the protocols, and the normal hints
758 (min/max sizes), so we're ready to set up the decorations/functions */
759 client_setup_decor_and_functions(self);
760
761 client_update_title(self);
762 client_update_class(self);
763 client_update_sm_client_id(self);
764 client_update_strut(self);
765 client_update_icons(self);
766 }
767
768 static void client_get_startup_id(ObClient *self)
769 {
770 if (!(PROP_GETS(self->window, net_startup_id, utf8, &self->startup_id)))
771 if (self->group)
772 PROP_GETS(self->group->leader,
773 net_startup_id, utf8, &self->startup_id);
774 }
775
776 static void client_get_area(ObClient *self)
777 {
778 XWindowAttributes wattrib;
779 Status ret;
780
781 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
782 g_assert(ret != BadWindow);
783
784 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
785 self->border_width = wattrib.border_width;
786 }
787
788 static void client_get_desktop(ObClient *self)
789 {
790 guint32 d = screen_num_desktops; /* an always-invalid value */
791
792 if (PROP_GET32(self->window, net_wm_desktop, cardinal, &d)) {
793 if (d >= screen_num_desktops && d != DESKTOP_ALL)
794 self->desktop = screen_num_desktops - 1;
795 else
796 self->desktop = d;
797 } else {
798 gboolean trdesk = FALSE;
799
800 if (self->transient_for) {
801 if (self->transient_for != OB_TRAN_GROUP) {
802 self->desktop = self->transient_for->desktop;
803 trdesk = TRUE;
804 } else {
805 GSList *it;
806
807 for (it = self->group->members; it; it = g_slist_next(it))
808 if (it->data != self &&
809 !((ObClient*)it->data)->transient_for) {
810 self->desktop = ((ObClient*)it->data)->desktop;
811 trdesk = TRUE;
812 break;
813 }
814 }
815 }
816 if (!trdesk) {
817 /* try get from the startup-notification protocol */
818 if (sn_get_desktop(self->startup_id, &self->desktop)) {
819 if (self->desktop >= screen_num_desktops &&
820 self->desktop != DESKTOP_ALL)
821 self->desktop = screen_num_desktops - 1;
822 } else
823 /* defaults to the current desktop */
824 self->desktop = screen_desktop;
825 }
826 }
827 if (self->desktop != d) {
828 /* set the desktop hint, to make sure that it always exists */
829 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
830 }
831 }
832
833 static void client_get_state(ObClient *self)
834 {
835 guint32 *state;
836 guint num;
837
838 if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
839 gulong i;
840 for (i = 0; i < num; ++i) {
841 if (state[i] == prop_atoms.net_wm_state_modal)
842 self->modal = TRUE;
843 else if (state[i] == prop_atoms.net_wm_state_shaded)
844 self->shaded = TRUE;
845 else if (state[i] == prop_atoms.net_wm_state_hidden)
846 self->iconic = TRUE;
847 else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
848 self->skip_taskbar = TRUE;
849 else if (state[i] == prop_atoms.net_wm_state_skip_pager)
850 self->skip_pager = TRUE;
851 else if (state[i] == prop_atoms.net_wm_state_fullscreen)
852 self->fullscreen = TRUE;
853 else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
854 self->max_vert = TRUE;
855 else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
856 self->max_horz = TRUE;
857 else if (state[i] == prop_atoms.net_wm_state_above)
858 self->above = TRUE;
859 else if (state[i] == prop_atoms.net_wm_state_below)
860 self->below = TRUE;
861 else if (state[i] == prop_atoms.ob_wm_state_undecorated)
862 self->undecorated = TRUE;
863 }
864
865 g_free(state);
866 }
867
868 if (!(self->above || self->below)) {
869 if (self->group) {
870 /* apply stuff from the group */
871 GSList *it;
872 gint layer = -2;
873
874 for (it = self->group->members; it; it = g_slist_next(it)) {
875 ObClient *c = it->data;
876 if (c != self && !client_search_transient(self, c) &&
877 client_normal(self) && client_normal(c))
878 {
879 layer = MAX(layer,
880 (c->above ? 1 : (c->below ? -1 : 0)));
881 }
882 }
883 switch (layer) {
884 case -1:
885 self->below = TRUE;
886 break;
887 case -2:
888 case 0:
889 break;
890 case 1:
891 self->above = TRUE;
892 break;
893 default:
894 g_assert_not_reached();
895 break;
896 }
897 }
898 }
899 }
900
901 static void client_get_shaped(ObClient *self)
902 {
903 self->shaped = FALSE;
904 #ifdef SHAPE
905 if (extensions_shape) {
906 gint foo;
907 guint ufoo;
908 gint s;
909
910 XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
911
912 XShapeQueryExtents(ob_display, self->window, &s, &foo,
913 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
914 &ufoo);
915 self->shaped = (s != 0);
916 }
917 #endif
918 }
919
920 void client_update_transient_for(ObClient *self)
921 {
922 Window t = None;
923 ObClient *target = NULL;
924
925 if (XGetTransientForHint(ob_display, self->window, &t)) {
926 self->transient = TRUE;
927 if (t != self->window) { /* cant be transient to itself! */
928 target = g_hash_table_lookup(window_map, &t);
929 /* if this happens then we need to check for it*/
930 g_assert(target != self);
931 if (target && !WINDOW_IS_CLIENT(target)) {
932 /* this can happen when a dialog is a child of
933 a dockapp, for example */
934 target = NULL;
935 }
936
937 if (!target && self->group) {
938 /* not transient to a client, see if it is transient for a
939 group */
940 if (t == self->group->leader ||
941 t == None ||
942 t == RootWindow(ob_display, ob_screen))
943 {
944 /* window is a transient for its group! */
945 target = OB_TRAN_GROUP;
946 }
947 }
948 }
949 } else if (self->type == OB_CLIENT_TYPE_DIALOG && self->group) {
950 self->transient = TRUE;
951 target = OB_TRAN_GROUP;
952 } else
953 self->transient = FALSE;
954
955 /* if anything has changed... */
956 if (target != self->transient_for) {
957 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
958 GSList *it;
959
960 /* remove from old parents */
961 for (it = self->group->members; it; it = g_slist_next(it)) {
962 ObClient *c = it->data;
963 if (c != self && !c->transient_for)
964 c->transients = g_slist_remove(c->transients, self);
965 }
966 } else if (self->transient_for != NULL) { /* transient of window */
967 /* remove from old parent */
968 self->transient_for->transients =
969 g_slist_remove(self->transient_for->transients, self);
970 }
971 self->transient_for = target;
972 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
973 GSList *it;
974
975 /* add to new parents */
976 for (it = self->group->members; it; it = g_slist_next(it)) {
977 ObClient *c = it->data;
978 if (c != self && !c->transient_for)
979 c->transients = g_slist_append(c->transients, self);
980 }
981
982 /* remove all transients which are in the group, that causes
983 circlular pointer hell of doom */
984 for (it = self->group->members; it; it = g_slist_next(it)) {
985 GSList *sit, *next;
986 for (sit = self->transients; sit; sit = next) {
987 next = g_slist_next(sit);
988 if (sit->data == it->data)
989 self->transients =
990 g_slist_delete_link(self->transients, sit);
991 }
992 }
993 } else if (self->transient_for != NULL) { /* transient of window */
994 /* add to new parent */
995 self->transient_for->transients =
996 g_slist_append(self->transient_for->transients, self);
997 }
998 }
999 }
1000
1001 static void client_get_mwm_hints(ObClient *self)
1002 {
1003 guint num;
1004 guint32 *hints;
1005
1006 self->mwmhints.flags = 0; /* default to none */
1007
1008 if (PROP_GETA32(self->window, motif_wm_hints, motif_wm_hints,
1009 &hints, &num)) {
1010 if (num >= OB_MWM_ELEMENTS) {
1011 self->mwmhints.flags = hints[0];
1012 self->mwmhints.functions = hints[1];
1013 self->mwmhints.decorations = hints[2];
1014 }
1015 g_free(hints);
1016 }
1017 }
1018
1019 void client_get_type(ObClient *self)
1020 {
1021 guint num, i;
1022 guint32 *val;
1023
1024 self->type = -1;
1025
1026 if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) {
1027 /* use the first value that we know about in the array */
1028 for (i = 0; i < num; ++i) {
1029 if (val[i] == prop_atoms.net_wm_window_type_desktop)
1030 self->type = OB_CLIENT_TYPE_DESKTOP;
1031 else if (val[i] == prop_atoms.net_wm_window_type_dock)
1032 self->type = OB_CLIENT_TYPE_DOCK;
1033 else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
1034 self->type = OB_CLIENT_TYPE_TOOLBAR;
1035 else if (val[i] == prop_atoms.net_wm_window_type_menu)
1036 self->type = OB_CLIENT_TYPE_MENU;
1037 else if (val[i] == prop_atoms.net_wm_window_type_utility)
1038 self->type = OB_CLIENT_TYPE_UTILITY;
1039 else if (val[i] == prop_atoms.net_wm_window_type_splash)
1040 self->type = OB_CLIENT_TYPE_SPLASH;
1041 else if (val[i] == prop_atoms.net_wm_window_type_dialog)
1042 self->type = OB_CLIENT_TYPE_DIALOG;
1043 else if (val[i] == prop_atoms.net_wm_window_type_normal)
1044 self->type = OB_CLIENT_TYPE_NORMAL;
1045 else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
1046 /* prevent this window from getting any decor or
1047 functionality */
1048 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1049 OB_MWM_FLAG_DECORATIONS);
1050 self->mwmhints.decorations = 0;
1051 self->mwmhints.functions = 0;
1052 }
1053 if (self->type != (ObClientType) -1)
1054 break; /* grab the first legit type */
1055 }
1056 g_free(val);
1057 }
1058
1059 if (self->type == (ObClientType) -1) {
1060 /*the window type hint was not set, which means we either classify
1061 ourself as a normal window or a dialog, depending on if we are a
1062 transient. */
1063 if (self->transient)
1064 self->type = OB_CLIENT_TYPE_DIALOG;
1065 else
1066 self->type = OB_CLIENT_TYPE_NORMAL;
1067 }
1068 }
1069
1070 void client_update_protocols(ObClient *self)
1071 {
1072 guint32 *proto;
1073 guint num_return, i;
1074
1075 self->focus_notify = FALSE;
1076 self->delete_window = FALSE;
1077
1078 if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) {
1079 for (i = 0; i < num_return; ++i) {
1080 if (proto[i] == prop_atoms.wm_delete_window) {
1081 /* this means we can request the window to close */
1082 self->delete_window = TRUE;
1083 } else if (proto[i] == prop_atoms.wm_take_focus)
1084 /* if this protocol is requested, then the window will be
1085 notified whenever we want it to receive focus */
1086 self->focus_notify = TRUE;
1087 }
1088 g_free(proto);
1089 }
1090 }
1091
1092 static void client_get_gravity(ObClient *self)
1093 {
1094 XWindowAttributes wattrib;
1095 Status ret;
1096
1097 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
1098 g_assert(ret != BadWindow);
1099 self->gravity = wattrib.win_gravity;
1100 }
1101
1102 void client_update_normal_hints(ObClient *self)
1103 {
1104 XSizeHints size;
1105 glong ret;
1106 gint oldgravity = self->gravity;
1107
1108 /* defaults */
1109 self->min_ratio = 0.0f;
1110 self->max_ratio = 0.0f;
1111 SIZE_SET(self->size_inc, 1, 1);
1112 SIZE_SET(self->base_size, 0, 0);
1113 SIZE_SET(self->min_size, 0, 0);
1114 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1115
1116 /* get the hints from the window */
1117 if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
1118 /* normal windows can't request placement! har har
1119 if (!client_normal(self))
1120 */
1121 self->positioned = !!(size.flags & (PPosition|USPosition));
1122
1123 if (size.flags & PWinGravity) {
1124 self->gravity = size.win_gravity;
1125
1126 /* if the client has a frame, i.e. has already been mapped and
1127 is changing its gravity */
1128 if (self->frame && self->gravity != oldgravity) {
1129 /* move our idea of the client's position based on its new
1130 gravity */
1131 self->area.x = self->frame->area.x;
1132 self->area.y = self->frame->area.y;
1133 frame_frame_gravity(self->frame, &self->area.x, &self->area.y);
1134 }
1135 }
1136
1137 if (size.flags & PAspect) {
1138 if (size.min_aspect.y)
1139 self->min_ratio =
1140 (gfloat) size.min_aspect.x / size.min_aspect.y;
1141 if (size.max_aspect.y)
1142 self->max_ratio =
1143 (gfloat) size.max_aspect.x / size.max_aspect.y;
1144 }
1145
1146 if (size.flags & PMinSize)
1147 SIZE_SET(self->min_size, size.min_width, size.min_height);
1148
1149 if (size.flags & PMaxSize)
1150 SIZE_SET(self->max_size, size.max_width, size.max_height);
1151
1152 if (size.flags & PBaseSize)
1153 SIZE_SET(self->base_size, size.base_width, size.base_height);
1154
1155 if (size.flags & PResizeInc)
1156 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1157 }
1158 }
1159
1160 void client_setup_decor_and_functions(ObClient *self)
1161 {
1162 /* start with everything (cept fullscreen) */
1163 self->decorations =
1164 (OB_FRAME_DECOR_TITLEBAR |
1165 (ob_rr_theme->show_handle ? OB_FRAME_DECOR_HANDLE : 0) |
1166 OB_FRAME_DECOR_GRIPS |
1167 OB_FRAME_DECOR_BORDER |
1168 OB_FRAME_DECOR_ICON |
1169 OB_FRAME_DECOR_ALLDESKTOPS |
1170 OB_FRAME_DECOR_ICONIFY |
1171 OB_FRAME_DECOR_MAXIMIZE |
1172 OB_FRAME_DECOR_SHADE |
1173 OB_FRAME_DECOR_CLOSE);
1174 self->functions =
1175 (OB_CLIENT_FUNC_RESIZE |
1176 OB_CLIENT_FUNC_MOVE |
1177 OB_CLIENT_FUNC_ICONIFY |
1178 OB_CLIENT_FUNC_MAXIMIZE |
1179 OB_CLIENT_FUNC_SHADE |
1180 OB_CLIENT_FUNC_CLOSE);
1181
1182 if (!(self->min_size.width < self->max_size.width ||
1183 self->min_size.height < self->max_size.height))
1184 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1185
1186 switch (self->type) {
1187 case OB_CLIENT_TYPE_NORMAL:
1188 /* normal windows retain all of the possible decorations and
1189 functionality, and are the only windows that you can fullscreen */
1190 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1191 break;
1192
1193 case OB_CLIENT_TYPE_DIALOG:
1194 case OB_CLIENT_TYPE_UTILITY:
1195 /* these windows cannot be maximized */
1196 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1197 break;
1198
1199 case OB_CLIENT_TYPE_MENU:
1200 case OB_CLIENT_TYPE_TOOLBAR:
1201 /* these windows get less functionality */
1202 self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_RESIZE);
1203 break;
1204
1205 case OB_CLIENT_TYPE_DESKTOP:
1206 case OB_CLIENT_TYPE_DOCK:
1207 case OB_CLIENT_TYPE_SPLASH:
1208 /* none of these windows are manipulated by the window manager */
1209 self->decorations = 0;
1210 self->functions = 0;
1211 break;
1212 }
1213
1214 /* Mwm Hints are applied subtractively to what has already been chosen for
1215 decor and functionality */
1216 if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1217 if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1218 if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1219 (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1220 /* if the mwm hints request no handle or title, then all
1221 decorations are disabled */
1222 self->decorations = 0;
1223 }
1224 }
1225
1226 if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1227 if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1228 if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1229 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1230 if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1231 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1232 /* dont let mwm hints kill any buttons
1233 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1234 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1235 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1236 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1237 */
1238 /* dont let mwm hints kill the close button
1239 if (! (self->mwmhints.functions & MwmFunc_Close))
1240 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1241 }
1242 }
1243
1244 if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1245 self->decorations &= ~OB_FRAME_DECOR_SHADE;
1246 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1247 self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1248 if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1249 self->decorations &= ~OB_FRAME_DECOR_GRIPS;
1250
1251 /* can't maximize without moving/resizing */
1252 if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1253 (self->functions & OB_CLIENT_FUNC_MOVE) &&
1254 (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1255 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1256 self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1257 }
1258
1259 /* kill the handle on fully maxed windows */
1260 if (self->max_vert && self->max_horz)
1261 self->decorations &= ~OB_FRAME_DECOR_HANDLE;
1262
1263 /* finally, the user can have requested no decorations, which overrides
1264 everything (but doesnt give it a border if it doesnt have one) */
1265 if (self->undecorated) {
1266 if (config_theme_keepborder)
1267 self->decorations &= OB_FRAME_DECOR_BORDER;
1268 else
1269 self->decorations = 0;
1270 }
1271
1272 /* if we don't have a titlebar, then we cannot shade! */
1273 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1274 self->functions &= ~OB_CLIENT_FUNC_SHADE;
1275
1276 /* now we need to check against rules for the client's current state */
1277 if (self->fullscreen) {
1278 self->functions &= (OB_CLIENT_FUNC_CLOSE |
1279 OB_CLIENT_FUNC_FULLSCREEN |
1280 OB_CLIENT_FUNC_ICONIFY);
1281 self->decorations = 0;
1282 }
1283
1284 client_change_allowed_actions(self);
1285
1286 if (self->frame) {
1287 /* adjust the client's decorations, etc. */
1288 client_reconfigure(self);
1289 }
1290 }
1291
1292 static void client_change_allowed_actions(ObClient *self)
1293 {
1294 guint32 actions[9];
1295 gint num = 0;
1296
1297 /* desktop windows are kept on all desktops */
1298 if (self->type != OB_CLIENT_TYPE_DESKTOP)
1299 actions[num++] = prop_atoms.net_wm_action_change_desktop;
1300
1301 if (self->functions & OB_CLIENT_FUNC_SHADE)
1302 actions[num++] = prop_atoms.net_wm_action_shade;
1303 if (self->functions & OB_CLIENT_FUNC_CLOSE)
1304 actions[num++] = prop_atoms.net_wm_action_close;
1305 if (self->functions & OB_CLIENT_FUNC_MOVE)
1306 actions[num++] = prop_atoms.net_wm_action_move;
1307 if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1308 actions[num++] = prop_atoms.net_wm_action_minimize;
1309 if (self->functions & OB_CLIENT_FUNC_RESIZE)
1310 actions[num++] = prop_atoms.net_wm_action_resize;
1311 if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1312 actions[num++] = prop_atoms.net_wm_action_fullscreen;
1313 if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1314 actions[num++] = prop_atoms.net_wm_action_maximize_horz;
1315 actions[num++] = prop_atoms.net_wm_action_maximize_vert;
1316 }
1317
1318 PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
1319
1320 /* make sure the window isn't breaking any rules now */
1321
1322 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1323 if (self->frame) client_shade(self, FALSE);
1324 else self->shaded = FALSE;
1325 }
1326 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
1327 if (self->frame) client_iconify(self, FALSE, TRUE);
1328 else self->iconic = FALSE;
1329 }
1330 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1331 if (self->frame) client_fullscreen(self, FALSE, TRUE);
1332 else self->fullscreen = FALSE;
1333 }
1334 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1335 self->max_vert)) {
1336 if (self->frame) client_maximize(self, FALSE, 0, TRUE);
1337 else self->max_vert = self->max_horz = FALSE;
1338 }
1339 }
1340
1341 void client_reconfigure(ObClient *self)
1342 {
1343 /* by making this pass FALSE for user, we avoid the emacs event storm where
1344 every configurenotify causes an update in its normal hints, i think this
1345 is generally what we want anyways... */
1346 client_configure(self, OB_CORNER_TOPLEFT, self->area.x, self->area.y,
1347 self->area.width, self->area.height, FALSE, TRUE);
1348 }
1349
1350 void client_update_wmhints(ObClient *self)
1351 {
1352 XWMHints *hints;
1353 gboolean ur = FALSE;
1354 GSList *it;
1355
1356 /* assume a window takes input if it doesnt specify */
1357 self->can_focus = TRUE;
1358
1359 if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
1360 if (hints->flags & InputHint)
1361 self->can_focus = hints->input;
1362
1363 /* only do this when first managing the window *AND* when we aren't
1364 starting up! */
1365 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1366 if (hints->flags & StateHint)
1367 self->iconic = hints->initial_state == IconicState;
1368
1369 if (hints->flags & XUrgencyHint)
1370 ur = TRUE;
1371
1372 if (!(hints->flags & WindowGroupHint))
1373 hints->window_group = None;
1374
1375 /* did the group state change? */
1376 if (hints->window_group !=
1377 (self->group ? self->group->leader : None)) {
1378 /* remove from the old group if there was one */
1379 if (self->group != NULL) {
1380 /* remove transients of the group */
1381 for (it = self->group->members; it; it = g_slist_next(it))
1382 self->transients = g_slist_remove(self->transients,
1383 it->data);
1384
1385 /* remove myself from parents in the group */
1386 if (self->transient_for == OB_TRAN_GROUP) {
1387 for (it = self->group->members; it;
1388 it = g_slist_next(it))
1389 {
1390 ObClient *c = it->data;
1391
1392 if (c != self && !c->transient_for)
1393 c->transients = g_slist_remove(c->transients,
1394 self);
1395 }
1396 }
1397
1398 group_remove(self->group, self);
1399 self->group = NULL;
1400 }
1401 if (hints->window_group != None) {
1402 self->group = group_add(hints->window_group, self);
1403
1404 /* i can only have transients from the group if i am not
1405 transient myself */
1406 if (!self->transient_for) {
1407 /* add other transients of the group that are already
1408 set up */
1409 for (it = self->group->members; it;
1410 it = g_slist_next(it))
1411 {
1412 ObClient *c = it->data;
1413 if (c != self && c->transient_for == OB_TRAN_GROUP)
1414 self->transients =
1415 g_slist_append(self->transients, c);
1416 }
1417 }
1418 }
1419
1420 /* because the self->transient flag wont change from this call,
1421 we don't need to update the window's type and such, only its
1422 transient_for, and the transients lists of other windows in
1423 the group may be affected */
1424 client_update_transient_for(self);
1425 }
1426
1427 /* the WM_HINTS can contain an icon */
1428 client_update_icons(self);
1429
1430 XFree(hints);
1431 }
1432
1433 if (ur != self->urgent) {
1434 self->urgent = ur;
1435 /* fire the urgent callback if we're mapped, otherwise, wait until
1436 after we're mapped */
1437 if (self->frame)
1438 client_urgent_notify(self);
1439 }
1440 }
1441
1442 void client_update_title(ObClient *self)
1443 {
1444 GList *it;
1445 guint32 nums;
1446 guint i;
1447 gchar *data = NULL;
1448 gboolean read_title;
1449 gchar *old_title;
1450
1451 old_title = self->title;
1452
1453 /* try netwm */
1454 if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) {
1455 /* try old x stuff */
1456 if (!PROP_GETS(self->window, wm_name, locale, &data)) {
1457 // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1458 if (self->transient) {
1459 data = g_strdup("");
1460 goto no_number;
1461 } else
1462 data = g_strdup("Unnamed Window");
1463 }
1464 }
1465
1466 /* did the title change? then reset the title_count */
1467 if (old_title && 0 != strncmp(old_title, data, strlen(data)))
1468 self->title_count = 1;
1469
1470 /* look for duplicates and append a number */
1471 nums = 0;
1472 for (it = client_list; it; it = g_list_next(it))
1473 if (it->data != self) {
1474 ObClient *c = it->data;
1475 if (0 == strncmp(c->title, data, strlen(data)))
1476 nums |= 1 << c->title_count;
1477 }
1478 /* find first free number */
1479 for (i = 1; i <= 32; ++i)
1480 if (!(nums & (1 << i))) {
1481 if (self->title_count == 1 || i == 1)
1482 self->title_count = i;
1483 break;
1484 }
1485 /* dont display the number for the first window */
1486 if (self->title_count > 1) {
1487 gchar *ndata;
1488 ndata = g_strdup_printf("%s - [%u]", data, self->title_count);
1489 g_free(data);
1490 data = ndata;
1491 }
1492
1493 PROP_SETS(self->window, net_wm_visible_name, data);
1494 no_number:
1495 self->title = data;
1496
1497 if (self->frame)
1498 frame_adjust_title(self->frame);
1499
1500 g_free(old_title);
1501
1502 /* update the icon title */
1503 data = NULL;
1504 g_free(self->icon_title);
1505
1506 read_title = TRUE;
1507 /* try netwm */
1508 if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data))
1509 /* try old x stuff */
1510 if (!PROP_GETS(self->window, wm_icon_name, locale, &data)) {
1511 data = g_strdup(self->title);
1512 read_title = FALSE;
1513 }
1514
1515 /* append the title count, dont display the number for the first window */
1516 if (read_title && self->title_count > 1) {
1517 gchar *vdata, *ndata;
1518 ndata = g_strdup_printf(" - [%u]", self->title_count);
1519 vdata = g_strconcat(data, ndata, NULL);
1520 g_free(ndata);
1521 g_free(data);
1522 data = vdata;
1523 }
1524
1525 PROP_SETS(self->window, net_wm_visible_icon_name, data);
1526
1527 self->icon_title = data;
1528 }
1529
1530 void client_update_class(ObClient *self)
1531 {
1532 gchar **data;
1533 gchar *s;
1534
1535 if (self->name) g_free(self->name);
1536 if (self->class) g_free(self->class);
1537 if (self->role) g_free(self->role);
1538
1539 self->name = self->class = self->role = NULL;
1540
1541 if (PROP_GETSS(self->window, wm_class, locale, &data)) {
1542 if (data[0]) {
1543 self->name = g_strdup(data[0]);
1544 if (data[1])
1545 self->class = g_strdup(data[1]);
1546 }
1547 g_strfreev(data);
1548 }
1549
1550 if (PROP_GETS(self->window, wm_window_role, locale, &s))
1551 self->role = s;
1552
1553 if (self->name == NULL) self->name = g_strdup("");
1554 if (self->class == NULL) self->class = g_strdup("");
1555 if (self->role == NULL) self->role = g_strdup("");
1556 }
1557
1558 void client_update_strut(ObClient *self)
1559 {
1560 guint num;
1561 guint32 *data;
1562 gboolean got = FALSE;
1563 StrutPartial strut;
1564
1565 if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
1566 &data, &num)) {
1567 if (num == 12) {
1568 got = TRUE;
1569 STRUT_PARTIAL_SET(strut,
1570 data[0], data[2], data[1], data[3],
1571 data[4], data[5], data[8], data[9],
1572 data[6], data[7], data[10], data[11]);
1573 }
1574 g_free(data);
1575 }
1576
1577 if (!got &&
1578 PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
1579 if (num == 4) {
1580 const Rect *a;
1581
1582 got = TRUE;
1583
1584 /* use the screen's width/height */
1585 a = screen_physical_area();
1586
1587 STRUT_PARTIAL_SET(strut,
1588 data[0], data[2], data[1], data[3],
1589 a->y, a->y + a->height - 1,
1590 a->x, a->x + a->width - 1,
1591 a->y, a->y + a->height - 1,
1592 a->x, a->x + a->width - 1);
1593 }
1594 g_free(data);
1595 }
1596
1597 if (!got)
1598 STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
1599 0, 0, 0, 0, 0, 0, 0, 0);
1600
1601 if (!STRUT_EQUAL(strut, self->strut)) {
1602 self->strut = strut;
1603
1604 /* updating here is pointless while we're being mapped cuz we're not in
1605 the client list yet */
1606 if (self->frame)
1607 screen_update_areas();
1608 }
1609 }
1610
1611 void client_update_icons(ObClient *self)
1612 {
1613 guint num;
1614 guint32 *data;
1615 guint w, h, i, j;
1616
1617 for (i = 0; i < self->nicons; ++i)
1618 g_free(self->icons[i].data);
1619 if (self->nicons > 0)
1620 g_free(self->icons);
1621 self->nicons = 0;
1622
1623 if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
1624 /* figure out how many valid icons are in here */
1625 i = 0;
1626 while (num - i > 2) {
1627 w = data[i++];
1628 h = data[i++];
1629 i += w * h;
1630 if (i > num || w*h == 0) break;
1631 ++self->nicons;
1632 }
1633
1634 self->icons = g_new(ObClientIcon, self->nicons);
1635
1636 /* store the icons */
1637 i = 0;
1638 for (j = 0; j < self->nicons; ++j) {
1639 guint x, y, t;
1640
1641 w = self->icons[j].width = data[i++];
1642 h = self->icons[j].height = data[i++];
1643
1644 if (w*h == 0) continue;
1645
1646 self->icons[j].data = g_new(RrPixel32, w * h);
1647 for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
1648 if (x >= w) {
1649 x = 0;
1650 ++y;
1651 }
1652 self->icons[j].data[t] =
1653 (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
1654 (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
1655 (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
1656 (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
1657 }
1658 g_assert(i <= num);
1659 }
1660
1661 g_free(data);
1662 } else if (PROP_GETA32(self->window, kwm_win_icon,
1663 kwm_win_icon, &data, &num)) {
1664 if (num == 2) {
1665 self->nicons++;
1666 self->icons = g_new(ObClientIcon, self->nicons);
1667 xerror_set_ignore(TRUE);
1668 if (!RrPixmapToRGBA(ob_rr_inst,
1669 data[0], data[1],
1670 &self->icons[self->nicons-1].width,
1671 &self->icons[self->nicons-1].height,
1672 &self->icons[self->nicons-1].data)) {
1673 g_free(&self->icons[self->nicons-1]);
1674 self->nicons--;
1675 }
1676 xerror_set_ignore(FALSE);
1677 }
1678 g_free(data);
1679 } else {
1680 XWMHints *hints;
1681
1682 if ((hints = XGetWMHints(ob_display, self->window))) {
1683 if (hints->flags & IconPixmapHint) {
1684 self->nicons++;
1685 self->icons = g_new(ObClientIcon, self->nicons);
1686 xerror_set_ignore(TRUE);
1687 if (!RrPixmapToRGBA(ob_rr_inst,
1688 hints->icon_pixmap,
1689 (hints->flags & IconMaskHint ?
1690 hints->icon_mask : None),
1691 &self->icons[self->nicons-1].width,
1692 &self->icons[self->nicons-1].height,
1693 &self->icons[self->nicons-1].data)){
1694 g_free(&self->icons[self->nicons-1]);
1695 self->nicons--;
1696 }
1697 xerror_set_ignore(FALSE);
1698 }
1699 XFree(hints);
1700 }
1701 }
1702
1703 if (self->frame)
1704 frame_adjust_icon(self->frame);
1705 }
1706
1707 static void client_change_state(ObClient *self)
1708 {
1709 guint32 state[2];
1710 guint32 netstate[11];
1711 guint num;
1712
1713 state[0] = self->wmstate;
1714 state[1] = None;
1715 PROP_SETA32(self->window, wm_state, wm_state, state, 2);
1716
1717 num = 0;
1718 if (self->modal)
1719 netstate[num++] = prop_atoms.net_wm_state_modal;
1720 if (self->shaded)
1721 netstate[num++] = prop_atoms.net_wm_state_shaded;
1722 if (self->iconic)
1723 netstate[num++] = prop_atoms.net_wm_state_hidden;
1724 if (self->skip_taskbar)
1725 netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
1726 if (self->skip_pager)
1727 netstate[num++] = prop_atoms.net_wm_state_skip_pager;
1728 if (self->fullscreen)
1729 netstate[num++] = prop_atoms.net_wm_state_fullscreen;
1730 if (self->max_vert)
1731 netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
1732 if (self->max_horz)
1733 netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
1734 if (self->above)
1735 netstate[num++] = prop_atoms.net_wm_state_above;
1736 if (self->below)
1737 netstate[num++] = prop_atoms.net_wm_state_below;
1738 if (self->undecorated)
1739 netstate[num++] = prop_atoms.ob_wm_state_undecorated;
1740 PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
1741
1742 client_calc_layer(self);
1743
1744 if (self->frame)
1745 frame_adjust_state(self->frame);
1746 }
1747
1748 ObClient *client_search_focus_tree(ObClient *self)
1749 {
1750 GSList *it;
1751 ObClient *ret;
1752
1753 for (it = self->transients; it; it = g_slist_next(it)) {
1754 if (client_focused(it->data)) return it->data;
1755 if ((ret = client_search_focus_tree(it->data))) return ret;
1756 }
1757 return NULL;
1758 }
1759
1760 ObClient *client_search_focus_tree_full(ObClient *self)
1761 {
1762 if (self->transient_for) {
1763 if (self->transient_for != OB_TRAN_GROUP) {
1764 return client_search_focus_tree_full(self->transient_for);
1765 } else {
1766 GSList *it;
1767 gboolean recursed = FALSE;
1768
1769 for (it = self->group->members; it; it = g_slist_next(it))
1770 if (!((ObClient*)it->data)->transient_for) {
1771 ObClient *c;
1772 if ((c = client_search_focus_tree_full(it->data)))
1773 return c;
1774 recursed = TRUE;
1775 }
1776 if (recursed)
1777 return NULL;
1778 }
1779 }
1780
1781 /* this function checks the whole tree, the client_search_focus_tree~
1782 does not, so we need to check this window */
1783 if (client_focused(self))
1784 return self;
1785 return client_search_focus_tree(self);
1786 }
1787
1788 static ObStackingLayer calc_layer(ObClient *self)
1789 {
1790 ObStackingLayer l;
1791
1792 if (self->fullscreen &&
1793 (client_focused(self) || client_search_focus_tree(self)))
1794 l = OB_STACKING_LAYER_FULLSCREEN;
1795 else if (self->type == OB_CLIENT_TYPE_DESKTOP)
1796 l = OB_STACKING_LAYER_DESKTOP;
1797 else if (self->type == OB_CLIENT_TYPE_DOCK) {
1798 if (self->above) l = OB_STACKING_LAYER_DOCK_ABOVE;
1799 else if (self->below) l = OB_STACKING_LAYER_DOCK_BELOW;
1800 else l = OB_STACKING_LAYER_DOCK_NORMAL;
1801 }
1802 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
1803 else if (self->below) l = OB_STACKING_LAYER_BELOW;
1804 else l = OB_STACKING_LAYER_NORMAL;
1805
1806 return l;
1807 }
1808
1809 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
1810 ObStackingLayer l, gboolean raised)
1811 {
1812 ObStackingLayer old, own;
1813 GSList *it;
1814
1815 old = self->layer;
1816 own = calc_layer(self);
1817 self->layer = l > own ? l : own;
1818
1819 for (it = self->transients; it; it = g_slist_next(it))
1820 client_calc_layer_recursive(it->data, orig,
1821 l, raised ? raised : l != old);
1822
1823 if (!raised && l != old)
1824 if (orig->frame) { /* only restack if the original window is managed */
1825 stacking_remove(CLIENT_AS_WINDOW(self));
1826 stacking_add(CLIENT_AS_WINDOW(self));
1827 }
1828 }
1829
1830 void client_calc_layer(ObClient *self)
1831 {
1832 ObStackingLayer l;
1833 ObClient *orig;
1834
1835 orig = self;
1836
1837 /* transients take on the layer of their parents */
1838 self = client_search_top_transient(self);
1839
1840 l = calc_layer(self);
1841
1842 client_calc_layer_recursive(self, orig, l, FALSE);
1843 }
1844
1845 gboolean client_should_show(ObClient *self)
1846 {
1847 if (self->iconic)
1848 return FALSE;
1849 if (client_normal(self) && screen_showing_desktop)
1850 return FALSE;
1851 /*
1852 if (self->transient_for) {
1853 if (self->transient_for != OB_TRAN_GROUP)
1854 return client_should_show(self->transient_for);
1855 else {
1856 GSList *it;
1857
1858 for (it = self->group->members; it; it = g_slist_next(it)) {
1859 ObClient *c = it->data;
1860 if (c != self && !c->transient_for) {
1861 if (client_should_show(c))
1862 return TRUE;
1863 }
1864 }
1865 }
1866 }
1867 */
1868 if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
1869 return TRUE;
1870
1871 return FALSE;
1872 }
1873
1874 static void client_showhide(ObClient *self)
1875 {
1876
1877 if (client_should_show(self))
1878 frame_show(self->frame);
1879 else
1880 frame_hide(self->frame);
1881 }
1882
1883 gboolean client_normal(ObClient *self) {
1884 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
1885 self->type == OB_CLIENT_TYPE_DOCK ||
1886 self->type == OB_CLIENT_TYPE_SPLASH);
1887 }
1888
1889 static void client_apply_startup_state(ObClient *self)
1890 {
1891 /* these are in a carefully crafted order.. */
1892
1893 if (self->iconic) {
1894 self->iconic = FALSE;
1895 client_iconify(self, TRUE, FALSE);
1896 }
1897 if (self->fullscreen) {
1898 self->fullscreen = FALSE;
1899 client_fullscreen(self, TRUE, FALSE);
1900 }
1901 if (self->undecorated) {
1902 self->undecorated = FALSE;
1903 client_set_undecorated(self, TRUE);
1904 }
1905 if (self->shaded) {
1906 self->shaded = FALSE;
1907 client_shade(self, TRUE);
1908 }
1909 if (self->urgent)
1910 client_urgent_notify(self);
1911
1912 if (self->max_vert && self->max_horz) {
1913 self->max_vert = self->max_horz = FALSE;
1914 client_maximize(self, TRUE, 0, FALSE);
1915 } else if (self->max_vert) {
1916 self->max_vert = FALSE;
1917 client_maximize(self, TRUE, 2, FALSE);
1918 } else if (self->max_horz) {
1919 self->max_horz = FALSE;
1920 client_maximize(self, TRUE, 1, FALSE);
1921 }
1922
1923 /* nothing to do for the other states:
1924 skip_taskbar
1925 skip_pager
1926 modal
1927 above
1928 below
1929 */
1930 }
1931
1932 void client_configure_full(ObClient *self, ObCorner anchor,
1933 gint x, gint y, gint w, gint h,
1934 gboolean user, gboolean final,
1935 gboolean force_reply)
1936 {
1937 gint oldw, oldh;
1938 gboolean send_resize_client;
1939 gboolean moved = FALSE, resized = FALSE;
1940 guint fdecor = self->frame->decorations;
1941 gboolean fhorz = self->frame->max_horz;
1942
1943 /* make the frame recalculate its dimentions n shit without changing
1944 anything visible for real, this way the constraints below can work with
1945 the updated frame dimensions. */
1946 frame_adjust_area(self->frame, TRUE, TRUE, TRUE);
1947
1948 /* gets the frame's position */
1949 frame_client_gravity(self->frame, &x, &y);
1950
1951 /* these positions are frame positions, not client positions */
1952
1953 /* set the size and position if fullscreen */
1954 if (self->fullscreen) {
1955 #ifdef VIDMODE
1956 gint dot;
1957 XF86VidModeModeLine mode;
1958 #endif
1959 Rect *a;
1960 guint i;
1961
1962 i = client_monitor(self);
1963 a = screen_physical_area_monitor(i);
1964
1965 #ifdef VIDMODE
1966 if (i == 0 && /* primary head */
1967 extensions_vidmode &&
1968 XF86VidModeGetViewPort(ob_display, ob_screen, &x, &y) &&
1969 /* get the mode last so the mode.privsize isnt freed incorrectly */
1970 XF86VidModeGetModeLine(ob_display, ob_screen, &dot, &mode)) {
1971 x += a->x;
1972 y += a->y;
1973 w = mode.hdisplay;
1974 h = mode.vdisplay;
1975 if (mode.privsize) XFree(mode.private);
1976 } else
1977 #endif
1978 {
1979 x = a->x;
1980 y = a->y;
1981 w = a->width;
1982 h = a->height;
1983 }
1984
1985 user = FALSE; /* ignore that increment etc shit when in fullscreen */
1986 } else {
1987 Rect *a;
1988
1989 a = screen_area_monitor(self->desktop, client_monitor(self));
1990
1991 /* set the size and position if maximized */
1992 if (self->max_horz) {
1993 x = a->x;
1994 w = a->width - self->frame->size.left - self->frame->size.right;
1995 }
1996 if (self->max_vert) {
1997 y = a->y;
1998 h = a->height - self->frame->size.top - self->frame->size.bottom;
1999 }
2000 }
2001
2002 /* gets the client's position */
2003 frame_frame_gravity(self->frame, &x, &y);
2004
2005 /* these override the above states! if you cant move you can't move! */
2006 if (user) {
2007 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
2008 x = self->area.x;
2009 y = self->area.y;
2010 }
2011 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
2012 w = self->area.width;
2013 h = self->area.height;
2014 }
2015 }
2016
2017 if (!(w == self->area.width && h == self->area.height)) {
2018 gint basew, baseh, minw, minh;
2019
2020 /* base size is substituted with min size if not specified */
2021 if (self->base_size.width || self->base_size.height) {
2022 basew = self->base_size.width;
2023 baseh = self->base_size.height;
2024 } else {
2025 basew = self->min_size.width;
2026 baseh = self->min_size.height;
2027 }
2028 /* min size is substituted with base size if not specified */
2029 if (self->min_size.width || self->min_size.height) {
2030 minw = self->min_size.width;
2031 minh = self->min_size.height;
2032 } else {
2033 minw = self->base_size.width;
2034 minh = self->base_size.height;
2035 }
2036
2037 /* if this is a user-requested resize, then check against min/max
2038 sizes */
2039
2040 /* smaller than min size or bigger than max size? */
2041 if (w > self->max_size.width) w = self->max_size.width;
2042 if (w < minw) w = minw;
2043 if (h > self->max_size.height) h = self->max_size.height;
2044 if (h < minh) h = minh;
2045
2046 w -= basew;
2047 h -= baseh;
2048
2049 /* keep to the increments */
2050 w /= self->size_inc.width;
2051 h /= self->size_inc.height;
2052
2053 /* you cannot resize to nothing */
2054 if (basew + w < 1) w = 1 - basew;
2055 if (baseh + h < 1) h = 1 - baseh;
2056
2057 /* store the logical size */
2058 SIZE_SET(self->logical_size,
2059 self->size_inc.width > 1 ? w : w + basew,
2060 self->size_inc.height > 1 ? h : h + baseh);
2061
2062 w *= self->size_inc.width;
2063 h *= self->size_inc.height;
2064
2065 w += basew;
2066 h += baseh;
2067
2068 /* adjust the height to match the width for the aspect ratios.
2069 for this, min size is not substituted for base size ever. */
2070 w -= self->base_size.width;
2071 h -= self->base_size.height;
2072
2073 if (!self->fullscreen) {
2074 if (self->min_ratio)
2075 if (h * self->min_ratio > w) {
2076 h = (gint)(w / self->min_ratio);
2077
2078 /* you cannot resize to nothing */
2079 if (h < 1) {
2080 h = 1;
2081 w = (gint)(h * self->min_ratio);
2082 }
2083 }
2084 if (self->max_ratio)
2085 if (h * self->max_ratio < w) {
2086 h = (gint)(w / self->max_ratio);
2087
2088 /* you cannot resize to nothing */
2089 if (h < 1) {
2090 h = 1;
2091 w = (gint)(h * self->min_ratio);
2092 }
2093 }
2094 }
2095
2096 w += self->base_size.width;
2097 h += self->base_size.height;
2098 }
2099
2100 g_assert(w > 0);
2101 g_assert(h > 0);
2102
2103 switch (anchor) {
2104 case OB_CORNER_TOPLEFT:
2105 break;
2106 case OB_CORNER_TOPRIGHT:
2107 x -= w - self->area.width;
2108 break;
2109 case OB_CORNER_BOTTOMLEFT:
2110 y -= h - self->area.height;
2111 break;
2112 case OB_CORNER_BOTTOMRIGHT:
2113 x -= w - self->area.width;
2114 y -= h - self->area.height;
2115 break;
2116 }
2117
2118 moved = x != self->area.x || y != self->area.y;
2119 resized = w != self->area.width || h != self->area.height;
2120
2121 oldw = self->area.width;
2122 oldh = self->area.height;
2123 RECT_SET(self->area, x, y, w, h);
2124
2125 /* for app-requested resizes, always resize if 'resized' is true.
2126 for user-requested ones, only resize if final is true, or when
2127 resizing in redraw mode */
2128 send_resize_client = ((!user && resized) ||
2129 (user && (final ||
2130 (resized && config_resize_redraw))));
2131
2132 /* if the client is enlarging, then resize the client before the frame */
2133 if (send_resize_client && user && (w > oldw || h > oldh))
2134 XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh));
2135
2136 /* move/resize the frame to match the request */
2137 if (self->frame) {
2138 if (self->decorations != fdecor || self->max_horz != fhorz)
2139 moved = resized = TRUE;
2140
2141 if (moved || resized)
2142 frame_adjust_area(self->frame, moved, resized, FALSE);
2143
2144 if (!resized && (force_reply || ((!user && moved) || (user && final))))
2145 {
2146 XEvent event;
2147 event.type = ConfigureNotify;
2148 event.xconfigure.display = ob_display;
2149 event.xconfigure.event = self->window;
2150 event.xconfigure.window = self->window;
2151
2152 /* root window real coords */
2153 event.xconfigure.x = self->frame->area.x + self->frame->size.left -
2154 self->border_width;
2155 event.xconfigure.y = self->frame->area.y + self->frame->size.top -
2156 self->border_width;
2157 event.xconfigure.width = w;
2158 event.xconfigure.height = h;
2159 event.xconfigure.border_width = 0;
2160 event.xconfigure.above = self->frame->plate;
2161 event.xconfigure.override_redirect = FALSE;
2162 XSendEvent(event.xconfigure.display, event.xconfigure.window,
2163 FALSE, StructureNotifyMask, &event);
2164 }
2165 }
2166
2167 /* if the client is shrinking, then resize the frame before the client */
2168 if (send_resize_client && (!user || (w <= oldw || h <= oldh)))
2169 XResizeWindow(ob_display, self->window, w, h);
2170
2171 XFlush(ob_display);
2172 }
2173
2174 void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea)
2175 {
2176 gint x, y, w, h;
2177
2178 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
2179 self->fullscreen == fs) return; /* already done */
2180
2181 self->fullscreen = fs;
2182 client_change_state(self); /* change the state hints on the client,
2183 and adjust out layer/stacking */
2184
2185 if (fs) {
2186 if (savearea)
2187 self->pre_fullscreen_area = self->area;
2188
2189 /* these are not actually used cuz client_configure will set them
2190 as appropriate when the window is fullscreened */
2191 x = y = w = h = 0;
2192 } else {
2193 Rect *a;
2194
2195 if (self->pre_fullscreen_area.width > 0 &&
2196 self->pre_fullscreen_area.height > 0)
2197 {
2198 x = self->pre_fullscreen_area.x;
2199 y = self->pre_fullscreen_area.y;
2200 w = self->pre_fullscreen_area.width;
2201 h = self->pre_fullscreen_area.height;
2202 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
2203 } else {
2204 /* pick some fallbacks... */
2205 a = screen_area_monitor(self->desktop, 0);
2206 x = a->x + a->width / 4;
2207 y = a->y + a->height / 4;
2208 w = a->width / 2;
2209 h = a->height / 2;
2210 }
2211 }
2212
2213 client_setup_decor_and_functions(self);
2214
2215 client_move_resize(self, x, y, w, h);
2216
2217 /* try focus us when we go into fullscreen mode */
2218 client_focus(self);
2219 }
2220
2221 static void client_iconify_recursive(ObClient *self,
2222 gboolean iconic, gboolean curdesk)
2223 {
2224 GSList *it;
2225 gboolean changed = FALSE;
2226
2227
2228 if (self->iconic != iconic) {
2229 ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
2230 self->window);
2231
2232 self->iconic = iconic;
2233
2234 if (iconic) {
2235 if (self->functions & OB_CLIENT_FUNC_ICONIFY) {
2236 glong old;
2237
2238 old = self->wmstate;
2239 self->wmstate = IconicState;
2240 if (old != self->wmstate)
2241 PROP_MSG(self->window, kde_wm_change_state,
2242 self->wmstate, 1, 0, 0);
2243
2244 /* update the focus lists.. iconic windows go to the bottom of
2245 the list, put the new iconic window at the 'top of the
2246 bottom'. */
2247 focus_order_to_top(self);
2248
2249 changed = TRUE;
2250 }
2251 } else {
2252 glong old;
2253
2254 if (curdesk)
2255 client_set_desktop(self, screen_desktop, FALSE);
2256
2257 old = self->wmstate;
2258 self->wmstate = self->shaded ? IconicState : NormalState;
2259 if (old != self->wmstate)
2260 PROP_MSG(self->window, kde_wm_change_state,
2261 self->wmstate, 1, 0, 0);
2262
2263 /* this puts it after the current focused window */
2264 focus_order_remove(self);
2265 focus_order_add_new(self);
2266
2267 /* this is here cuz with the VIDMODE extension, the viewport can
2268 change while a fullscreen window is iconic, and when it
2269 uniconifies, it would be nice if it did so to the new position
2270 of the viewport */
2271 client_reconfigure(self);
2272
2273 changed = TRUE;
2274 }
2275 }
2276
2277 if (changed) {
2278 client_change_state(self);
2279 client_showhide(self);
2280 screen_update_areas();
2281 }
2282
2283 /* iconify all transients */
2284 for (it = self->transients; it; it = g_slist_next(it))
2285 if (it->data != self) client_iconify_recursive(it->data,
2286 iconic, curdesk);
2287 }
2288
2289 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
2290 {
2291 /* move up the transient chain as far as possible first */
2292 self = client_search_top_transient(self);
2293
2294 client_iconify_recursive(client_search_top_transient(self),
2295 iconic, curdesk);
2296 }
2297
2298 void client_maximize(ObClient *self, gboolean max, gint dir, gboolean savearea)
2299 {
2300 gint x, y, w, h;
2301
2302 g_assert(dir == 0 || dir == 1 || dir == 2);
2303 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
2304
2305 /* check if already done */
2306 if (max) {
2307 if (dir == 0 && self->max_horz && self->max_vert) return;
2308 if (dir == 1 && self->max_horz) return;
2309 if (dir == 2 && self->max_vert) return;
2310 } else {
2311 if (dir == 0 && !self->max_horz && !self->max_vert) return;
2312 if (dir == 1 && !self->max_horz) return;
2313 if (dir == 2 && !self->max_vert) return;
2314 }
2315
2316 /* we just tell it to configure in the same place and client_configure
2317 worries about filling the screen with the window */
2318 x = self->area.x;
2319 y = self->area.y;
2320 w = self->area.width;
2321 h = self->area.height;
2322
2323 if (max) {
2324 if (savearea) {
2325 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
2326 RECT_SET(self->pre_max_area,
2327 self->area.x, self->pre_max_area.y,
2328 self->area.width, self->pre_max_area.height);
2329 }
2330 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
2331 RECT_SET(self->pre_max_area,
2332 self->pre_max_area.x, self->area.y,
2333 self->pre_max_area.width, self->area.height);
2334 }
2335 }
2336 } else {
2337 Rect *a;
2338
2339 a = screen_area_monitor(self->desktop, 0);
2340 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
2341 if (self->pre_max_area.width > 0) {
2342 x = self->pre_max_area.x;
2343 w = self->pre_max_area.width;
2344
2345 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
2346 0, self->pre_max_area.height);
2347 } else {
2348 /* pick some fallbacks... */
2349 x = a->x + a->width / 4;
2350 w = a->width / 2;
2351 }
2352 }
2353 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
2354 if (self->pre_max_area.height > 0) {
2355 y = self->pre_max_area.y;
2356 h = self->pre_max_area.height;
2357
2358 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
2359 self->pre_max_area.width, 0);
2360 } else {
2361 /* pick some fallbacks... */
2362 y = a->y + a->height / 4;
2363 h = a->height / 2;
2364 }
2365 }
2366 }
2367
2368 if (dir == 0 || dir == 1) /* horz */
2369 self->max_horz = max;
2370 if (dir == 0 || dir == 2) /* vert */
2371 self->max_vert = max;
2372
2373 client_change_state(self); /* change the state hints on the client */
2374
2375 client_setup_decor_and_functions(self);
2376
2377 client_move_resize(self, x, y, w, h);
2378 }
2379
2380 void client_shade(ObClient *self, gboolean shade)
2381 {
2382 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
2383 shade) || /* can't shade */
2384 self->shaded == shade) return; /* already done */
2385
2386 /* when we're iconic, don't change the wmstate */
2387 if (!self->iconic) {
2388 glong old;
2389
2390 old = self->wmstate;
2391 self->wmstate = shade ? IconicState : NormalState;
2392 if (old != self->wmstate)
2393 PROP_MSG(self->window, kde_wm_change_state,
2394 self->wmstate, 1, 0, 0);
2395 }
2396
2397 self->shaded = shade;
2398 client_change_state(self);
2399 /* resize the frame to just the titlebar */
2400 frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
2401 }
2402
2403 void client_close(ObClient *self)
2404 {
2405 XEvent ce;
2406
2407 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
2408
2409 /* in the case that the client provides no means to requesting that it
2410 close, we just kill it */
2411 if (!self->delete_window)
2412 client_kill(self);
2413
2414 /*
2415 XXX: itd be cool to do timeouts and shit here for killing the client's
2416 process off
2417 like... if the window is around after 5 seconds, then the close button
2418 turns a nice red, and if this function is called again, the client is
2419 explicitly killed.
2420 */
2421
2422 ce.xclient.type = ClientMessage;
2423 ce.xclient.message_type = prop_atoms.wm_protocols;
2424 ce.xclient.display = ob_display;
2425 ce.xclient.window = self->window;
2426 ce.xclient.format = 32;
2427 ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
2428 ce.xclient.data.l[1] = event_lasttime;
2429 ce.xclient.data.l[2] = 0l;
2430 ce.xclient.data.l[3] = 0l;
2431 ce.xclient.data.l[4] = 0l;
2432 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2433 }
2434
2435 void client_kill(ObClient *self)
2436 {
2437 XKillClient(ob_display, self->window);
2438 }
2439
2440 void client_set_desktop_recursive(ObClient *self,
2441 guint target, gboolean donthide)
2442 {
2443 guint old;
2444 GSList *it;
2445
2446 if (target != self->desktop) {
2447
2448 ob_debug("Setting desktop %u\n", target+1);
2449
2450 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
2451
2452 /* remove from the old desktop(s) */
2453 focus_order_remove(self);
2454
2455 old = self->desktop;
2456 self->desktop = target;
2457 PROP_SET32(self->window, net_wm_desktop, cardinal, target);
2458 /* the frame can display the current desktop state */
2459 frame_adjust_state(self->frame);
2460 /* 'move' the window to the new desktop */
2461 if (!donthide)
2462 client_showhide(self);
2463 /* raise if it was not already on the desktop */
2464 if (old != DESKTOP_ALL)
2465 client_raise(self);
2466 screen_update_areas();
2467
2468 /* add to the new desktop(s) */
2469 if (config_focus_new)
2470 focus_order_to_top(self);
2471 else
2472 focus_order_to_bottom(self);
2473 }
2474
2475 /* move all transients */
2476 for (it = self->transients; it; it = g_slist_next(it))
2477 if (it->data != self) client_set_desktop_recursive(it->data,
2478 target, donthide);
2479 }
2480
2481 void client_set_desktop(ObClient *self, guint target, gboolean donthide)
2482 {
2483 client_set_desktop_recursive(client_search_top_transient(self),
2484 target, donthide);
2485 }
2486
2487 ObClient *client_search_modal_child(ObClient *self)
2488 {
2489 GSList *it;
2490 ObClient *ret;
2491
2492 for (it = self->transients; it; it = g_slist_next(it)) {
2493 ObClient *c = it->data;
2494 if ((ret = client_search_modal_child(c))) return ret;
2495 if (c->modal) return c;
2496 }
2497 return NULL;
2498 }
2499
2500 gboolean client_validate(ObClient *self)
2501 {
2502 XEvent e;
2503
2504 XSync(ob_display, FALSE); /* get all events on the server */
2505
2506 if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
2507 XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
2508 XPutBackEvent(ob_display, &e);
2509 return FALSE;
2510 }
2511
2512 return TRUE;
2513 }
2514
2515 void client_set_wm_state(ObClient *self, glong state)
2516 {
2517 if (state == self->wmstate) return; /* no change */
2518
2519 switch (state) {
2520 case IconicState:
2521 client_iconify(self, TRUE, TRUE);
2522 break;
2523 case NormalState:
2524 client_iconify(self, FALSE, TRUE);
2525 break;
2526 }
2527 }
2528
2529 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
2530 {
2531 gboolean shaded = self->shaded;
2532 gboolean fullscreen = self->fullscreen;
2533 gboolean undecorated = self->undecorated;
2534 gboolean max_horz = self->max_horz;
2535 gboolean max_vert = self->max_vert;
2536 gboolean modal = self->modal;
2537 gboolean iconic = self->iconic;
2538 gint i;
2539
2540 if (!(action == prop_atoms.net_wm_state_add ||
2541 action == prop_atoms.net_wm_state_remove ||
2542 action == prop_atoms.net_wm_state_toggle))
2543 /* an invalid action was passed to the client message, ignore it */
2544 return;
2545
2546 for (i = 0; i < 2; ++i) {
2547 Atom state = i == 0 ? data1 : data2;
2548
2549 if (!state) continue;
2550
2551 /* if toggling, then pick whether we're adding or removing */
2552 if (action == prop_atoms.net_wm_state_toggle) {
2553 if (state == prop_atoms.net_wm_state_modal)
2554 action = modal ? prop_atoms.net_wm_state_remove :
2555 prop_atoms.net_wm_state_add;
2556 else if (state == prop_atoms.net_wm_state_maximized_vert)
2557 action = self->max_vert ? prop_atoms.net_wm_state_remove :
2558 prop_atoms.net_wm_state_add;
2559 else if (state == prop_atoms.net_wm_state_maximized_horz)
2560 action = self->max_horz ? prop_atoms.net_wm_state_remove :
2561 prop_atoms.net_wm_state_add;
2562 else if (state == prop_atoms.net_wm_state_shaded)
2563 action = shaded ? prop_atoms.net_wm_state_remove :
2564 prop_atoms.net_wm_state_add;
2565 else if (state == prop_atoms.net_wm_state_skip_taskbar)
2566 action = self->skip_taskbar ?
2567 prop_atoms.net_wm_state_remove :
2568 prop_atoms.net_wm_state_add;
2569 else if (state == prop_atoms.net_wm_state_skip_pager)
2570 action = self->skip_pager ?
2571 prop_atoms.net_wm_state_remove :
2572 prop_atoms.net_wm_state_add;
2573 else if (state == prop_atoms.net_wm_state_hidden)
2574 action = self->iconic ?
2575 prop_atoms.net_wm_state_remove :
2576 prop_atoms.net_wm_state_add;
2577 else if (state == prop_atoms.net_wm_state_fullscreen)
2578 action = fullscreen ?
2579 prop_atoms.net_wm_state_remove :
2580 prop_atoms.net_wm_state_add;
2581 else if (state == prop_atoms.net_wm_state_above)
2582 action = self->above ? prop_atoms.net_wm_state_remove :
2583 prop_atoms.net_wm_state_add;
2584 else if (state == prop_atoms.net_wm_state_below)
2585 action = self->below ? prop_atoms.net_wm_state_remove :
2586 prop_atoms.net_wm_state_add;
2587 else if (state == prop_atoms.ob_wm_state_undecorated)
2588 action = undecorated ? prop_atoms.net_wm_state_remove :
2589 prop_atoms.net_wm_state_add;
2590 }
2591
2592 if (action == prop_atoms.net_wm_state_add) {
2593 if (state == prop_atoms.net_wm_state_modal) {
2594 modal = TRUE;
2595 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2596 max_vert = TRUE;
2597 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2598 max_horz = TRUE;
2599 } else if (state == prop_atoms.net_wm_state_shaded) {
2600 shaded = TRUE;
2601 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2602 self->skip_taskbar = TRUE;
2603 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2604 self->skip_pager = TRUE;
2605 } else if (state == prop_atoms.net_wm_state_hidden) {
2606 iconic = TRUE;
2607 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2608 fullscreen = TRUE;
2609 } else if (state == prop_atoms.net_wm_state_above) {
2610 self->above = TRUE;
2611 self->below = FALSE;
2612 } else if (state == prop_atoms.net_wm_state_below) {
2613 self->above = FALSE;
2614 self->below = TRUE;
2615 } else if (state == prop_atoms.ob_wm_state_undecorated) {
2616 undecorated = TRUE;
2617 }
2618
2619 } else { /* action == prop_atoms.net_wm_state_remove */
2620 if (state == prop_atoms.net_wm_state_modal) {
2621 modal = FALSE;
2622 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2623 max_vert = FALSE;
2624 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2625 max_horz = FALSE;
2626 } else if (state == prop_atoms.net_wm_state_shaded) {
2627 shaded = FALSE;
2628 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2629 self->skip_taskbar = FALSE;
2630 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2631 self->skip_pager = FALSE;
2632 } else if (state == prop_atoms.net_wm_state_hidden) {
2633 iconic = FALSE;
2634 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2635 fullscreen = FALSE;
2636 } else if (state == prop_atoms.net_wm_state_above) {
2637 self->above = FALSE;
2638 } else if (state == prop_atoms.net_wm_state_below) {
2639 self->below = FALSE;
2640 } else if (state == prop_atoms.ob_wm_state_undecorated) {
2641 undecorated = FALSE;
2642 }
2643 }
2644 }
2645 if (max_horz != self->max_horz || max_vert != self->max_vert) {
2646 if (max_horz != self->max_horz && max_vert != self->max_vert) {
2647 /* toggling both */
2648 if (max_horz == max_vert) { /* both going the same way */
2649 client_maximize(self, max_horz, 0, TRUE);
2650 } else {
2651 client_maximize(self, max_horz, 1, TRUE);
2652 client_maximize(self, max_vert, 2, TRUE);
2653 }
2654 } else {
2655 /* toggling one */
2656 if (max_horz != self->max_horz)
2657 client_maximize(self, max_horz, 1, TRUE);
2658 else
2659 client_maximize(self, max_vert, 2, TRUE);
2660 }
2661 }
2662 /* change fullscreen state before shading, as it will affect if the window
2663 can shade or not */
2664 if (fullscreen != self->fullscreen)
2665 client_fullscreen(self, fullscreen, TRUE);
2666 if (shaded != self->shaded)
2667 client_shade(self, shaded);
2668 if (undecorated != self->undecorated)
2669 client_set_undecorated(self, undecorated);
2670 if (modal != self->modal) {
2671 self->modal = modal;
2672 /* when a window changes modality, then its stacking order with its
2673 transients needs to change */
2674 client_raise(self);
2675 }
2676 if (iconic != self->iconic)
2677 client_iconify(self, iconic, FALSE);
2678
2679 client_calc_layer(self);
2680 client_change_state(self); /* change the hint to reflect these changes */
2681 }
2682
2683 ObClient *client_focus_target(ObClient *self)
2684 {
2685 ObClient *child;
2686
2687 /* if we have a modal child, then focus it, not us */
2688 child = client_search_modal_child(client_search_top_transient(self));
2689 if (child) return child;
2690 return self;
2691 }
2692
2693 gboolean client_can_focus(ObClient *self)
2694 {
2695 XEvent ev;
2696
2697 /* choose the correct target */
2698 self = client_focus_target(self);
2699
2700 if (!self->frame->visible)
2701 return FALSE;
2702
2703 if (!(self->can_focus || self->focus_notify))
2704 return FALSE;
2705
2706 /* do a check to see if the window has already been unmapped or destroyed
2707 do this intelligently while watching out for unmaps we've generated
2708 (ignore_unmaps > 0) */
2709 if (XCheckTypedWindowEvent(ob_display, self->window,
2710 DestroyNotify, &ev)) {
2711 XPutBackEvent(ob_display, &ev);
2712 return FALSE;
2713 }
2714 while (XCheckTypedWindowEvent(ob_display, self->window,
2715 UnmapNotify, &ev)) {
2716 if (self->ignore_unmaps) {
2717 self->ignore_unmaps--;
2718 } else {
2719 XPutBackEvent(ob_display, &ev);
2720 return FALSE;
2721 }
2722 }
2723
2724 return TRUE;
2725 }
2726
2727 gboolean client_focus(ObClient *self)
2728 {
2729 /* choose the correct target */
2730 self = client_focus_target(self);
2731
2732 if (!client_can_focus(self)) {
2733 if (!self->frame->visible) {
2734 /* update the focus lists */
2735 focus_order_to_top(self);
2736 }
2737 return FALSE;
2738 }
2739
2740 if (self->can_focus) {
2741 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2742 I choose to use it always, hopefully to find errors quicker, if any
2743 are left. (I hate X. I hate focus events.)
2744
2745 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2746 #799. So now it is RevertToNone again.
2747 */
2748 XSetInputFocus(ob_display, self->window, RevertToNone,
2749 event_lasttime);
2750 }
2751
2752 if (self->focus_notify) {
2753 XEvent ce;
2754 ce.xclient.type = ClientMessage;
2755 ce.xclient.message_type = prop_atoms.wm_protocols;
2756 ce.xclient.display = ob_display;
2757 ce.xclient.window = self->window;
2758 ce.xclient.format = 32;
2759 ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
2760 ce.xclient.data.l[1] = event_lasttime;
2761 ce.xclient.data.l[2] = 0l;
2762 ce.xclient.data.l[3] = 0l;
2763 ce.xclient.data.l[4] = 0l;
2764 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2765 }
2766
2767 #ifdef DEBUG_FOCUS
2768 ob_debug("%sively focusing %lx at %d\n",
2769 (self->can_focus ? "act" : "pass"),
2770 self->window, (gint) event_lasttime);
2771 #endif
2772
2773 /* Cause the FocusIn to come back to us. Important for desktop switches,
2774 since otherwise we'll have no FocusIn on the queue and send it off to
2775 the focus_backup. */
2776 XSync(ob_display, FALSE);
2777 return TRUE;
2778 }
2779
2780 void client_unfocus(ObClient *self)
2781 {
2782 if (focus_client == self) {
2783 #ifdef DEBUG_FOCUS
2784 ob_debug("client_unfocus for %lx\n", self->window);
2785 #endif
2786 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING);
2787 }
2788 }
2789
2790 void client_activate(ObClient *self, gboolean here)
2791 {
2792 if (client_normal(self) && screen_showing_desktop)
2793 screen_show_desktop(FALSE);
2794 if (self->iconic)
2795 client_iconify(self, FALSE, here);
2796 if (self->desktop != DESKTOP_ALL &&
2797 self->desktop != screen_desktop) {
2798 if (here)
2799 client_set_desktop(self, screen_desktop, FALSE);
2800 else
2801 screen_set_desktop(self->desktop);
2802 } else if (!self->frame->visible)
2803 /* if its not visible for other reasons, then don't mess
2804 with it */
2805 return;
2806 if (self->shaded)
2807 client_shade(self, FALSE);
2808
2809 client_focus(self);
2810
2811 /* we do this an action here. this is rather important. this is because
2812 we want the results from the focus change to take place BEFORE we go
2813 about raising the window. when a fullscreen window loses focus, we need
2814 this or else the raise wont be able to raise above the to-lose-focus
2815 fullscreen window. */
2816 client_raise(self);
2817 }
2818
2819 void client_raise(ObClient *self)
2820 {
2821 action_run_string("Raise", self);
2822 }
2823
2824 void client_lower(ObClient *self)
2825 {
2826 action_run_string("Lower", self);
2827 }
2828
2829 gboolean client_focused(ObClient *self)
2830 {
2831 return self == focus_client;
2832 }
2833
2834 static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
2835 {
2836 guint i;
2837 /* si is the smallest image >= req */
2838 /* li is the largest image < req */
2839 gulong size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
2840
2841 if (!self->nicons) {
2842 ObClientIcon *parent = NULL;
2843
2844 if (self->transient_for) {
2845 if (self->transient_for != OB_TRAN_GROUP)
2846 parent = client_icon_recursive(self->transient_for, w, h);
2847 else {
2848 GSList *it;
2849 for (it = self->group->members; it; it = g_slist_next(it)) {
2850 ObClient *c = it->data;
2851 if (c != self && !c->transient_for) {
2852 if ((parent = client_icon_recursive(c, w, h)))
2853 break;
2854 }
2855 }
2856 }
2857 }
2858
2859 return parent;
2860 }
2861
2862 for (i = 0; i < self->nicons; ++i) {
2863 size = self->icons[i].width * self->icons[i].height;
2864 if (size < smallest && size >= (unsigned)(w * h)) {
2865 smallest = size;
2866 si = i;
2867 }
2868 if (size > largest && size <= (unsigned)(w * h)) {
2869 largest = size;
2870 li = i;
2871 }
2872 }
2873 if (largest == 0) /* didnt find one smaller than the requested size */
2874 return &self->icons[si];
2875 return &self->icons[li];
2876 }
2877
2878 const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
2879 {
2880 ObClientIcon *ret;
2881 static ObClientIcon deficon;
2882
2883 if (!(ret = client_icon_recursive(self, w, h))) {
2884 deficon.width = deficon.height = 48;
2885 deficon.data = ob_rr_theme->def_win_icon;
2886 ret = &deficon;
2887 }
2888 return ret;
2889 }
2890
2891 /* this be mostly ripped from fvwm */
2892 ObClient *client_find_directional(ObClient *c, ObDirection dir)
2893 {
2894 gint my_cx, my_cy, his_cx, his_cy;
2895 gint offset = 0;
2896 gint distance = 0;
2897 gint score, best_score;
2898 ObClient *best_client, *cur;
2899 GList *it;
2900
2901 if(!client_list)
2902 return NULL;
2903
2904 /* first, find the centre coords of the currently focused window */
2905 my_cx = c->frame->area.x + c->frame->area.width / 2;
2906 my_cy = c->frame->area.y + c->frame->area.height / 2;
2907
2908 best_score = -1;
2909 best_client = NULL;
2910
2911 for(it = g_list_first(client_list); it; it = g_list_next(it)) {
2912 cur = it->data;
2913
2914 /* the currently selected window isn't interesting */
2915 if(cur == c)
2916 continue;
2917 if (!client_normal(cur))
2918 continue;
2919 /* using c->desktop instead of screen_desktop doesn't work if the
2920 * current window was omnipresent, hope this doesn't have any other
2921 * side effects */
2922 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
2923 continue;
2924 if(cur->iconic)
2925 continue;
2926 if(client_focus_target(cur) == cur &&
2927 !(cur->can_focus || cur->focus_notify))
2928 continue;
2929
2930 /* find the centre coords of this window, from the
2931 * currently focused window's point of view */
2932 his_cx = (cur->frame->area.x - my_cx)
2933 + cur->frame->area.width / 2;
2934 his_cy = (cur->frame->area.y - my_cy)
2935 + cur->frame->area.height / 2;
2936
2937 if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
2938 dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
2939 gint tx;
2940 /* Rotate the diagonals 45 degrees counterclockwise.
2941 * To do this, multiply the matrix /+h +h\ with the
2942 * vector (x y). \-h +h/
2943 * h = sqrt(0.5). We can set h := 1 since absolute
2944 * distance doesn't matter here. */
2945 tx = his_cx + his_cy;
2946 his_cy = -his_cx + his_cy;
2947 his_cx = tx;
2948 }
2949
2950 switch(dir) {
2951 case OB_DIRECTION_NORTH:
2952 case OB_DIRECTION_SOUTH:
2953 case OB_DIRECTION_NORTHEAST:
2954 case OB_DIRECTION_SOUTHWEST:
2955 offset = (his_cx < 0) ? -his_cx : his_cx;
2956 distance = ((dir == OB_DIRECTION_NORTH ||
2957 dir == OB_DIRECTION_NORTHEAST) ?
2958 -his_cy : his_cy);
2959 break;
2960 case OB_DIRECTION_EAST:
2961 case OB_DIRECTION_WEST:
2962 case OB_DIRECTION_SOUTHEAST:
2963 case OB_DIRECTION_NORTHWEST:
2964 offset = (his_cy < 0) ? -his_cy : his_cy;
2965 distance = ((dir == OB_DIRECTION_WEST ||
2966 dir == OB_DIRECTION_NORTHWEST) ?
2967 -his_cx : his_cx);
2968 break;
2969 }
2970
2971 /* the target must be in the requested direction */
2972 if(distance <= 0)
2973 continue;
2974
2975 /* Calculate score for this window. The smaller the better. */
2976 score = distance + offset;
2977
2978 /* windows more than 45 degrees off the direction are
2979 * heavily penalized and will only be chosen if nothing
2980 * else within a million pixels */
2981 if(offset > distance)
2982 score += 1000000;
2983
2984 if(best_score == -1 || score < best_score)
2985 best_client = cur,
2986 best_score = score;
2987 }
2988
2989 return best_client;
2990 }
2991
2992 void client_set_layer(ObClient *self, gint layer)
2993 {
2994 if (layer < 0) {
2995 self->below = TRUE;
2996 self->above = FALSE;
2997 } else if (layer == 0) {
2998 self->below = self->above = FALSE;
2999 } else {
3000 self->below = FALSE;
3001 self->above = TRUE;
3002 }
3003 client_calc_layer(self);
3004 client_change_state(self); /* reflect this in the state hints */
3005 }
3006
3007 void client_set_undecorated(ObClient *self, gboolean undecorated)
3008 {
3009 if (self->undecorated != undecorated) {
3010 self->undecorated = undecorated;
3011 client_setup_decor_and_functions(self);
3012 client_change_state(self); /* reflect this in the state hints */
3013 }
3014 }
3015
3016 /* Determines which physical monitor a client is on by calculating the
3017 area of the part of the client on each monitor. The number of the
3018 monitor containing the greatest area of the client is returned.*/
3019 guint client_monitor(ObClient *self)
3020 {
3021 guint i;
3022 guint most = 0;
3023 guint mostv = 0;
3024
3025 for (i = 0; i < screen_num_monitors; ++i) {
3026 Rect *area = screen_physical_area_monitor(i);
3027 if (RECT_INTERSECTS_RECT(*area, self->frame->area)) {
3028 Rect r;
3029 guint v;
3030
3031 RECT_SET_INTERSECTION(r, *area, self->frame->area);
3032 v = r.width * r.height;
3033
3034 if (v > mostv) {
3035 mostv = v;
3036 most = i;
3037 }
3038 }
3039 }
3040 return most;
3041 }
3042
3043 ObClient *client_search_top_transient(ObClient *self)
3044 {
3045 /* move up the transient chain as far as possible */
3046 if (self->transient_for) {
3047 if (self->transient_for != OB_TRAN_GROUP) {
3048 return client_search_top_transient(self->transient_for);
3049 } else {
3050 GSList *it;
3051
3052 g_assert(self->group);
3053
3054 for (it = self->group->members; it; it = g_slist_next(it)) {
3055 ObClient *c = it->data;
3056
3057 /* checking transient_for prevents infinate loops! */
3058 if (c != self && !c->transient_for)
3059 break;
3060 }
3061 if (it)
3062 return it->data;
3063 }
3064 }
3065
3066 return self;
3067 }
3068
3069 ObClient *client_search_focus_parent(ObClient *self)
3070 {
3071 if (self->transient_for) {
3072 if (self->transient_for != OB_TRAN_GROUP) {
3073 if (client_focused(self->transient_for))
3074 return self->transient_for;
3075 } else {
3076 GSList *it;
3077
3078 for (it = self->group->members; it; it = g_slist_next(it)) {
3079 ObClient *c = it->data;
3080
3081 /* checking transient_for prevents infinate loops! */
3082 if (c != self && !c->transient_for)
3083 if (client_focused(c))
3084 return c;
3085 }
3086 }
3087 }
3088
3089 return NULL;
3090 }
3091
3092 ObClient *client_search_parent(ObClient *self, ObClient *search)
3093 {
3094 if (self->transient_for) {
3095 if (self->transient_for != OB_TRAN_GROUP) {
3096 if (self->transient_for == search)
3097 return search;
3098 } else {
3099 GSList *it;
3100
3101 for (it = self->group->members; it; it = g_slist_next(it)) {
3102 ObClient *c = it->data;
3103
3104 /* checking transient_for prevents infinate loops! */
3105 if (c != self && !c->transient_for)
3106 if (c == search)
3107 return search;
3108 }
3109 }
3110 }
3111
3112 return NULL;
3113 }
3114
3115 ObClient *client_search_transient(ObClient *self, ObClient *search)
3116 {
3117 GSList *sit;
3118
3119 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
3120 if (sit->data == search)
3121 return search;
3122 if (client_search_transient(sit->data, search))
3123 return search;
3124 }
3125 return NULL;
3126 }
3127
3128 void client_update_sm_client_id(ObClient *self)
3129 {
3130 g_free(self->sm_client_id);
3131 self->sm_client_id = NULL;
3132
3133 if (!PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id) &&
3134 self->group)
3135 PROP_GETS(self->group->leader, sm_client_id, locale,
3136 &self->sm_client_id);
3137 }
3138
3139 /* finds the nearest edge in the given direction from the current client
3140 * note to self: the edge is the -frame- edge (the actual one), not the
3141 * client edge.
3142 */
3143 gint client_directional_edge_search(ObClient *c, ObDirection dir)
3144 {
3145 gint dest, monitor_dest;
3146 gint my_edge_start, my_edge_end, my_offset;
3147 GList *it;
3148 Rect *a, *monitor;
3149
3150 if(!client_list)
3151 return -1;
3152
3153 a = screen_area(c->desktop);
3154 monitor = screen_area_monitor(c->desktop, client_monitor(c));
3155
3156 switch(dir) {
3157 case OB_DIRECTION_NORTH:
3158 my_edge_start = c->frame->area.x;
3159 my_edge_end = c->frame->area.x + c->frame->area.width;
3160 my_offset = c->frame->area.y;
3161
3162 /* default: top of screen */
3163 dest = a->y;
3164 monitor_dest = monitor->y;
3165 /* if the monitor edge comes before the screen edge, */
3166 /* use that as the destination instead. (For xinerama) */
3167 if (monitor_dest != dest && my_offset > monitor_dest)
3168 dest = monitor_dest;
3169
3170 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3171 gint his_edge_start, his_edge_end, his_offset;
3172 ObClient *cur = it->data;
3173
3174 if(cur == c)
3175 continue;
3176 if(!client_normal(cur))
3177 continue;
3178 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3179 continue;
3180 if(cur->iconic)
3181 continue;
3182 if(cur->layer < c->layer && !config_resist_layers_below)
3183 continue;
3184
3185 his_edge_start = cur->frame->area.x;
3186 his_edge_end = cur->frame->area.x + cur->frame->area.width;
3187 his_offset = cur->frame->area.y + cur->frame->area.height;
3188
3189 if(his_offset + 1 > my_offset)
3190 continue;
3191
3192 if(his_offset < dest)
3193 continue;
3194
3195 if(his_edge_start >= my_edge_start &&
3196 his_edge_start <= my_edge_end)
3197 dest = his_offset;
3198
3199 if(my_edge_start >= his_edge_start &&
3200 my_edge_start <= his_edge_end)
3201 dest = his_offset;
3202
3203 }
3204 break;
3205 case OB_DIRECTION_SOUTH:
3206 my_edge_start = c->frame->area.x;
3207 my_edge_end = c->frame->area.x + c->frame->area.width;
3208 my_offset = c->frame->area.y + c->frame->area.height;
3209
3210 /* default: bottom of screen */
3211 dest = a->y + a->height;
3212 monitor_dest = monitor->y + monitor->height;
3213 /* if the monitor edge comes before the screen edge, */
3214 /* use that as the destination instead. (For xinerama) */
3215 if (monitor_dest != dest && my_offset < monitor_dest)
3216 dest = monitor_dest;
3217
3218 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3219 gint his_edge_start, his_edge_end, his_offset;
3220 ObClient *cur = it->data;
3221
3222 if(cur == c)
3223 continue;
3224 if(!client_normal(cur))
3225 continue;
3226 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3227 continue;
3228 if(cur->iconic)
3229 continue;
3230 if(cur->layer < c->layer && !config_resist_layers_below)
3231 continue;
3232
3233 his_edge_start = cur->frame->area.x;
3234 his_edge_end = cur->frame->area.x + cur->frame->area.width;
3235 his_offset = cur->frame->area.y;
3236
3237
3238 if(his_offset - 1 < my_offset)
3239 continue;
3240
3241 if(his_offset > dest)
3242 continue;
3243
3244 if(his_edge_start >= my_edge_start &&
3245 his_edge_start <= my_edge_end)
3246 dest = his_offset;
3247
3248 if(my_edge_start >= his_edge_start &&
3249 my_edge_start <= his_edge_end)
3250 dest = his_offset;
3251
3252 }
3253 break;
3254 case OB_DIRECTION_WEST:
3255 my_edge_start = c->frame->area.y;
3256 my_edge_end = c->frame->area.y + c->frame->area.height;
3257 my_offset = c->frame->area.x;
3258
3259 /* default: leftmost egde of screen */
3260 dest = a->x;
3261 monitor_dest = monitor->x;
3262 /* if the monitor edge comes before the screen edge, */
3263 /* use that as the destination instead. (For xinerama) */
3264 if (monitor_dest != dest && my_offset > monitor_dest)
3265 dest = monitor_dest;
3266
3267 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3268 gint his_edge_start, his_edge_end, his_offset;
3269 ObClient *cur = it->data;
3270
3271 if(cur == c)
3272 continue;
3273 if(!client_normal(cur))
3274 continue;
3275 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3276 continue;
3277 if(cur->iconic)
3278 continue;
3279 if(cur->layer < c->layer && !config_resist_layers_below)
3280 continue;
3281
3282 his_edge_start = cur->frame->area.y;
3283 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3284 his_offset = cur->frame->area.x + cur->frame->area.width;
3285
3286 if(his_offset + 1 > my_offset)
3287 continue;
3288
3289 if(his_offset < dest)
3290 continue;
3291
3292 if(his_edge_start >= my_edge_start &&
3293 his_edge_start <= my_edge_end)
3294 dest = his_offset;
3295
3296 if(my_edge_start >= his_edge_start &&
3297 my_edge_start <= his_edge_end)
3298 dest = his_offset;
3299
3300
3301 }
3302 break;
3303 case OB_DIRECTION_EAST:
3304 my_edge_start = c->frame->area.y;
3305 my_edge_end = c->frame->area.y + c->frame->area.height;
3306 my_offset = c->frame->area.x + c->frame->area.width;
3307
3308 /* default: rightmost edge of screen */
3309 dest = a->x + a->width;
3310 monitor_dest = monitor->x + monitor->width;
3311 /* if the monitor edge comes before the screen edge, */
3312 /* use that as the destination instead. (For xinerama) */
3313 if (monitor_dest != dest && my_offset < monitor_dest)
3314 dest = monitor_dest;
3315
3316 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3317 gint his_edge_start, his_edge_end, his_offset;
3318 ObClient *cur = it->data;
3319
3320 if(cur == c)
3321 continue;
3322 if(!client_normal(cur))
3323 continue;
3324 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3325 continue;
3326 if(cur->iconic)
3327 continue;
3328 if(cur->layer < c->layer && !config_resist_layers_below)
3329 continue;
3330
3331 his_edge_start = cur->frame->area.y;
3332 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3333 his_offset = cur->frame->area.x;
3334
3335 if(his_offset - 1 < my_offset)
3336 continue;
3337
3338 if(his_offset > dest)
3339 continue;
3340
3341 if(his_edge_start >= my_edge_start &&
3342 his_edge_start <= my_edge_end)
3343 dest = his_offset;
3344
3345 if(my_edge_start >= his_edge_start &&
3346 my_edge_start <= his_edge_end)
3347 dest = his_offset;
3348
3349 }
3350 break;
3351 case OB_DIRECTION_NORTHEAST:
3352 case OB_DIRECTION_SOUTHEAST:
3353 case OB_DIRECTION_NORTHWEST:
3354 case OB_DIRECTION_SOUTHWEST:
3355 /* not implemented */
3356 default:
3357 g_assert_not_reached();
3358 dest = 0; /* suppress warning */
3359 }
3360 return dest;
3361 }
3362
3363 ObClient* client_under_pointer()
3364 {
3365 gint x, y;
3366 GList *it;
3367 ObClient *ret = NULL;
3368
3369 if (screen_pointer_pos(&x, &y)) {
3370 for (it = stacking_list; it; it = g_list_next(it)) {
3371 if (WINDOW_IS_CLIENT(it->data)) {
3372 ObClient *c = WINDOW_AS_CLIENT(it->data);
3373 if (c->frame->visible &&
3374 RECT_CONTAINS(c->frame->area, x, y)) {
3375 ret = c;
3376 break;
3377 }
3378 }
3379 }
3380 }
3381 return ret;
3382 }
3383
3384 gboolean client_has_group_siblings(ObClient *self)
3385 {
3386 return self->group && self->group->members->next;
3387 }
This page took 0.175941 seconds and 3 git commands to generate.