X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=3ebcc69ae8f568a0d6c5deafb129b464f8763ec8;hb=fde5ca09e9ea7ab08b6dc06723f19bad6d03af22;hp=600acfcc85e81759722ab65e1adf0525b8f38cc0;hpb=d7108df02b18c309e06fa1d12987e9540486fcef;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index 600acfcc..3ebcc69a 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -77,6 +77,7 @@ static void client_get_mwm_hints(ObClient *self); static void client_get_gravity(ObClient *self); static void client_get_client_machine(ObClient *self); static void client_get_colormap(ObClient *self); +static void client_get_transientness(ObClient *self); static void client_change_allowed_actions(ObClient *self); static void client_change_state(ObClient *self); static void client_change_wm_state(ObClient *self); @@ -84,6 +85,10 @@ static void client_apply_startup_state(ObClient *self, gint x, gint y); static void client_restore_session_state(ObClient *self); static void client_restore_session_stacking(ObClient *self); static ObAppSettings *client_get_settings_state(ObClient *self); +static void client_update_transient_tree(ObClient *self, + ObGroup *oldgroup, ObGroup *newgroup, + ObClient* oldparent, + ObClient *newparent); void client_startup(gboolean reconfig) { @@ -277,7 +282,7 @@ void client_manage(Window window) self->wmstate = WithdrawnState; /* make sure it gets updated first time */ self->layer = -1; self->desktop = screen_num_desktops; /* always an invalid value */ - self->user_time = CurrentTime; + self->user_time = focus_client ? focus_client->user_time : CurrentTime; client_get_all(self); /* per-app settings override stuff, and return the settings for other @@ -347,8 +352,8 @@ void client_manage(Window window) /* make sure the window is visible. */ client_find_onscreen(self, &newx, &newy, - self->frame->area.width, - self->frame->area.height, + self->area.width, + self->area.height, /* non-normal clients has less rules, and windows that are being restored from a session do also. we can assume you want @@ -381,8 +386,10 @@ void client_manage(Window window) focus_client->user_time : CurrentTime; /* This is focus stealing prevention */ - ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n", - self->window, self->user_time, last_time); + ob_debug_type(OB_DEBUG_FOCUS, + "Want to focus new window 0x%x with time %u " + "(last time %u)\n", + self->window, self->user_time, last_time); /* if it's on another desktop */ if (!(self->desktop == screen_desktop || self->desktop == DESKTOP_ALL) @@ -391,31 +398,38 @@ void client_manage(Window window) !event_time_after(self->user_time, screen_desktop_user_time)) { activate = FALSE; + ob_debug_type(OB_DEBUG_FOCUS, + "Not focusing the window because its on another " + "desktop\n"); } - /* If nothing is focused, or a parent was focused, then focus this - always - */ - else if (!focus_client || client_search_focus_parent(self) != NULL) - activate = TRUE; - else + /* If something is focused, and it's not our parent... */ + else if (focus_client && client_search_focus_parent(self) == NULL) { /* If time stamp is old, don't steal focus */ if (self->user_time && last_time && !event_time_after(self->user_time, last_time)) { activate = FALSE; + ob_debug_type(OB_DEBUG_FOCUS, + "Not focusing the window because the time is " + "too old\n"); } /* Don't steal focus from globally active clients. I stole this idea from KWin. It seems nice. */ - if (!(focus_client->can_focus || focus_client->focus_notify)) + 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"); + } } if (!activate) { - ob_debug("Focus stealing prevention activated for %s with time %u " - "(last time %u)\n", - self->title, self->user_time, last_time); + ob_debug_type(OB_DEBUG_FOCUS, + "Focus stealing prevention activated for %s with " + "time %u (last time %u)\n", + self->title, self->user_time, last_time); /* if the client isn't focused, then hilite it so the user knows it is there */ client_hilite(self, TRUE); @@ -494,22 +508,8 @@ void client_unmanage(ObClient *self) /* update the focus lists */ focus_order_remove(self); if (client_focused(self)) { - /* we have to fall back here because we might not get a focus out. - 1. we need to xselectinput off the window before we unmap it because - otherwise we end up getting unmapnotifies we don't want and they - can mess up mapping it again quickly - 2. this means that if we unmanage from a synthetic unmapnotify, we - are the ones unmapped it, and causing the focusout. so we won't - get the focusout event. - 3. we can't handle focusin events on the root window because they - come from all screens, so the focus change gets lost - - if this ever gets removed in the future MAKE SURE to replace it - with: - /- don't leave an invalid focus_client -/ - focus_client = NULL; - */ - focus_fallback(FALSE); + /* don't leave an invalid focus_client */ + focus_client = NULL; } client_list = g_list_remove(client_list, self); @@ -742,8 +742,8 @@ void client_move_onscreen(ObClient *self, gboolean rude) gint x = self->area.x; gint y = self->area.y; if (client_find_onscreen(self, &x, &y, - self->frame->area.width, - self->frame->area.height, rude)) { + self->area.width, + self->area.height, rude)) { client_move(self, x, y); } } @@ -917,16 +917,22 @@ static void client_get_all(ObClient *self) client_get_area(self); client_get_mwm_hints(self); - /* The transient hint is used to pick a type, but the type can also affect - 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); + /* The transient-ness of a window is used to pick a type, but the type can + also affect transiency. + + Dialogs are always made transients for their group if they have one. + + I also have made non-application type windows be transients for their + group (eg utility windows). + */ + client_get_transientness(self); client_get_type(self);/* this can change the mwmhints for special cases */ client_get_state(self); - client_update_transient_for(self); client_update_wmhints(self); + /* this may have already been called from client_update_wmhints */ + if (self->transient_for == NULL) + client_update_transient_for(self); client_get_startup_id(self); client_get_desktop(self);/* uses transient data/group/startup id if a desktop is not specified */ @@ -1123,6 +1129,13 @@ static void client_get_shaped(ObClient *self) #endif } +void client_get_transientness(ObClient *self) +{ + Window t; + if (XGetTransientForHint(ob_display, self->window, &t)) + self->transient = TRUE; +} + void client_update_transient_for(ObClient *self) { Window t = None; @@ -1166,62 +1179,126 @@ void client_update_transient_for(ObClient *self) } } } - } else if (self->group) { - if (self->type == OB_CLIENT_TYPE_DIALOG || - self->type == OB_CLIENT_TYPE_TOOLBAR || - self->type == OB_CLIENT_TYPE_MENU || - self->type == OB_CLIENT_TYPE_UTILITY) - { - self->transient = TRUE; + } else if (self->type == OB_CLIENT_TYPE_DIALOG || + self->type == OB_CLIENT_TYPE_TOOLBAR || + self->type == OB_CLIENT_TYPE_MENU || + self->type == OB_CLIENT_TYPE_UTILITY) + { + self->transient = TRUE; + if (self->group) target = OB_TRAN_GROUP; - } } else self->transient = FALSE; - /* if anything has changed... */ - if (target != self->transient_for) { - if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */ - GSList *it; + client_update_transient_tree(self, self->group, self->group, + self->transient_for, target); + self->transient_for = target; + +} - /* remove from old parents */ - for (it = self->group->members; it; it = g_slist_next(it)) { - ObClient *c = it->data; - if (c != self && (!c->transient_for || - c->transient_for != OB_TRAN_GROUP)) - c->transients = g_slist_remove(c->transients, self); - } - } else if (self->transient_for != NULL) { /* transient of window */ - /* remove from old parent */ - self->transient_for->transients = - g_slist_remove(self->transient_for->transients, self); +static void client_update_transient_tree(ObClient *self, + ObGroup *oldgroup, ObGroup *newgroup, + ObClient* oldparent, + ObClient *newparent) +{ + GSList *it, *next; + ObClient *c; + + /* No change has occured */ + if (oldgroup == newgroup && oldparent == newparent) return; + + /** Remove the client from the transient tree wherever it has changed **/ + + /* If the window is becoming a direct transient for a window in its group + then that window can't be a child of this window anymore */ + if (oldparent != newparent && + newparent != NULL && newparent != OB_TRAN_GROUP && + newparent->transient_for == OB_TRAN_GROUP && + newgroup != NULL && newgroup == oldgroup) + { + self->transients = g_slist_remove(self->transients, newparent); + } + + + /* If the group changed then we need to remove any old group transient + windows from our children. But if we're transient for the group, then + other group transients are not our children. */ + if (oldgroup != newgroup && oldgroup != NULL && + oldparent != OB_TRAN_GROUP) + { + for (it = self->transients; it; it = next) { + next = g_slist_next(it); + c = it->data; + if (c->group == oldgroup) + self->transients = g_slist_delete_link(self->transients, it); } - self->transient_for = target; - if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */ - GSList *it; + } - /* add to new parents */ - for (it = self->group->members; it; it = g_slist_next(it)) { - ObClient *c = it->data; - if (c != self && (!c->transient_for || - c->transient_for != OB_TRAN_GROUP)) - c->transients = g_slist_append(c->transients, self); - } + /* If we used to be transient for a group and now we are not, or we're + transient for a new group, then we need to remove ourselves from all + our ex-parents */ + if (oldparent == OB_TRAN_GROUP && (oldgroup != newgroup || + oldparent != newparent)) + { + for (it = oldgroup->members; it; it = g_slist_next(it)) { + c = it->data; + if (c != self && (!c->transient_for || + c->transient_for != OB_TRAN_GROUP)) + c->transients = g_slist_remove(c->transients, self); + } + } + /* If we used to be transient for a single window and we are no longer + transient for it, then we need to remove ourself from its children */ + else if (oldparent != NULL && oldparent != OB_TRAN_GROUP && + oldparent != newparent) + oldparent->transients = g_slist_remove(oldparent->transients, self); - /* remove all transients which are in the group, that causes - circlular pointer hell of doom */ - for (it = self->group->members; it; it = g_slist_next(it)) { - GSList *sit, *next; - for (sit = self->transients; sit; sit = next) { - next = g_slist_next(sit); - if (sit->data == it->data) - self->transients = - g_slist_delete_link(self->transients, sit); - } + + /** Re-add the client to the transient tree wherever it has changed **/ + + /* If we're now transient for a group and we weren't transient for it + before then we need to add ourselves to all our new parents */ + if (newparent == OB_TRAN_GROUP && (oldgroup != newgroup || + oldparent != newparent)) + { + for (it = oldgroup->members; it; it = g_slist_next(it)) { + c = it->data; + if (c != self && (!c->transient_for || + c->transient_for != OB_TRAN_GROUP)) + c->transients = g_slist_append(c->transients, self); + } + } + /* If we are now transient for a single window which we weren't before, + we need to add ourselves to its children + + WARNING: Cyclical transient ness is possible if two windows are + transient for eachother. + */ + else if (newparent != NULL && newparent != OB_TRAN_GROUP && + newparent != oldparent && + /* don't make ourself its child if it is already our child */ + !client_is_direct_child(self, newparent)) + newparent->transients = g_slist_append(newparent->transients, self); + + /* If the group changed then we need to add any new group transient + windows to our children. But if we're transient for the group, then + other group transients are not our children. + + WARNING: Cyclical transient-ness is possible. For e.g. if: + A is transient for the group + B is a member of the group and transient for A + */ + if (oldgroup != newgroup && newgroup != NULL && + newparent != OB_TRAN_GROUP) + { + for (it = newgroup->members; it; it = g_slist_next(it)) { + c = it->data; + if (c != self && c->transient_for == OB_TRAN_GROUP && + /* Don't make it our child if it is already our parent */ + !client_is_direct_child(c, self)) + { + self->transients = g_slist_append(self->transients, c); } - } else if (self->transient_for != NULL) { /* transient of window */ - /* add to new parent */ - self->transient_for->transients = - g_slist_append(self->transient_for->transients, self); } } } @@ -1616,7 +1693,6 @@ void client_reconfigure(ObClient *self) void client_update_wmhints(ObClient *self) { XWMHints *hints; - GSList *it; /* assume a window takes input if it doesnt specify */ self->can_focus = TRUE; @@ -1645,54 +1721,50 @@ void client_update_wmhints(ObClient *self) /* did the group state change? */ if (hints->window_group != - (self->group ? self->group->leader : None)) { + (self->group ? self->group->leader : None)) + { + ObGroup *oldgroup = self->group; + /* remove from the old group if there was one */ if (self->group != NULL) { - /* remove transients of the group */ - for (it = self->group->members; it; it = g_slist_next(it)) - self->transients = g_slist_remove(self->transients, - it->data); - - /* remove myself from parents in the group */ - if (self->transient_for == OB_TRAN_GROUP) { - for (it = self->group->members; it; - it = g_slist_next(it)) - { - ObClient *c = it->data; - - if (c != self && !c->transient_for) - c->transients = g_slist_remove(c->transients, - self); - } - } - group_remove(self->group, self); self->group = NULL; } + + /* add ourself to the group if we have one */ if (hints->window_group != None) { self->group = group_add(hints->window_group, self); - - /* i can only have transients from the group if i am not - transient myself */ - if (!self->transient_for) { - /* add other transients of the group that are already - set up */ - for (it = self->group->members; it; - it = g_slist_next(it)) - { - ObClient *c = it->data; - if (c != self && c->transient_for == OB_TRAN_GROUP) - self->transients = - g_slist_append(self->transients, c); - } - } } - /* because the self->transient flag wont change from this call, - we don't need to update the window's type and such, only its - transient_for, and the transients lists of other windows in - the group may be affected */ - client_update_transient_for(self); + /* Put ourselves into the new group's transient tree, and remove + ourselves from the old group's */ + client_update_transient_tree(self, oldgroup, self->group, + self->transient_for, + self->transient_for); + + /* Lastly, being in a group, or not, can change if the window is + transient for anything. + + The logic for this is: + self->transient = TRUE always if the window wants to be + transient for something, even if transient_for was NULL because + it wasn't in a group before. + + If transient_for was NULL and oldgroup was NULL we can assume + that when we add the new group, it will become transient for + something. + + If transient_for was OB_TRAN_GROUP, then it must have already + had a group. If it is getting a new group, the above call to + client_update_transient_tree has already taken care of + everything ! If it is losing all group status then it will + no longer be transient for anything and that needs to be + updated. + */ + if (self->transient && + ((self->transient_for == NULL && oldgroup == NULL) || + (self->transient_for == OB_TRAN_GROUP && !self->group))) + client_update_transient_for(self); } /* the WM_HINTS can contain an icon */ @@ -2198,6 +2270,12 @@ gboolean client_normal(ObClient *self) { self->type == OB_CLIENT_TYPE_SPLASH); } +gboolean client_application(ObClient *self) +{ + return (self->type == OB_CLIENT_TYPE_NORMAL || + self->type == OB_CLIENT_TYPE_DIALOG); +} + static void client_apply_startup_state(ObClient *self, gint x, gint y) { gboolean pos = FALSE; /* has the window's position been configured? */ @@ -2615,7 +2693,8 @@ static void client_iconify_recursive(ObClient *self, } else { self->iconic = iconic; - if (curdesk) + if (curdesk && self->desktop != screen_desktop && + self->desktop != DESKTOP_ALL) client_set_desktop(self, screen_desktop, FALSE); /* this puts it after the current focused window */ @@ -3182,11 +3261,12 @@ void client_activate(ObClient *self, gboolean here, gboolean user) event_halt_focus_delay(); if (client_normal(self) && screen_showing_desktop) - screen_show_desktop(FALSE); + screen_show_desktop(FALSE, FALSE); if (self->iconic) client_iconify(self, FALSE, here); if (self->desktop != DESKTOP_ALL && - self->desktop != screen_desktop) { + self->desktop != screen_desktop) + { if (here) client_set_desktop(self, screen_desktop, FALSE); else @@ -3633,3 +3713,17 @@ gboolean client_has_group_siblings(ObClient *self) { return self->group && self->group->members->next; } + +gboolean client_has_application_group_siblings(ObClient *self) +{ + GSList *it; + + if (!self->group) return FALSE; + + for (it = self->group->members; it; it = g_slist_next(it)) { + ObClient *c = it->data; + if (c != self && client_application(c)) + return TRUE; + } + return FALSE; +}