]> Dogcows Code - chaz/openbox/blob - openbox/client.c
remove the obconf stuff
[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 {
850 /* window is a transient for its group! */
851 target = OB_TRAN_GROUP;
852 }
853 }
854 }
855 } else
856 self->transient = FALSE;
857
858 /* if anything has changed... */
859 if (target != self->transient_for) {
860 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
861 GSList *it;
862
863 /* remove from old parents */
864 for (it = self->group->members; it; it = g_slist_next(it)) {
865 ObClient *c = it->data;
866 if (c != self && !c->transient_for)
867 c->transients = g_slist_remove(c->transients, self);
868 }
869 } else if (self->transient_for != NULL) { /* transient of window */
870 /* remove from old parent */
871 self->transient_for->transients =
872 g_slist_remove(self->transient_for->transients, self);
873 }
874 self->transient_for = target;
875 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
876 GSList *it;
877
878 /* add to new parents */
879 for (it = self->group->members; it; it = g_slist_next(it)) {
880 ObClient *c = it->data;
881 if (c != self && !c->transient_for)
882 c->transients = g_slist_append(c->transients, self);
883 }
884
885 /* remove all transients which are in the group, that causes
886 circlular pointer hell of doom */
887 for (it = self->group->members; it; it = g_slist_next(it)) {
888 GSList *sit, *next;
889 for (sit = self->transients; sit; sit = next) {
890 next = g_slist_next(sit);
891 if (sit->data == it->data)
892 self->transients =
893 g_slist_delete_link(self->transients, sit);
894 }
895 }
896 } else if (self->transient_for != NULL) { /* transient of window */
897 /* add to new parent */
898 self->transient_for->transients =
899 g_slist_append(self->transient_for->transients, self);
900 }
901 }
902 }
903
904 static void client_get_mwm_hints(ObClient *self)
905 {
906 guint num;
907 guint32 *hints;
908
909 self->mwmhints.flags = 0; /* default to none */
910
911 if (PROP_GETA32(self->window, motif_wm_hints, motif_wm_hints,
912 &hints, &num)) {
913 if (num >= OB_MWM_ELEMENTS) {
914 self->mwmhints.flags = hints[0];
915 self->mwmhints.functions = hints[1];
916 self->mwmhints.decorations = hints[2];
917 }
918 g_free(hints);
919 }
920 }
921
922 void client_get_type(ObClient *self)
923 {
924 guint num, i;
925 guint32 *val;
926
927 self->type = -1;
928
929 if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) {
930 /* use the first value that we know about in the array */
931 for (i = 0; i < num; ++i) {
932 if (val[i] == prop_atoms.net_wm_window_type_desktop)
933 self->type = OB_CLIENT_TYPE_DESKTOP;
934 else if (val[i] == prop_atoms.net_wm_window_type_dock)
935 self->type = OB_CLIENT_TYPE_DOCK;
936 else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
937 self->type = OB_CLIENT_TYPE_TOOLBAR;
938 else if (val[i] == prop_atoms.net_wm_window_type_menu)
939 self->type = OB_CLIENT_TYPE_MENU;
940 else if (val[i] == prop_atoms.net_wm_window_type_utility)
941 self->type = OB_CLIENT_TYPE_UTILITY;
942 else if (val[i] == prop_atoms.net_wm_window_type_splash)
943 self->type = OB_CLIENT_TYPE_SPLASH;
944 else if (val[i] == prop_atoms.net_wm_window_type_dialog)
945 self->type = OB_CLIENT_TYPE_DIALOG;
946 else if (val[i] == prop_atoms.net_wm_window_type_normal)
947 self->type = OB_CLIENT_TYPE_NORMAL;
948 else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
949 /* prevent this window from getting any decor or
950 functionality */
951 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
952 OB_MWM_FLAG_DECORATIONS);
953 self->mwmhints.decorations = 0;
954 self->mwmhints.functions = 0;
955 }
956 if (self->type != (ObClientType) -1)
957 break; /* grab the first legit type */
958 }
959 g_free(val);
960 }
961
962 if (self->type == (ObClientType) -1) {
963 /*the window type hint was not set, which means we either classify
964 ourself as a normal window or a dialog, depending on if we are a
965 transient. */
966 if (self->transient)
967 self->type = OB_CLIENT_TYPE_DIALOG;
968 else
969 self->type = OB_CLIENT_TYPE_NORMAL;
970 }
971 }
972
973 void client_update_protocols(ObClient *self)
974 {
975 guint32 *proto;
976 guint num_return, i;
977
978 self->focus_notify = FALSE;
979 self->delete_window = FALSE;
980
981 if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) {
982 for (i = 0; i < num_return; ++i) {
983 if (proto[i] == prop_atoms.wm_delete_window) {
984 /* this means we can request the window to close */
985 self->delete_window = TRUE;
986 } else if (proto[i] == prop_atoms.wm_take_focus)
987 /* if this protocol is requested, then the window will be
988 notified whenever we want it to receive focus */
989 self->focus_notify = TRUE;
990 }
991 g_free(proto);
992 }
993 }
994
995 static void client_get_gravity(ObClient *self)
996 {
997 XWindowAttributes wattrib;
998 Status ret;
999
1000 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
1001 g_assert(ret != BadWindow);
1002 self->gravity = wattrib.win_gravity;
1003 }
1004
1005 void client_update_normal_hints(ObClient *self)
1006 {
1007 XSizeHints size;
1008 long ret;
1009 int oldgravity = self->gravity;
1010
1011 /* defaults */
1012 self->min_ratio = 0.0f;
1013 self->max_ratio = 0.0f;
1014 SIZE_SET(self->size_inc, 1, 1);
1015 SIZE_SET(self->base_size, 0, 0);
1016 SIZE_SET(self->min_size, 0, 0);
1017 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1018
1019 /* get the hints from the window */
1020 if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
1021 self->positioned = !!(size.flags & (PPosition|USPosition));
1022
1023 if (size.flags & PWinGravity) {
1024 self->gravity = size.win_gravity;
1025
1026 /* if the client has a frame, i.e. has already been mapped and
1027 is changing its gravity */
1028 if (self->frame && self->gravity != oldgravity) {
1029 /* move our idea of the client's position based on its new
1030 gravity */
1031 self->area.x = self->frame->area.x;
1032 self->area.y = self->frame->area.y;
1033 frame_frame_gravity(self->frame, &self->area.x, &self->area.y);
1034 }
1035 }
1036
1037 if (size.flags & PAspect) {
1038 if (size.min_aspect.y)
1039 self->min_ratio = (float)size.min_aspect.x / size.min_aspect.y;
1040 if (size.max_aspect.y)
1041 self->max_ratio = (float)size.max_aspect.x / size.max_aspect.y;
1042 }
1043
1044 if (size.flags & PMinSize)
1045 SIZE_SET(self->min_size, size.min_width, size.min_height);
1046
1047 if (size.flags & PMaxSize)
1048 SIZE_SET(self->max_size, size.max_width, size.max_height);
1049
1050 if (size.flags & PBaseSize)
1051 SIZE_SET(self->base_size, size.base_width, size.base_height);
1052
1053 if (size.flags & PResizeInc)
1054 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1055 }
1056 }
1057
1058 void client_setup_decor_and_functions(ObClient *self)
1059 {
1060 /* start with everything (cept fullscreen) */
1061 self->decorations =
1062 (OB_FRAME_DECOR_TITLEBAR |
1063 (ob_rr_theme->show_handle ? OB_FRAME_DECOR_HANDLE : 0) |
1064 OB_FRAME_DECOR_GRIPS |
1065 OB_FRAME_DECOR_BORDER |
1066 OB_FRAME_DECOR_ICON |
1067 OB_FRAME_DECOR_ALLDESKTOPS |
1068 OB_FRAME_DECOR_ICONIFY |
1069 OB_FRAME_DECOR_MAXIMIZE |
1070 OB_FRAME_DECOR_SHADE);
1071 self->functions =
1072 (OB_CLIENT_FUNC_RESIZE |
1073 OB_CLIENT_FUNC_MOVE |
1074 OB_CLIENT_FUNC_ICONIFY |
1075 OB_CLIENT_FUNC_MAXIMIZE |
1076 OB_CLIENT_FUNC_SHADE);
1077 if (self->delete_window) {
1078 self->functions |= OB_CLIENT_FUNC_CLOSE;
1079 self->decorations |= OB_FRAME_DECOR_CLOSE;
1080 }
1081
1082 if (!(self->min_size.width < self->max_size.width ||
1083 self->min_size.height < self->max_size.height))
1084 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1085
1086 switch (self->type) {
1087 case OB_CLIENT_TYPE_NORMAL:
1088 /* normal windows retain all of the possible decorations and
1089 functionality, and are the only windows that you can fullscreen */
1090 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1091 break;
1092
1093 case OB_CLIENT_TYPE_DIALOG:
1094 case OB_CLIENT_TYPE_UTILITY:
1095 /* these windows cannot be maximized */
1096 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1097 break;
1098
1099 case OB_CLIENT_TYPE_MENU:
1100 case OB_CLIENT_TYPE_TOOLBAR:
1101 /* these windows get less functionality */
1102 self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_RESIZE);
1103 break;
1104
1105 case OB_CLIENT_TYPE_DESKTOP:
1106 case OB_CLIENT_TYPE_DOCK:
1107 case OB_CLIENT_TYPE_SPLASH:
1108 /* none of these windows are manipulated by the window manager */
1109 self->decorations = 0;
1110 self->functions = 0;
1111 break;
1112 }
1113
1114 /* Mwm Hints are applied subtractively to what has already been chosen for
1115 decor and functionality */
1116 if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1117 if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1118 if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1119 (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1120 /* if the mwm hints request no handle or title, then all
1121 decorations are disabled */
1122 self->decorations = 0;
1123 }
1124 }
1125
1126 if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1127 if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1128 if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1129 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1130 if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1131 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1132 /* dont let mwm hints kill any buttons
1133 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1134 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1135 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1136 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1137 */
1138 /* dont let mwm hints kill the close button
1139 if (! (self->mwmhints.functions & MwmFunc_Close))
1140 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1141 }
1142 }
1143
1144 if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1145 self->decorations &= ~OB_FRAME_DECOR_SHADE;
1146 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1147 self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1148 if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1149 self->decorations &= ~OB_FRAME_DECOR_GRIPS;
1150
1151 /* can't maximize without moving/resizing */
1152 if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1153 (self->functions & OB_CLIENT_FUNC_MOVE) &&
1154 (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1155 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1156 self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1157 }
1158
1159 /* kill the handle on fully maxed windows */
1160 if (self->max_vert && self->max_horz)
1161 self->decorations &= ~OB_FRAME_DECOR_HANDLE;
1162
1163 /* finally, the user can have requested no decorations, which overrides
1164 everything */
1165 if (self->undecorated)
1166 self->decorations = OB_FRAME_DECOR_BORDER;
1167
1168 /* if we don't have a titlebar, then we cannot shade! */
1169 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1170 self->functions &= ~OB_CLIENT_FUNC_SHADE;
1171
1172 /* now we need to check against rules for the client's current state */
1173 if (self->fullscreen) {
1174 self->functions &= (OB_CLIENT_FUNC_CLOSE |
1175 OB_CLIENT_FUNC_FULLSCREEN |
1176 OB_CLIENT_FUNC_ICONIFY);
1177 self->decorations = 0;
1178 }
1179
1180 client_change_allowed_actions(self);
1181
1182 if (self->frame) {
1183 /* adjust the client's decorations, etc. */
1184 client_reconfigure(self);
1185 } else {
1186 /* this makes sure that these windows appear on all desktops */
1187 if (self->type == OB_CLIENT_TYPE_DESKTOP &&
1188 self->desktop != DESKTOP_ALL)
1189 {
1190 self->desktop = DESKTOP_ALL;
1191 }
1192 }
1193 }
1194
1195 static void client_change_allowed_actions(ObClient *self)
1196 {
1197 guint32 actions[9];
1198 int num = 0;
1199
1200 /* desktop windows are kept on all desktops */
1201 if (self->type != OB_CLIENT_TYPE_DESKTOP)
1202 actions[num++] = prop_atoms.net_wm_action_change_desktop;
1203
1204 if (self->functions & OB_CLIENT_FUNC_SHADE)
1205 actions[num++] = prop_atoms.net_wm_action_shade;
1206 if (self->functions & OB_CLIENT_FUNC_CLOSE)
1207 actions[num++] = prop_atoms.net_wm_action_close;
1208 if (self->functions & OB_CLIENT_FUNC_MOVE)
1209 actions[num++] = prop_atoms.net_wm_action_move;
1210 if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1211 actions[num++] = prop_atoms.net_wm_action_minimize;
1212 if (self->functions & OB_CLIENT_FUNC_RESIZE)
1213 actions[num++] = prop_atoms.net_wm_action_resize;
1214 if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1215 actions[num++] = prop_atoms.net_wm_action_fullscreen;
1216 if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1217 actions[num++] = prop_atoms.net_wm_action_maximize_horz;
1218 actions[num++] = prop_atoms.net_wm_action_maximize_vert;
1219 }
1220
1221 PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
1222
1223 /* make sure the window isn't breaking any rules now */
1224
1225 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1226 if (self->frame) client_shade(self, FALSE);
1227 else self->shaded = FALSE;
1228 }
1229 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
1230 if (self->frame) client_iconify(self, FALSE, TRUE);
1231 else self->iconic = FALSE;
1232 }
1233 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1234 if (self->frame) client_fullscreen(self, FALSE, TRUE);
1235 else self->fullscreen = FALSE;
1236 }
1237 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1238 self->max_vert)) {
1239 if (self->frame) client_maximize(self, FALSE, 0, TRUE);
1240 else self->max_vert = self->max_horz = FALSE;
1241 }
1242 }
1243
1244 void client_reconfigure(ObClient *self)
1245 {
1246 /* by making this pass FALSE for user, we avoid the emacs event storm where
1247 every configurenotify causes an update in its normal hints, i think this
1248 is generally what we want anyways... */
1249 client_configure(self, OB_CORNER_TOPLEFT, self->area.x, self->area.y,
1250 self->area.width, self->area.height, FALSE, TRUE);
1251 }
1252
1253 void client_update_wmhints(ObClient *self)
1254 {
1255 XWMHints *hints;
1256 gboolean ur = FALSE;
1257 GSList *it;
1258
1259 /* assume a window takes input if it doesnt specify */
1260 self->can_focus = TRUE;
1261
1262 if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
1263 if (hints->flags & InputHint)
1264 self->can_focus = hints->input;
1265
1266 /* only do this when first managing the window *AND* when we aren't
1267 starting up! */
1268 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1269 if (hints->flags & StateHint)
1270 self->iconic = hints->initial_state == IconicState;
1271
1272 if (hints->flags & XUrgencyHint)
1273 ur = TRUE;
1274
1275 if (!(hints->flags & WindowGroupHint))
1276 hints->window_group = None;
1277
1278 /* did the group state change? */
1279 if (hints->window_group !=
1280 (self->group ? self->group->leader : None)) {
1281 /* remove from the old group if there was one */
1282 if (self->group != NULL) {
1283 /* remove transients of the group */
1284 for (it = self->group->members; it; it = it->next)
1285 self->transients = g_slist_remove(self->transients,
1286 it->data);
1287
1288 /* remove myself from parents in the group */
1289 if (self->transient_for == OB_TRAN_GROUP) {
1290 for (it = self->group->members; it; it = it->next) {
1291 ObClient *c = it->data;
1292
1293 if (c != self && !c->transient_for)
1294 c->transients = g_slist_remove(c->transients,
1295 self);
1296 }
1297 }
1298
1299 group_remove(self->group, self);
1300 self->group = NULL;
1301 }
1302 if (hints->window_group != None) {
1303 self->group = group_add(hints->window_group, self);
1304
1305 /* i can only have transients from the group if i am not
1306 transient myself */
1307 if (!self->transient_for) {
1308 /* add other transients of the group that are already
1309 set up */
1310 for (it = self->group->members; it; it = it->next) {
1311 ObClient *c = it->data;
1312 if (c != self && c->transient_for == OB_TRAN_GROUP)
1313 self->transients =
1314 g_slist_append(self->transients, c);
1315 }
1316 }
1317 }
1318
1319 /* because the self->transient flag wont change from this call,
1320 we don't need to update the window's type and such, only its
1321 transient_for, and the transients lists of other windows in
1322 the group may be affected */
1323 client_update_transient_for(self);
1324 }
1325
1326 /* the WM_HINTS can contain an icon */
1327 client_update_icons(self);
1328
1329 XFree(hints);
1330 }
1331
1332 if (ur != self->urgent) {
1333 self->urgent = ur;
1334 /* fire the urgent callback if we're mapped, otherwise, wait until
1335 after we're mapped */
1336 if (self->frame)
1337 client_urgent_notify(self);
1338 }
1339 }
1340
1341 void client_update_title(ObClient *self)
1342 {
1343 GList *it;
1344 guint32 nums;
1345 guint i;
1346 gchar *data = NULL;
1347 gboolean read_title;
1348 gchar *old_title;
1349
1350 old_title = self->title;
1351
1352 /* try netwm */
1353 if (!PROP_GETS(self->window, net_wm_name, utf8, &data))
1354 /* try old x stuff */
1355 if (!PROP_GETS(self->window, wm_name, locale, &data))
1356 data = g_strdup("Unnamed Window");
1357
1358 /* did the title change? then reset the title_count */
1359 if (old_title && 0 != strncmp(old_title, data, strlen(data)))
1360 self->title_count = 1;
1361
1362 /* look for duplicates and append a number */
1363 nums = 0;
1364 for (it = client_list; it; it = it->next)
1365 if (it->data != self) {
1366 ObClient *c = it->data;
1367 if (0 == strncmp(c->title, data, strlen(data)))
1368 nums |= 1 << c->title_count;
1369 }
1370 /* find first free number */
1371 for (i = 1; i <= 32; ++i)
1372 if (!(nums & (1 << i))) {
1373 if (self->title_count == 1 || i == 1)
1374 self->title_count = i;
1375 break;
1376 }
1377 /* dont display the number for the first window */
1378 if (self->title_count > 1) {
1379 char *ndata;
1380 ndata = g_strdup_printf("%s - [%u]", data, self->title_count);
1381 g_free(data);
1382 data = ndata;
1383 }
1384
1385 PROP_SETS(self->window, net_wm_visible_name, data);
1386
1387 self->title = data;
1388
1389 if (self->frame)
1390 frame_adjust_title(self->frame);
1391
1392 g_free(old_title);
1393
1394 /* update the icon title */
1395 data = NULL;
1396 g_free(self->icon_title);
1397
1398 read_title = TRUE;
1399 /* try netwm */
1400 if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data))
1401 /* try old x stuff */
1402 if (!PROP_GETS(self->window, wm_icon_name, locale, &data)) {
1403 data = g_strdup(self->title);
1404 read_title = FALSE;
1405 }
1406
1407 /* append the title count, dont display the number for the first window */
1408 if (read_title && self->title_count > 1) {
1409 char *vdata, *ndata;
1410 ndata = g_strdup_printf(" - [%u]", self->title_count);
1411 vdata = g_strconcat(data, ndata, NULL);
1412 g_free(ndata);
1413 g_free(data);
1414 data = vdata;
1415 }
1416
1417 PROP_SETS(self->window, net_wm_visible_icon_name, data);
1418
1419 self->icon_title = data;
1420 }
1421
1422 void client_update_class(ObClient *self)
1423 {
1424 char **data;
1425 char *s;
1426
1427 if (self->name) g_free(self->name);
1428 if (self->class) g_free(self->class);
1429 if (self->role) g_free(self->role);
1430
1431 self->name = self->class = self->role = NULL;
1432
1433 if (PROP_GETSS(self->window, wm_class, locale, &data)) {
1434 if (data[0]) {
1435 self->name = g_strdup(data[0]);
1436 if (data[1])
1437 self->class = g_strdup(data[1]);
1438 }
1439 g_strfreev(data);
1440 }
1441
1442 if (PROP_GETS(self->window, wm_window_role, locale, &s))
1443 self->role = s;
1444
1445 if (self->name == NULL) self->name = g_strdup("");
1446 if (self->class == NULL) self->class = g_strdup("");
1447 if (self->role == NULL) self->role = g_strdup("");
1448 }
1449
1450 void client_update_strut(ObClient *self)
1451 {
1452 guint num;
1453 guint32 *data;
1454 gboolean got = FALSE;
1455 StrutPartial strut;
1456
1457 if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
1458 &data, &num)) {
1459 if (num == 12) {
1460 got = TRUE;
1461 STRUT_PARTIAL_SET(strut,
1462 data[0], data[2], data[1], data[3],
1463 data[4], data[5], data[8], data[9],
1464 data[6], data[7], data[10], data[11]);
1465 }
1466 g_free(data);
1467 }
1468
1469 if (!got &&
1470 PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
1471 if (num == 4) {
1472 got = TRUE;
1473 STRUT_PARTIAL_SET(strut,
1474 data[0], data[2], data[1], data[3],
1475 0, 0, 0, 0, 0, 0, 0, 0);
1476 }
1477 g_free(data);
1478 }
1479
1480 if (!got)
1481 STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
1482 0, 0, 0, 0, 0, 0, 0, 0);
1483
1484 if (!STRUT_EQUAL(strut, self->strut)) {
1485 self->strut = strut;
1486
1487 /* updating here is pointless while we're being mapped cuz we're not in
1488 the client list yet */
1489 if (self->frame)
1490 screen_update_areas();
1491 }
1492 }
1493
1494 void client_update_icons(ObClient *self)
1495 {
1496 guint num;
1497 guint32 *data;
1498 guint w, h, i, j;
1499
1500 for (i = 0; i < self->nicons; ++i)
1501 g_free(self->icons[i].data);
1502 if (self->nicons > 0)
1503 g_free(self->icons);
1504 self->nicons = 0;
1505
1506 if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
1507 /* figure out how many valid icons are in here */
1508 i = 0;
1509 while (num - i > 2) {
1510 w = data[i++];
1511 h = data[i++];
1512 i += w * h;
1513 if (i > num || w*h == 0) break;
1514 ++self->nicons;
1515 }
1516
1517 self->icons = g_new(ObClientIcon, self->nicons);
1518
1519 /* store the icons */
1520 i = 0;
1521 for (j = 0; j < self->nicons; ++j) {
1522 guint x, y, t;
1523
1524 w = self->icons[j].width = data[i++];
1525 h = self->icons[j].height = data[i++];
1526
1527 if (w*h == 0) continue;
1528
1529 self->icons[j].data = g_new(RrPixel32, w * h);
1530 for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
1531 if (x >= w) {
1532 x = 0;
1533 ++y;
1534 }
1535 self->icons[j].data[t] =
1536 (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
1537 (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
1538 (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
1539 (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
1540 }
1541 g_assert(i <= num);
1542 }
1543
1544 g_free(data);
1545 } else if (PROP_GETA32(self->window, kwm_win_icon,
1546 kwm_win_icon, &data, &num)) {
1547 if (num == 2) {
1548 self->nicons++;
1549 self->icons = g_new(ObClientIcon, self->nicons);
1550 xerror_set_ignore(TRUE);
1551 if (!RrPixmapToRGBA(ob_rr_inst,
1552 data[0], data[1],
1553 &self->icons[self->nicons-1].width,
1554 &self->icons[self->nicons-1].height,
1555 &self->icons[self->nicons-1].data)) {
1556 g_free(&self->icons[self->nicons-1]);
1557 self->nicons--;
1558 }
1559 xerror_set_ignore(FALSE);
1560 }
1561 g_free(data);
1562 } else {
1563 XWMHints *hints;
1564
1565 if ((hints = XGetWMHints(ob_display, self->window))) {
1566 if (hints->flags & IconPixmapHint) {
1567 self->nicons++;
1568 self->icons = g_new(ObClientIcon, self->nicons);
1569 xerror_set_ignore(TRUE);
1570 if (!RrPixmapToRGBA(ob_rr_inst,
1571 hints->icon_pixmap,
1572 (hints->flags & IconMaskHint ?
1573 hints->icon_mask : None),
1574 &self->icons[self->nicons-1].width,
1575 &self->icons[self->nicons-1].height,
1576 &self->icons[self->nicons-1].data)){
1577 g_free(&self->icons[self->nicons-1]);
1578 self->nicons--;
1579 }
1580 xerror_set_ignore(FALSE);
1581 }
1582 XFree(hints);
1583 }
1584 }
1585
1586 if (!self->nicons) {
1587 self->nicons++;
1588 self->icons = g_new(ObClientIcon, self->nicons);
1589 self->icons[self->nicons-1].width = 48;
1590 self->icons[self->nicons-1].height = 48;
1591 self->icons[self->nicons-1].data = g_memdup(ob_rr_theme->def_win_icon,
1592 sizeof(RrPixel32)
1593 * 48 * 48);
1594 }
1595
1596 if (self->frame)
1597 frame_adjust_icon(self->frame);
1598 }
1599
1600 static void client_change_state(ObClient *self)
1601 {
1602 guint32 state[2];
1603 guint32 netstate[11];
1604 guint num;
1605
1606 state[0] = self->wmstate;
1607 state[1] = None;
1608 PROP_SETA32(self->window, wm_state, wm_state, state, 2);
1609
1610 num = 0;
1611 if (self->modal)
1612 netstate[num++] = prop_atoms.net_wm_state_modal;
1613 if (self->shaded)
1614 netstate[num++] = prop_atoms.net_wm_state_shaded;
1615 if (self->iconic)
1616 netstate[num++] = prop_atoms.net_wm_state_hidden;
1617 if (self->skip_taskbar)
1618 netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
1619 if (self->skip_pager)
1620 netstate[num++] = prop_atoms.net_wm_state_skip_pager;
1621 if (self->fullscreen)
1622 netstate[num++] = prop_atoms.net_wm_state_fullscreen;
1623 if (self->max_vert)
1624 netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
1625 if (self->max_horz)
1626 netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
1627 if (self->above)
1628 netstate[num++] = prop_atoms.net_wm_state_above;
1629 if (self->below)
1630 netstate[num++] = prop_atoms.net_wm_state_below;
1631 if (self->undecorated)
1632 netstate[num++] = prop_atoms.ob_wm_state_undecorated;
1633 PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
1634
1635 client_calc_layer(self);
1636
1637 if (self->frame)
1638 frame_adjust_state(self->frame);
1639 }
1640
1641 ObClient *client_search_focus_tree(ObClient *self)
1642 {
1643 GSList *it;
1644 ObClient *ret;
1645
1646 for (it = self->transients; it != NULL; it = it->next) {
1647 if (client_focused(it->data)) return it->data;
1648 if ((ret = client_search_focus_tree(it->data))) return ret;
1649 }
1650 return NULL;
1651 }
1652
1653 ObClient *client_search_focus_tree_full(ObClient *self)
1654 {
1655 if (self->transient_for) {
1656 if (self->transient_for != OB_TRAN_GROUP) {
1657 return client_search_focus_tree_full(self->transient_for);
1658 } else {
1659 GSList *it;
1660 gboolean recursed = FALSE;
1661
1662 for (it = self->group->members; it; it = it->next)
1663 if (!((ObClient*)it->data)->transient_for) {
1664 ObClient *c;
1665 if ((c = client_search_focus_tree_full(it->data)))
1666 return c;
1667 recursed = TRUE;
1668 }
1669 if (recursed)
1670 return NULL;
1671 }
1672 }
1673
1674 /* this function checks the whole tree, the client_search_focus_tree~
1675 does not, so we need to check this window */
1676 if (client_focused(self))
1677 return self;
1678 return client_search_focus_tree(self);
1679 }
1680
1681 static ObStackingLayer calc_layer(ObClient *self)
1682 {
1683 ObStackingLayer l;
1684
1685 if (self->fullscreen &&
1686 (client_focused(self) || client_search_focus_tree(self)))
1687 l = OB_STACKING_LAYER_FULLSCREEN;
1688 else if (self->type == OB_CLIENT_TYPE_DESKTOP)
1689 l = OB_STACKING_LAYER_DESKTOP;
1690 else if (self->type == OB_CLIENT_TYPE_DOCK) {
1691 if (!self->below) l = OB_STACKING_LAYER_TOP;
1692 else l = OB_STACKING_LAYER_NORMAL;
1693 }
1694 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
1695 else if (self->below) l = OB_STACKING_LAYER_BELOW;
1696 else l = OB_STACKING_LAYER_NORMAL;
1697
1698 return l;
1699 }
1700
1701 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
1702 ObStackingLayer l, gboolean raised)
1703 {
1704 ObStackingLayer old, own;
1705 GSList *it;
1706
1707 old = self->layer;
1708 own = calc_layer(self);
1709 self->layer = l > own ? l : own;
1710
1711 for (it = self->transients; it; it = it->next)
1712 client_calc_layer_recursive(it->data, orig,
1713 l, raised ? raised : l != old);
1714
1715 if (!raised && l != old)
1716 if (orig->frame) { /* only restack if the original window is managed */
1717 /* XXX add_non_intrusive ever? */
1718 stacking_remove(CLIENT_AS_WINDOW(self));
1719 stacking_add(CLIENT_AS_WINDOW(self));
1720 }
1721 }
1722
1723 void client_calc_layer(ObClient *self)
1724 {
1725 ObStackingLayer l;
1726 ObClient *orig;
1727
1728 orig = self;
1729
1730 /* transients take on the layer of their parents */
1731 self = client_search_top_transient(self);
1732
1733 l = calc_layer(self);
1734
1735 client_calc_layer_recursive(self, orig, l, FALSE);
1736 }
1737
1738 gboolean client_should_show(ObClient *self)
1739 {
1740 if (self->iconic) return FALSE;
1741 else if (!(self->desktop == screen_desktop ||
1742 self->desktop == DESKTOP_ALL)) return FALSE;
1743 else if (client_normal(self) && screen_showing_desktop) return FALSE;
1744
1745 return TRUE;
1746 }
1747
1748 static void client_showhide(ObClient *self)
1749 {
1750
1751 if (client_should_show(self))
1752 frame_show(self->frame);
1753 else
1754 frame_hide(self->frame);
1755 }
1756
1757 gboolean client_normal(ObClient *self) {
1758 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
1759 self->type == OB_CLIENT_TYPE_DOCK ||
1760 self->type == OB_CLIENT_TYPE_SPLASH);
1761 }
1762
1763 static void client_apply_startup_state(ObClient *self)
1764 {
1765 /* these are in a carefully crafted order.. */
1766
1767 if (self->iconic) {
1768 self->iconic = FALSE;
1769 client_iconify(self, TRUE, FALSE);
1770 }
1771 if (self->fullscreen) {
1772 self->fullscreen = FALSE;
1773 client_fullscreen(self, TRUE, FALSE);
1774 }
1775 if (self->undecorated) {
1776 self->undecorated = FALSE;
1777 client_set_undecorated(self, TRUE);
1778 }
1779 if (self->shaded) {
1780 self->shaded = FALSE;
1781 client_shade(self, TRUE);
1782 }
1783 if (self->urgent)
1784 client_urgent_notify(self);
1785
1786 if (self->max_vert && self->max_horz) {
1787 self->max_vert = self->max_horz = FALSE;
1788 client_maximize(self, TRUE, 0, FALSE);
1789 } else if (self->max_vert) {
1790 self->max_vert = FALSE;
1791 client_maximize(self, TRUE, 2, FALSE);
1792 } else if (self->max_horz) {
1793 self->max_horz = FALSE;
1794 client_maximize(self, TRUE, 1, FALSE);
1795 }
1796
1797 /* nothing to do for the other states:
1798 skip_taskbar
1799 skip_pager
1800 modal
1801 above
1802 below
1803 */
1804 }
1805
1806 void client_configure_full(ObClient *self, ObCorner anchor,
1807 int x, int y, int w, int h,
1808 gboolean user, gboolean final,
1809 gboolean force_reply)
1810 {
1811 gint oldw, oldh;
1812 gboolean send_resize_client;
1813 gboolean moved = FALSE, resized = FALSE;
1814 guint fdecor = self->frame->decorations;
1815 gboolean fhorz = self->frame->max_horz;
1816
1817 /* make the frame recalculate its dimentions n shit without changing
1818 anything visible for real, this way the constraints below can work with
1819 the updated frame dimensions. */
1820 frame_adjust_area(self->frame, TRUE, TRUE, TRUE);
1821
1822 /* gets the frame's position */
1823 frame_client_gravity(self->frame, &x, &y);
1824
1825 /* these positions are frame positions, not client positions */
1826
1827 /* set the size and position if fullscreen */
1828 if (self->fullscreen) {
1829 #ifdef VIDMODE
1830 int dot;
1831 XF86VidModeModeLine mode;
1832 #endif
1833 Rect *a;
1834 guint i;
1835
1836 i = client_monitor(self);
1837 a = screen_physical_area_monitor(i);
1838
1839 #ifdef VIDMODE
1840 if (i == 0 && /* primary head */
1841 extensions_vidmode &&
1842 XF86VidModeGetViewPort(ob_display, ob_screen, &x, &y) &&
1843 /* get the mode last so the mode.privsize isnt freed incorrectly */
1844 XF86VidModeGetModeLine(ob_display, ob_screen, &dot, &mode)) {
1845 x += a->x;
1846 y += a->y;
1847 w = mode.hdisplay;
1848 h = mode.vdisplay;
1849 if (mode.privsize) XFree(mode.private);
1850 } else
1851 #endif
1852 {
1853 x = a->x;
1854 y = a->y;
1855 w = a->width;
1856 h = a->height;
1857 }
1858
1859 user = FALSE; /* ignore that increment etc shit when in fullscreen */
1860 } else {
1861 Rect *a;
1862
1863 a = screen_area_monitor(self->desktop, client_monitor(self));
1864
1865 /* set the size and position if maximized */
1866 if (self->max_horz) {
1867 x = a->x;
1868 w = a->width - self->frame->size.left - self->frame->size.right;
1869 }
1870 if (self->max_vert) {
1871 y = a->y;
1872 h = a->height - self->frame->size.top - self->frame->size.bottom;
1873 }
1874 }
1875
1876 /* gets the client's position */
1877 frame_frame_gravity(self->frame, &x, &y);
1878
1879 /* these override the above states! if you cant move you can't move! */
1880 if (user) {
1881 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
1882 x = self->area.x;
1883 y = self->area.y;
1884 }
1885 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
1886 w = self->area.width;
1887 h = self->area.height;
1888 }
1889 }
1890
1891 if (!(w == self->area.width && h == self->area.height)) {
1892 int basew, baseh, minw, minh;
1893
1894 /* base size is substituted with min size if not specified */
1895 if (self->base_size.width || self->base_size.height) {
1896 basew = self->base_size.width;
1897 baseh = self->base_size.height;
1898 } else {
1899 basew = self->min_size.width;
1900 baseh = self->min_size.height;
1901 }
1902 /* min size is substituted with base size if not specified */
1903 if (self->min_size.width || self->min_size.height) {
1904 minw = self->min_size.width;
1905 minh = self->min_size.height;
1906 } else {
1907 minw = self->base_size.width;
1908 minh = self->base_size.height;
1909 }
1910
1911 /* if this is a user-requested resize, then check against min/max
1912 sizes */
1913
1914 /* smaller than min size or bigger than max size? */
1915 if (w > self->max_size.width) w = self->max_size.width;
1916 if (w < minw) w = minw;
1917 if (h > self->max_size.height) h = self->max_size.height;
1918 if (h < minh) h = minh;
1919
1920 w -= basew;
1921 h -= baseh;
1922
1923 /* keep to the increments */
1924 w /= self->size_inc.width;
1925 h /= self->size_inc.height;
1926
1927 /* you cannot resize to nothing */
1928 if (basew + w < 1) w = 1 - basew;
1929 if (baseh + h < 1) h = 1 - baseh;
1930
1931 /* store the logical size */
1932 SIZE_SET(self->logical_size,
1933 self->size_inc.width > 1 ? w : w + basew,
1934 self->size_inc.height > 1 ? h : h + baseh);
1935
1936 w *= self->size_inc.width;
1937 h *= self->size_inc.height;
1938
1939 w += basew;
1940 h += baseh;
1941
1942 /* adjust the height to match the width for the aspect ratios.
1943 for this, min size is not substituted for base size ever. */
1944 w -= self->base_size.width;
1945 h -= self->base_size.height;
1946
1947 if (self->min_ratio)
1948 if (h * self->min_ratio > w) h = (int)(w / self->min_ratio);
1949 if (self->max_ratio)
1950 if (h * self->max_ratio < w) h = (int)(w / self->max_ratio);
1951
1952 w += self->base_size.width;
1953 h += self->base_size.height;
1954 }
1955
1956 switch (anchor) {
1957 case OB_CORNER_TOPLEFT:
1958 break;
1959 case OB_CORNER_TOPRIGHT:
1960 x -= w - self->area.width;
1961 break;
1962 case OB_CORNER_BOTTOMLEFT:
1963 y -= h - self->area.height;
1964 break;
1965 case OB_CORNER_BOTTOMRIGHT:
1966 x -= w - self->area.width;
1967 y -= h - self->area.height;
1968 break;
1969 }
1970
1971 moved = x != self->area.x || y != self->area.y;
1972 resized = w != self->area.width || h != self->area.height;
1973
1974 oldw = self->area.width;
1975 oldh = self->area.height;
1976 RECT_SET(self->area, x, y, w, h);
1977
1978 /* for app-requested resizes, always resize if 'resized' is true.
1979 for user-requested ones, only resize if final is true, or when
1980 resizing in redraw mode */
1981 send_resize_client = ((!user && resized) ||
1982 (user && (final ||
1983 (resized && config_redraw_resize))));
1984
1985 /* if the client is enlarging, the resize the client before the frame */
1986 if (send_resize_client && user && (w > oldw || h > oldh))
1987 XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh));
1988
1989 /* move/resize the frame to match the request */
1990 if (self->frame) {
1991 if (self->decorations != fdecor || self->max_horz != fhorz)
1992 moved = resized = TRUE;
1993
1994 if (moved || resized)
1995 frame_adjust_area(self->frame, moved, resized, FALSE);
1996
1997 if (!resized && (force_reply || ((!user && moved) || (user && final))))
1998 {
1999 XEvent event;
2000 event.type = ConfigureNotify;
2001 event.xconfigure.display = ob_display;
2002 event.xconfigure.event = self->window;
2003 event.xconfigure.window = self->window;
2004
2005 /* root window real coords */
2006 event.xconfigure.x = self->frame->area.x + self->frame->size.left -
2007 self->border_width;
2008 event.xconfigure.y = self->frame->area.y + self->frame->size.top -
2009 self->border_width;
2010 event.xconfigure.width = w;
2011 event.xconfigure.height = h;
2012 event.xconfigure.border_width = 0;
2013 event.xconfigure.above = self->frame->plate;
2014 event.xconfigure.override_redirect = FALSE;
2015 XSendEvent(event.xconfigure.display, event.xconfigure.window,
2016 FALSE, StructureNotifyMask, &event);
2017 }
2018 }
2019
2020 /* if the client is shrinking, then resize the frame before the client */
2021 if (send_resize_client && (!user || (w <= oldw || h <= oldh)))
2022 XResizeWindow(ob_display, self->window, w, h);
2023
2024 XFlush(ob_display);
2025 }
2026
2027 void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea)
2028 {
2029 int x, y, w, h;
2030
2031 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
2032 self->fullscreen == fs) return; /* already done */
2033
2034 self->fullscreen = fs;
2035 client_change_state(self); /* change the state hints on the client,
2036 and adjust out layer/stacking */
2037
2038 if (fs) {
2039 if (savearea)
2040 self->pre_fullscreen_area = self->area;
2041
2042 /* these are not actually used cuz client_configure will set them
2043 as appropriate when the window is fullscreened */
2044 x = y = w = h = 0;
2045 } else {
2046 Rect *a;
2047
2048 if (self->pre_fullscreen_area.width > 0 &&
2049 self->pre_fullscreen_area.height > 0)
2050 {
2051 x = self->pre_fullscreen_area.x;
2052 y = self->pre_fullscreen_area.y;
2053 w = self->pre_fullscreen_area.width;
2054 h = self->pre_fullscreen_area.height;
2055 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
2056 } else {
2057 /* pick some fallbacks... */
2058 a = screen_area_monitor(self->desktop, 0);
2059 x = a->x + a->width / 4;
2060 y = a->y + a->height / 4;
2061 w = a->width / 2;
2062 h = a->height / 2;
2063 }
2064 }
2065
2066 client_setup_decor_and_functions(self);
2067
2068 client_move_resize(self, x, y, w, h);
2069
2070 /* try focus us when we go into fullscreen mode */
2071 client_focus(self);
2072 }
2073
2074 static void client_iconify_recursive(ObClient *self,
2075 gboolean iconic, gboolean curdesk)
2076 {
2077 GSList *it;
2078 gboolean changed = FALSE;
2079
2080
2081 if (self->iconic != iconic) {
2082 ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
2083 self->window);
2084
2085 self->iconic = iconic;
2086
2087 if (iconic) {
2088 if (self->functions & OB_CLIENT_FUNC_ICONIFY) {
2089 long old;
2090
2091 old = self->wmstate;
2092 self->wmstate = IconicState;
2093 if (old != self->wmstate)
2094 PROP_MSG(self->window, kde_wm_change_state,
2095 self->wmstate, 1, 0, 0);
2096
2097 self->ignore_unmaps++;
2098 /* we unmap the client itself so that we can get MapRequest
2099 events, and because the ICCCM tells us to! */
2100 XUnmapWindow(ob_display, self->window);
2101
2102 /* update the focus lists.. iconic windows go to the bottom of
2103 the list, put the new iconic window at the 'top of the
2104 bottom'. */
2105 focus_order_to_top(self);
2106
2107 changed = TRUE;
2108 }
2109 } else {
2110 long old;
2111
2112 if (curdesk)
2113 client_set_desktop(self, screen_desktop, FALSE);
2114
2115 old = self->wmstate;
2116 self->wmstate = self->shaded ? IconicState : NormalState;
2117 if (old != self->wmstate)
2118 PROP_MSG(self->window, kde_wm_change_state,
2119 self->wmstate, 1, 0, 0);
2120
2121 XMapWindow(ob_display, self->window);
2122
2123 /* this puts it after the current focused window */
2124 focus_order_remove(self);
2125 focus_order_add_new(self);
2126
2127 /* this is here cuz with the VIDMODE extension, the viewport can
2128 change while a fullscreen window is iconic, and when it
2129 uniconifies, it would be nice if it did so to the new position
2130 of the viewport */
2131 client_reconfigure(self);
2132
2133 changed = TRUE;
2134 }
2135 }
2136
2137 if (changed) {
2138 client_change_state(self);
2139 client_showhide(self);
2140 screen_update_areas();
2141 }
2142
2143 /* iconify all transients */
2144 for (it = self->transients; it != NULL; it = it->next)
2145 if (it->data != self) client_iconify_recursive(it->data,
2146 iconic, curdesk);
2147 }
2148
2149 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
2150 {
2151 /* move up the transient chain as far as possible first */
2152 self = client_search_top_transient(self);
2153
2154 client_iconify_recursive(client_search_top_transient(self),
2155 iconic, curdesk);
2156 }
2157
2158 void client_maximize(ObClient *self, gboolean max, int dir, gboolean savearea)
2159 {
2160 int x, y, w, h;
2161
2162 g_assert(dir == 0 || dir == 1 || dir == 2);
2163 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
2164
2165 /* check if already done */
2166 if (max) {
2167 if (dir == 0 && self->max_horz && self->max_vert) return;
2168 if (dir == 1 && self->max_horz) return;
2169 if (dir == 2 && self->max_vert) return;
2170 } else {
2171 if (dir == 0 && !self->max_horz && !self->max_vert) return;
2172 if (dir == 1 && !self->max_horz) return;
2173 if (dir == 2 && !self->max_vert) return;
2174 }
2175
2176 /* we just tell it to configure in the same place and client_configure
2177 worries about filling the screen with the window */
2178 x = self->area.x;
2179 y = self->area.y;
2180 w = self->area.width;
2181 h = self->area.height;
2182
2183 if (max) {
2184 if (savearea)
2185 self->pre_max_area = self->area;
2186 } else {
2187 Rect *a;
2188
2189 a = screen_area_monitor(self->desktop, 0);
2190 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
2191 if (self->pre_max_area.width > 0) {
2192 x = self->pre_max_area.x;
2193 w = self->pre_max_area.width;
2194
2195 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
2196 0, self->pre_max_area.height);
2197 } else {
2198 /* pick some fallbacks... */
2199 x = a->x + a->width / 4;
2200 w = a->width / 2;
2201 }
2202 }
2203 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
2204 if (self->pre_max_area.height > 0) {
2205 y = self->pre_max_area.y;
2206 h = self->pre_max_area.height;
2207
2208 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
2209 self->pre_max_area.width, 0);
2210 } else {
2211 /* pick some fallbacks... */
2212 y = a->y + a->height / 4;
2213 h = a->height / 2;
2214 }
2215 }
2216 }
2217
2218 if (dir == 0 || dir == 1) /* horz */
2219 self->max_horz = max;
2220 if (dir == 0 || dir == 2) /* vert */
2221 self->max_vert = max;
2222
2223 client_change_state(self); /* change the state hints on the client */
2224
2225 client_setup_decor_and_functions(self);
2226
2227 client_move_resize(self, x, y, w, h);
2228 }
2229
2230 void client_shade(ObClient *self, gboolean shade)
2231 {
2232 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
2233 shade) || /* can't shade */
2234 self->shaded == shade) return; /* already done */
2235
2236 /* when we're iconic, don't change the wmstate */
2237 if (!self->iconic) {
2238 long old;
2239
2240 old = self->wmstate;
2241 self->wmstate = shade ? IconicState : NormalState;
2242 if (old != self->wmstate)
2243 PROP_MSG(self->window, kde_wm_change_state,
2244 self->wmstate, 1, 0, 0);
2245 }
2246
2247 self->shaded = shade;
2248 client_change_state(self);
2249 /* resize the frame to just the titlebar */
2250 frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
2251 }
2252
2253 void client_close(ObClient *self)
2254 {
2255 XEvent ce;
2256
2257 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
2258
2259 /*
2260 XXX: itd be cool to do timeouts and shit here for killing the client's
2261 process off
2262 like... if the window is around after 5 seconds, then the close button
2263 turns a nice red, and if this function is called again, the client is
2264 explicitly killed.
2265 */
2266
2267 ce.xclient.type = ClientMessage;
2268 ce.xclient.message_type = prop_atoms.wm_protocols;
2269 ce.xclient.display = ob_display;
2270 ce.xclient.window = self->window;
2271 ce.xclient.format = 32;
2272 ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
2273 ce.xclient.data.l[1] = event_lasttime;
2274 ce.xclient.data.l[2] = 0l;
2275 ce.xclient.data.l[3] = 0l;
2276 ce.xclient.data.l[4] = 0l;
2277 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2278 }
2279
2280 void client_kill(ObClient *self)
2281 {
2282 XKillClient(ob_display, self->window);
2283 }
2284
2285 void client_set_desktop_recursive(ObClient *self,
2286 guint target, gboolean donthide)
2287 {
2288 guint old;
2289 GSList *it;
2290
2291 if (target != self->desktop) {
2292
2293 ob_debug("Setting desktop %u\n", target+1);
2294
2295 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
2296
2297 /* remove from the old desktop(s) */
2298 focus_order_remove(self);
2299
2300 old = self->desktop;
2301 self->desktop = target;
2302 PROP_SET32(self->window, net_wm_desktop, cardinal, target);
2303 /* the frame can display the current desktop state */
2304 frame_adjust_state(self->frame);
2305 /* 'move' the window to the new desktop */
2306 if (!donthide)
2307 client_showhide(self);
2308 /* raise if it was not already on the desktop */
2309 if (old != DESKTOP_ALL)
2310 client_raise(self);
2311 screen_update_areas();
2312
2313 /* add to the new desktop(s) */
2314 if (config_focus_new)
2315 focus_order_to_top(self);
2316 else
2317 focus_order_to_bottom(self);
2318 }
2319
2320 /* move all transients */
2321 for (it = self->transients; it != NULL; it = it->next)
2322 if (it->data != self) client_set_desktop_recursive(it->data,
2323 target, donthide);
2324 }
2325
2326 void client_set_desktop(ObClient *self, guint target, gboolean donthide)
2327 {
2328 client_set_desktop_recursive(client_search_top_transient(self),
2329 target, donthide);
2330 }
2331
2332 ObClient *client_search_modal_child(ObClient *self)
2333 {
2334 GSList *it;
2335 ObClient *ret;
2336
2337 for (it = self->transients; it != NULL; it = it->next) {
2338 ObClient *c = it->data;
2339 if ((ret = client_search_modal_child(c))) return ret;
2340 if (c->modal) return c;
2341 }
2342 return NULL;
2343 }
2344
2345 gboolean client_validate(ObClient *self)
2346 {
2347 XEvent e;
2348
2349 XSync(ob_display, FALSE); /* get all events on the server */
2350
2351 if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
2352 XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
2353 XPutBackEvent(ob_display, &e);
2354 return FALSE;
2355 }
2356
2357 return TRUE;
2358 }
2359
2360 void client_set_wm_state(ObClient *self, long state)
2361 {
2362 if (state == self->wmstate) return; /* no change */
2363
2364 switch (state) {
2365 case IconicState:
2366 client_iconify(self, TRUE, TRUE);
2367 break;
2368 case NormalState:
2369 client_iconify(self, FALSE, TRUE);
2370 break;
2371 }
2372 }
2373
2374 void client_set_state(ObClient *self, Atom action, long data1, long data2)
2375 {
2376 gboolean shaded = self->shaded;
2377 gboolean fullscreen = self->fullscreen;
2378 gboolean undecorated = self->undecorated;
2379 gboolean max_horz = self->max_horz;
2380 gboolean max_vert = self->max_vert;
2381 int i;
2382
2383 if (!(action == prop_atoms.net_wm_state_add ||
2384 action == prop_atoms.net_wm_state_remove ||
2385 action == prop_atoms.net_wm_state_toggle))
2386 /* an invalid action was passed to the client message, ignore it */
2387 return;
2388
2389 for (i = 0; i < 2; ++i) {
2390 Atom state = i == 0 ? data1 : data2;
2391
2392 if (!state) continue;
2393
2394 /* if toggling, then pick whether we're adding or removing */
2395 if (action == prop_atoms.net_wm_state_toggle) {
2396 if (state == prop_atoms.net_wm_state_modal)
2397 action = self->modal ? prop_atoms.net_wm_state_remove :
2398 prop_atoms.net_wm_state_add;
2399 else if (state == prop_atoms.net_wm_state_maximized_vert)
2400 action = self->max_vert ? prop_atoms.net_wm_state_remove :
2401 prop_atoms.net_wm_state_add;
2402 else if (state == prop_atoms.net_wm_state_maximized_horz)
2403 action = self->max_horz ? prop_atoms.net_wm_state_remove :
2404 prop_atoms.net_wm_state_add;
2405 else if (state == prop_atoms.net_wm_state_shaded)
2406 action = shaded ? prop_atoms.net_wm_state_remove :
2407 prop_atoms.net_wm_state_add;
2408 else if (state == prop_atoms.net_wm_state_skip_taskbar)
2409 action = self->skip_taskbar ?
2410 prop_atoms.net_wm_state_remove :
2411 prop_atoms.net_wm_state_add;
2412 else if (state == prop_atoms.net_wm_state_skip_pager)
2413 action = self->skip_pager ?
2414 prop_atoms.net_wm_state_remove :
2415 prop_atoms.net_wm_state_add;
2416 else if (state == prop_atoms.net_wm_state_fullscreen)
2417 action = fullscreen ?
2418 prop_atoms.net_wm_state_remove :
2419 prop_atoms.net_wm_state_add;
2420 else if (state == prop_atoms.net_wm_state_above)
2421 action = self->above ? prop_atoms.net_wm_state_remove :
2422 prop_atoms.net_wm_state_add;
2423 else if (state == prop_atoms.net_wm_state_below)
2424 action = self->below ? prop_atoms.net_wm_state_remove :
2425 prop_atoms.net_wm_state_add;
2426 else if (state == prop_atoms.ob_wm_state_undecorated)
2427 action = undecorated ? prop_atoms.net_wm_state_remove :
2428 prop_atoms.net_wm_state_add;
2429 }
2430
2431 if (action == prop_atoms.net_wm_state_add) {
2432 if (state == prop_atoms.net_wm_state_modal) {
2433 /* XXX raise here or something? */
2434 self->modal = TRUE;
2435 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2436 max_vert = TRUE;
2437 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2438 max_horz = TRUE;
2439 } else if (state == prop_atoms.net_wm_state_shaded) {
2440 shaded = TRUE;
2441 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2442 self->skip_taskbar = TRUE;
2443 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2444 self->skip_pager = TRUE;
2445 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2446 fullscreen = TRUE;
2447 } else if (state == prop_atoms.net_wm_state_above) {
2448 self->above = TRUE;
2449 } else if (state == prop_atoms.net_wm_state_below) {
2450 self->below = TRUE;
2451 } else if (state == prop_atoms.ob_wm_state_undecorated) {
2452 undecorated = TRUE;
2453 }
2454
2455 } else { /* action == prop_atoms.net_wm_state_remove */
2456 if (state == prop_atoms.net_wm_state_modal) {
2457 self->modal = FALSE;
2458 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2459 max_vert = FALSE;
2460 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2461 max_horz = FALSE;
2462 } else if (state == prop_atoms.net_wm_state_shaded) {
2463 shaded = FALSE;
2464 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2465 self->skip_taskbar = FALSE;
2466 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2467 self->skip_pager = FALSE;
2468 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2469 fullscreen = FALSE;
2470 } else if (state == prop_atoms.net_wm_state_above) {
2471 self->above = FALSE;
2472 } else if (state == prop_atoms.net_wm_state_below) {
2473 self->below = FALSE;
2474 } else if (state == prop_atoms.ob_wm_state_undecorated) {
2475 undecorated = FALSE;
2476 }
2477 }
2478 }
2479 if (max_horz != self->max_horz || max_vert != self->max_vert) {
2480 if (max_horz != self->max_horz && max_vert != self->max_vert) {
2481 /* toggling both */
2482 if (max_horz == max_vert) { /* both going the same way */
2483 client_maximize(self, max_horz, 0, TRUE);
2484 } else {
2485 client_maximize(self, max_horz, 1, TRUE);
2486 client_maximize(self, max_vert, 2, TRUE);
2487 }
2488 } else {
2489 /* toggling one */
2490 if (max_horz != self->max_horz)
2491 client_maximize(self, max_horz, 1, TRUE);
2492 else
2493 client_maximize(self, max_vert, 2, TRUE);
2494 }
2495 }
2496 /* change fullscreen state before shading, as it will affect if the window
2497 can shade or not */
2498 if (fullscreen != self->fullscreen)
2499 client_fullscreen(self, fullscreen, TRUE);
2500 if (shaded != self->shaded)
2501 client_shade(self, shaded);
2502 if (undecorated != self->undecorated)
2503 client_set_undecorated(self, undecorated);
2504 client_calc_layer(self);
2505 client_change_state(self); /* change the hint to reflect these changes */
2506 }
2507
2508 ObClient *client_focus_target(ObClient *self)
2509 {
2510 ObClient *child;
2511
2512 /* if we have a modal child, then focus it, not us */
2513 child = client_search_modal_child(client_search_top_transient(self));
2514 if (child) return child;
2515 return self;
2516 }
2517
2518 gboolean client_can_focus(ObClient *self)
2519 {
2520 XEvent ev;
2521
2522 /* choose the correct target */
2523 self = client_focus_target(self);
2524
2525 if (!self->frame->visible)
2526 return FALSE;
2527
2528 if (!((self->can_focus || self->focus_notify) &&
2529 (self->desktop == screen_desktop ||
2530 self->desktop == DESKTOP_ALL) &&
2531 !self->iconic))
2532 return FALSE;
2533
2534 /* do a check to see if the window has already been unmapped or destroyed
2535 do this intelligently while watching out for unmaps we've generated
2536 (ignore_unmaps > 0) */
2537 if (XCheckTypedWindowEvent(ob_display, self->window,
2538 DestroyNotify, &ev)) {
2539 XPutBackEvent(ob_display, &ev);
2540 return FALSE;
2541 }
2542 while (XCheckTypedWindowEvent(ob_display, self->window,
2543 UnmapNotify, &ev)) {
2544 if (self->ignore_unmaps) {
2545 self->ignore_unmaps--;
2546 } else {
2547 XPutBackEvent(ob_display, &ev);
2548 return FALSE;
2549 }
2550 }
2551
2552 return TRUE;
2553 }
2554
2555 gboolean client_focus(ObClient *self)
2556 {
2557 /* choose the correct target */
2558 self = client_focus_target(self);
2559
2560 if (!client_can_focus(self)) {
2561 if (!self->frame->visible) {
2562 /* update the focus lists */
2563 focus_order_to_top(self);
2564 }
2565 return FALSE;
2566 }
2567
2568 if (self->can_focus) {
2569 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2570 I choose to use it always, hopefully to find errors quicker, if any
2571 are left. (I hate X. I hate focus events.)
2572
2573 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2574 #799. So now it is RevertToNone again.
2575 */
2576 XSetInputFocus(ob_display, self->window, RevertToNone,
2577 event_lasttime);
2578 }
2579
2580 if (self->focus_notify) {
2581 XEvent ce;
2582 ce.xclient.type = ClientMessage;
2583 ce.xclient.message_type = prop_atoms.wm_protocols;
2584 ce.xclient.display = ob_display;
2585 ce.xclient.window = self->window;
2586 ce.xclient.format = 32;
2587 ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
2588 ce.xclient.data.l[1] = event_lasttime;
2589 ce.xclient.data.l[2] = 0l;
2590 ce.xclient.data.l[3] = 0l;
2591 ce.xclient.data.l[4] = 0l;
2592 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2593 }
2594
2595 #ifdef DEBUG_FOCUS
2596 ob_debug("%sively focusing %lx at %d\n",
2597 (self->can_focus ? "act" : "pass"),
2598 self->window, (int) event_lasttime);
2599 #endif
2600
2601 /* Cause the FocusIn to come back to us. Important for desktop switches,
2602 since otherwise we'll have no FocusIn on the queue and send it off to
2603 the focus_backup. */
2604 XSync(ob_display, FALSE);
2605 return TRUE;
2606 }
2607
2608 void client_unfocus(ObClient *self)
2609 {
2610 if (focus_client == self) {
2611 #ifdef DEBUG_FOCUS
2612 ob_debug("client_unfocus for %lx\n", self->window);
2613 #endif
2614 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING);
2615 }
2616 }
2617
2618 void client_activate(ObClient *self, gboolean here)
2619 {
2620 if (client_normal(self) && screen_showing_desktop)
2621 screen_show_desktop(FALSE);
2622 if (self->iconic)
2623 client_iconify(self, FALSE, here);
2624 if (self->desktop != DESKTOP_ALL &&
2625 self->desktop != screen_desktop) {
2626 if (here)
2627 client_set_desktop(self, screen_desktop, FALSE);
2628 else
2629 screen_set_desktop(self->desktop);
2630 } else if (!self->frame->visible)
2631 /* if its not visible for other reasons, then don't mess
2632 with it */
2633 return;
2634 if (self->shaded)
2635 client_shade(self, FALSE);
2636
2637 client_focus(self);
2638
2639 /* we do this an action here. this is rather important. this is because
2640 we want the results from the focus change to take place BEFORE we go
2641 about raising the window. when a fullscreen window loses focus, we need
2642 this or else the raise wont be able to raise above the to-lose-focus
2643 fullscreen window. */
2644 client_raise(self);
2645 }
2646
2647 void client_raise(ObClient *self)
2648 {
2649 action_run_string("Raise", self);
2650 }
2651
2652 void client_lower(ObClient *self)
2653 {
2654 action_run_string("Raise", self);
2655 }
2656
2657 gboolean client_focused(ObClient *self)
2658 {
2659 return self == focus_client;
2660 }
2661
2662 ObClientIcon *client_icon(ObClient *self, int w, int h)
2663 {
2664 guint i;
2665 /* si is the smallest image >= req */
2666 /* li is the largest image < req */
2667 unsigned long size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
2668
2669 for (i = 0; i < self->nicons; ++i) {
2670 size = self->icons[i].width * self->icons[i].height;
2671 if (size < smallest && size >= (unsigned)(w * h)) {
2672 smallest = size;
2673 si = i;
2674 }
2675 if (size > largest && size <= (unsigned)(w * h)) {
2676 largest = size;
2677 li = i;
2678 }
2679 }
2680 if (largest == 0) /* didnt find one smaller than the requested size */
2681 return &self->icons[si];
2682 return &self->icons[li];
2683 }
2684
2685 /* this be mostly ripped from fvwm */
2686 ObClient *client_find_directional(ObClient *c, ObDirection dir)
2687 {
2688 int my_cx, my_cy, his_cx, his_cy;
2689 int offset = 0;
2690 int distance = 0;
2691 int score, best_score;
2692 ObClient *best_client, *cur;
2693 GList *it;
2694
2695 if(!client_list)
2696 return NULL;
2697
2698 /* first, find the centre coords of the currently focused window */
2699 my_cx = c->frame->area.x + c->frame->area.width / 2;
2700 my_cy = c->frame->area.y + c->frame->area.height / 2;
2701
2702 best_score = -1;
2703 best_client = NULL;
2704
2705 for(it = g_list_first(client_list); it; it = it->next) {
2706 cur = it->data;
2707
2708 /* the currently selected window isn't interesting */
2709 if(cur == c)
2710 continue;
2711 if (!client_normal(cur))
2712 continue;
2713 if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
2714 continue;
2715 if(cur->iconic)
2716 continue;
2717 if(client_focus_target(cur) == cur &&
2718 !(cur->can_focus || cur->focus_notify))
2719 continue;
2720
2721 /* find the centre coords of this window, from the
2722 * currently focused window's point of view */
2723 his_cx = (cur->frame->area.x - my_cx)
2724 + cur->frame->area.width / 2;
2725 his_cy = (cur->frame->area.y - my_cy)
2726 + cur->frame->area.height / 2;
2727
2728 if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
2729 dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
2730 int tx;
2731 /* Rotate the diagonals 45 degrees counterclockwise.
2732 * To do this, multiply the matrix /+h +h\ with the
2733 * vector (x y). \-h +h/
2734 * h = sqrt(0.5). We can set h := 1 since absolute
2735 * distance doesn't matter here. */
2736 tx = his_cx + his_cy;
2737 his_cy = -his_cx + his_cy;
2738 his_cx = tx;
2739 }
2740
2741 switch(dir) {
2742 case OB_DIRECTION_NORTH:
2743 case OB_DIRECTION_SOUTH:
2744 case OB_DIRECTION_NORTHEAST:
2745 case OB_DIRECTION_SOUTHWEST:
2746 offset = (his_cx < 0) ? -his_cx : his_cx;
2747 distance = ((dir == OB_DIRECTION_NORTH ||
2748 dir == OB_DIRECTION_NORTHEAST) ?
2749 -his_cy : his_cy);
2750 break;
2751 case OB_DIRECTION_EAST:
2752 case OB_DIRECTION_WEST:
2753 case OB_DIRECTION_SOUTHEAST:
2754 case OB_DIRECTION_NORTHWEST:
2755 offset = (his_cy < 0) ? -his_cy : his_cy;
2756 distance = ((dir == OB_DIRECTION_WEST ||
2757 dir == OB_DIRECTION_NORTHWEST) ?
2758 -his_cx : his_cx);
2759 break;
2760 }
2761
2762 /* the target must be in the requested direction */
2763 if(distance <= 0)
2764 continue;
2765
2766 /* Calculate score for this window. The smaller the better. */
2767 score = distance + offset;
2768
2769 /* windows more than 45 degrees off the direction are
2770 * heavily penalized and will only be chosen if nothing
2771 * else within a million pixels */
2772 if(offset > distance)
2773 score += 1000000;
2774
2775 if(best_score == -1 || score < best_score)
2776 best_client = cur,
2777 best_score = score;
2778 }
2779
2780 return best_client;
2781 }
2782
2783 void client_set_layer(ObClient *self, int layer)
2784 {
2785 if (layer < 0) {
2786 self->below = TRUE;
2787 self->above = FALSE;
2788 } else if (layer == 0) {
2789 self->below = self->above = FALSE;
2790 } else {
2791 self->below = FALSE;
2792 self->above = TRUE;
2793 }
2794 client_calc_layer(self);
2795 client_change_state(self); /* reflect this in the state hints */
2796 }
2797
2798 void client_set_undecorated(ObClient *self, gboolean undecorated)
2799 {
2800 if (self->undecorated != undecorated) {
2801 self->undecorated = undecorated;
2802 client_setup_decor_and_functions(self);
2803 client_change_state(self); /* reflect this in the state hints */
2804 }
2805 }
2806
2807 guint client_monitor(ObClient *self)
2808 {
2809 guint i;
2810
2811 for (i = 0; i < screen_num_monitors; ++i) {
2812 Rect *area = screen_physical_area_monitor(i);
2813 if (RECT_INTERSECTS_RECT(*area, self->frame->area))
2814 break;
2815 }
2816 if (i == screen_num_monitors) i = 0;
2817 g_assert(i < screen_num_monitors);
2818 return i;
2819 }
2820
2821 ObClient *client_search_top_transient(ObClient *self)
2822 {
2823 /* move up the transient chain as far as possible */
2824 if (self->transient_for) {
2825 if (self->transient_for != OB_TRAN_GROUP) {
2826 return client_search_top_transient(self->transient_for);
2827 } else {
2828 GSList *it;
2829
2830 g_assert(self->group);
2831
2832 for (it = self->group->members; it; it = it->next) {
2833 ObClient *c = it->data;
2834
2835 /* checking transient_for prevents infinate loops! */
2836 if (c != self && !c->transient_for)
2837 break;
2838 }
2839 if (it)
2840 return it->data;
2841 }
2842 }
2843
2844 return self;
2845 }
2846
2847 ObClient *client_search_focus_parent(ObClient *self)
2848 {
2849 if (self->transient_for) {
2850 if (self->transient_for != OB_TRAN_GROUP) {
2851 if (client_focused(self->transient_for))
2852 return self->transient_for;
2853 } else {
2854 GSList *it;
2855
2856 for (it = self->group->members; it; it = it->next) {
2857 ObClient *c = it->data;
2858
2859 /* checking transient_for prevents infinate loops! */
2860 if (c != self && !c->transient_for)
2861 if (client_focused(c))
2862 return c;
2863 }
2864 }
2865 }
2866
2867 return NULL;
2868 }
2869
2870 ObClient *client_search_parent(ObClient *self, ObClient *search)
2871 {
2872 if (self->transient_for) {
2873 if (self->transient_for != OB_TRAN_GROUP) {
2874 if (self->transient_for == search)
2875 return search;
2876 } else {
2877 GSList *it;
2878
2879 for (it = self->group->members; it; it = it->next) {
2880 ObClient *c = it->data;
2881
2882 /* checking transient_for prevents infinate loops! */
2883 if (c != self && !c->transient_for)
2884 if (c == search)
2885 return search;
2886 }
2887 }
2888 }
2889
2890 return NULL;
2891 }
2892
2893 ObClient *client_search_transient(ObClient *self, ObClient *search)
2894 {
2895 GSList *sit;
2896
2897 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
2898 if (sit->data == search)
2899 return search;
2900 if (client_search_transient(sit->data, search))
2901 return search;
2902 }
2903 return NULL;
2904 }
2905
2906 void client_update_sm_client_id(ObClient *self)
2907 {
2908 g_free(self->sm_client_id);
2909 self->sm_client_id = NULL;
2910
2911 if (!PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id) &&
2912 self->group)
2913 PROP_GETS(self->group->leader, sm_client_id, locale,
2914 &self->sm_client_id);
2915 }
2916
2917 /* finds the nearest edge in the given direction from the current client
2918 * note to self: the edge is the -frame- edge (the actual one), not the
2919 * client edge.
2920 */
2921 int client_directional_edge_search(ObClient *c, ObDirection dir)
2922 {
2923 int dest;
2924 int my_edge_start, my_edge_end, my_offset;
2925 GList *it;
2926 Rect *a;
2927
2928 if(!client_list)
2929 return -1;
2930
2931 a = screen_area(c->desktop);
2932
2933 switch(dir) {
2934 case OB_DIRECTION_NORTH:
2935 my_edge_start = c->frame->area.x;
2936 my_edge_end = c->frame->area.x + c->frame->area.width;
2937 my_offset = c->frame->area.y;
2938
2939 dest = a->y; /* default: top of screen */
2940
2941 for(it = g_list_first(client_list); it; it = it->next) {
2942 int his_edge_start, his_edge_end, his_offset;
2943 ObClient *cur = it->data;
2944
2945 if(cur == c)
2946 continue;
2947 if(!client_normal(cur))
2948 continue;
2949 if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
2950 continue;
2951 if(cur->iconic)
2952 continue;
2953
2954 his_edge_start = cur->frame->area.x;
2955 his_edge_end = cur->frame->area.x + cur->frame->area.width;
2956 his_offset = cur->frame->area.y + cur->frame->area.height;
2957
2958 if(his_offset + 1 > my_offset)
2959 continue;
2960
2961 if(his_offset < dest)
2962 continue;
2963
2964 if(his_edge_start >= my_edge_start &&
2965 his_edge_start <= my_edge_end)
2966 dest = his_offset;
2967
2968 if(my_edge_start >= his_edge_start &&
2969 my_edge_start <= his_edge_end)
2970 dest = his_offset;
2971
2972 }
2973 break;
2974 case OB_DIRECTION_SOUTH:
2975 my_edge_start = c->frame->area.x;
2976 my_edge_end = c->frame->area.x + c->frame->area.width;
2977 my_offset = c->frame->area.y + c->frame->area.height;
2978
2979 dest = a->y + a->height; /* default: bottom of screen */
2980
2981 for(it = g_list_first(client_list); it; it = it->next) {
2982 int his_edge_start, his_edge_end, his_offset;
2983 ObClient *cur = it->data;
2984
2985 if(cur == c)
2986 continue;
2987 if(!client_normal(cur))
2988 continue;
2989 if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
2990 continue;
2991 if(cur->iconic)
2992 continue;
2993
2994 his_edge_start = cur->frame->area.x;
2995 his_edge_end = cur->frame->area.x + cur->frame->area.width;
2996 his_offset = cur->frame->area.y;
2997
2998
2999 if(his_offset - 1 < my_offset)
3000 continue;
3001
3002 if(his_offset > dest)
3003 continue;
3004
3005 if(his_edge_start >= my_edge_start &&
3006 his_edge_start <= my_edge_end)
3007 dest = his_offset;
3008
3009 if(my_edge_start >= his_edge_start &&
3010 my_edge_start <= his_edge_end)
3011 dest = his_offset;
3012
3013 }
3014 break;
3015 case OB_DIRECTION_WEST:
3016 my_edge_start = c->frame->area.y;
3017 my_edge_end = c->frame->area.y + c->frame->area.height;
3018 my_offset = c->frame->area.x;
3019
3020 dest = a->x; /* default: leftmost egde of screen */
3021
3022 for(it = g_list_first(client_list); it; it = it->next) {
3023 int his_edge_start, his_edge_end, his_offset;
3024 ObClient *cur = it->data;
3025
3026 if(cur == c)
3027 continue;
3028 if(!client_normal(cur))
3029 continue;
3030 if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3031 continue;
3032 if(cur->iconic)
3033 continue;
3034
3035 his_edge_start = cur->frame->area.y;
3036 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3037 his_offset = cur->frame->area.x + cur->frame->area.width;
3038
3039 if(his_offset + 1 > my_offset)
3040 continue;
3041
3042 if(his_offset < dest)
3043 continue;
3044
3045 if(his_edge_start >= my_edge_start &&
3046 his_edge_start <= my_edge_end)
3047 dest = his_offset;
3048
3049 if(my_edge_start >= his_edge_start &&
3050 my_edge_start <= his_edge_end)
3051 dest = his_offset;
3052
3053
3054 }
3055 break;
3056 case OB_DIRECTION_EAST:
3057 my_edge_start = c->frame->area.y;
3058 my_edge_end = c->frame->area.y + c->frame->area.height;
3059 my_offset = c->frame->area.x + c->frame->area.width;
3060
3061 dest = a->x + a->width; /* default: rightmost edge of screen */
3062
3063 for(it = g_list_first(client_list); it; it = it->next) {
3064 int his_edge_start, his_edge_end, his_offset;
3065 ObClient *cur = it->data;
3066
3067 if(cur == c)
3068 continue;
3069 if(!client_normal(cur))
3070 continue;
3071 if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3072 continue;
3073 if(cur->iconic)
3074 continue;
3075
3076 his_edge_start = cur->frame->area.y;
3077 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3078 his_offset = cur->frame->area.x;
3079
3080 if(his_offset - 1 < my_offset)
3081 continue;
3082
3083 if(his_offset > dest)
3084 continue;
3085
3086 if(his_edge_start >= my_edge_start &&
3087 his_edge_start <= my_edge_end)
3088 dest = his_offset;
3089
3090 if(my_edge_start >= his_edge_start &&
3091 my_edge_start <= his_edge_end)
3092 dest = his_offset;
3093
3094 }
3095 break;
3096 case OB_DIRECTION_NORTHEAST:
3097 case OB_DIRECTION_SOUTHEAST:
3098 case OB_DIRECTION_NORTHWEST:
3099 case OB_DIRECTION_SOUTHWEST:
3100 /* not implemented */
3101 default:
3102 g_assert_not_reached();
3103 }
3104 return dest;
3105 }
3106
3107 ObClient* client_under_pointer()
3108 {
3109 int x, y;
3110 GList *it;
3111 ObClient *ret = NULL;
3112
3113 if (screen_pointer_pos(&x, &y)) {
3114 for (it = stacking_list; it != NULL; it = it->next) {
3115 if (WINDOW_IS_CLIENT(it->data)) {
3116 ObClient *c = WINDOW_AS_CLIENT(it->data);
3117 if (c->desktop == screen_desktop &&
3118 RECT_CONTAINS(c->frame->area, x, y)) {
3119 ret = c;
3120 break;
3121 }
3122 }
3123 }
3124 }
3125 return ret;
3126 }
This page took 0.17442 seconds and 4 git commands to generate.