X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=fd2afed1be69559ff6c328ccaa226a55b1195436;hb=567fd15eebdd44e50cef140419dbf7a336708109;hp=060499b0653ec7713822ae0acdfe03438f92ed50;hpb=c828154886a40fa48b0545fd5d22e277f6d210e9;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index 060499b0..fd2afed1 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -31,6 +31,7 @@ #include "grab.h" #include "prompt.h" #include "focus.h" +#include "focus_cycle.h" #include "stacking.h" #include "openbox.h" #include "group.h" @@ -38,7 +39,7 @@ #include "menuframe.h" #include "keyboard.h" #include "mouse.h" -#include "render/render.h" +#include "obrender/render.h" #include "gettext.h" #include "obt/display.h" #include "obt/prop.h" @@ -75,6 +76,7 @@ static RrImage *client_default_icon = NULL; static void client_get_all(ObClient *self, gboolean real); static void client_get_startup_id(ObClient *self); static void client_get_session_ids(ObClient *self); +static void client_save_app_rule_values(ObClient *self); static void client_get_area(ObClient *self); static void client_get_desktop(ObClient *self); static void client_get_state(ObClient *self); @@ -105,6 +107,8 @@ static GSList *client_search_all_top_parents_internal(ObClient *self, 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) { @@ -220,6 +224,7 @@ void client_manage(Window window, ObPrompt *prompt) self->obwin.type = OB_WINDOW_CLASS_CLIENT; self->window = window; self->prompt = prompt; + self->managed = TRUE; /* non-zero defaults */ self->wmstate = WithdrawnState; /* make sure it gets updated first time */ @@ -287,7 +292,9 @@ void client_manage(Window window, ObPrompt *prompt) (user_time != 0) && /* this checks for focus=false for the window */ (!settings || settings->focus != 0) && - focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE)) + focus_valid_target(self, self->desktop, + FALSE, FALSE, TRUE, FALSE, FALSE, + settings->focus == 1)) { activate = TRUE; } @@ -322,9 +329,7 @@ void client_manage(Window window, ObPrompt *prompt) "program + user specified" : "BADNESS !?")))), place.width, place.height); - /* splash screens are also returned as TRUE for transient, - and so will be forced on screen below */ - transient = place_client(self, &place.x, &place.y, settings); + place_client(self, &place.x, &place.y, settings); /* make sure the window is visible. */ client_find_onscreen(self, &place.x, &place.y, @@ -340,11 +345,13 @@ void client_manage(Window window, ObPrompt *prompt) it is up to the placement routines to avoid the xinerama divides) - splash screens get "transient" set to TRUE by - the place_client call + children and splash screens are forced on + screen, but i don't remember why i decided to + do that. */ ob_state() == OB_STATE_RUNNING && - (transient || + (self->type == OB_CLIENT_TYPE_DIALOG || + self->type == OB_CLIENT_TYPE_SPLASH || (!((self->positioned & USPosition) || (settings && settings->pos_given)) && client_normal(self) && @@ -418,121 +425,14 @@ void client_manage(Window window, ObPrompt *prompt) ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s", activate ? "yes" : "no"); if (activate) { - gboolean raise = FALSE; - gboolean relative_focused; - gboolean parent_focused; - - 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)", - self->window, map_time, launch_time, - event_last_user_time); - ob_debug_type(OB_DEBUG_FOCUS, - "Current focus_client: %s", - (focus_client ? focus_client->title : "(none)")); - ob_debug_type(OB_DEBUG_FOCUS, - "parent focused: %d relative focused: %d", - parent_focused, relative_focused); - - 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"); - } - - /* 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"); - } - /* 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, - 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 that is not " - "its parent"); - } - /* If the new window is a transient (and its relatives aren't - focused) */ - else if (client_has_parent(self) && !relative_focused) { - activate = FALSE; - ob_debug_type(OB_DEBUG_FOCUS, - "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)) - { - activate = FALSE; - ob_debug_type(OB_DEBUG_FOCUS, - "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) { - activate = FALSE; - raise = TRUE; - ob_debug_type(OB_DEBUG_FOCUS, - "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) - { - 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)", - 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 { @@ -653,6 +553,8 @@ void client_unmanage(ObClient *self) mouse_grab_for_client(self, FALSE); + self->managed = FALSE; + /* remove the window from our save set, unless we are managing an internal ObPrompt window */ if (!self->prompt) @@ -781,6 +683,105 @@ void client_fake_unmanage(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)", + 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"); + } + /* 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"); + } + /* 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"); + } + /* 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"); + } + /* 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)", + 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) @@ -794,10 +795,9 @@ static ObAppSettings *client_get_settings_state(ObClient *self) ObAppSettings *app = it->data; gboolean match = TRUE; - g_assert(app->name != NULL || app->class != NULL); + g_assert(app->name != NULL || app->class != NULL || + app->role != NULL || (signed)app->type >= 0); - /* we know that either name or class is not NULL so it will have to - match to use the rule */ if (app->name && !g_pattern_match(app->name, strlen(self->name), self->name, NULL)) match = FALSE; @@ -809,8 +809,9 @@ static ObAppSettings *client_get_settings_state(ObClient *self) !g_pattern_match(app->role, strlen(self->role), self->role, NULL)) match = FALSE; - else if ((signed)app->type >= 0 && app->type != self->type) + else if ((signed)app->type >= 0 && app->type != self->type) { match = FALSE; + } if (match) { ob_debug("Window matching: %s", app->name); @@ -1082,6 +1083,9 @@ static void client_get_all(ObClient *self, gboolean real) from per-app settings */ client_get_session_ids(self); + /* save the values of the variables used for app rule matching */ + client_save_app_rule_values(self); + /* now we got everything that can affect the decorations */ if (!real) return; @@ -1752,7 +1756,8 @@ void client_setup_decor_and_functions(ObClient *self, gboolean reconfig) /* 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)) @@ -1913,6 +1918,8 @@ void client_update_wmhints(ObClient *self) XFree(hints); } + + focus_cycle_addremove(self, TRUE); } void client_update_title(ObClient *self) @@ -2036,7 +2043,7 @@ void client_update_strut(ObClient *self) STRUT_PARTIAL_SET(strut, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - if (!STRUT_EQUAL(strut, self->strut)) { + if (!PARTIAL_STRUT_EQUAL(strut, self->strut)) { self->strut = strut; /* updating here is pointless while we're being mapped cuz we're not in @@ -2293,6 +2300,38 @@ static void client_get_session_ids(ObClient *self) } } +/*! Save the properties used for app matching rules, as seen by Openbox when + the window mapped, so that users can still access them later if the app + changes them */ +static void client_save_app_rule_values(ObClient *self) +{ + const gchar *type; + + OBT_PROP_SETS(self->window, OB_APP_ROLE, utf8, self->role); + OBT_PROP_SETS(self->window, OB_APP_NAME, utf8, self->name); + OBT_PROP_SETS(self->window, OB_APP_CLASS, utf8, self->class); + + switch (self->type) { + case OB_CLIENT_TYPE_NORMAL: + type = "normal"; break; + case OB_CLIENT_TYPE_DIALOG: + type = "dialog"; break; + case OB_CLIENT_TYPE_UTILITY: + type = "utility"; break; + case OB_CLIENT_TYPE_MENU: + type = "menu"; break; + case OB_CLIENT_TYPE_TOOLBAR: + type = "toolbar"; break; + case OB_CLIENT_TYPE_SPLASH: + type = "splash"; break; + case OB_CLIENT_TYPE_DESKTOP: + type = "desktop"; break; + case OB_CLIENT_TYPE_DOCK: + type = "dock"; break; + } + OBT_PROP_SETS(self->window, OB_APP_TYPE, utf8, type); +} + static void client_change_wm_state(ObClient *self) { gulong state[2]; @@ -2549,10 +2588,6 @@ gboolean client_hide(ObClient *self) 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 @@ -3145,7 +3180,7 @@ static void client_iconify_recursive(ObClient *self, self->iconic = iconic; /* update the focus lists.. iconic windows go to the bottom of - the list */ + the list. this will also call focus_cycle_addremove(). */ focus_order_to_bottom(self); changed = TRUE; @@ -3157,9 +3192,10 @@ static void client_iconify_recursive(ObClient *self, self->desktop != DESKTOP_ALL) client_set_desktop(self, screen_desktop, FALSE, FALSE); - /* this puts it after the current focused window */ - focus_order_remove(self); - focus_order_add_new(self); + /* this puts it after the current focused window, this will + also cause focus_cycle_addremove() to be called for the + client */ + focus_order_like_new(self); changed = TRUE; } @@ -3442,8 +3478,18 @@ void client_hilite(ObClient *self, gboolean hilite) /* 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); @@ -3482,6 +3528,8 @@ static void client_set_desktop_recursive(ObClient *self, /* the new desktop's geometry may be different, so we may need to resize, for example if we are maximized */ client_reconfigure(self, FALSE); + + focus_cycle_addremove(self, FALSE); } /* move all transients */ @@ -3497,6 +3545,8 @@ void client_set_desktop(ObClient *self, guint target, { self = client_search_top_direct_parent(self); client_set_desktop_recursive(self, target, donthide, dontraise); + + focus_cycle_addremove(NULL, TRUE); } gboolean client_is_direct_child(ObClient *parent, ObClient *child) @@ -3710,6 +3760,8 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) client_hilite(self, demands_attention); client_change_state(self); /* change the hint to reflect these changes */ + + focus_cycle_addremove(self, TRUE); } ObClient *client_focus_target(ObClient *self) @@ -3758,8 +3810,6 @@ gboolean client_focus(ObClient *self) go moving on us */ event_halt_focus_delay(); - event_cancel_all_key_grabs(); - obt_display_ignore_errors(TRUE); if (self->can_focus) { @@ -3818,12 +3868,17 @@ static void client_present(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 here, gboolean raise, +void client_activate(ObClient *self, gboolean desktop, + gboolean here, gboolean raise, gboolean unshade, gboolean user) { - if (user || (self->desktop == DESKTOP_ALL || - self->desktop == screen_desktop)) + if ((user && (desktop || + self->desktop == DESKTOP_ALL || + self->desktop == screen_desktop)) || + client_can_steal_focus(self, event_curtime, CurrentTime)) + { client_present(self, here, raise, unshade); + } else client_hilite(self, TRUE); } @@ -4119,39 +4174,26 @@ void client_find_edge_directional(ObClient *self, ObDirection dir, gint *dest, gboolean *near_edge) { GList *it; - Rect *a, *mon; + Rect *a; Rect dock_area; gint edge; + guint i; a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS, &self->frame->area); - mon = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, - &self->frame->area); switch (dir) { case OB_DIRECTION_NORTH: - if (my_head >= RECT_TOP(*mon) + 1) - edge = RECT_TOP(*mon) - 1; - else - edge = RECT_TOP(*a) - 1; + edge = RECT_TOP(*a) - 1; break; case OB_DIRECTION_SOUTH: - if (my_head <= RECT_BOTTOM(*mon) - 1) - edge = RECT_BOTTOM(*mon) + 1; - else - edge = RECT_BOTTOM(*a) + 1; + edge = RECT_BOTTOM(*a) + 1; break; case OB_DIRECTION_EAST: - if (my_head <= RECT_RIGHT(*mon) - 1) - edge = RECT_RIGHT(*mon) + 1; - else - edge = RECT_RIGHT(*a) + 1; + edge = RECT_RIGHT(*a) + 1; break; case OB_DIRECTION_WEST: - if (my_head >= RECT_LEFT(*mon) + 1) - edge = RECT_LEFT(*mon) - 1; - else - edge = RECT_LEFT(*a) - 1; + edge = RECT_LEFT(*a) - 1; break; default: g_assert_not_reached(); @@ -4160,6 +4202,15 @@ void client_find_edge_directional(ObClient *self, ObDirection dir, *dest = edge; *near_edge = TRUE; + /* search for edges of monitors */ + for (i = 0; i < screen_num_monitors; ++i) { + Rect *area = screen_area(self->desktop, i, NULL); + detect_edge(*area, dir, my_head, my_size, my_edge_start, + my_edge_size, dest, near_edge); + g_free(area); + } + + /* search for edges of clients */ for (it = client_list; it; it = g_list_next(it)) { ObClient *cur = it->data; @@ -4181,7 +4232,6 @@ void client_find_edge_directional(ObClient *self, ObDirection dir, detect_edge(dock_area, dir, my_head, my_size, my_edge_start, my_edge_size, dest, near_edge); g_free(a); - g_free(mon); } void client_find_move_directional(ObClient *self, ObDirection dir,