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