static void client_get_desktop(ObClient *self);
static void client_get_state(ObClient *self);
static void client_get_shaped(ObClient *self);
-static void client_get_mwm_hints(ObClient *self);
static void client_get_colormap(ObClient *self);
static void client_set_desktop_recursive(ObClient *self,
guint target,
static void client_call_notifies(ObClient *self, GSList *list);
static void client_ping_event(ObClient *self, gboolean dead);
static void client_prompt_kill(ObClient *self);
-
+static gboolean client_can_steal_focus(ObClient *self, Time steal_time,
+ Time launch_time);
void client_startup(gboolean reconfig)
{
gboolean transient = FALSE;
Rect place, *monitor;
Time launch_time, map_time;
+ guint32 user_time;
grab_server(TRUE);
ob_debug("Window type: %d\n", self->type);
ob_debug("Window group: 0x%x\n", self->group?self->group->leader:0);
+ ob_debug("Window name: %s class: %s role: %s\n", self->name, self->class, self->role);
+
+ /* per-app settings override stuff from client_get_all, and return the
+ settings for other uses too. the returned settings is a shallow copy,
+ that needs to be freed with g_free(). */
+ settings = client_get_settings_state(self);
/* now we have all of the window's information so we can set this up.
do this before creating the frame, so it can tell that we are still
time now */
grab_server(FALSE);
- /* per-app settings override stuff from client_get_all, and return the
- settings for other uses too. the returned settings is a shallow copy,
- that needs to be freed with g_free(). */
- settings = client_get_settings_state(self);
/* the session should get the last say though */
client_restore_session_state(self);
/* tell startup notification that this app started */
launch_time = sn_app_started(self->startup_id, self->class, self->name);
+ if (!PROP_GET32(self->window, net_wm_user_time, cardinal, &user_time))
+ user_time = map_time;
+
/* do this after we have a frame.. it uses the frame to help determine the
WM_STATE to apply. */
client_change_state(self);
/* this means focus=true for window is same as config_focus_new=true */
((config_focus_new || (settings && settings->focus == 1)) ||
client_search_focus_tree_full(self)) &&
+ /* NET_WM_USER_TIME 0 when mapping means don't focus */
+ (user_time != 0) &&
/* this checks for focus=false for the window */
(!settings || settings->focus != 0) &&
focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE))
ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s\n",
activate ? "yes" : "no");
if (activate) {
- gboolean raise = FALSE;
-
- /* This is focus stealing prevention */
- ob_debug_type(OB_DEBUG_FOCUS,
- "Want to focus new window 0x%x at time %u "
- "launched at %u (last user interaction time %u)\n",
- self->window, map_time, launch_time,
- event_last_user_time);
-
- if (menu_frame_visible || moveresize_in_progress) {
- activate = FALSE;
- raise = TRUE;
- ob_debug_type(OB_DEBUG_FOCUS,
- "Not focusing the window because the user is inside "
- "an Openbox menu or is move/resizing a window and "
- "we don't want to interrupt them\n");
- }
-
- /* if it's on another desktop */
- else if (!(self->desktop == screen_desktop ||
- self->desktop == DESKTOP_ALL) &&
- /* the timestamp is from before you changed desktops */
- launch_time && screen_desktop_user_time &&
- !event_time_after(launch_time, screen_desktop_user_time))
- {
- activate = FALSE;
- raise = TRUE;
- ob_debug_type(OB_DEBUG_FOCUS,
- "Not focusing the window because its on another "
- "desktop\n");
- }
- /* If something is focused, and it's not our relative... */
- else if (focus_client && client_search_focus_tree_full(self) == NULL &&
- client_search_focus_group_full(self) == NULL)
- {
- /* If the user is working in another window right now, then don't
- steal focus */
- if (event_last_user_time && launch_time &&
- event_time_after(event_last_user_time, launch_time) &&
- event_last_user_time != launch_time &&
- event_time_after(event_last_user_time,
- map_time - OB_EVENT_USER_TIME_DELAY))
- {
- activate = FALSE;
- ob_debug_type(OB_DEBUG_FOCUS,
- "Not focusing the window because the user is "
- "working in another window\n");
- }
- /* If it's a transient (and its parents aren't focused) */
- else if (client_has_parent(self)) {
- activate = FALSE;
- ob_debug_type(OB_DEBUG_FOCUS,
- "Not focusing the window because it is a "
- "transient, and its relatives aren't focused\n");
- }
- /* Don't steal focus from globally active clients.
- I stole this idea from KWin. It seems nice.
- */
- else if (!(focus_client->can_focus ||
- focus_client->focus_notify))
- {
- activate = FALSE;
- ob_debug_type(OB_DEBUG_FOCUS,
- "Not focusing the window because a globally "
- "active client has focus\n");
- }
- /* Don't move focus if it's not going to go to this window
- anyway */
- else if (client_focus_target(self) != self) {
- activate = FALSE;
- raise = TRUE;
- ob_debug_type(OB_DEBUG_FOCUS,
- "Not focusing the window because another window "
- "would get the focus anyway\n");
- }
- else if (!(self->desktop == screen_desktop ||
- self->desktop == DESKTOP_ALL))
- {
- activate = FALSE;
- raise = TRUE;
- ob_debug_type(OB_DEBUG_FOCUS,
- "Not focusing the window because it is on "
- "another desktop and no relatives are focused ");
- }
- }
+ activate = client_can_steal_focus(self, map_time, launch_time);
if (!activate) {
- ob_debug_type(OB_DEBUG_FOCUS,
- "Focus stealing prevention activated for %s at "
- "time %u (last user interaction time %u)\n",
- self->title, map_time, event_last_user_time);
- /* if the client isn't focused, then hilite it so the user
- knows it is there */
- client_hilite(self, TRUE);
- /* we may want to raise it even tho we're not activating it */
- if (raise && !client_restore_session_stacking(self))
- stacking_raise(CLIENT_AS_WINDOW(self));
+ /* if the client isn't stealing focus, then hilite it so the user
+ knows it is there, but don't do this if we're restoring from a
+ session */
+ if (!client_restore_session_stacking(self))
+ client_hilite(self, TRUE);
}
}
else {
return;
}
-
ObClient *client_fake_manage(Window window)
{
ObClient *self;
g_free(self);
}
+static gboolean client_can_steal_focus(ObClient *self, Time steal_time,
+ Time launch_time)
+{
+ gboolean steal;
+ gboolean relative_focused;
+ gboolean parent_focused;
+
+ steal = TRUE;
+
+ parent_focused = (focus_client != NULL &&
+ client_search_focus_parent(self));
+ relative_focused = (focus_client != NULL &&
+ (client_search_focus_tree_full(self) != NULL ||
+ client_search_focus_group_full(self) != NULL));
+
+ /* This is focus stealing prevention */
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Want to focus new window 0x%x at time %u "
+ "launched at %u (last user interaction time %u)\n",
+ self->window, steal_time, launch_time,
+ event_last_user_time);
+
+ /* if it's on another desktop */
+ if (!(self->desktop == screen_desktop ||
+ self->desktop == DESKTOP_ALL) &&
+ /* the timestamp is from before you changed desktops */
+ launch_time && screen_desktop_user_time &&
+ !event_time_after(launch_time, screen_desktop_user_time))
+ {
+ steal = FALSE;
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Not focusing the window because its on another "
+ "desktop\n");
+ }
+ /* If something is focused... */
+ else if (focus_client) {
+ /* If the user is working in another window right now, then don't
+ steal focus */
+ if (!parent_focused &&
+ event_last_user_time && launch_time &&
+ event_time_after(event_last_user_time, launch_time) &&
+ event_last_user_time != launch_time &&
+ event_time_after(event_last_user_time,
+ steal_time - OB_EVENT_USER_TIME_DELAY))
+ {
+ steal = FALSE;
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Not focusing the window because the user is "
+ "working in another window that is not "
+ "its parent\n");
+ }
+ /* If the new window is a transient (and its relatives aren't
+ focused) */
+ else if (client_has_parent(self) && !relative_focused) {
+ steal = FALSE;
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Not focusing the window because it is a "
+ "transient, and its relatives aren't focused\n");
+ }
+ /* Don't steal focus from globally active clients.
+ I stole this idea from KWin. It seems nice.
+ */
+ else if (!(focus_client->can_focus ||
+ focus_client->focus_notify))
+ {
+ steal = FALSE;
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Not focusing the window because a globally "
+ "active client has focus\n");
+ }
+ /* Don't move focus if it's not going to go to this window
+ anyway */
+ else if (client_focus_target(self) != self) {
+ steal = FALSE;
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Not focusing the window because another window "
+ "would get the focus anyway\n");
+ }
+ /* Don't move focus if the window is not visible on the current
+ desktop and none of its relatives are focused */
+ else if (!(self->desktop == screen_desktop ||
+ self->desktop == DESKTOP_ALL) &&
+ !relative_focused)
+ {
+ steal = FALSE;
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Not focusing the window because it is on "
+ "another desktop and no relatives are focused ");
+ }
+ }
+
+ if (!steal)
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Focus stealing prevention activated for %s at "
+ "time %u (last user interaction time %u)\n",
+ self->title, steal_time, event_last_user_time);
+ return steal;
+}
+
/*! Returns a new structure containing the per-app settings for this client.
The returned structure needs to be freed with g_free. */
static ObAppSettings *client_get_settings_state(ObClient *self)
transient windows as their children.
* * */
-
/* No change has occured */
if (oldgroup == newgroup &&
oldgtran == newgtran &&
}
}
-static void client_get_mwm_hints(ObClient *self)
+void client_get_mwm_hints(ObClient *self)
{
guint num;
guint32 *hints;
/* finally, the user can have requested no decorations, which overrides
everything (but doesnt give it a border if it doesnt have one) */
if (self->undecorated)
- self->decorations = 0;
+ self->decorations &= (config_theme_keepborder ?
+ OB_FRAME_DECOR_BORDER : 0);
/* if we don't have a titlebar, then we cannot shade! */
if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
(gint*)&w, (gint*)&h, &data);
xerror_set_ignore(FALSE);
-
if (xicon) {
if (w > 0 && h > 0) {
/* is this icon in the cache yet? */
gboolean hide = FALSE;
if (!client_should_show(self)) {
- if (self == focus_client) {
- event_cancel_all_key_grabs();
- }
-
/* We don't need to ignore enter events here.
The window can hide/iconify in 3 different ways:
1 - through an x message. in this case we ignore all enter events
self->type != OB_CLIENT_TYPE_DESKTOP);
}
-
static void client_apply_startup_state(ObClient *self,
gint x, gint y, gint w, gint h)
{
/* gets the client's position */
frame_frame_gravity(self->frame, x, y);
- /* work within the preferred sizes given by the window */
- if (!(*w == self->area.width && *h == self->area.height)) {
+ /* work within the preferred sizes given by the window, these may have
+ changed rather than it's requested width and height, so always run
+ through this code */
+ {
gint basew, baseh, minw, minh;
gint incw, inch;
gfloat minratio, maxratio;
g_assert(*h > 0);
}
-
void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
gboolean user, gboolean final, gboolean force_reply)
{
gint x, y, w, h;
g_assert(dir == 0 || dir == 1 || dir == 2);
- if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
+ if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && max) return;/* can't */
/* check if already done */
if (max) {
#define OB_KILL_RESULT_NO 0
#define OB_KILL_RESULT_YES 1
-static void client_kill_requested(ObPrompt *p, gint result, gpointer data)
+static gboolean client_kill_requested(ObPrompt *p, gint result, gpointer data)
{
ObClient *self = data;
if (result == OB_KILL_RESULT_YES)
client_kill(self);
+ return TRUE; /* call the cleanup func */
+}
+
+static void client_kill_cleanup(ObPrompt *p, gpointer data)
+{
+ ObClient *self = data;
+
+ g_assert(p == self->kill_prompt);
prompt_unref(self->kill_prompt);
self->kill_prompt = NULL;
answers[0].text = _("Cancel"); /* "no" */
answers[1].text = y; /* "yes" */
- self->kill_prompt = prompt_new(m, answers,
+ self->kill_prompt = prompt_new(m, NULL, answers,
sizeof(answers)/sizeof(answers[0]),
OB_KILL_RESULT_NO, /* default = no */
OB_KILL_RESULT_NO, /* cancel = no */
- client_kill_requested, self);
+ client_kill_requested,
+ client_kill_cleanup,
+ self);
g_free(m);
}
/* don't allow focused windows to hilite */
self->demands_attention = hilite && !client_focused(self);
if (self->frame != NULL) { /* if we're mapping, just set the state */
- if (self->demands_attention)
+ if (self->demands_attention) {
frame_flash_start(self->frame);
+
+ /* if the window is on another desktop then raise it and make it
+ the most recently used window */
+ if (self->desktop != screen_desktop &&
+ self->desktop != DESKTOP_ALL)
+ {
+ stacking_raise(CLIENT_AS_WINDOW(self));
+ focus_order_to_top(self);
+ }
+ }
else
frame_flash_stop(self->frame);
client_change_state(self);
return NULL;
}
+static gboolean client_validate_unmap(ObClient *self, int n)
+{
+ XEvent e;
+ gboolean ret = TRUE;
+
+ if (XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
+ if (n < self->ignore_unmaps) // ignore this one, but look for more
+ ret = client_validate_unmap(self, n+1);
+ else
+ ret = FALSE; // the window is going to become unmanaged
+
+ /* put them back on the event stack so they end up in the same order */
+ XPutBackEvent(ob_display, &e);
+ }
+
+ return ret;
+}
+
gboolean client_validate(ObClient *self)
{
XEvent e;
XSync(ob_display, FALSE); /* get all events on the server */
- if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
- XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
+ if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e)) {
XPutBackEvent(ob_display, &e);
return FALSE;
}
+ if (!client_validate_unmap(self, 0))
+ return FALSE;
+
return TRUE;
}
go moving on us */
event_halt_focus_delay();
- event_cancel_all_key_grabs();
-
xerror_set_ignore(TRUE);
xerror_occured = FALSE;
client_focus(self);
}
-/* this function exists to map to the client_activate message in the ewmh,
- the user arg is unused because nobody uses it correctly anyway. */
-void client_activate(ObClient *self, gboolean here, gboolean raise,
+/* this function exists to map to the net_active_window message in the ewmh */
+void client_activate(ObClient *self, gboolean desktop, gboolean raise,
gboolean unshade, gboolean user)
{
- client_present(self, here, raise, unshade);
+ if ((user && (desktop ||
+ self->desktop == DESKTOP_ALL ||
+ self->desktop == screen_desktop)) ||
+ client_can_steal_focus(self, event_curtime, CurrentTime))
+ {
+ client_present(self, FALSE, raise, unshade);
+ }
+ else
+ client_hilite(self, TRUE);
}
static void client_bring_windows_recursive(ObClient *self,
return NULL;
}
+ObClient *client_search_focus_parent_full(ObClient *self)
+{
+ GSList *it;
+ ObClient *ret = NULL;
+
+ for (it = self->parents; it; it = g_slist_next(it)) {
+ if (client_focused(it->data))
+ ret = it->data;
+ else
+ ret = client_search_focus_parent_full(it->data);
+ if (ret) break;
+ }
+ return ret;
+}
+
ObClient *client_search_parent(ObClient *self, ObClient *search)
{
GSList *it;
if (my_head <= head + 1)
skip_head = TRUE;
/* check if our window's tail is past the tail of this window */
- if (my_head + my_size - 1 < tail)
+ if (my_head + my_size - 1 <= tail)
skip_tail = TRUE;
/* check if the head of this window is closer than the previously
chosen edge (take into account that the previously chosen
if (my_head >= head - 1)
skip_head = TRUE;
/* check if our window's tail is past the tail of this window */
- if (my_head - my_size + 1 > tail)
+ if (my_head - my_size + 1 >= tail)
skip_tail = TRUE;
/* check if the head of this window is closer than the previously
chosen edge (take into account that the previously chosen
switch (side) {
case OB_DIRECTION_EAST:
head = RECT_RIGHT(self->frame->area) +
- (self->size_inc.width - 1) * (grow ? 1 : -1);
+ (self->size_inc.width - 1) * (grow ? 1 : 0);
e_start = RECT_TOP(self->frame->area);
e_size = self->frame->area.height;
dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
break;
case OB_DIRECTION_WEST:
head = RECT_LEFT(self->frame->area) -
- (self->size_inc.width - 1) * (grow ? 1 : -1);
+ (self->size_inc.width - 1) * (grow ? 1 : 0);
e_start = RECT_TOP(self->frame->area);
e_size = self->frame->area.height;
dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
break;
case OB_DIRECTION_NORTH:
head = RECT_TOP(self->frame->area) -
- (self->size_inc.height - 1) * (grow ? 1 : -1);
+ (self->size_inc.height - 1) * (grow ? 1 : 0);
e_start = RECT_LEFT(self->frame->area);
e_size = self->frame->area.width;
dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
break;
case OB_DIRECTION_SOUTH:
head = RECT_BOTTOM(self->frame->area) +
- (self->size_inc.height - 1) * (grow ? 1 : -1);
+ (self->size_inc.height - 1) * (grow ? 1 : 0);
e_start = RECT_LEFT(self->frame->area);
e_size = self->frame->area.width;
dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;