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