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