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