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