/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
client.c for the Openbox window manager
- Copyright (c) 2004 Mikael Magnusson
+ Copyright (c) 2006 Mikael Magnusson
Copyright (c) 2003 Ben Jansens
This program is free software; you can redistribute it and/or modify
gpointer data;
} Destructor;
-GList *client_list = NULL;
-GSList *client_destructors = NULL;
+GList *client_list = NULL;
+
+static GSList *client_destructors = NULL;
+static Time client_last_user_time = CurrentTime;
static void client_get_all(ObClient *self);
static void client_toggle_border(ObClient *self, gboolean show);
static void client_apply_startup_state(ObClient *self);
static void client_restore_session_state(ObClient *self);
static void client_restore_session_stacking(ObClient *self);
-static void client_urgent_notify(ObClient *self);
void client_startup(gboolean reconfig)
{
&& !strcmp(app->name, client->name))
) {
ob_debug("Window matching: %s\n", app->name);
- /* Match if no role was specified in the per app setting, or if the string
- * matches the beginning of the role, since apps like to set the role to
- * things like browser-window-23c4b2f */
- if (!app->role || !strncmp(app->role, client->role, strlen(app->role)))
+ /* Match if no role was specified in the per app setting, or if the
+ * string matches the beginning of the role, since apps like to set
+ * the role to things like browser-window-23c4b2f */
+ if (!app->role
+ || !strncmp(app->role, client->role, strlen(app->role)))
return app;
}
/* check if it has already been unmapped by the time we started mapping
the grab does a sync so we don't have to here */
if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
- XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e)) {
+ XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e))
+ {
XPutBackEvent(ob_display, &e);
grab_server(FALSE);
/* make sure it isn't an override-redirect window */
if (!XGetWindowAttributes(ob_display, window, &attrib) ||
- attrib.override_redirect) {
+ attrib.override_redirect)
+ {
grab_server(FALSE);
return; /* don't manage it */
}
/* is the window a docking app */
if ((wmhint = XGetWMHints(ob_display, window))) {
if ((wmhint->flags & StateHint) &&
- wmhint->initial_state == WithdrawnState) {
+ wmhint->initial_state == WithdrawnState)
+ {
dock_add(window, wmhint);
grab_server(FALSE);
XFree(wmhint);
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);
- sn_app_started(self->class);
+ {
+ 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! */
XChangeSaveSet(ob_display, window, SetModeInsert);
/* create the decoration frame for the client window */
- self->frame = frame_new();
+ self->frame = frame_new(self);
frame_grab_client(self->frame, self);
/* get and set application level settings */
settings = get_settings(self);
+ stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
+ client_restore_session_stacking(self);
+
if (settings) {
/* Don't worry, we won't actually both shade and undecorate the
* window when push comes to shove. */
if (settings->shade != -1)
- client_shade(self, settings->shade);
+ client_shade(self, !!settings->shade);
if (settings->decor != -1)
client_set_undecorated(self, !settings->decor);
if (settings->iconic != -1)
- client_iconify(self, settings->iconic, FALSE);
+ client_iconify(self, !!settings->iconic, FALSE);
if (settings->skip_pager != -1) {
self->skip_pager = !!settings->skip_pager;
client_change_state(self);
if (settings->fullscreen != -1)
client_fullscreen(self, !!settings->fullscreen, TRUE);
- if (settings->desktop < screen_num_desktops)
- client_set_desktop(self, settings->desktop, FALSE);
+ if (settings->desktop < screen_num_desktops
+ || settings->desktop == DESKTOP_ALL)
+ client_set_desktop(self, settings->desktop, TRUE);
if (settings->layer > -2 && settings->layer < 2)
client_set_layer(self, settings->layer);
}
- stacking_add(CLIENT_AS_WINDOW(self));
- client_restore_session_stacking(self);
-
/* focus the new window? */
if (ob_state() != OB_STATE_STARTING &&
- (config_focus_new || client_search_focus_parent(self)) ||
- (settings && settings->focus == TRUE) &&
+ /* this means focus=true for window is same as config_focus_new=true */
+ ((config_focus_new || (settings && settings->focus == 1)) ||
+ client_search_focus_parent(self)) &&
+ /* this checks for focus=false for the window */
+ (!settings || settings->focus != 0) &&
/* note the check against Type_Normal/Dialog, not client_normal(self),
which would also include other types. in this case we want more
strict rules for focus */
(self->type == OB_CLIENT_TYPE_NORMAL ||
self->type == OB_CLIENT_TYPE_DIALOG))
- {
+ {
activate = TRUE;
#if 0
if (self->desktop != screen_desktop) {
if (ob_state() == OB_STATE_RUNNING) {
gint x = self->area.x, ox = x;
gint y = self->area.y, oy = y;
+ gboolean transient;
- place_client(self, &x, &y, settings);
+ transient = place_client(self, &x, &y, settings);
/* make sure the window is visible. */
client_find_onscreen(self, &x, &y,
off-screen and on xinerama divides (ie,
it is up to the placement routines to avoid
the xinerama divides) */
- ((self->positioned & PPosition) &&
- !(self->positioned & USPosition)) &&
- client_normal(self) &&
- !self->session);
+ transient ||
+ (((self->positioned & PPosition) &&
+ !(self->positioned & USPosition)) &&
+ client_normal(self) &&
+ !self->session));
if (x != ox || y != oy)
client_move(self, x, y);
}
keyboard_grab_for_client(self, TRUE);
mouse_grab_for_client(self, TRUE);
+ if (activate) {
+ /* This is focus stealing prevention, if a user_time has been set */
+ 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 (!self->user_time || self->user_time >= client_last_user_time)
+ {
+ /* 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 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);
+
+ /* don't focus it ! (focus stealing prevention) */
+ activate = FALSE;
+ }
+ }
+ 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 is. so keep the new window out of the way
+ 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 */
+ /* if using focus_delay, stop the timer now so that focus doesn't
+ go moving on us */
event_halt_focus_delay();
-
client_focus(self);
- /* 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);
}
/* client_activate does this but we aret using it so we have to do it
g_hash_table_insert(window_map, &self->window, self);
/* this has to happen after we're in the client_list */
- screen_update_areas();
+ if (STRUT_EXISTS(self->strut))
+ screen_update_areas();
/* update the list hints */
client_set_list();
/* once the client is out of the list, update the struts to remove it's
influence */
- screen_update_areas();
+ if (STRUT_EXISTS(self->strut))
+ screen_update_areas();
for (it = client_destructors; it; it = g_slist_next(it)) {
Destructor *d = it->data;
grab_pointer(FALSE, OB_CURSOR_NONE);
}
-static void client_urgent_notify(ObClient *self)
-{
- if (self->urgent)
- frame_flash_start(self->frame);
- else
- frame_flash_stop(self->frame);
-}
-
static void client_restore_session_state(ObClient *self)
{
GList *it;
/* XXX watch for xinerama dead areas */
/* This makes sure windows aren't entirely outside of the screen so you
- * can't see them at all */
+ can't see them at all.
+ It makes sure 10% of the window is on the screen at least. At don't let
+ it move itself off the top of the screen, which would hide the titlebar
+ on you. (The user can still do this if they want too, it's only limiting
+ the application.
+ */
if (client_normal(self)) {
a = screen_area(self->desktop);
- if (!self->strut.right && *x >= a->x + a->width - 1)
- *x = a->x + a->width - self->frame->area.width;
- if (!self->strut.bottom && *y >= a->y + a->height - 1)
- *y = a->y + a->height - self->frame->area.height;
- if (!self->strut.left && *x + self->frame->area.width - 1 < a->x)
- *x = a->x;
- if (!self->strut.top && *y + self->frame->area.height - 1 < a->y)
- *y = a->y;
+ if (!self->strut.right &&
+ *x + self->frame->area.width/10 >= a->x + a->width - 1)
+ *x = a->x + a->width - self->frame->area.width/10;
+ if (!self->strut.bottom &&
+ *y + self->frame->area.height/10 >= a->y + a->height - 1)
+ *y = a->y + a->height - self->frame->area.height/10;
+ if (!self->strut.left && *x + self->frame->area.width*9/10 - 1 < a->x)
+ *x = a->x - self->frame->area.width*9/10;
+ if (!self->strut.top && *y + self->frame->area.height*9/10 - 1 < a->y)
+ *y = a->y - self->frame->area.width*9/10;
}
/* This here doesn't let windows even a pixel outside the screen,
/* avoid the xinerama monitor divide while we're at it,
* remember to fix the placement stuff to avoid it also and
* then remove this XXX */
- a = screen_physical_area_monitor(client_monitor(self));
- /* dont let windows map/move into the strut unless they
+ a = screen_area_monitor(self->desktop, client_monitor(self));
+ /* dont let windows map into the strut unless they
are bigger than the available area */
if (w <= a->width) {
if (!self->strut.left && *x < a->x) *x = a->x;
static void client_get_all(ObClient *self)
{
client_get_area(self);
- client_update_transient_for(self);
- client_update_wmhints(self);
- client_get_startup_id(self);
- client_get_desktop(self);
- client_get_shaped(self);
-
client_get_mwm_hints(self);
- client_get_type(self);/* this can change the mwmhints for special cases */
/* The transient hint is used to pick a type, but the type can also affect
- transiency (dialogs are always made transients). This is Havoc's idea,
- but it is needed to make some apps work right (eg tsclient). */
+ transiency (dialogs are always made transients of their group if they
+ have one). This is Havoc's idea, but it is needed to make some apps
+ work right (eg tsclient). */
+ client_update_transient_for(self);
+ client_get_type(self);/* this can change the mwmhints for special cases */
client_update_transient_for(self);
+ client_update_wmhints(self);
+ client_get_startup_id(self);
+ client_get_desktop(self);/* uses transient data/group/startup id if a
+ desktop is not specified */
+ client_get_shaped(self);
+
client_get_state(self);
{
client_update_sm_client_id(self);
client_update_strut(self);
client_update_icons(self);
+ client_update_user_time(self, FALSE);
}
static void client_get_startup_id(ObClient *self)
self->above = TRUE;
else if (state[i] == prop_atoms.net_wm_state_below)
self->below = TRUE;
+ else if (state[i] == prop_atoms.net_wm_state_demands_attention)
+ self->demands_attention = TRUE;
else if (state[i] == prop_atoms.ob_wm_state_undecorated)
self->undecorated = TRUE;
}
a dockapp, for example */
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
+*/
if (!target && self->group) {
/* not transient to a client, see if it is transient for a
group */
target = OB_TRAN_GROUP;
}
}
+#endif
+
}
} else if (self->type == OB_CLIENT_TYPE_DIALOG && self->group) {
self->transient = TRUE;
if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
(self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
+ {
/* if the mwm hints request no handle or title, then all
- decorations are disabled */
- self->decorations = config_theme_keepborder ? OB_FRAME_DECOR_BORDER : 0;
+ decorations are disabled, but keep the border if that's
+ specified */
+ if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
+ self->decorations = OB_FRAME_DECOR_BORDER;
+ else
+ self->decorations = 0;
+ }
}
}
void client_update_wmhints(ObClient *self)
{
XWMHints *hints;
- gboolean ur = FALSE;
GSList *it;
/* assume a window takes input if it doesnt specify */
if (hints->flags & StateHint)
self->iconic = hints->initial_state == IconicState;
- if (hints->flags & XUrgencyHint)
- ur = TRUE;
-
if (!(hints->flags & WindowGroupHint))
hints->window_group = None;
XFree(hints);
}
-
- if (ur != self->urgent) {
- self->urgent = ur;
- /* fire the urgent callback if we're mapped, otherwise, wait until
- after we're mapped */
- if (self->frame)
- client_urgent_notify(self);
- }
}
void client_update_title(ObClient *self)
for (it = client_list; it; it = g_list_next(it))
if (it->data != self) {
ObClient *c = it->data;
- if (0 == strncmp(c->title, data, strlen(data)))
- nums |= 1 << c->title_count;
+
+ if (c->title_count == 1) {
+ if (!strcmp(c->title, data))
+ nums |= 1 << c->title_count;
+ } else {
+ size_t len;
+ gchar *end;
+
+ /* find the beginning of our " - [%u]", this relies on
+ that syntax being used */
+ end = strrchr(c->title, '-') - 1;
+ len = end - c->title;
+ if (!strncmp(c->title, data, len))
+ nums |= 1 << c->title_count;
+ }
}
/* find first free number */
for (i = 1; i <= 32; ++i)
* We don't need to check for config_title_number here since title_count
* is not set above 1 then. */
if (read_title && self->title_count > 1) {
- gchar *vdata, *ndata;
- ndata = g_strdup_printf(" - [%u]", self->title_count);
- vdata = g_strconcat(data, ndata, NULL);
- g_free(ndata);
+ gchar *newdata;
+ newdata = g_strdup_printf("%s - [%u]", data, self->title_count);
g_free(data);
- data = vdata;
+ data = newdata;
}
PROP_SETS(self->window, net_wm_visible_icon_name, data);
g_assert(i <= num);
}
- g_free(data);
- } else if (PROP_GETA32(self->window, kwm_win_icon,
- kwm_win_icon, &data, &num)) {
- if (num == 2) {
- self->nicons++;
- self->icons = g_new(ObClientIcon, self->nicons);
- xerror_set_ignore(TRUE);
- if (!RrPixmapToRGBA(ob_rr_inst,
- data[0], data[1],
- &self->icons[self->nicons-1].width,
- &self->icons[self->nicons-1].height,
- &self->icons[self->nicons-1].data)) {
- g_free(&self->icons[self->nicons-1]);
- self->nicons--;
- }
- xerror_set_ignore(FALSE);
- }
g_free(data);
} else {
XWMHints *hints;
frame_adjust_icon(self->frame);
}
+void client_update_user_time(ObClient *self, gboolean new_event)
+{
+ guint32 time;
+
+ if (PROP_GET32(self->window, net_wm_user_time, cardinal, &time)) {
+ self->user_time = time;
+ /* we set this every time, not just when it grows, because in practice
+ sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
+ backward we don't want all windows to stop focusing. we'll just
+ assume noone is setting times older than the last one, cuz that
+ would be pretty stupid anyways
+ However! This is called when a window is mapped to get its user time
+ but it's an old number, it's not changing it from new user
+ interaction, so in that case, don't change the last user time.
+ */
+ if (new_event)
+ client_last_user_time = time;
+
+ /*ob_debug("window 0x%x user time %u\n", self->window, time);*/
+ }
+}
+
static void client_change_state(ObClient *self)
{
gulong state[2];
netstate[num++] = prop_atoms.net_wm_state_above;
if (self->below)
netstate[num++] = prop_atoms.net_wm_state_below;
+ if (self->demands_attention)
+ netstate[num++] = prop_atoms.net_wm_state_demands_attention;
if (self->undecorated)
netstate[num++] = prop_atoms.ob_wm_state_undecorated;
PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
self->shaded = FALSE;
client_shade(self, TRUE);
}
- if (self->urgent)
- client_urgent_notify(self);
+ if (self->demands_attention) {
+ self->demands_attention = FALSE;
+ client_hilite(self, TRUE);
+ }
if (self->max_vert && self->max_horz) {
self->max_vert = self->max_horz = FALSE;
if (changed) {
client_change_state(self);
client_showhide(self);
- screen_update_areas();
+ if (STRUT_EXISTS(self->strut))
+ screen_update_areas();
}
/* iconify all transients */
ce.xclient.window = self->window;
ce.xclient.format = 32;
ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
- ce.xclient.data.l[1] = event_lasttime;
+ ce.xclient.data.l[1] = event_curtime;
ce.xclient.data.l[2] = 0l;
ce.xclient.data.l[3] = 0l;
ce.xclient.data.l[4] = 0l;
XKillClient(ob_display, self->window);
}
+void client_hilite(ObClient *self, gboolean hilite)
+{
+ /* don't allow focused windows to hilite */
+ self->demands_attention = hilite && !client_focused(self);
+ if (self->demands_attention)
+ frame_flash_start(self->frame);
+ else
+ frame_flash_stop(self->frame);
+ client_change_state(self);
+}
+
void client_set_desktop_recursive(ObClient *self,
guint target, gboolean donthide)
{
/* raise if it was not already on the desktop */
if (old != DESKTOP_ALL)
client_raise(self);
- screen_update_areas();
+ if (STRUT_EXISTS(self->strut))
+ screen_update_areas();
/* add to the new desktop(s) */
if (config_focus_new)
gboolean max_vert = self->max_vert;
gboolean modal = self->modal;
gboolean iconic = self->iconic;
+ gboolean demands_attention = self->demands_attention;
gint i;
if (!(action == prop_atoms.net_wm_state_add ||
else if (state == prop_atoms.net_wm_state_below)
action = self->below ? prop_atoms.net_wm_state_remove :
prop_atoms.net_wm_state_add;
+ else if (state == prop_atoms.net_wm_state_demands_attention)
+ action = self->demands_attention ?
+ prop_atoms.net_wm_state_remove :
+ prop_atoms.net_wm_state_add;
else if (state == prop_atoms.ob_wm_state_undecorated)
action = undecorated ? prop_atoms.net_wm_state_remove :
prop_atoms.net_wm_state_add;
} else if (state == prop_atoms.net_wm_state_below) {
self->above = FALSE;
self->below = TRUE;
+ } else if (state == prop_atoms.net_wm_state_demands_attention) {
+ demands_attention = TRUE;
} else if (state == prop_atoms.ob_wm_state_undecorated) {
undecorated = TRUE;
}
self->above = FALSE;
} else if (state == prop_atoms.net_wm_state_below) {
self->below = FALSE;
+ } else if (state == prop_atoms.net_wm_state_demands_attention) {
+ demands_attention = FALSE;
} else if (state == prop_atoms.ob_wm_state_undecorated) {
undecorated = FALSE;
}
if (iconic != self->iconic)
client_iconify(self, iconic, FALSE);
+ 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 */
}
#799. So now it is RevertToNone again.
*/
XSetInputFocus(ob_display, self->window, RevertToNone,
- event_lasttime);
+ event_curtime);
}
if (self->focus_notify) {
ce.xclient.window = self->window;
ce.xclient.format = 32;
ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
- ce.xclient.data.l[1] = event_lasttime;
+ ce.xclient.data.l[1] = event_curtime;
ce.xclient.data.l[2] = 0l;
ce.xclient.data.l[3] = 0l;
ce.xclient.data.l[4] = 0l;
#ifdef DEBUG_FOCUS
ob_debug("%sively focusing %lx at %d\n",
(self->can_focus ? "act" : "pass"),
- self->window, (gint) event_lasttime);
+ self->window, (gint) event_curtime);
#endif
/* Cause the FocusIn to come back to us. Important for desktop switches,
}
}
-void client_activate(ObClient *self, gboolean here)
+void client_activate(ObClient *self, gboolean here, gboolean user, Time time)
{
- /* This check is for the client_list_menu trying to activate
- * a closed client. */
- if (!g_list_find(client_list, self)) return;
- 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);
+ /* 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)?
+ */
+ 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)
continue;
if(cur->iconic)
continue;
- if(client_focus_target(cur) == cur &&
- !(cur->can_focus || cur->focus_notify))
+ if(!(client_focus_target(cur) == cur &&
+ client_can_focus(cur)))
continue;
/* find the centre coords of this window, from the
&self->sm_client_id);
}
+#define WANT_EDGE(cur, c) \
+ if(cur == c) \
+ continue; \
+ if(!client_normal(cur)) \
+ continue; \
+ if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
+ continue; \
+ if(cur->iconic) \
+ continue; \
+ if(cur->layer < c->layer && !config_resist_layers_below) \
+ continue;
+
+#define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
+ if ((his_edge_start >= my_edge_start && \
+ his_edge_start <= my_edge_end) || \
+ (my_edge_start >= his_edge_start && \
+ my_edge_start <= his_edge_end)) \
+ dest = his_offset;
+
/* finds the nearest edge in the given direction from the current client
* note to self: the edge is the -frame- edge (the actual one), not the
* client edge.
*/
-gint client_directional_edge_search(ObClient *c, ObDirection dir)
+gint client_directional_edge_search(ObClient *c, ObDirection dir, gboolean hang)
{
gint dest, monitor_dest;
gint my_edge_start, my_edge_end, my_offset;
case OB_DIRECTION_NORTH:
my_edge_start = c->frame->area.x;
my_edge_end = c->frame->area.x + c->frame->area.width;
- my_offset = c->frame->area.y;
+ my_offset = c->frame->area.y + (hang ? c->frame->area.height : 0);
/* default: top of screen */
- dest = a->y;
- monitor_dest = monitor->y;
+ dest = a->y + (hang ? c->frame->area.height : 0);
+ monitor_dest = monitor->y + (hang ? c->frame->area.height : 0);
/* if the monitor edge comes before the screen edge, */
/* use that as the destination instead. (For xinerama) */
if (monitor_dest != dest && my_offset > monitor_dest)
gint his_edge_start, his_edge_end, his_offset;
ObClient *cur = it->data;
- if(cur == c)
- continue;
- if(!client_normal(cur))
- continue;
- if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
- continue;
- if(cur->iconic)
- continue;
- if(cur->layer < c->layer && !config_resist_layers_below)
- continue;
+ WANT_EDGE(cur, c)
his_edge_start = cur->frame->area.x;
his_edge_end = cur->frame->area.x + cur->frame->area.width;
- his_offset = cur->frame->area.y + cur->frame->area.height;
+ his_offset = cur->frame->area.y +
+ (hang ? 0 : cur->frame->area.height);
if(his_offset + 1 > my_offset)
continue;
if(his_offset < dest)
continue;
-
- if(his_edge_start >= my_edge_start &&
- his_edge_start <= my_edge_end)
- dest = his_offset;
-
- if(my_edge_start >= his_edge_start &&
- my_edge_start <= his_edge_end)
- dest = his_offset;
+ HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
}
break;
case OB_DIRECTION_SOUTH:
my_edge_start = c->frame->area.x;
my_edge_end = c->frame->area.x + c->frame->area.width;
- my_offset = c->frame->area.y + c->frame->area.height;
+ my_offset = c->frame->area.y + (hang ? 0 : c->frame->area.height);
/* default: bottom of screen */
- dest = a->y + a->height;
- monitor_dest = monitor->y + monitor->height;
+ dest = a->y + a->height - (hang ? c->frame->area.height : 0);
+ monitor_dest = monitor->y + monitor->height -
+ (hang ? c->frame->area.height : 0);
/* if the monitor edge comes before the screen edge, */
/* use that as the destination instead. (For xinerama) */
if (monitor_dest != dest && my_offset < monitor_dest)
gint his_edge_start, his_edge_end, his_offset;
ObClient *cur = it->data;
- if(cur == c)
- continue;
- if(!client_normal(cur))
- continue;
- if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
- continue;
- if(cur->iconic)
- continue;
- if(cur->layer < c->layer && !config_resist_layers_below)
- continue;
+ WANT_EDGE(cur, c)
his_edge_start = cur->frame->area.x;
his_edge_end = cur->frame->area.x + cur->frame->area.width;
- his_offset = cur->frame->area.y;
+ his_offset = cur->frame->area.y +
+ (hang ? cur->frame->area.height : 0);
if(his_offset - 1 < my_offset)
if(his_offset > dest)
continue;
-
- if(his_edge_start >= my_edge_start &&
- his_edge_start <= my_edge_end)
- dest = his_offset;
-
- if(my_edge_start >= his_edge_start &&
- my_edge_start <= his_edge_end)
- dest = his_offset;
+ HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
}
break;
case OB_DIRECTION_WEST:
my_edge_start = c->frame->area.y;
my_edge_end = c->frame->area.y + c->frame->area.height;
- my_offset = c->frame->area.x;
+ my_offset = c->frame->area.x + (hang ? c->frame->area.width : 0);
/* default: leftmost egde of screen */
- dest = a->x;
- monitor_dest = monitor->x;
+ dest = a->x + (hang ? c->frame->area.width : 0);
+ monitor_dest = monitor->x + (hang ? c->frame->area.width : 0);
/* if the monitor edge comes before the screen edge, */
/* use that as the destination instead. (For xinerama) */
if (monitor_dest != dest && my_offset > monitor_dest)
gint his_edge_start, his_edge_end, his_offset;
ObClient *cur = it->data;
- if(cur == c)
- continue;
- if(!client_normal(cur))
- continue;
- if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
- continue;
- if(cur->iconic)
- continue;
- if(cur->layer < c->layer && !config_resist_layers_below)
- continue;
+ WANT_EDGE(cur, c)
his_edge_start = cur->frame->area.y;
his_edge_end = cur->frame->area.y + cur->frame->area.height;
- his_offset = cur->frame->area.x + cur->frame->area.width;
+ his_offset = cur->frame->area.x +
+ (hang ? 0 : cur->frame->area.width);
if(his_offset + 1 > my_offset)
continue;
-
+
if(his_offset < dest)
continue;
-
- if(his_edge_start >= my_edge_start &&
- his_edge_start <= my_edge_end)
- dest = his_offset;
-
- if(my_edge_start >= his_edge_start &&
- my_edge_start <= his_edge_end)
- dest = his_offset;
-
+ HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
}
- break;
+ break;
case OB_DIRECTION_EAST:
my_edge_start = c->frame->area.y;
my_edge_end = c->frame->area.y + c->frame->area.height;
- my_offset = c->frame->area.x + c->frame->area.width;
+ my_offset = c->frame->area.x + (hang ? 0 : c->frame->area.width);
/* default: rightmost edge of screen */
- dest = a->x + a->width;
- monitor_dest = monitor->x + monitor->width;
+ dest = a->x + a->width - (hang ? c->frame->area.width : 0);
+ monitor_dest = monitor->x + monitor->width -
+ (hang ? c->frame->area.width : 0);
/* if the monitor edge comes before the screen edge, */
/* use that as the destination instead. (For xinerama) */
if (monitor_dest != dest && my_offset < monitor_dest)
gint his_edge_start, his_edge_end, his_offset;
ObClient *cur = it->data;
- if(cur == c)
- continue;
- if(!client_normal(cur))
- continue;
- if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
- continue;
- if(cur->iconic)
- continue;
- if(cur->layer < c->layer && !config_resist_layers_below)
- continue;
+ WANT_EDGE(cur, c)
his_edge_start = cur->frame->area.y;
his_edge_end = cur->frame->area.y + cur->frame->area.height;
- his_offset = cur->frame->area.x;
+ his_offset = cur->frame->area.x +
+ (hang ? cur->frame->area.width : 0);
if(his_offset - 1 < my_offset)
continue;
if(his_offset > dest)
continue;
-
- if(his_edge_start >= my_edge_start &&
- his_edge_start <= my_edge_end)
- dest = his_offset;
-
- if(my_edge_start >= his_edge_start &&
- my_edge_start <= his_edge_end)
- dest = his_offset;
+ HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
}
break;
case OB_DIRECTION_NORTHEAST: