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