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