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