static void client_prompt_kill(ObClient *self);
static gboolean client_can_steal_focus(ObClient *self,
gboolean allow_other_desktop,
+ gboolean request_from_user,
Time steal_time, Time launch_time);
+static void client_setup_default_decor_and_functions(ObClient *self);
+static void client_setup_decor_undecorated(ObClient *self);
void client_startup(gboolean reconfig)
{
}
}
+void client_remove_destroy_notify_data(ObClientCallback func, gpointer data)
+{
+ GSList *it;
+
+ for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
+ ClientCallback *d = it->data;
+ if (d->func == func && d->data == data) {
+ g_slice_free(ClientCallback, d);
+ client_destroy_notifies =
+ g_slist_delete_link(client_destroy_notifies, it);
+ break;
+ }
+ }
+}
+
void client_set_list(void)
{
Window *windows, *win_it;
Time launch_time;
guint32 user_time;
gboolean obplaced;
+ gulong ignore_start;
ob_debug("Managing window: 0x%lx", window);
ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
ob_debug("Window name: %s class: %s role: %s title: %s",
self->name, self->class, self->role, self->title);
+ ob_debug("Window group name: %s group class: %s",
+ self->group_name, self->group_class);
/* 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);
+
+ /* the per-app settings/session may have changed the decorations for
+ the window, so we setup decorations for that here. this is a special
+ case because we want to place the window according to these decoration
+ changes.
+ we do this before setting up the frame so that it will reflect the
+ decorations of the window as it will be placed on screen.
+ */
+ client_setup_decor_undecorated(self);
+
/* specify that if we exit, the window should not be destroyed and
should be reparented back to root automatically, unless we are managing
an internal ObPrompt window */
time now */
grab_server(FALSE);
- /* the session should get the last say though */
- client_restore_session_state(self);
+ /* this needs to occur once we have a frame, since it sets a property on
+ the frame */
+ client_update_opacity(self);
+
+ /* don't put helper/modal windows on a different desktop if they are
+ related to the focused window. */
+ if (!screen_compare_desktops(self->desktop, screen_desktop) &&
+ focus_client && client_search_transient(focus_client, self) &&
+ (client_helper(self) || self->modal))
+ {
+ self->desktop = screen_desktop;
+ }
/* tell startup notification that this app started */
launch_time = sn_app_started(self->startup_id, self->class, self->name);
ob_debug("Going to try activate new window? %s",
try_activate ? "yes" : "no");
if (try_activate)
- do_activate = client_can_steal_focus(self, settings->focus,
- event_time(), launch_time);
+ do_activate = client_can_steal_focus(
+ self, settings->focus == 1,
+ (!!launch_time || settings->focus == 1),
+ event_time(), launch_time);
else
do_activate = FALSE;
"program + user specified" :
"BADNESS !?")))), place.width, place.height);
- obplaced = place_client(self, do_activate, &place.x, &place.y,
- settings);
+ obplaced = place_client(self, do_activate, &place, settings);
/* watch for buggy apps that ask to be placed at (0,0) when there is
a strut there */
client_apply_startup_state(self, place.x, place.y,
place.width, place.height);
+ /* set the initial value of the desktop hint, when one wasn't requested
+ on map. */
+ OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
+
/* grab mouse bindings before showing the window */
mouse_grab_for_client(self, TRUE);
+ if (!config_focus_under_mouse)
+ ignore_start = event_start_ignore_all_enters();
+
/* 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
*/
- {
- gulong ignore_start;
- if (!config_focus_under_mouse)
- ignore_start = event_start_ignore_all_enters();
-
- client_show(self);
-
- if (!config_focus_under_mouse)
- event_end_ignore_all_enters(ignore_start);
- }
+ client_show(self);
/* activate/hilight/raise the window */
if (try_activate) {
stacking_raise(CLIENT_AS_WINDOW(self));
}
+ if (!config_focus_under_mouse)
+ event_end_ignore_all_enters(ignore_start);
+
/* add to client list/map */
client_list = g_list_append(client_list, self);
window_add(&self->window, CLIENT_AS_WINDOW(self));
g_free(self->name);
g_free(self->class);
g_free(self->role);
+ g_free(self->group_name);
+ g_free(self->group_class);
g_free(self->client_machine);
g_free(self->sm_client_id);
g_slice_free(ObClient, self);
static gboolean client_can_steal_focus(ObClient *self,
gboolean allow_other_desktop,
+ gboolean request_from_user,
Time steal_time,
Time launch_time)
{
/* This is focus stealing prevention */
ob_debug("Want to focus window 0x%x at time %u "
- "launched at %u (last user interaction time %u)",
+ "launched at %u (last user interaction time %u) "
+ "request from %s, allow other desktop: %s, "
+ "desktop switch time %u",
self->window, steal_time, launch_time,
- event_last_user_time);
+ event_last_user_time,
+ (request_from_user ? "user" : "other"),
+ (allow_other_desktop ? "yes" : "no"),
+ screen_desktop_user_time);
/*
if no launch time is provided for an application, make one up.
if (event_last_user_time && client_search_focus_group_full(self)) {
/* our relative is focused */
launch_time = event_last_user_time;
- ob_debug("Unknown launch time, using %u window in active "
+ ob_debug("Unknown launch time, using %u - window in active "
"group", launch_time);
}
- else {
+ else if (!request_from_user) {
/* has relatives which are not being used. suspicious */
launch_time = event_time() - OB_EVENT_USER_TIME_DELAY;
- ob_debug("Unknown launch time, using %u window in inactive "
+ ob_debug("Unknown launch time, using %u - window in inactive "
"group", launch_time);
}
+ else {
+ /* has relatives which are not being used, but the user seems
+ to want to go there! */
+ launch_time = event_last_user_time;
+ ob_debug("Unknown launch time, using %u - user request",
+ launch_time);
+ }
}
else {
/* the window is on its own, probably the user knows it is going
to appear */
launch_time = event_last_user_time;
- ob_debug("Unknown launch time, using %u for solo window",
+ ob_debug("Unknown launch time, using %u - independent window",
launch_time);
}
}
/* if it's on another desktop
- then if allow_other_desktop is false, we don't want to let it steal
- focus, unless it was launched after we changed desktops
+ and if allow_other_desktop is true, we generally let it steal focus.
+ but if it didn't come from the user, don't let it steal unless it was
+ launched before the user switched desktops.
+ focus, unless it was launched after we changed desktops and the request
+ came from the user
*/
- if (!(self->desktop == screen_desktop ||
- self->desktop == DESKTOP_ALL) &&
- (!allow_other_desktop ||
- (screen_desktop_user_time &&
- !event_time_after(launch_time, screen_desktop_user_time))))
- {
- steal = FALSE;
- ob_debug("Not focusing the window because its on another desktop\n");
+ if (!screen_compare_desktops(screen_desktop, self->desktop)) {
+ /* must be allowed */
+ if (!allow_other_desktop) {
+ steal = FALSE;
+ ob_debug("Not focusing the window because its on another desktop");
+ }
+ /* if we don't know when the desktop changed, but request is from an
+ application, don't let it change desktop on you */
+ else if (!request_from_user) {
+ steal = FALSE;
+ ob_debug("Not focusing the window because non-user request");
+ }
}
/* If something is focused... */
else if (focus_client) {
ob_debug("Not focusing the window because the user is "
"working in another window that is not its relative");
}
- /* 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("Not focusing the window because it is a "
- "transient, and its relatives aren't focused");
- }
- /* 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("Not focusing the window because a globally "
- "active client has focus");
- }
/* Don't move focus if it's not going to go to this window
anyway */
else if (client_focus_target(self) != self) {
ob_debug("Not focusing the window because another window "
"would get the focus anyway");
}
- /* 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("Not focusing the window because it is on "
- "another desktop and no relatives are focused ");
+ /* For requests that don't come from the user */
+ else if (!request_from_user) {
+ /* If the new window is a transient (and its relatives aren't
+ focused) */
+ if (client_has_parent(self) && !relative_focused) {
+ steal = FALSE;
+ ob_debug("Not focusing the window because it is a "
+ "transient, and its relatives aren't focused");
+ }
+ /* 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("Not focusing the window because a globally "
+ "active client has focus");
+ }
+ /* Don't move focus if the window is not visible on the current
+ desktop and none of its relatives are focused */
+ else if (!allow_other_desktop &&
+ !screen_compare_desktops(self->desktop, screen_desktop) &&
+ !relative_focused)
+ {
+ steal = FALSE;
+ ob_debug("Not focusing the window because it is on "
+ "another desktop and no relatives are focused ");
+ }
}
}
ob_debug("Focus stealing prevention activated for %s at "
"time %u (last user interaction time %u)",
self->title, steal_time, event_last_user_time);
+ else
+ ob_debug("Allowing focus stealing for %s at time %u (last user "
+ "interaction time %u)",
+ self->title, steal_time, event_last_user_time);
return steal;
}
g_assert(app->name != NULL || app->class != NULL ||
app->role != NULL || app->title != NULL ||
+ app->group_name != NULL || app->group_class != NULL ||
(signed)app->type >= 0);
if (app->name &&
!g_pattern_match(app->name, strlen(self->name), self->name, NULL))
match = FALSE;
+ else if (app->group_name &&
+ !g_pattern_match(app->group_name,
+ strlen(self->group_name), self->group_name, NULL))
+ match = FALSE;
else if (app->class &&
!g_pattern_match(app->class,
strlen(self->class), self->class, NULL))
match = FALSE;
+ else if (app->group_class &&
+ !g_pattern_match(app->group_class,
+ strlen(self->group_class), self->group_class,
+ NULL))
+ match = FALSE;
else if (app->role &&
!g_pattern_match(app->role,
strlen(self->role), self->role, NULL))
client_get_type_and_transientness(self);
client_update_normal_hints(self);
- /* set up the decor/functions before getting the state. the states may
- affect which functions are available, but we want to know the maximum
- decor/functions are available to this window, so we can then apply them
- in client_apply_startup_state() */
- client_setup_decor_and_functions(self, FALSE);
+ /* set up the maximum possible decor/functions */
+ client_setup_default_decor_and_functions(self);
client_get_state(self);
from per-app settings */
client_get_session_ids(self);
- /* now we got everything that can affect the decorations */
+ /* get this early so we have it for debugging, also this can be used
+ by app rule matching */
+ client_update_title(self);
+
+ /* now we got everything that can affect the decorations or app rule
+ matching */
if (!real)
return;
- /* get this early so we have it for debugging */
- client_update_title(self);
-
/* save the values of the variables used for app rule matching */
client_save_app_rule_values(self);
self->colormap = colormap;
}
+void client_update_opacity(ObClient *self)
+{
+ guint32 o;
+
+ if (OBT_PROP_GET32(self->window, NET_WM_WINDOW_OPACITY, CARDINAL, &o))
+ OBT_PROP_SET32(self->frame->window, NET_WM_WINDOW_OPACITY, CARDINAL, o);
+ else
+ OBT_PROP_ERASE(self->frame->window, NET_WM_WINDOW_OPACITY);
+}
+
void client_update_normal_hints(ObClient *self)
{
XSizeHints size;
ob_debug("Normal hints: not set");
}
-void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
+static void client_setup_default_decor_and_functions(ObClient *self)
{
/* start with everything (cept fullscreen) */
self->decorations =
self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
}
+}
+
+/*! Set up decor for a client based on its undecorated state. */
+static void client_setup_decor_undecorated(ObClient *self)
+{
+ /* If the user requested no decorations, then remove all the decorations,
+ except the border. But don't add a border if there wasn't one. */
+ if (self->undecorated)
+ self->decorations &= (config_theme_keepborder ?
+ OB_FRAME_DECOR_BORDER : 0);
+}
+
+void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
+{
+ client_setup_default_decor_and_functions(self);
+
+ client_setup_decor_undecorated(self);
if (self->max_horz && self->max_vert) {
/* once upon a time you couldn't resize maximized windows, that is not
self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
}
- /* 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 &= (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))
self->functions &= ~OB_CLIENT_FUNC_SHADE;
if (self->name == NULL) self->name = g_strdup("");
if (self->class == NULL) self->class = g_strdup("");
+ /* get the WM_CLASS (name and class) from the group leader. make them "" if
+ they are not provided */
+ if (leader)
+ got = OBT_PROP_GETSS_TYPE(leader, WM_CLASS, STRING_NO_CC, &ss);
+ else
+ got = FALSE;
+
+ if (got) {
+ if (ss[0]) {
+ self->group_name = g_strdup(ss[0]);
+ if (ss[1])
+ self->group_class = g_strdup(ss[1]);
+ }
+ g_strfreev(ss);
+ }
+
+ if (self->group_name == NULL) self->group_name = g_strdup("");
+ if (self->group_class == NULL) self->group_class = g_strdup("");
+
/* get the WM_WINDOW_ROLE. make it "" if it is not provided */
got = OBT_PROP_GETS_XPCS(self->window, WM_WINDOW_ROLE, &s);
OBT_PROP_SETS(self->window, OB_APP_ROLE, self->role);
OBT_PROP_SETS(self->window, OB_APP_NAME, self->name);
OBT_PROP_SETS(self->window, OB_APP_CLASS, self->class);
+ OBT_PROP_SETS(self->window, OB_APP_GROUP_NAME, self->group_name);
+ OBT_PROP_SETS(self->window, OB_APP_GROUP_CLASS, self->group_class);
OBT_PROP_SETS(self->window, OB_APP_TITLE, self->original_title);
switch (self->type) {
self->type == OB_CLIENT_TYPE_TOOLBAR);
}
+gboolean client_occupies_space(ObClient *self)
+{
+ return !(self->type == OB_CLIENT_TYPE_DESKTOP ||
+ self->type == OB_CLIENT_TYPE_SPLASH);
+}
+
gboolean client_mouse_focusable(ObClient *self)
{
return !(self->type == OB_CLIENT_TYPE_MENU ||
if (fullscreen)
client_fullscreen(self, TRUE);
+ /* make sure client_setup_decor_and_functions() is called at least once */
+ client_setup_decor_and_functions(self, FALSE);
+
/* if the window hasn't been configured yet, then do so now, in fact the
x,y,w,h may _not_ be the same as the area rect, which can end up
meaning that the client isn't properly moved/resized by the fullscreen
self->area = oldarea;
client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
- /* set the desktop hint, to make sure that it always exists */
- OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop);
-
/* nothing to do for the other states:
skip_taskbar
skip_pager
the updated frame dimensions. */
frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
+ /* cap any X windows at the size of an unsigned short */
+ *w = MIN(*w,
+ (gint)G_MAXUSHORT
+ - self->frame->size.left - self->frame->size.right);
+ *h = MIN(*h,
+ (gint)G_MAXUSHORT
+ - self->frame->size.top - self->frame->size.bottom);
+
/* gets the frame's position */
frame_client_gravity(self->frame, x, y);
return FALSE;
}
+ /* if we have helper windows they should be there with the window */
+ client_bring_helper_windows(self);
+
ob_debug_type(OB_DEBUG_FOCUS,
"Focusing client \"%s\" (0x%x) at time %u",
self->title, self->window, event_time());
gboolean here, gboolean raise,
gboolean unshade, gboolean user)
{
- if ((user && (desktop ||
- self->desktop == DESKTOP_ALL ||
- self->desktop == screen_desktop)) ||
- client_can_steal_focus(self, desktop, event_time(), CurrentTime))
- {
+ self = client_focus_target(self);
+
+ if (client_can_steal_focus(self, desktop, user, event_time(), CurrentTime))
client_present(self, here, raise, unshade);
- }
else
client_hilite(self, TRUE);
}
if (((helpers && client_helper(self)) ||
(modals && self->modal)) &&
- ((self->desktop != desktop && self->desktop != DESKTOP_ALL) ||
+ (!screen_compare_desktops(self->desktop, desktop) ||
(iconic && self->iconic)))
{
if (iconic && self->iconic)
}
/* search for edges of clients */
- if (((dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH) &&
- !self->max_vert) ||
- ((dir == OB_DIRECTION_EAST || dir == OB_DIRECTION_WEST) &&
- !self->max_horz))
- {
- for (it = client_list; it; it = g_list_next(it)) {
- ObClient *cur = it->data;
+ for (it = client_list; it; it = g_list_next(it)) {
+ ObClient *cur = it->data;
- /* skip windows to not bump into */
- if (cur == self)
- continue;
- if (cur->iconic)
- continue;
- if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
- cur->desktop != screen_desktop)
- continue;
+ /* skip windows to not bump into */
+ if (cur == self)
+ continue;
+ if (cur->iconic)
+ continue;
+ if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
+ cur->desktop != screen_desktop)
+ continue;
- ob_debug("trying window %s", cur->title);
+ ob_debug("trying window %s", cur->title);
- detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
- my_edge_size, dest, near_edge);
- }
- dock_get_area(&dock_area);
- detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
+ detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
my_edge_size, dest, near_edge);
}
+ dock_get_area(&dock_area);
+ detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
+ my_edge_size, dest, near_edge);
g_slice_free(Rect, a);
}