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