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