]> Dogcows Code - chaz/openbox/blob - openbox/client.c
for dock windows, honor both the above and below states, keeping docks above and...
[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->above) l = OB_STACKING_LAYER_DOCK_ABOVE;
1701 else if (self->below) l = OB_STACKING_LAYER_DOCK_BELOW;
1702 else l = OB_STACKING_LAYER_NORMAL;
1703 }
1704 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
1705 else if (self->below) l = OB_STACKING_LAYER_BELOW;
1706 else l = OB_STACKING_LAYER_NORMAL;
1707
1708 return l;
1709 }
1710
1711 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
1712 ObStackingLayer l, gboolean raised)
1713 {
1714 ObStackingLayer old, own;
1715 GSList *it;
1716
1717 old = self->layer;
1718 own = calc_layer(self);
1719 self->layer = l > own ? l : own;
1720
1721 for (it = self->transients; it; it = it->next)
1722 client_calc_layer_recursive(it->data, orig,
1723 l, raised ? raised : l != old);
1724
1725 if (!raised && l != old)
1726 if (orig->frame) { /* only restack if the original window is managed */
1727 /* XXX add_non_intrusive ever? */
1728 stacking_remove(CLIENT_AS_WINDOW(self));
1729 stacking_add(CLIENT_AS_WINDOW(self));
1730 }
1731 }
1732
1733 void client_calc_layer(ObClient *self)
1734 {
1735 ObStackingLayer l;
1736 ObClient *orig;
1737
1738 orig = self;
1739
1740 /* transients take on the layer of their parents */
1741 self = client_search_top_transient(self);
1742
1743 l = calc_layer(self);
1744
1745 client_calc_layer_recursive(self, orig, l, FALSE);
1746 }
1747
1748 gboolean client_should_show(ObClient *self)
1749 {
1750 if (self->iconic) return FALSE;
1751 else if (!(self->desktop == screen_desktop ||
1752 self->desktop == DESKTOP_ALL)) return FALSE;
1753 else if (client_normal(self) && screen_showing_desktop) return FALSE;
1754
1755 return TRUE;
1756 }
1757
1758 static void client_showhide(ObClient *self)
1759 {
1760
1761 if (client_should_show(self))
1762 frame_show(self->frame);
1763 else
1764 frame_hide(self->frame);
1765 }
1766
1767 gboolean client_normal(ObClient *self) {
1768 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
1769 self->type == OB_CLIENT_TYPE_DOCK ||
1770 self->type == OB_CLIENT_TYPE_SPLASH);
1771 }
1772
1773 static void client_apply_startup_state(ObClient *self)
1774 {
1775 /* these are in a carefully crafted order.. */
1776
1777 if (self->iconic) {
1778 self->iconic = FALSE;
1779 client_iconify(self, TRUE, FALSE);
1780 }
1781 if (self->fullscreen) {
1782 self->fullscreen = FALSE;
1783 client_fullscreen(self, TRUE, FALSE);
1784 }
1785 if (self->undecorated) {
1786 self->undecorated = FALSE;
1787 client_set_undecorated(self, TRUE);
1788 }
1789 if (self->shaded) {
1790 self->shaded = FALSE;
1791 client_shade(self, TRUE);
1792 }
1793 if (self->urgent)
1794 client_urgent_notify(self);
1795
1796 if (self->max_vert && self->max_horz) {
1797 self->max_vert = self->max_horz = FALSE;
1798 client_maximize(self, TRUE, 0, FALSE);
1799 } else if (self->max_vert) {
1800 self->max_vert = FALSE;
1801 client_maximize(self, TRUE, 2, FALSE);
1802 } else if (self->max_horz) {
1803 self->max_horz = FALSE;
1804 client_maximize(self, TRUE, 1, FALSE);
1805 }
1806
1807 /* nothing to do for the other states:
1808 skip_taskbar
1809 skip_pager
1810 modal
1811 above
1812 below
1813 */
1814 }
1815
1816 void client_configure_full(ObClient *self, ObCorner anchor,
1817 int x, int y, int w, int h,
1818 gboolean user, gboolean final,
1819 gboolean force_reply)
1820 {
1821 gint oldw, oldh;
1822 gboolean send_resize_client;
1823 gboolean moved = FALSE, resized = FALSE;
1824 guint fdecor = self->frame->decorations;
1825 gboolean fhorz = self->frame->max_horz;
1826
1827 /* make the frame recalculate its dimentions n shit without changing
1828 anything visible for real, this way the constraints below can work with
1829 the updated frame dimensions. */
1830 frame_adjust_area(self->frame, TRUE, TRUE, TRUE);
1831
1832 /* gets the frame's position */
1833 frame_client_gravity(self->frame, &x, &y);
1834
1835 /* these positions are frame positions, not client positions */
1836
1837 /* set the size and position if fullscreen */
1838 if (self->fullscreen) {
1839 #ifdef VIDMODE
1840 int dot;
1841 XF86VidModeModeLine mode;
1842 #endif
1843 Rect *a;
1844 guint i;
1845
1846 i = client_monitor(self);
1847 a = screen_physical_area_monitor(i);
1848
1849 #ifdef VIDMODE
1850 if (i == 0 && /* primary head */
1851 extensions_vidmode &&
1852 XF86VidModeGetViewPort(ob_display, ob_screen, &x, &y) &&
1853 /* get the mode last so the mode.privsize isnt freed incorrectly */
1854 XF86VidModeGetModeLine(ob_display, ob_screen, &dot, &mode)) {
1855 x += a->x;
1856 y += a->y;
1857 w = mode.hdisplay;
1858 h = mode.vdisplay;
1859 if (mode.privsize) XFree(mode.private);
1860 } else
1861 #endif
1862 {
1863 x = a->x;
1864 y = a->y;
1865 w = a->width;
1866 h = a->height;
1867 }
1868
1869 user = FALSE; /* ignore that increment etc shit when in fullscreen */
1870 } else {
1871 Rect *a;
1872
1873 a = screen_area_monitor(self->desktop, client_monitor(self));
1874
1875 /* set the size and position if maximized */
1876 if (self->max_horz) {
1877 x = a->x;
1878 w = a->width - self->frame->size.left - self->frame->size.right;
1879 }
1880 if (self->max_vert) {
1881 y = a->y;
1882 h = a->height - self->frame->size.top - self->frame->size.bottom;
1883 }
1884 }
1885
1886 /* gets the client's position */
1887 frame_frame_gravity(self->frame, &x, &y);
1888
1889 /* these override the above states! if you cant move you can't move! */
1890 if (user) {
1891 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
1892 x = self->area.x;
1893 y = self->area.y;
1894 }
1895 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
1896 w = self->area.width;
1897 h = self->area.height;
1898 }
1899 }
1900
1901 if (!(w == self->area.width && h == self->area.height)) {
1902 int basew, baseh, minw, minh;
1903
1904 /* base size is substituted with min size if not specified */
1905 if (self->base_size.width || self->base_size.height) {
1906 basew = self->base_size.width;
1907 baseh = self->base_size.height;
1908 } else {
1909 basew = self->min_size.width;
1910 baseh = self->min_size.height;
1911 }
1912 /* min size is substituted with base size if not specified */
1913 if (self->min_size.width || self->min_size.height) {
1914 minw = self->min_size.width;
1915 minh = self->min_size.height;
1916 } else {
1917 minw = self->base_size.width;
1918 minh = self->base_size.height;
1919 }
1920
1921 /* if this is a user-requested resize, then check against min/max
1922 sizes */
1923
1924 /* smaller than min size or bigger than max size? */
1925 if (w > self->max_size.width) w = self->max_size.width;
1926 if (w < minw) w = minw;
1927 if (h > self->max_size.height) h = self->max_size.height;
1928 if (h < minh) h = minh;
1929
1930 w -= basew;
1931 h -= baseh;
1932
1933 /* keep to the increments */
1934 w /= self->size_inc.width;
1935 h /= self->size_inc.height;
1936
1937 /* you cannot resize to nothing */
1938 if (basew + w < 1) w = 1 - basew;
1939 if (baseh + h < 1) h = 1 - baseh;
1940
1941 /* store the logical size */
1942 SIZE_SET(self->logical_size,
1943 self->size_inc.width > 1 ? w : w + basew,
1944 self->size_inc.height > 1 ? h : h + baseh);
1945
1946 w *= self->size_inc.width;
1947 h *= self->size_inc.height;
1948
1949 w += basew;
1950 h += baseh;
1951
1952 /* adjust the height to match the width for the aspect ratios.
1953 for this, min size is not substituted for base size ever. */
1954 w -= self->base_size.width;
1955 h -= self->base_size.height;
1956
1957 if (self->min_ratio)
1958 if (h * self->min_ratio > w) {
1959 h = (int)(w / self->min_ratio);
1960
1961 /* you cannot resize to nothing */
1962 if (h < 1) {
1963 h = 1;
1964 w = (int)(h * self->min_ratio);
1965 }
1966 }
1967 if (self->max_ratio)
1968 if (h * self->max_ratio < w) {
1969 h = (int)(w / self->max_ratio);
1970
1971 /* you cannot resize to nothing */
1972 if (h < 1) {
1973 h = 1;
1974 w = (int)(h * self->min_ratio);
1975 }
1976 }
1977
1978 w += self->base_size.width;
1979 h += self->base_size.height;
1980 }
1981
1982 g_assert(w > 0);
1983 g_assert(h > 0);
1984
1985 switch (anchor) {
1986 case OB_CORNER_TOPLEFT:
1987 break;
1988 case OB_CORNER_TOPRIGHT:
1989 x -= w - self->area.width;
1990 break;
1991 case OB_CORNER_BOTTOMLEFT:
1992 y -= h - self->area.height;
1993 break;
1994 case OB_CORNER_BOTTOMRIGHT:
1995 x -= w - self->area.width;
1996 y -= h - self->area.height;
1997 break;
1998 }
1999
2000 moved = x != self->area.x || y != self->area.y;
2001 resized = w != self->area.width || h != self->area.height;
2002
2003 oldw = self->area.width;
2004 oldh = self->area.height;
2005 RECT_SET(self->area, x, y, w, h);
2006
2007 /* for app-requested resizes, always resize if 'resized' is true.
2008 for user-requested ones, only resize if final is true, or when
2009 resizing in redraw mode */
2010 send_resize_client = ((!user && resized) ||
2011 (user && (final ||
2012 (resized && config_redraw_resize))));
2013
2014 /* if the client is enlarging, the resize the client before the frame */
2015 if (send_resize_client && user && (w > oldw || h > oldh))
2016 XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh));
2017
2018 /* move/resize the frame to match the request */
2019 if (self->frame) {
2020 if (self->decorations != fdecor || self->max_horz != fhorz)
2021 moved = resized = TRUE;
2022
2023 if (moved || resized)
2024 frame_adjust_area(self->frame, moved, resized, FALSE);
2025
2026 if (!resized && (force_reply || ((!user && moved) || (user && final))))
2027 {
2028 XEvent event;
2029 event.type = ConfigureNotify;
2030 event.xconfigure.display = ob_display;
2031 event.xconfigure.event = self->window;
2032 event.xconfigure.window = self->window;
2033
2034 /* root window real coords */
2035 event.xconfigure.x = self->frame->area.x + self->frame->size.left -
2036 self->border_width;
2037 event.xconfigure.y = self->frame->area.y + self->frame->size.top -
2038 self->border_width;
2039 event.xconfigure.width = w;
2040 event.xconfigure.height = h;
2041 event.xconfigure.border_width = 0;
2042 event.xconfigure.above = self->frame->plate;
2043 event.xconfigure.override_redirect = FALSE;
2044 XSendEvent(event.xconfigure.display, event.xconfigure.window,
2045 FALSE, StructureNotifyMask, &event);
2046 }
2047 }
2048
2049 /* if the client is shrinking, then resize the frame before the client */
2050 if (send_resize_client && (!user || (w <= oldw || h <= oldh)))
2051 XResizeWindow(ob_display, self->window, w, h);
2052
2053 XFlush(ob_display);
2054 }
2055
2056 void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea)
2057 {
2058 int x, y, w, h;
2059
2060 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
2061 self->fullscreen == fs) return; /* already done */
2062
2063 self->fullscreen = fs;
2064 client_change_state(self); /* change the state hints on the client,
2065 and adjust out layer/stacking */
2066
2067 if (fs) {
2068 if (savearea)
2069 self->pre_fullscreen_area = self->area;
2070
2071 /* these are not actually used cuz client_configure will set them
2072 as appropriate when the window is fullscreened */
2073 x = y = w = h = 0;
2074 } else {
2075 Rect *a;
2076
2077 if (self->pre_fullscreen_area.width > 0 &&
2078 self->pre_fullscreen_area.height > 0)
2079 {
2080 x = self->pre_fullscreen_area.x;
2081 y = self->pre_fullscreen_area.y;
2082 w = self->pre_fullscreen_area.width;
2083 h = self->pre_fullscreen_area.height;
2084 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
2085 } else {
2086 /* pick some fallbacks... */
2087 a = screen_area_monitor(self->desktop, 0);
2088 x = a->x + a->width / 4;
2089 y = a->y + a->height / 4;
2090 w = a->width / 2;
2091 h = a->height / 2;
2092 }
2093 }
2094
2095 client_setup_decor_and_functions(self);
2096
2097 client_move_resize(self, x, y, w, h);
2098
2099 /* try focus us when we go into fullscreen mode */
2100 client_focus(self);
2101 }
2102
2103 static void client_iconify_recursive(ObClient *self,
2104 gboolean iconic, gboolean curdesk)
2105 {
2106 GSList *it;
2107 gboolean changed = FALSE;
2108
2109
2110 if (self->iconic != iconic) {
2111 ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
2112 self->window);
2113
2114 self->iconic = iconic;
2115
2116 if (iconic) {
2117 if (self->functions & OB_CLIENT_FUNC_ICONIFY) {
2118 long old;
2119
2120 old = self->wmstate;
2121 self->wmstate = IconicState;
2122 if (old != self->wmstate)
2123 PROP_MSG(self->window, kde_wm_change_state,
2124 self->wmstate, 1, 0, 0);
2125
2126 self->ignore_unmaps++;
2127 /* we unmap the client itself so that we can get MapRequest
2128 events, and because the ICCCM tells us to! */
2129 XUnmapWindow(ob_display, self->window);
2130
2131 /* update the focus lists.. iconic windows go to the bottom of
2132 the list, put the new iconic window at the 'top of the
2133 bottom'. */
2134 focus_order_to_top(self);
2135
2136 changed = TRUE;
2137 }
2138 } else {
2139 long old;
2140
2141 if (curdesk)
2142 client_set_desktop(self, screen_desktop, FALSE);
2143
2144 old = self->wmstate;
2145 self->wmstate = self->shaded ? IconicState : NormalState;
2146 if (old != self->wmstate)
2147 PROP_MSG(self->window, kde_wm_change_state,
2148 self->wmstate, 1, 0, 0);
2149
2150 XMapWindow(ob_display, self->window);
2151
2152 /* this puts it after the current focused window */
2153 focus_order_remove(self);
2154 focus_order_add_new(self);
2155
2156 /* this is here cuz with the VIDMODE extension, the viewport can
2157 change while a fullscreen window is iconic, and when it
2158 uniconifies, it would be nice if it did so to the new position
2159 of the viewport */
2160 client_reconfigure(self);
2161
2162 changed = TRUE;
2163 }
2164 }
2165
2166 if (changed) {
2167 client_change_state(self);
2168 client_showhide(self);
2169 screen_update_areas();
2170 }
2171
2172 /* iconify all transients */
2173 for (it = self->transients; it != NULL; it = it->next)
2174 if (it->data != self) client_iconify_recursive(it->data,
2175 iconic, curdesk);
2176 }
2177
2178 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
2179 {
2180 /* move up the transient chain as far as possible first */
2181 self = client_search_top_transient(self);
2182
2183 client_iconify_recursive(client_search_top_transient(self),
2184 iconic, curdesk);
2185 }
2186
2187 void client_maximize(ObClient *self, gboolean max, int dir, gboolean savearea)
2188 {
2189 int x, y, w, h;
2190
2191 g_assert(dir == 0 || dir == 1 || dir == 2);
2192 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
2193
2194 /* check if already done */
2195 if (max) {
2196 if (dir == 0 && self->max_horz && self->max_vert) return;
2197 if (dir == 1 && self->max_horz) return;
2198 if (dir == 2 && self->max_vert) return;
2199 } else {
2200 if (dir == 0 && !self->max_horz && !self->max_vert) return;
2201 if (dir == 1 && !self->max_horz) return;
2202 if (dir == 2 && !self->max_vert) return;
2203 }
2204
2205 /* we just tell it to configure in the same place and client_configure
2206 worries about filling the screen with the window */
2207 x = self->area.x;
2208 y = self->area.y;
2209 w = self->area.width;
2210 h = self->area.height;
2211
2212 if (max) {
2213 if (savearea) {
2214 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
2215 RECT_SET(self->pre_max_area,
2216 self->area.x, self->pre_max_area.y,
2217 self->area.width, self->pre_max_area.height);
2218 }
2219 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
2220 RECT_SET(self->pre_max_area,
2221 self->pre_max_area.x, self->area.y,
2222 self->pre_max_area.width, self->area.height);
2223 }
2224 }
2225 } else {
2226 Rect *a;
2227
2228 a = screen_area_monitor(self->desktop, 0);
2229 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
2230 if (self->pre_max_area.width > 0) {
2231 x = self->pre_max_area.x;
2232 w = self->pre_max_area.width;
2233
2234 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
2235 0, self->pre_max_area.height);
2236 } else {
2237 /* pick some fallbacks... */
2238 x = a->x + a->width / 4;
2239 w = a->width / 2;
2240 }
2241 }
2242 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
2243 if (self->pre_max_area.height > 0) {
2244 y = self->pre_max_area.y;
2245 h = self->pre_max_area.height;
2246
2247 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
2248 self->pre_max_area.width, 0);
2249 } else {
2250 /* pick some fallbacks... */
2251 y = a->y + a->height / 4;
2252 h = a->height / 2;
2253 }
2254 }
2255 }
2256
2257 if (dir == 0 || dir == 1) /* horz */
2258 self->max_horz = max;
2259 if (dir == 0 || dir == 2) /* vert */
2260 self->max_vert = max;
2261
2262 client_change_state(self); /* change the state hints on the client */
2263
2264 client_setup_decor_and_functions(self);
2265
2266 client_move_resize(self, x, y, w, h);
2267 }
2268
2269 void client_shade(ObClient *self, gboolean shade)
2270 {
2271 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
2272 shade) || /* can't shade */
2273 self->shaded == shade) return; /* already done */
2274
2275 /* when we're iconic, don't change the wmstate */
2276 if (!self->iconic) {
2277 long old;
2278
2279 old = self->wmstate;
2280 self->wmstate = shade ? IconicState : NormalState;
2281 if (old != self->wmstate)
2282 PROP_MSG(self->window, kde_wm_change_state,
2283 self->wmstate, 1, 0, 0);
2284 }
2285
2286 self->shaded = shade;
2287 client_change_state(self);
2288 /* resize the frame to just the titlebar */
2289 frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
2290 }
2291
2292 void client_close(ObClient *self)
2293 {
2294 XEvent ce;
2295
2296 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
2297
2298 /*
2299 XXX: itd be cool to do timeouts and shit here for killing the client's
2300 process off
2301 like... if the window is around after 5 seconds, then the close button
2302 turns a nice red, and if this function is called again, the client is
2303 explicitly killed.
2304 */
2305
2306 ce.xclient.type = ClientMessage;
2307 ce.xclient.message_type = prop_atoms.wm_protocols;
2308 ce.xclient.display = ob_display;
2309 ce.xclient.window = self->window;
2310 ce.xclient.format = 32;
2311 ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
2312 ce.xclient.data.l[1] = event_lasttime;
2313 ce.xclient.data.l[2] = 0l;
2314 ce.xclient.data.l[3] = 0l;
2315 ce.xclient.data.l[4] = 0l;
2316 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2317 }
2318
2319 void client_kill(ObClient *self)
2320 {
2321 XKillClient(ob_display, self->window);
2322 }
2323
2324 void client_set_desktop_recursive(ObClient *self,
2325 guint target, gboolean donthide)
2326 {
2327 guint old;
2328 GSList *it;
2329
2330 if (target != self->desktop) {
2331
2332 ob_debug("Setting desktop %u\n", target+1);
2333
2334 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
2335
2336 /* remove from the old desktop(s) */
2337 focus_order_remove(self);
2338
2339 old = self->desktop;
2340 self->desktop = target;
2341 PROP_SET32(self->window, net_wm_desktop, cardinal, target);
2342 /* the frame can display the current desktop state */
2343 frame_adjust_state(self->frame);
2344 /* 'move' the window to the new desktop */
2345 if (!donthide)
2346 client_showhide(self);
2347 /* raise if it was not already on the desktop */
2348 if (old != DESKTOP_ALL)
2349 client_raise(self);
2350 screen_update_areas();
2351
2352 /* add to the new desktop(s) */
2353 if (config_focus_new)
2354 focus_order_to_top(self);
2355 else
2356 focus_order_to_bottom(self);
2357 }
2358
2359 /* move all transients */
2360 for (it = self->transients; it != NULL; it = it->next)
2361 if (it->data != self) client_set_desktop_recursive(it->data,
2362 target, donthide);
2363 }
2364
2365 void client_set_desktop(ObClient *self, guint target, gboolean donthide)
2366 {
2367 client_set_desktop_recursive(client_search_top_transient(self),
2368 target, donthide);
2369 }
2370
2371 ObClient *client_search_modal_child(ObClient *self)
2372 {
2373 GSList *it;
2374 ObClient *ret;
2375
2376 for (it = self->transients; it != NULL; it = it->next) {
2377 ObClient *c = it->data;
2378 if ((ret = client_search_modal_child(c))) return ret;
2379 if (c->modal) return c;
2380 }
2381 return NULL;
2382 }
2383
2384 gboolean client_validate(ObClient *self)
2385 {
2386 XEvent e;
2387
2388 XSync(ob_display, FALSE); /* get all events on the server */
2389
2390 if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
2391 XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
2392 XPutBackEvent(ob_display, &e);
2393 return FALSE;
2394 }
2395
2396 return TRUE;
2397 }
2398
2399 void client_set_wm_state(ObClient *self, long state)
2400 {
2401 if (state == self->wmstate) return; /* no change */
2402
2403 switch (state) {
2404 case IconicState:
2405 client_iconify(self, TRUE, TRUE);
2406 break;
2407 case NormalState:
2408 client_iconify(self, FALSE, TRUE);
2409 break;
2410 }
2411 }
2412
2413 void client_set_state(ObClient *self, Atom action, long data1, long data2)
2414 {
2415 gboolean shaded = self->shaded;
2416 gboolean fullscreen = self->fullscreen;
2417 gboolean undecorated = self->undecorated;
2418 gboolean max_horz = self->max_horz;
2419 gboolean max_vert = self->max_vert;
2420 int i;
2421
2422 if (!(action == prop_atoms.net_wm_state_add ||
2423 action == prop_atoms.net_wm_state_remove ||
2424 action == prop_atoms.net_wm_state_toggle))
2425 /* an invalid action was passed to the client message, ignore it */
2426 return;
2427
2428 for (i = 0; i < 2; ++i) {
2429 Atom state = i == 0 ? data1 : data2;
2430
2431 if (!state) continue;
2432
2433 /* if toggling, then pick whether we're adding or removing */
2434 if (action == prop_atoms.net_wm_state_toggle) {
2435 if (state == prop_atoms.net_wm_state_modal)
2436 action = self->modal ? prop_atoms.net_wm_state_remove :
2437 prop_atoms.net_wm_state_add;
2438 else if (state == prop_atoms.net_wm_state_maximized_vert)
2439 action = self->max_vert ? prop_atoms.net_wm_state_remove :
2440 prop_atoms.net_wm_state_add;
2441 else if (state == prop_atoms.net_wm_state_maximized_horz)
2442 action = self->max_horz ? prop_atoms.net_wm_state_remove :
2443 prop_atoms.net_wm_state_add;
2444 else if (state == prop_atoms.net_wm_state_shaded)
2445 action = shaded ? prop_atoms.net_wm_state_remove :
2446 prop_atoms.net_wm_state_add;
2447 else if (state == prop_atoms.net_wm_state_skip_taskbar)
2448 action = self->skip_taskbar ?
2449 prop_atoms.net_wm_state_remove :
2450 prop_atoms.net_wm_state_add;
2451 else if (state == prop_atoms.net_wm_state_skip_pager)
2452 action = self->skip_pager ?
2453 prop_atoms.net_wm_state_remove :
2454 prop_atoms.net_wm_state_add;
2455 else if (state == prop_atoms.net_wm_state_fullscreen)
2456 action = fullscreen ?
2457 prop_atoms.net_wm_state_remove :
2458 prop_atoms.net_wm_state_add;
2459 else if (state == prop_atoms.net_wm_state_above)
2460 action = self->above ? prop_atoms.net_wm_state_remove :
2461 prop_atoms.net_wm_state_add;
2462 else if (state == prop_atoms.net_wm_state_below)
2463 action = self->below ? prop_atoms.net_wm_state_remove :
2464 prop_atoms.net_wm_state_add;
2465 else if (state == prop_atoms.ob_wm_state_undecorated)
2466 action = undecorated ? prop_atoms.net_wm_state_remove :
2467 prop_atoms.net_wm_state_add;
2468 }
2469
2470 if (action == prop_atoms.net_wm_state_add) {
2471 if (state == prop_atoms.net_wm_state_modal) {
2472 /* XXX raise here or something? */
2473 self->modal = TRUE;
2474 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2475 max_vert = TRUE;
2476 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2477 max_horz = TRUE;
2478 } else if (state == prop_atoms.net_wm_state_shaded) {
2479 shaded = TRUE;
2480 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2481 self->skip_taskbar = TRUE;
2482 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2483 self->skip_pager = TRUE;
2484 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2485 fullscreen = TRUE;
2486 } else if (state == prop_atoms.net_wm_state_above) {
2487 self->above = TRUE;
2488 } else if (state == prop_atoms.net_wm_state_below) {
2489 self->below = TRUE;
2490 } else if (state == prop_atoms.ob_wm_state_undecorated) {
2491 undecorated = TRUE;
2492 }
2493
2494 } else { /* action == prop_atoms.net_wm_state_remove */
2495 if (state == prop_atoms.net_wm_state_modal) {
2496 self->modal = FALSE;
2497 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2498 max_vert = FALSE;
2499 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2500 max_horz = FALSE;
2501 } else if (state == prop_atoms.net_wm_state_shaded) {
2502 shaded = FALSE;
2503 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2504 self->skip_taskbar = FALSE;
2505 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2506 self->skip_pager = FALSE;
2507 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2508 fullscreen = FALSE;
2509 } else if (state == prop_atoms.net_wm_state_above) {
2510 self->above = FALSE;
2511 } else if (state == prop_atoms.net_wm_state_below) {
2512 self->below = FALSE;
2513 } else if (state == prop_atoms.ob_wm_state_undecorated) {
2514 undecorated = FALSE;
2515 }
2516 }
2517 }
2518 if (max_horz != self->max_horz || max_vert != self->max_vert) {
2519 if (max_horz != self->max_horz && max_vert != self->max_vert) {
2520 /* toggling both */
2521 if (max_horz == max_vert) { /* both going the same way */
2522 client_maximize(self, max_horz, 0, TRUE);
2523 } else {
2524 client_maximize(self, max_horz, 1, TRUE);
2525 client_maximize(self, max_vert, 2, TRUE);
2526 }
2527 } else {
2528 /* toggling one */
2529 if (max_horz != self->max_horz)
2530 client_maximize(self, max_horz, 1, TRUE);
2531 else
2532 client_maximize(self, max_vert, 2, TRUE);
2533 }
2534 }
2535 /* change fullscreen state before shading, as it will affect if the window
2536 can shade or not */
2537 if (fullscreen != self->fullscreen)
2538 client_fullscreen(self, fullscreen, TRUE);
2539 if (shaded != self->shaded)
2540 client_shade(self, shaded);
2541 if (undecorated != self->undecorated)
2542 client_set_undecorated(self, undecorated);
2543 client_calc_layer(self);
2544 client_change_state(self); /* change the hint to reflect these changes */
2545 }
2546
2547 ObClient *client_focus_target(ObClient *self)
2548 {
2549 ObClient *child;
2550
2551 /* if we have a modal child, then focus it, not us */
2552 child = client_search_modal_child(client_search_top_transient(self));
2553 if (child) return child;
2554 return self;
2555 }
2556
2557 gboolean client_can_focus(ObClient *self)
2558 {
2559 XEvent ev;
2560
2561 /* choose the correct target */
2562 self = client_focus_target(self);
2563
2564 if (!self->frame->visible)
2565 return FALSE;
2566
2567 if (!(self->can_focus || self->focus_notify))
2568 return FALSE;
2569
2570 /* do a check to see if the window has already been unmapped or destroyed
2571 do this intelligently while watching out for unmaps we've generated
2572 (ignore_unmaps > 0) */
2573 if (XCheckTypedWindowEvent(ob_display, self->window,
2574 DestroyNotify, &ev)) {
2575 XPutBackEvent(ob_display, &ev);
2576 return FALSE;
2577 }
2578 while (XCheckTypedWindowEvent(ob_display, self->window,
2579 UnmapNotify, &ev)) {
2580 if (self->ignore_unmaps) {
2581 self->ignore_unmaps--;
2582 } else {
2583 XPutBackEvent(ob_display, &ev);
2584 return FALSE;
2585 }
2586 }
2587
2588 return TRUE;
2589 }
2590
2591 gboolean client_focus(ObClient *self)
2592 {
2593 /* choose the correct target */
2594 self = client_focus_target(self);
2595
2596 if (!client_can_focus(self)) {
2597 if (!self->frame->visible) {
2598 /* update the focus lists */
2599 focus_order_to_top(self);
2600 }
2601 return FALSE;
2602 }
2603
2604 if (self->can_focus) {
2605 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2606 I choose to use it always, hopefully to find errors quicker, if any
2607 are left. (I hate X. I hate focus events.)
2608
2609 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2610 #799. So now it is RevertToNone again.
2611 */
2612 XSetInputFocus(ob_display, self->window, RevertToNone,
2613 event_lasttime);
2614 }
2615
2616 if (self->focus_notify) {
2617 XEvent ce;
2618 ce.xclient.type = ClientMessage;
2619 ce.xclient.message_type = prop_atoms.wm_protocols;
2620 ce.xclient.display = ob_display;
2621 ce.xclient.window = self->window;
2622 ce.xclient.format = 32;
2623 ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
2624 ce.xclient.data.l[1] = event_lasttime;
2625 ce.xclient.data.l[2] = 0l;
2626 ce.xclient.data.l[3] = 0l;
2627 ce.xclient.data.l[4] = 0l;
2628 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2629 }
2630
2631 #ifdef DEBUG_FOCUS
2632 ob_debug("%sively focusing %lx at %d\n",
2633 (self->can_focus ? "act" : "pass"),
2634 self->window, (int) event_lasttime);
2635 #endif
2636
2637 /* Cause the FocusIn to come back to us. Important for desktop switches,
2638 since otherwise we'll have no FocusIn on the queue and send it off to
2639 the focus_backup. */
2640 XSync(ob_display, FALSE);
2641 return TRUE;
2642 }
2643
2644 void client_unfocus(ObClient *self)
2645 {
2646 if (focus_client == self) {
2647 #ifdef DEBUG_FOCUS
2648 ob_debug("client_unfocus for %lx\n", self->window);
2649 #endif
2650 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING);
2651 }
2652 }
2653
2654 void client_activate(ObClient *self, gboolean here)
2655 {
2656 if (client_normal(self) && screen_showing_desktop)
2657 screen_show_desktop(FALSE);
2658 if (self->iconic)
2659 client_iconify(self, FALSE, here);
2660 if (self->desktop != DESKTOP_ALL &&
2661 self->desktop != screen_desktop) {
2662 if (here)
2663 client_set_desktop(self, screen_desktop, FALSE);
2664 else
2665 screen_set_desktop(self->desktop);
2666 } else if (!self->frame->visible)
2667 /* if its not visible for other reasons, then don't mess
2668 with it */
2669 return;
2670 if (self->shaded)
2671 client_shade(self, FALSE);
2672
2673 client_focus(self);
2674
2675 /* we do this an action here. this is rather important. this is because
2676 we want the results from the focus change to take place BEFORE we go
2677 about raising the window. when a fullscreen window loses focus, we need
2678 this or else the raise wont be able to raise above the to-lose-focus
2679 fullscreen window. */
2680 client_raise(self);
2681 }
2682
2683 void client_raise(ObClient *self)
2684 {
2685 action_run_string("Raise", self);
2686 }
2687
2688 void client_lower(ObClient *self)
2689 {
2690 action_run_string("Raise", self);
2691 }
2692
2693 gboolean client_focused(ObClient *self)
2694 {
2695 return self == focus_client;
2696 }
2697
2698 static ObClientIcon* client_icon_recursive(ObClient *self, int w, int h)
2699 {
2700 guint i;
2701 /* si is the smallest image >= req */
2702 /* li is the largest image < req */
2703 unsigned long size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
2704
2705 if (!self->nicons) {
2706 ObClientIcon *parent = NULL;
2707
2708 if (self->transient_for) {
2709 if (self->transient_for != OB_TRAN_GROUP)
2710 parent = client_icon_recursive(self->transient_for, w, h);
2711 else {
2712 GSList *it;
2713 for (it = self->group->members; it; it = g_slist_next(it)) {
2714 ObClient *c = it->data;
2715 if (c != self && !c->transient_for) {
2716 if ((parent = client_icon_recursive(c, w, h)))
2717 break;
2718 }
2719 }
2720 }
2721 }
2722
2723 return parent;
2724 }
2725
2726 for (i = 0; i < self->nicons; ++i) {
2727 size = self->icons[i].width * self->icons[i].height;
2728 if (size < smallest && size >= (unsigned)(w * h)) {
2729 smallest = size;
2730 si = i;
2731 }
2732 if (size > largest && size <= (unsigned)(w * h)) {
2733 largest = size;
2734 li = i;
2735 }
2736 }
2737 if (largest == 0) /* didnt find one smaller than the requested size */
2738 return &self->icons[si];
2739 return &self->icons[li];
2740 }
2741
2742 const ObClientIcon* client_icon(ObClient *self, int w, int h)
2743 {
2744 ObClientIcon *ret;
2745 static ObClientIcon deficon;
2746
2747 if (!(ret = client_icon_recursive(self, w, h))) {
2748 deficon.width = deficon.height = 48;
2749 deficon.data = ob_rr_theme->def_win_icon;
2750 ret = &deficon;
2751 }
2752 return ret;
2753 }
2754
2755 /* this be mostly ripped from fvwm */
2756 ObClient *client_find_directional(ObClient *c, ObDirection dir)
2757 {
2758 int my_cx, my_cy, his_cx, his_cy;
2759 int offset = 0;
2760 int distance = 0;
2761 int score, best_score;
2762 ObClient *best_client, *cur;
2763 GList *it;
2764
2765 if(!client_list)
2766 return NULL;
2767
2768 /* first, find the centre coords of the currently focused window */
2769 my_cx = c->frame->area.x + c->frame->area.width / 2;
2770 my_cy = c->frame->area.y + c->frame->area.height / 2;
2771
2772 best_score = -1;
2773 best_client = NULL;
2774
2775 for(it = g_list_first(client_list); it; it = it->next) {
2776 cur = it->data;
2777
2778 /* the currently selected window isn't interesting */
2779 if(cur == c)
2780 continue;
2781 if (!client_normal(cur))
2782 continue;
2783 if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
2784 continue;
2785 if(cur->iconic)
2786 continue;
2787 if(client_focus_target(cur) == cur &&
2788 !(cur->can_focus || cur->focus_notify))
2789 continue;
2790
2791 /* find the centre coords of this window, from the
2792 * currently focused window's point of view */
2793 his_cx = (cur->frame->area.x - my_cx)
2794 + cur->frame->area.width / 2;
2795 his_cy = (cur->frame->area.y - my_cy)
2796 + cur->frame->area.height / 2;
2797
2798 if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
2799 dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
2800 int tx;
2801 /* Rotate the diagonals 45 degrees counterclockwise.
2802 * To do this, multiply the matrix /+h +h\ with the
2803 * vector (x y). \-h +h/
2804 * h = sqrt(0.5). We can set h := 1 since absolute
2805 * distance doesn't matter here. */
2806 tx = his_cx + his_cy;
2807 his_cy = -his_cx + his_cy;
2808 his_cx = tx;
2809 }
2810
2811 switch(dir) {
2812 case OB_DIRECTION_NORTH:
2813 case OB_DIRECTION_SOUTH:
2814 case OB_DIRECTION_NORTHEAST:
2815 case OB_DIRECTION_SOUTHWEST:
2816 offset = (his_cx < 0) ? -his_cx : his_cx;
2817 distance = ((dir == OB_DIRECTION_NORTH ||
2818 dir == OB_DIRECTION_NORTHEAST) ?
2819 -his_cy : his_cy);
2820 break;
2821 case OB_DIRECTION_EAST:
2822 case OB_DIRECTION_WEST:
2823 case OB_DIRECTION_SOUTHEAST:
2824 case OB_DIRECTION_NORTHWEST:
2825 offset = (his_cy < 0) ? -his_cy : his_cy;
2826 distance = ((dir == OB_DIRECTION_WEST ||
2827 dir == OB_DIRECTION_NORTHWEST) ?
2828 -his_cx : his_cx);
2829 break;
2830 }
2831
2832 /* the target must be in the requested direction */
2833 if(distance <= 0)
2834 continue;
2835
2836 /* Calculate score for this window. The smaller the better. */
2837 score = distance + offset;
2838
2839 /* windows more than 45 degrees off the direction are
2840 * heavily penalized and will only be chosen if nothing
2841 * else within a million pixels */
2842 if(offset > distance)
2843 score += 1000000;
2844
2845 if(best_score == -1 || score < best_score)
2846 best_client = cur,
2847 best_score = score;
2848 }
2849
2850 return best_client;
2851 }
2852
2853 void client_set_layer(ObClient *self, int layer)
2854 {
2855 if (layer < 0) {
2856 self->below = TRUE;
2857 self->above = FALSE;
2858 } else if (layer == 0) {
2859 self->below = self->above = FALSE;
2860 } else {
2861 self->below = FALSE;
2862 self->above = TRUE;
2863 }
2864 client_calc_layer(self);
2865 client_change_state(self); /* reflect this in the state hints */
2866 }
2867
2868 void client_set_undecorated(ObClient *self, gboolean undecorated)
2869 {
2870 if (self->undecorated != undecorated) {
2871 self->undecorated = undecorated;
2872 client_setup_decor_and_functions(self);
2873 client_change_state(self); /* reflect this in the state hints */
2874 }
2875 }
2876
2877 guint client_monitor(ObClient *self)
2878 {
2879 guint i;
2880
2881 for (i = 0; i < screen_num_monitors; ++i) {
2882 Rect *area = screen_physical_area_monitor(i);
2883 if (RECT_INTERSECTS_RECT(*area, self->frame->area))
2884 break;
2885 }
2886 if (i == screen_num_monitors) i = 0;
2887 g_assert(i < screen_num_monitors);
2888 return i;
2889 }
2890
2891 ObClient *client_search_top_transient(ObClient *self)
2892 {
2893 /* move up the transient chain as far as possible */
2894 if (self->transient_for) {
2895 if (self->transient_for != OB_TRAN_GROUP) {
2896 return client_search_top_transient(self->transient_for);
2897 } else {
2898 GSList *it;
2899
2900 g_assert(self->group);
2901
2902 for (it = self->group->members; it; it = it->next) {
2903 ObClient *c = it->data;
2904
2905 /* checking transient_for prevents infinate loops! */
2906 if (c != self && !c->transient_for)
2907 break;
2908 }
2909 if (it)
2910 return it->data;
2911 }
2912 }
2913
2914 return self;
2915 }
2916
2917 ObClient *client_search_focus_parent(ObClient *self)
2918 {
2919 if (self->transient_for) {
2920 if (self->transient_for != OB_TRAN_GROUP) {
2921 if (client_focused(self->transient_for))
2922 return self->transient_for;
2923 } else {
2924 GSList *it;
2925
2926 for (it = self->group->members; it; it = it->next) {
2927 ObClient *c = it->data;
2928
2929 /* checking transient_for prevents infinate loops! */
2930 if (c != self && !c->transient_for)
2931 if (client_focused(c))
2932 return c;
2933 }
2934 }
2935 }
2936
2937 return NULL;
2938 }
2939
2940 ObClient *client_search_parent(ObClient *self, ObClient *search)
2941 {
2942 if (self->transient_for) {
2943 if (self->transient_for != OB_TRAN_GROUP) {
2944 if (self->transient_for == search)
2945 return search;
2946 } else {
2947 GSList *it;
2948
2949 for (it = self->group->members; it; it = it->next) {
2950 ObClient *c = it->data;
2951
2952 /* checking transient_for prevents infinate loops! */
2953 if (c != self && !c->transient_for)
2954 if (c == search)
2955 return search;
2956 }
2957 }
2958 }
2959
2960 return NULL;
2961 }
2962
2963 ObClient *client_search_transient(ObClient *self, ObClient *search)
2964 {
2965 GSList *sit;
2966
2967 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
2968 if (sit->data == search)
2969 return search;
2970 if (client_search_transient(sit->data, search))
2971 return search;
2972 }
2973 return NULL;
2974 }
2975
2976 void client_update_sm_client_id(ObClient *self)
2977 {
2978 g_free(self->sm_client_id);
2979 self->sm_client_id = NULL;
2980
2981 if (!PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id) &&
2982 self->group)
2983 PROP_GETS(self->group->leader, sm_client_id, locale,
2984 &self->sm_client_id);
2985 }
2986
2987 /* finds the nearest edge in the given direction from the current client
2988 * note to self: the edge is the -frame- edge (the actual one), not the
2989 * client edge.
2990 */
2991 int client_directional_edge_search(ObClient *c, ObDirection dir)
2992 {
2993 int dest;
2994 int my_edge_start, my_edge_end, my_offset;
2995 GList *it;
2996 Rect *a;
2997
2998 if(!client_list)
2999 return -1;
3000
3001 a = screen_area(c->desktop);
3002
3003 switch(dir) {
3004 case OB_DIRECTION_NORTH:
3005 my_edge_start = c->frame->area.x;
3006 my_edge_end = c->frame->area.x + c->frame->area.width;
3007 my_offset = c->frame->area.y;
3008
3009 dest = a->y; /* default: top of screen */
3010
3011 for(it = g_list_first(client_list); it; it = it->next) {
3012 int his_edge_start, his_edge_end, his_offset;
3013 ObClient *cur = it->data;
3014
3015 if(cur == c)
3016 continue;
3017 if(!client_normal(cur))
3018 continue;
3019 if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3020 continue;
3021 if(cur->iconic)
3022 continue;
3023
3024 his_edge_start = cur->frame->area.x;
3025 his_edge_end = cur->frame->area.x + cur->frame->area.width;
3026 his_offset = cur->frame->area.y + cur->frame->area.height;
3027
3028 if(his_offset + 1 > my_offset)
3029 continue;
3030
3031 if(his_offset < dest)
3032 continue;
3033
3034 if(his_edge_start >= my_edge_start &&
3035 his_edge_start <= my_edge_end)
3036 dest = his_offset;
3037
3038 if(my_edge_start >= his_edge_start &&
3039 my_edge_start <= his_edge_end)
3040 dest = his_offset;
3041
3042 }
3043 break;
3044 case OB_DIRECTION_SOUTH:
3045 my_edge_start = c->frame->area.x;
3046 my_edge_end = c->frame->area.x + c->frame->area.width;
3047 my_offset = c->frame->area.y + c->frame->area.height;
3048
3049 dest = a->y + a->height; /* default: bottom of screen */
3050
3051 for(it = g_list_first(client_list); it; it = it->next) {
3052 int his_edge_start, his_edge_end, his_offset;
3053 ObClient *cur = it->data;
3054
3055 if(cur == c)
3056 continue;
3057 if(!client_normal(cur))
3058 continue;
3059 if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3060 continue;
3061 if(cur->iconic)
3062 continue;
3063
3064 his_edge_start = cur->frame->area.x;
3065 his_edge_end = cur->frame->area.x + cur->frame->area.width;
3066 his_offset = cur->frame->area.y;
3067
3068
3069 if(his_offset - 1 < my_offset)
3070 continue;
3071
3072 if(his_offset > dest)
3073 continue;
3074
3075 if(his_edge_start >= my_edge_start &&
3076 his_edge_start <= my_edge_end)
3077 dest = his_offset;
3078
3079 if(my_edge_start >= his_edge_start &&
3080 my_edge_start <= his_edge_end)
3081 dest = his_offset;
3082
3083 }
3084 break;
3085 case OB_DIRECTION_WEST:
3086 my_edge_start = c->frame->area.y;
3087 my_edge_end = c->frame->area.y + c->frame->area.height;
3088 my_offset = c->frame->area.x;
3089
3090 dest = a->x; /* default: leftmost egde of screen */
3091
3092 for(it = g_list_first(client_list); it; it = it->next) {
3093 int his_edge_start, his_edge_end, his_offset;
3094 ObClient *cur = it->data;
3095
3096 if(cur == c)
3097 continue;
3098 if(!client_normal(cur))
3099 continue;
3100 if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3101 continue;
3102 if(cur->iconic)
3103 continue;
3104
3105 his_edge_start = cur->frame->area.y;
3106 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3107 his_offset = cur->frame->area.x + cur->frame->area.width;
3108
3109 if(his_offset + 1 > my_offset)
3110 continue;
3111
3112 if(his_offset < dest)
3113 continue;
3114
3115 if(his_edge_start >= my_edge_start &&
3116 his_edge_start <= my_edge_end)
3117 dest = his_offset;
3118
3119 if(my_edge_start >= his_edge_start &&
3120 my_edge_start <= his_edge_end)
3121 dest = his_offset;
3122
3123
3124 }
3125 break;
3126 case OB_DIRECTION_EAST:
3127 my_edge_start = c->frame->area.y;
3128 my_edge_end = c->frame->area.y + c->frame->area.height;
3129 my_offset = c->frame->area.x + c->frame->area.width;
3130
3131 dest = a->x + a->width; /* default: rightmost edge of screen */
3132
3133 for(it = g_list_first(client_list); it; it = it->next) {
3134 int his_edge_start, his_edge_end, his_offset;
3135 ObClient *cur = it->data;
3136
3137 if(cur == c)
3138 continue;
3139 if(!client_normal(cur))
3140 continue;
3141 if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3142 continue;
3143 if(cur->iconic)
3144 continue;
3145
3146 his_edge_start = cur->frame->area.y;
3147 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3148 his_offset = cur->frame->area.x;
3149
3150 if(his_offset - 1 < my_offset)
3151 continue;
3152
3153 if(his_offset > dest)
3154 continue;
3155
3156 if(his_edge_start >= my_edge_start &&
3157 his_edge_start <= my_edge_end)
3158 dest = his_offset;
3159
3160 if(my_edge_start >= his_edge_start &&
3161 my_edge_start <= his_edge_end)
3162 dest = his_offset;
3163
3164 }
3165 break;
3166 case OB_DIRECTION_NORTHEAST:
3167 case OB_DIRECTION_SOUTHEAST:
3168 case OB_DIRECTION_NORTHWEST:
3169 case OB_DIRECTION_SOUTHWEST:
3170 /* not implemented */
3171 default:
3172 g_assert_not_reached();
3173 }
3174 return dest;
3175 }
3176
3177 ObClient* client_under_pointer()
3178 {
3179 int x, y;
3180 GList *it;
3181 ObClient *ret = NULL;
3182
3183 if (screen_pointer_pos(&x, &y)) {
3184 for (it = stacking_list; it != NULL; it = it->next) {
3185 if (WINDOW_IS_CLIENT(it->data)) {
3186 ObClient *c = WINDOW_AS_CLIENT(it->data);
3187 if (c->frame->visible &&
3188 RECT_CONTAINS(c->frame->area, x, y)) {
3189 ret = c;
3190 break;
3191 }
3192 }
3193 }
3194 }
3195 return ret;
3196 }
This page took 0.176172 seconds and 5 git commands to generate.