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