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