client.c for the Openbox window manager
Copyright (c) 2006 Mikael Magnusson
- Copyright (c) 2003 Ben Jansens
+ Copyright (c) 2003-2007 Dana Jansens
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
static void client_get_area(ObClient *self);
static void client_get_desktop(ObClient *self);
static void client_get_state(ObClient *self);
+static void client_get_layer(ObClient *self);
static void client_get_shaped(ObClient *self);
static void client_get_mwm_hints(ObClient *self);
static void client_get_gravity(ObClient *self);
self->wmstate = NormalState;
self->layer = -1;
self->desktop = screen_num_desktops; /* always an invalid value */
+ self->user_time = ~0; /* maximum value, always newer than the real time */
client_get_all(self);
client_restore_session_state(self);
- self->user_time = sn_app_started(self->startup_id, self->class);
+ client_calc_layer(self);
+
+ {
+ Time t = sn_app_started(self->startup_id, self->class);
+ if (t) self->user_time = t;
+ }
/* update the focus lists, do this before the call to change_state or
it can end up in the list twice! */
/* get and set application level settings */
settings = get_settings(self);
- stacking_add(CLIENT_AS_WINDOW(self));
+ stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
client_restore_session_stacking(self);
if (settings) {
keyboard_grab_for_client(self, TRUE);
mouse_grab_for_client(self, TRUE);
- client_showhide(self);
-
- /* use client_focus instead of client_activate cuz client_activate does
- stuff like switch desktops etc and I'm not interested in all that when
- a window maps since its not based on an action from the user like
- clicking a window to activate is. so keep the new window out of the way
- but do focus it. */
if (activate) {
- /* This is focus stealing prevention, if a user_time has been set */
- if (self->user_time == CurrentTime ||
- self->user_time > client_last_user_time)
+ /* This is focus stealing prevention */
+ ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n",
+ self->window, self->user_time, client_last_user_time);
+
+ /* If a nothing at all, or a parent was focused, then focus this
+ always
+ */
+ if (!focus_client || client_search_focus_parent(self) != NULL)
+ activate = TRUE;
+ else
{
- /* if using focus_delay, stop the timer now so that focus doesn't
- go moving on us */
- event_halt_focus_delay();
+ /* If time stamp is old, don't steal focus */
+ if (self->user_time && self->user_time < client_last_user_time)
+ activate = FALSE;
+ /* Don't steal focus from globally active clients.
+ I stole this idea from KWin. It seems nice.
+ */
+ if (!focus_client->can_focus && focus_client->focus_notify)
+ activate = FALSE;
+ }
- client_focus(self);
+ if (activate)
+ {
/* since focus can change the stacking orders, if we focus the
window then the standard raise it gets is not enough, we need
to queue one for after the focus change takes place */
client_raise(self);
} else {
- ob_debug("Focus stealing prevention activated for %s\n",
- self->title);
+ ob_debug("Focus stealing prevention activated for %s with time %u "
+ "(last time %u)\n",
+ self->title, self->user_time, client_last_user_time);
/* if the client isn't focused, then hilite it so the user
knows it is there */
client_hilite(self, TRUE);
}
}
+ else {
+ /* This may look rather odd. Well it's because new windows are added
+ to the stacking order non-intrusively. If we're not going to focus
+ the new window or hilite it, then we raise it to the top. This will
+ take affect for things that don't get focused like splash screens.
+ Also if you don't have focus_new enabled, then it's going to get
+ raised to the top. Legacy begets legacy I guess?
+ */
+ client_raise(self);
+ }
+
+ /* this has to happen before we try focus the window, but we want it to
+ happen after the client's stacking has been determined or it looks bad
+ */
+ client_showhide(self);
+
+ /* use client_focus instead of client_activate cuz client_activate does
+ stuff like switch desktops etc and I'm not interested in all that when
+ a window maps since its not based on an action from the user like
+ clicking a window to activate it. so keep the new window out of the way
+ but do focus it. */
+ if (activate) {
+ /* if using focus_delay, stop the timer now so that focus doesn't
+ go moving on us */
+ event_halt_focus_delay();
+ client_focus(self);
+ }
/* client_activate does this but we aret using it so we have to do it
here as well */
work right (eg tsclient). */
client_update_transient_for(self);
client_get_type(self);/* this can change the mwmhints for special cases */
+ client_get_state(self);
client_update_transient_for(self);
client_update_wmhints(self);
desktop is not specified */
client_get_shaped(self);
- client_get_state(self);
+ client_get_layer(self); /* if layer hasn't been specified, get it from
+ other sources if possible */
{
/* a couple type-based defaults for new windows */
}
}
+static void client_get_layer(ObClient *self)
+{
+ if (!(self->above || self->below)) {
+ if (self->group) {
+ /* apply stuff from the group */
+ GSList *it;
+ gint layer = -2;
+
+ for (it = self->group->members; it; it = g_slist_next(it)) {
+ ObClient *c = it->data;
+ if (c != self && !client_search_transient(self, c) &&
+ client_normal(self) && client_normal(c))
+ {
+ layer = MAX(layer,
+ (c->above ? 1 : (c->below ? -1 : 0)));
+ }
+ }
+ switch (layer) {
+ case -1:
+ self->below = TRUE;
+ break;
+ case -2:
+ case 0:
+ break;
+ case 1:
+ self->above = TRUE;
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ }
+ }
+}
+
static void client_get_state(ObClient *self)
{
guint32 *state;
g_free(state);
}
-
- if (!(self->above || self->below)) {
- if (self->group) {
- /* apply stuff from the group */
- GSList *it;
- gint layer = -2;
-
- for (it = self->group->members; it; it = g_slist_next(it)) {
- ObClient *c = it->data;
- if (c != self && !client_search_transient(self, c) &&
- client_normal(self) && client_normal(c))
- {
- layer = MAX(layer,
- (c->above ? 1 : (c->below ? -1 : 0)));
- }
- }
- switch (layer) {
- case -1:
- self->below = TRUE;
- break;
- case -2:
- case 0:
- break;
- case 1:
- self->above = TRUE;
- break;
- default:
- g_assert_not_reached();
- break;
- }
- }
- }
}
static void client_get_shaped(ObClient *self)
target = NULL;
}
-#if 0
-/* we used to do this, but it violates the ICCCM and causes problems because
- toolkits seem to set transient_for = root rather arbitrarily (eg kicker's
- config dialogs), so it is being removed. the ewmh provides other ways to
- make things transient for their group. -dana
-*/
+ /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
+
+ Setting the transient_for to Root is actually illegal, however
+ applications from time have done this to specify transient for
+ their group.
+
+ Now you can do that by being a TYPE_DIALOG and not setting
+ the transient_for hint at all on your window. But people still
+ use Root, and Kwin is very strange in this regard.
+
+ KWin 3.0 will not consider windows with transient_for set to
+ Root as transient for their group *UNLESS* they are also modal.
+ In that case, it will make them transient for the group. This
+ leads to all sorts of weird behavior from KDE apps which are
+ only tested in KWin. I'd like to follow their behavior just to
+ make this work right with KDE stuff, but that seems wrong.
+ */
if (!target && self->group) {
/* not transient to a client, see if it is transient for a
group */
- if (t == self->group->leader ||
- t == None ||
- t == RootWindow(ob_display, ob_screen))
- {
+ if (t == RootWindow(ob_display, ob_screen)) {
/* window is a transient for its group! */
target = OB_TRAN_GROUP;
}
}
-#endif
-
}
} else if (self->type == OB_CLIENT_TYPE_DIALOG && self->group) {
self->transient = TRUE;
*/
if (new_event)
client_last_user_time = time;
+
+ /*ob_debug("window 0x%x user time %u\n", self->window, time);*/
}
}
netstate[num++] = prop_atoms.ob_wm_state_undecorated;
PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
- client_calc_layer(self);
-
if (self->frame)
frame_adjust_state(self->frame);
}
}
static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
- ObStackingLayer l, gboolean raised)
+ ObStackingLayer min, gboolean raised)
{
ObStackingLayer old, own;
GSList *it;
old = self->layer;
own = calc_layer(self);
- self->layer = l > own ? l : own;
+ self->layer = MAX(own, min);
for (it = self->transients; it; it = g_slist_next(it))
client_calc_layer_recursive(it->data, orig,
- l, raised ? raised : l != old);
+ self->layer,
+ raised ? raised : self->layer != old);
- if (!raised && l != old)
+ if (!raised && self->layer != old)
if (orig->frame) { /* only restack if the original window is managed */
stacking_remove(CLIENT_AS_WINDOW(self));
stacking_add(CLIENT_AS_WINDOW(self));
void client_calc_layer(ObClient *self)
{
- ObStackingLayer l;
ObClient *orig;
+ GSList *it;
orig = self;
/* transients take on the layer of their parents */
- self = client_search_top_transient(self);
-
- l = calc_layer(self);
+ it = client_search_top_transients(self);
- client_calc_layer_recursive(self, orig, l, FALSE);
+ for (; it; it = g_slist_next(it))
+ client_calc_layer_recursive(it->data, orig, 0, FALSE);
}
gboolean client_should_show(ObClient *self)
gboolean moved = FALSE, resized = FALSE;
guint fdecor = self->frame->decorations;
gboolean fhorz = self->frame->max_horz;
+ Rect desired_area = {x, y, w, h};
/* make the frame recalculate its dimentions n shit without changing
anything visible for real, this way the constraints below can work with
Rect *a;
guint i;
- i = client_monitor(self);
+ i = screen_find_monitor(&desired_area);
a = screen_physical_area_monitor(i);
x = a->x;
user = FALSE; /* ignore that increment etc shit when in fullscreen */
} else {
Rect *a;
+ guint i;
- a = screen_area_monitor(self->desktop, client_monitor(self));
+ i = screen_find_monitor(&desired_area);
+ a = screen_area_monitor(self->desktop, i);
/* set the size and position if maximized */
if (self->max_horz) {
self->fullscreen == fs) return; /* already done */
self->fullscreen = fs;
- client_change_state(self); /* change the state hints on the client,
- and adjust out layer/stacking */
+ client_change_state(self); /* change the state hints on the client */
+ client_calc_layer(self); /* and adjust out layer/stacking */
if (fs) {
if (savearea)
void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
{
+ GSList *it;
+
/* move up the transient chain as far as possible first */
- self = client_search_top_transient(self);
+ it = client_search_top_transients(self);
- client_iconify_recursive(client_search_top_transient(self),
- iconic, curdesk);
+ for (; it; it = g_slist_next(it))
+ client_iconify_recursive(it->data, iconic, curdesk);
}
void client_maximize(ObClient *self, gboolean max, gint dir, gboolean savearea)
void client_set_desktop(ObClient *self, guint target, gboolean donthide)
{
- client_set_desktop_recursive(client_search_top_transient(self),
- target, donthide);
+ GSList *it;
+
+ it = client_search_top_transients(self);
+
+ for(; it; it = g_slist_next(it))
+ client_set_desktop_recursive(it->data, target, donthide);
}
ObClient *client_search_modal_child(ObClient *self)
if (demands_attention != self->demands_attention)
client_hilite(self, demands_attention);
- client_calc_layer(self);
client_change_state(self); /* change the hint to reflect these changes */
}
ObClient *client_focus_target(ObClient *self)
{
- ObClient *child;
-
- /* if we have a modal child, then focus it, not us */
- child = client_search_modal_child(client_search_top_transient(self));
+ ObClient *child = NULL;
+
+ child = client_search_modal_child(self);
if (child) return child;
return self;
}
}
}
-void client_activate(ObClient *self, gboolean here, gboolean user)
+void client_activate(ObClient *self, gboolean here, gboolean user, Time time)
{
/* XXX do some stuff here if user is false to determine if we really want
- to activate it or not (a parent or group member is currently active) */
-
- if (client_normal(self) && screen_showing_desktop)
- screen_show_desktop(FALSE);
- if (self->iconic)
- client_iconify(self, FALSE, here);
- if (self->desktop != DESKTOP_ALL &&
- self->desktop != screen_desktop) {
- if (here)
- client_set_desktop(self, screen_desktop, FALSE);
- else
- screen_set_desktop(self->desktop);
- } else if (!self->frame->visible)
- /* if its not visible for other reasons, then don't mess
- with it */
- return;
- if (self->shaded)
- client_shade(self, FALSE);
+ to activate it or not (a parent or group member is currently
+ active)?
+ */
+ ob_debug("Want to activate window 0x%x with time %u (last time %u), "
+ "source=%s\n",
+ self->window, time, client_last_user_time,
+ (user ? "user" : "application"));
+ if (!user && time && time < client_last_user_time)
+ client_hilite(self, TRUE);
+ else {
+ if (client_normal(self) && screen_showing_desktop)
+ screen_show_desktop(FALSE);
+ if (self->iconic)
+ client_iconify(self, FALSE, here);
+ if (self->desktop != DESKTOP_ALL &&
+ self->desktop != screen_desktop) {
+ if (here)
+ client_set_desktop(self, screen_desktop, FALSE);
+ else
+ screen_set_desktop(self->desktop);
+ } else if (!self->frame->visible)
+ /* if its not visible for other reasons, then don't mess
+ with it */
+ return;
+ if (self->shaded)
+ client_shade(self, FALSE);
- client_focus(self);
+ client_focus(self);
- /* we do this an action here. this is rather important. this is because
- we want the results from the focus change to take place BEFORE we go
- about raising the window. when a fullscreen window loses focus, we need
- this or else the raise wont be able to raise above the to-lose-focus
- fullscreen window. */
- client_raise(self);
+ /* we do this an action here. this is rather important. this is because
+ we want the results from the focus change to take place BEFORE we go
+ about raising the window. when a fullscreen window loses focus, we
+ need this or else the raise wont be able to raise above the
+ to-lose-focus fullscreen window. */
+ client_raise(self);
+ }
}
void client_raise(ObClient *self)
{
- action_run_string("Raise", self);
+ action_run_string("Raise", self, CurrentTime);
}
void client_lower(ObClient *self)
{
- action_run_string("Lower", self);
+ action_run_string("Lower", self, CurrentTime);
}
gboolean client_focused(ObClient *self)
}
}
-/* Determines which physical monitor a client is on by calculating the
- area of the part of the client on each monitor. The number of the
- monitor containing the greatest area of the client is returned.*/
guint client_monitor(ObClient *self)
{
- guint i;
- guint most = 0;
- guint mostv = 0;
-
- for (i = 0; i < screen_num_monitors; ++i) {
- Rect *area = screen_physical_area_monitor(i);
- if (RECT_INTERSECTS_RECT(*area, self->frame->area)) {
- Rect r;
- guint v;
-
- RECT_SET_INTERSECTION(r, *area, self->frame->area);
- v = r.width * r.height;
-
- if (v > mostv) {
- mostv = v;
- most = i;
- }
- }
- }
- return most;
+ return screen_find_monitor(&self->frame->area);
}
-ObClient *client_search_top_transient(ObClient *self)
+GSList *client_search_top_transients(ObClient *self)
{
- /* move up the transient chain as far as possible */
- if (self->transient_for) {
- if (self->transient_for != OB_TRAN_GROUP) {
- return client_search_top_transient(self->transient_for);
- } else {
+ GSList *ret = NULL;
+
+ /* move up the direct transient chain as far as possible */
+ while (self->transient_for && self->transient_for != OB_TRAN_GROUP)
+ self = self->transient_for;
+
+ if (!self->transient_for)
+ ret = g_slist_prepend(ret, self);
+ else {
GSList *it;
g_assert(self->group);
for (it = self->group->members; it; it = g_slist_next(it)) {
ObClient *c = it->data;
- /* checking transient_for prevents infinate loops! */
- if (c != self && !c->transient_for)
- break;
+ if (!c->transient_for)
+ ret = g_slist_prepend(ret, c);
}
- if (it)
- return it->data;
- }
+
+ if (ret == NULL) /* no group parents */
+ ret = g_slist_prepend(ret, self);
}
- return self;
+ return ret;
}
ObClient *client_search_focus_parent(ObClient *self)