X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=fd2afed1be69559ff6c328ccaa226a55b1195436;hb=567fd15eebdd44e50cef140419dbf7a336708109;hp=66006a48917571f8758fa9cab3fa2c36ccda9a75;hpb=26cc41f6c6187dabd5c7ee4365c8fa44751009e5;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index 66006a48..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,11 +76,11 @@ 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); 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, @@ -106,7 +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) { @@ -202,6 +204,7 @@ void client_manage(Window window, ObPrompt *prompt) gboolean transient = FALSE; Rect place, *monitor; Time launch_time, map_time; + guint32 user_time; ob_debug("Managing window: 0x%lx", window); @@ -221,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 */ @@ -232,6 +236,12 @@ void client_manage(Window window, ObPrompt *prompt) ob_debug("Window type: %d", self->type); ob_debug("Window group: 0x%x", self->group?self->group->leader:0); + ob_debug("Window name: %s class: %s role: %s", 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 @@ -253,16 +263,15 @@ void client_manage(Window window, ObPrompt *prompt) 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 (!OBT_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); @@ -279,9 +288,13 @@ void client_manage(Window window, ObPrompt *prompt) /* 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)) + focus_valid_target(self, self->desktop, + FALSE, FALSE, TRUE, FALSE, FALSE, + settings->focus == 1)) { activate = TRUE; } @@ -316,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, @@ -334,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) && @@ -412,103 +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; - - /* 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); - - 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, 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"); - } - /* 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"); - } - /* 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"); - } - 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)", - 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 { @@ -562,7 +486,6 @@ void client_manage(Window window, ObPrompt *prompt) window, self->frame->window, self->class); } - ObClient *client_fake_manage(Window window) { ObClient *self; @@ -630,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) @@ -758,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) @@ -771,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; @@ -786,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); @@ -1059,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; @@ -1286,7 +1313,6 @@ static void client_update_transient_tree(ObClient *self, transient windows as their children. * * */ - /* No change has occured */ if (oldgroup == newgroup && oldgtran == newgtran && @@ -1380,7 +1406,7 @@ static void client_update_transient_tree(ObClient *self, } } -static void client_get_mwm_hints(ObClient *self) +void client_get_mwm_hints(ObClient *self) { guint num; guint32 *hints; @@ -1730,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)) @@ -1891,6 +1918,8 @@ void client_update_wmhints(ObClient *self) XFree(hints); } + + focus_cycle_addremove(self, TRUE); } void client_update_title(ObClient *self) @@ -2014,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 @@ -2102,7 +2131,6 @@ void client_update_icons(ObClient *self) (gint*)&w, (gint*)&h, &data); obt_display_ignore_errors(FALSE); - if (xicon) { if (w > 0 && h > 0) { /* is this icon in the cache yet? */ @@ -2272,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]; @@ -2351,7 +2411,7 @@ ObClient *client_search_focus_tree_full(ObClient *self) for (it = self->parents; it; it = g_slist_next(it)) { ObClient *c = it->data; - if ((c = client_search_focus_tree_full(it->data))) return c; + if ((c = client_search_focus_tree_full(c))) return c; } return NULL; @@ -2528,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 @@ -2595,7 +2651,6 @@ gboolean client_enter_focusable(ObClient *self) self->type != OB_CLIENT_TYPE_DESKTOP); } - static void client_apply_startup_state(ObClient *self, gint x, gint y, gint w, gint h) { @@ -2796,8 +2851,10 @@ void client_try_configure(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; @@ -2903,7 +2960,6 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, g_assert(*h > 0); } - void client_configure(ObClient *self, gint x, gint y, gint w, gint h, gboolean user, gboolean final, gboolean force_reply) { @@ -3124,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; @@ -3136,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; } @@ -3176,7 +3233,7 @@ void client_maximize(ObClient *self, gboolean max, gint dir) 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) { @@ -3312,12 +3369,20 @@ void client_close(ObClient *self) #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; @@ -3332,7 +3397,14 @@ static void client_prompt_kill(ObClient *self) { 0, OB_KILL_RESULT_YES } }; gchar *m; - const gchar *y; + const gchar *y, *title; + + title = self->original_title; + if (title[0] == '\0') { + /* empty string, so use its parent */ + ObClient *p = client_search_top_direct_parent(self); + if (p) title = p->original_title; + } if (client_on_localhost(self)) { const gchar *sig; @@ -3344,24 +3416,26 @@ static void client_prompt_kill(ObClient *self) m = g_strdup_printf (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"), - self->original_title, sig); + title, sig); y = _("End Process"); } else { m = g_strdup_printf (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"), - self->original_title); + title); y = _("Disconnect"); } /* set the dialog buttons' text */ 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); } @@ -3404,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); @@ -3444,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 */ @@ -3459,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) @@ -3480,19 +3568,38 @@ ObClient *client_search_modal_child(ObClient *self) return NULL; } +static gboolean client_validate_unmap(ObClient *self, int n) +{ + XEvent e; + gboolean ret = TRUE; + + if (XCheckTypedWindowEvent(obt_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(obt_display, &e); + } + + return ret; +} + gboolean client_validate(ObClient *self) { XEvent e; XSync(obt_display, FALSE); /* get all events on the server */ - if (XCheckTypedWindowEvent(obt_display, self->window, DestroyNotify, &e) || - XCheckTypedWindowEvent(obt_display, self->window, UnmapNotify, &e)) - { + if (XCheckTypedWindowEvent(obt_display, self->window, DestroyNotify, &e)) { XPutBackEvent(obt_display, &e); return FALSE; } + if (!client_validate_unmap(self, 0)) + return FALSE; + return TRUE; } @@ -3523,6 +3630,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) gboolean above = self->above; gboolean below = self->below; gint i; + gboolean value; if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) || action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) || @@ -3538,103 +3646,67 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) /* if toggling, then pick whether we're adding or removing */ if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) { if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) - action = modal ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) : - OBT_PROP_ATOM(NET_WM_STATE_ADD); + value = modal; else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) - action = self->max_vert ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) : - OBT_PROP_ATOM(NET_WM_STATE_ADD); + value = self->max_vert; else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) - action = self->max_horz ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) : - OBT_PROP_ATOM(NET_WM_STATE_ADD); + value = self->max_horz; else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) - action = shaded ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) : - OBT_PROP_ATOM(NET_WM_STATE_ADD); + value = shaded; else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) - action = self->skip_taskbar ? - OBT_PROP_ATOM(NET_WM_STATE_REMOVE) : - OBT_PROP_ATOM(NET_WM_STATE_ADD); + value = self->skip_taskbar; else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) - action = self->skip_pager ? - OBT_PROP_ATOM(NET_WM_STATE_REMOVE) : - OBT_PROP_ATOM(NET_WM_STATE_ADD); + value = self->skip_pager; else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) - action = self->iconic ? - OBT_PROP_ATOM(NET_WM_STATE_REMOVE) : - OBT_PROP_ATOM(NET_WM_STATE_ADD); + value = self->iconic; else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) - action = fullscreen ? - OBT_PROP_ATOM(NET_WM_STATE_REMOVE) : - OBT_PROP_ATOM(NET_WM_STATE_ADD); + value = fullscreen; else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) - action = self->above ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) : - OBT_PROP_ATOM(NET_WM_STATE_ADD); + value = self->above; else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) - action = self->below ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) : - OBT_PROP_ATOM(NET_WM_STATE_ADD); + value = self->below; else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)) - action = self->demands_attention ? - OBT_PROP_ATOM(NET_WM_STATE_REMOVE) : - OBT_PROP_ATOM(NET_WM_STATE_ADD); + value = self->demands_attention; else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) - action = undecorated ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) : - OBT_PROP_ATOM(NET_WM_STATE_ADD); + value = undecorated; + else + g_assert_not_reached(); + action = value ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) : + OBT_PROP_ATOM(NET_WM_STATE_ADD); } - if (action == OBT_PROP_ATOM(NET_WM_STATE_ADD)) { - if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) { - modal = TRUE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) { - max_vert = TRUE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) { - max_horz = TRUE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) { - shaded = TRUE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) { - self->skip_taskbar = TRUE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) { - self->skip_pager = TRUE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) { - iconic = TRUE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) { - fullscreen = TRUE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) { - above = TRUE; + value = action == OBT_PROP_ATOM(NET_WM_STATE_ADD); + if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) { + modal = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) { + max_vert = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) { + max_horz = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) { + shaded = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) { + self->skip_taskbar = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) { + self->skip_pager = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) { + iconic = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) { + fullscreen = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) { + above = value; + /* only unset below when setting above, otherwise you can't get to + the normal layer */ + if (value) below = FALSE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) { + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) { + /* and vice versa */ + if (value) above = FALSE; - below = TRUE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){ - demands_attention = TRUE; - } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) { - undecorated = TRUE; - } - - } else { /* action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) */ - if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) { - modal = FALSE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) { - max_vert = FALSE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) { - max_horz = FALSE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) { - shaded = FALSE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) { - self->skip_taskbar = FALSE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) { - self->skip_pager = FALSE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) { - iconic = FALSE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) { - fullscreen = FALSE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) { - above = FALSE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) { - below = FALSE; - } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){ - demands_attention = FALSE; - } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) { - undecorated = FALSE; - } + below = value; + } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){ + demands_attention = value; + } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) { + undecorated = value; } } @@ -3688,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) @@ -3736,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) { @@ -3795,12 +3867,20 @@ static void client_present(ObClient *self, gboolean here, gboolean raise, 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 here, 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, here, raise, unshade); + } + else + client_hilite(self, TRUE); } static void client_bring_windows_recursive(ObClient *self, @@ -3945,6 +4025,21 @@ ObClient *client_search_focus_parent(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; @@ -4028,12 +4123,12 @@ static void detect_edge(Rect area, ObDirection dir, /* check if the head of this window is closer than the previously chosen edge (take into account that the previously chosen edge might have been a tail, not a head) */ - if (head + (*near_edge ? 0 : my_size) < *dest) + if (head + (*near_edge ? 0 : my_size) <= *dest) skip_head = TRUE; /* check if the tail of this window is closer than the previously chosen edge (take into account that the previously chosen edge might have been a head, not a tail) */ - if (tail - (!*near_edge ? 0 : my_size) < *dest) + if (tail - (!*near_edge ? 0 : my_size) <= *dest) skip_tail = TRUE; break; case OB_DIRECTION_SOUTH: @@ -4047,12 +4142,12 @@ static void detect_edge(Rect area, ObDirection dir, /* check if the head of this window is closer than the previously chosen edge (take into account that the previously chosen edge might have been a tail, not a head) */ - if (head - (*near_edge ? 0 : my_size) > *dest) + if (head - (*near_edge ? 0 : my_size) >= *dest) skip_head = TRUE; /* check if the tail of this window is closer than the previously chosen edge (take into account that the previously chosen edge might have been a head, not a tail) */ - if (tail + (!*near_edge ? 0 : my_size) > *dest) + if (tail + (!*near_edge ? 0 : my_size) >= *dest) skip_tail = TRUE; break; default: @@ -4060,7 +4155,7 @@ static void detect_edge(Rect area, ObDirection dir, } ob_debug("my head %d size %d", my_head, my_size); - ob_debug("head %d tail %d deest %d", head, tail, *dest); + ob_debug("head %d tail %d dest %d", head, tail, *dest); if (!skip_head) { ob_debug("using near edge %d", head); *dest = head; @@ -4079,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(); @@ -4120,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; @@ -4141,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, @@ -4223,28 +4313,28 @@ void client_find_resize_directional(ObClient *self, ObDirection side, 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;