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