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