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