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