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