X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=0d74882ce87abf53cee7df2157d4f455d21665df;hb=280529221e9349aa07c6c498df6b80b3a8951198;hp=5e1a78513992929a178fb871bfbffa0b89a1b3b2;hpb=97cbacd9e41ae2315434d6e83ce78502a881d54f;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index 5e1a7851..0d74882c 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -57,17 +57,19 @@ typedef struct { - ObClientDestructor func; + ObClientCallback func; gpointer data; -} Destructor; +} ClientCallback; -GList *client_list = NULL; +GList *client_list = NULL; -static GSList *client_destructors = NULL; +static GSList *client_destructors = NULL; +static GSList *client_desktop_notifies = NULL; static void client_get_all(ObClient *self); static void client_toggle_border(ObClient *self, gboolean show); static void client_get_startup_id(ObClient *self); +static void client_get_session_ids(ObClient *self); static void client_get_area(ObClient *self); static void client_get_desktop(ObClient *self); static void client_get_state(ObClient *self); @@ -75,15 +77,23 @@ static void client_get_layer(ObClient *self); static void client_get_shaped(ObClient *self); 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); 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 gboolean 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); +static void client_present(ObClient *self, gboolean here, gboolean raise); +static GSList *client_search_all_top_parents_internal(ObClient *self, + gboolean bylayer, + ObStackingLayer layer); void client_startup(gboolean reconfig) { @@ -96,20 +106,20 @@ void client_shutdown(gboolean reconfig) { } -void client_add_destructor(ObClientDestructor func, gpointer data) +void client_add_destructor(ObClientCallback func, gpointer data) { - Destructor *d = g_new(Destructor, 1); + ClientCallback *d = g_new(ClientCallback, 1); d->func = func; d->data = data; client_destructors = g_slist_prepend(client_destructors, d); } -void client_remove_destructor(ObClientDestructor func) +void client_remove_destructor(ObClientCallback func) { GSList *it; for (it = client_destructors; it; it = g_slist_next(it)) { - Destructor *d = it->data; + ClientCallback *d = it->data; if (d->func == func) { g_free(d); client_destructors = g_slist_delete_link(client_destructors, it); @@ -118,6 +128,29 @@ void client_remove_destructor(ObClientDestructor func) } } +void client_add_desktop_notify(ObClientCallback func, gpointer data) +{ + ClientCallback *d = g_new(ClientCallback, 1); + d->func = func; + d->data = data; + client_desktop_notifies = g_slist_prepend(client_desktop_notifies, d); +} + +void client_remove_desktop_notify(ObClientCallback func) +{ + GSList *it; + + for (it = client_desktop_notifies; it; it = g_slist_next(it)) { + ClientCallback *d = it->data; + if (d->func == func) { + g_free(d); + client_desktop_notifies = + g_slist_delete_link(client_desktop_notifies, it); + break; + } + } +} + void client_set_list() { Window *windows, *win_it; @@ -277,7 +310,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 @@ -316,10 +349,10 @@ void client_manage(Window window) grab_server(FALSE); stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); - client_restore_session_stacking(self); /* focus the new window? */ if (ob_state() != OB_STATE_STARTING && + (!self->session || self->session->focused) && !self->iconic && /* this means focus=true for window is same as config_focus_new=true */ ((config_focus_new || (settings && settings->focus == 1)) || @@ -347,8 +380,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 @@ -372,6 +405,10 @@ void client_manage(Window window) */ ob_debug("placing window 0x%x at %d, %d with size %d x %d\n", self->window, newx, newy, self->area.width, self->area.height); + if (self->session) + ob_debug("session requested %d %d\n", + self->session->x, self->session->y); + client_apply_startup_state(self, newx, newy); mouse_grab_for_client(self, TRUE); @@ -381,8 +418,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 +430,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); @@ -429,7 +475,8 @@ void client_manage(Window window) Also if you don't have focus_new enabled, then it's going to get raised to the top. Legacy begets legacy I guess? */ - client_raise(self); + if (!client_restore_session_stacking(self)) + client_raise(self); } /* this has to happen before we try focus the window, but we want it to @@ -442,8 +489,10 @@ void client_manage(Window window) a window maps since its not based on an action from the user like clicking a window to activate it. so keep the new window out of the way but do focus it. */ - if (activate) - client_activate(self, FALSE, TRUE); + if (activate) { + gboolean stacked = client_restore_session_stacking(self); + client_present(self, FALSE, !stacked); + } /* add to client list/map */ client_list = g_list_append(client_list, self); @@ -494,22 +543,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); @@ -522,7 +557,7 @@ void client_unmanage(ObClient *self) screen_update_areas(); for (it = client_destructors; it; it = g_slist_next(it)) { - Destructor *d = it->data; + ClientCallback *d = it->data; d->func(self, d->data); } @@ -601,6 +636,7 @@ void client_unmanage(ObClient *self) g_free(self->icons[j].data); if (self->nicons > 0) g_free(self->icons); + g_free(self->wm_command); g_free(self->title); g_free(self->icon_title); g_free(self->name); @@ -627,13 +663,13 @@ static ObAppSettings *client_get_settings_state(ObClient *self) || (app->class && app->name && !strcmp(app->class, self->class) && !strcmp(app->name, self->name))) { - ob_debug("Window matching: %s\n", app->name); /* Match if no role was specified in the per app setting, or if the * string matches the beginning of the role, since apps like to set * the role to things like browser-window-23c4b2f */ if (!app->role || !strncmp(app->role, self->role, strlen(app->role))) { + ob_debug("Window matching: %s\n", app->name); /* use this one */ settings = app; break; @@ -661,9 +697,13 @@ static ObAppSettings *client_get_settings_state(ObClient *self) if (settings->fullscreen != -1) self->fullscreen = !!settings->fullscreen; - if (settings->desktop < screen_num_desktops - || settings->desktop == DESKTOP_ALL) - self->desktop = settings->desktop; + if (settings->desktop) { + if (settings->desktop == DESKTOP_ALL) + self->desktop = settings->desktop; + else if (settings->desktop > 0 && + settings->desktop <= screen_num_desktops) + self->desktop = settings->desktop - 1; + } if (settings->layer == -1) { self->below = TRUE; @@ -685,13 +725,22 @@ static void client_restore_session_state(ObClient *self) { GList *it; - if (!(it = session_state_find(self))) + ob_debug_type(OB_DEBUG_SM, + "Restore session for client %s\n", self->title); + + if (!(it = session_state_find(self))) { + ob_debug_type(OB_DEBUG_SM, + "Session data not found for client %s\n", self->title); return; + } self->session = it->data; + ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s\n", + self->title); + RECT_SET_POINT(self->area, self->session->x, self->session->y); - self->positioned = PPosition; + self->positioned = USPosition; if (self->session->w > 0) self->area.width = self->session->w; if (self->session->h > 0) @@ -713,28 +762,33 @@ static void client_restore_session_state(ObClient *self) self->below = self->session->below; self->max_horz = self->session->max_horz; self->max_vert = self->session->max_vert; + self->undecorated = self->session->undecorated; } -static void client_restore_session_stacking(ObClient *self) +static gboolean client_restore_session_stacking(ObClient *self) { - GList *it; + GList *it, *mypos; + + if (!self->session) return FALSE; - if (!self->session) return; + mypos = g_list_find(session_saved_state, self->session); + if (!mypos) return FALSE; - it = g_list_find(session_saved_state, self->session); - for (it = g_list_previous(it); it; it = g_list_previous(it)) { + /* start above me and look for the first client */ + for (it = g_list_previous(mypos); it; it = g_list_previous(it)) { GList *cit; - for (cit = client_list; cit; cit = g_list_next(cit)) - if (session_state_cmp(it->data, cit->data)) - break; - if (cit) { - client_calc_layer(self); - stacking_below(CLIENT_AS_WINDOW(self), - CLIENT_AS_WINDOW(cit->data)); - break; + for (cit = client_list; cit; cit = g_list_next(cit)) { + ObClient *c = cit->data; + /* found a client that was in the session, so go below it */ + if (c->session == it->data) { + stacking_below(CLIENT_AS_WINDOW(self), + CLIENT_AS_WINDOW(cit->data)); + return TRUE; + } } } + return FALSE; } void client_move_onscreen(ObClient *self, gboolean rude) @@ -742,8 +796,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); } } @@ -793,7 +847,7 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, if (!rude) { Point oldtl, oldtr, oldbl, oldbr; Point newtl, newtr, newbl, newbr; - gboolean stationary; + gboolean stationary_l, stationary_r, stationary_t, stationary_b; POINT_SET(oldtl, self->frame->area.x, self->frame->area.y); POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1, @@ -807,20 +861,22 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, POINT_SET(newbl, newtl.x, newbr.y); /* is it moving or just resizing from some corner? */ - stationary = (POINT_EQUAL(oldtl, newtl) || POINT_EQUAL(oldtr, newtr) || - POINT_EQUAL(oldbl, newbl) || POINT_EQUAL(oldbr, newbr)); + stationary_l = oldtl.x == oldtl.x; + stationary_r = oldtr.x == oldtr.x; + stationary_t = oldtl.y == oldtl.y; + stationary_b = oldbl.y == oldbl.y; - /* if left edge is growing */ - if (stationary && newtl.x < oldtl.x) + /* if left edge is growing and didnt move right edge */ + if (stationary_r && newtl.x < oldtl.x) rudel = TRUE; - /* if right edge is growing */ - if (stationary && newtr.x > oldtr.x) + /* if right edge is growing and didnt move left edge */ + if (stationary_l && newtr.x > oldtr.x) ruder = TRUE; - /* if top edge is growing */ - if (stationary && newtl.y < oldtl.y) + /* if top edge is growing and didnt move bottom edge */ + if (stationary_b && newtl.y < oldtl.y) rudet = TRUE; - /* if bottom edge is growing */ - if (stationary && newbl.y > oldbl.y) + /* if bottom edge is growing and didnt move top edge */ + if (stationary_t && newbl.y > oldbl.y) rudeb = TRUE; } @@ -917,16 +973,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 */ @@ -956,14 +1018,16 @@ static void client_get_all(ObClient *self) #ifdef SYNC client_update_sync_request_counter(self); #endif - client_get_client_machine(self); + + /* get the session related properties */ + client_get_session_ids(self); + client_get_colormap(self); client_update_title(self); - client_update_class(self); - client_update_sm_client_id(self); client_update_strut(self); client_update_icons(self); client_update_user_time(self); + client_update_icon_geometry(self); } static void client_get_startup_id(ObClient *self) @@ -1096,7 +1160,7 @@ static void client_get_state(ObClient *self) self->below = TRUE; else if (state[i] == prop_atoms.net_wm_state_demands_attention) self->demands_attention = TRUE; - else if (state[i] == prop_atoms.ob_wm_state_undecorated) + else if (state[i] == prop_atoms.openbox_wm_state_undecorated) self->undecorated = TRUE; } @@ -1123,6 +1187,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 +1237,128 @@ 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, or if we are just becoming transient for the + group, then we need to remove any old group transient windows + from our children. But if we were already transient for the group, then + other group transients are not our children. */ + if ((oldgroup != newgroup || + (newparent == OB_TRAN_GROUP && oldparent != newparent)) && + 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_prepend(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_prepend(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_prepend(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,12 +1753,13 @@ 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; if ((hints = XGetWMHints(ob_display, self->window)) != NULL) { + gboolean ur; + if (hints->flags & InputHint) self->can_focus = hints->input; @@ -1631,59 +1769,62 @@ void client_update_wmhints(ObClient *self) if (hints->flags & StateHint) self->iconic = hints->initial_state == IconicState; + ur = self->urgent; + self->urgent = (hints->flags & XUrgencyHint); + if (self->urgent && !ur) + client_hilite(self, TRUE); + else if (!self->urgent && ur && self->demands_attention) + client_hilite(self, FALSE); + if (!(hints->flags & WindowGroupHint)) hints->window_group = None; /* 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 */ @@ -1743,34 +1884,6 @@ void client_update_title(ObClient *self) self->icon_title = data; } -void client_update_class(ObClient *self) -{ - gchar **data; - gchar *s; - - if (self->name) g_free(self->name); - if (self->class) g_free(self->class); - if (self->role) g_free(self->role); - - self->name = self->class = self->role = NULL; - - if (PROP_GETSS(self->window, wm_class, locale, &data)) { - if (data[0]) { - self->name = g_strdup(data[0]); - if (data[1]) - self->class = g_strdup(data[1]); - } - g_strfreev(data); - } - - if (PROP_GETS(self->window, wm_window_role, locale, &s)) - self->role = s; - - if (self->name == NULL) self->name = g_strdup(""); - if (self->class == NULL) self->class = g_strdup(""); - if (self->role == NULL) self->role = g_strdup(""); -} - void client_update_strut(ObClient *self) { guint num; @@ -1941,18 +2054,111 @@ void client_update_user_time(ObClient *self) } } -static void client_get_client_machine(ObClient *self) +void client_update_icon_geometry(ObClient *self) { - gchar *data = NULL; - gchar localhost[128]; + guint num; + guint32 *data; - g_free(self->client_machine); + RECT_SET(self->icon_geometry, 0, 0, 0, 0); + + if (PROP_GETA32(self->window, net_wm_icon_geometry, cardinal, &data, &num) + && num == 4) + { + /* don't let them set it with an area < 0 */ + RECT_SET(self->icon_geometry, data[0], data[1], + MAX(data[2],0), MAX(data[3],0)); + } +} + +static void client_get_session_ids(ObClient *self) +{ + guint32 leader; + gboolean got; + gchar *s; + gchar **ss; + + if (!PROP_GET32(self->window, wm_client_leader, window, &leader)) + leader = None; + + /* get the SM_CLIENT_ID */ + got = FALSE; + if (leader) + got = PROP_GETS(leader, sm_client_id, locale, &self->sm_client_id); + if (!got) + PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id); + + /* get the WM_CLASS (name and class). make them "" if they are not + provided */ + got = FALSE; + if (leader) + got = PROP_GETSS(leader, wm_class, locale, &ss); + if (!got) + got = PROP_GETSS(self->window, wm_class, locale, &ss); + + if (got) { + if (ss[0]) { + self->name = g_strdup(ss[0]); + if (ss[1]) + self->class = g_strdup(ss[1]); + } + g_strfreev(ss); + } + + if (self->name == NULL) self->name = g_strdup(""); + if (self->class == NULL) self->class = g_strdup(""); + + /* get the WM_WINDOW_ROLE. make it "" if it is not provided */ + got = FALSE; + if (leader) + got = PROP_GETS(leader, wm_window_role, locale, &s); + if (!got) + got = PROP_GETS(self->window, wm_window_role, locale, &s); + + if (got) + self->role = s; + else + self->role = g_strdup(""); + + /* get the WM_COMMAND */ + got = FALSE; + + if (leader) + got = PROP_GETSS(leader, wm_command, locale, &ss); + if (!got) + got = PROP_GETSS(self->window, wm_command, locale, &ss); + + if (got) { + /* merge/mash them all together */ + gchar *merge = NULL; + gint i; + + for (i = 0; ss[i]; ++i) { + gchar *tmp = merge; + if (merge) + merge = g_strconcat(merge, ss[i], NULL); + else + merge = g_strconcat(ss[i], NULL); + g_free(tmp); + } + g_strfreev(ss); + + self->wm_command = merge; + } + + /* get the WM_CLIENT_MACHINE */ + got = FALSE; + if (leader) + got = PROP_GETS(leader, wm_client_machine, locale, &s); + if (!got) + got = PROP_GETS(self->window, wm_client_machine, locale, &s); + + if (got) { + gchar localhost[128]; - if (PROP_GETS(self->window, wm_client_machine, locale, &data)) { gethostname(localhost, 127); localhost[127] = '\0'; - if (strcmp(localhost, data)) - self->client_machine = data; + if (strcmp(localhost, s) != 0) + self->client_machine = s; } } @@ -1963,9 +2169,11 @@ static void client_change_wm_state(ObClient *self) old = self->wmstate; - if (self->shaded || self->iconic || !self->frame->visible) + if (self->shaded || self->iconic || + (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop)) + { self->wmstate = IconicState; - else + } else self->wmstate = NormalState; if (old != self->wmstate) { @@ -2007,7 +2215,7 @@ static void client_change_state(ObClient *self) if (self->demands_attention) netstate[num++] = prop_atoms.net_wm_state_demands_attention; if (self->undecorated) - netstate[num++] = prop_atoms.ob_wm_state_undecorated; + netstate[num++] = prop_atoms.openbox_wm_state_undecorated; PROP_SETA32(self->window, net_wm_state, atom, netstate, num); if (self->frame) @@ -2058,15 +2266,22 @@ static ObStackingLayer calc_layer(ObClient *self) { ObStackingLayer l; - if (self->fullscreen && - (client_focused(self) || client_search_focus_tree(self))) - l = OB_STACKING_LAYER_FULLSCREEN; - else if (self->type == OB_CLIENT_TYPE_DESKTOP) + if (self->type == OB_CLIENT_TYPE_DESKTOP) l = OB_STACKING_LAYER_DESKTOP; else if (self->type == OB_CLIENT_TYPE_DOCK) { if (self->below) l = OB_STACKING_LAYER_NORMAL; else l = OB_STACKING_LAYER_ABOVE; } + else if ((self->fullscreen || + /* no decorations and fills the monitor = oldskool fullscreen */ + (self->frame != NULL && + (self->frame->size.right == 0 && self->frame->size.left == 0 && + self->frame->size.bottom == 0 && self->frame->size.top == 0 && + RECT_EQUAL(self->area, + *screen_physical_area_monitor + (client_monitor(self)))))) && + (client_focused(self) || client_search_focus_tree(self))) + l = OB_STACKING_LAYER_FULLSCREEN; else if (self->above) l = OB_STACKING_LAYER_ABOVE; else if (self->below) l = OB_STACKING_LAYER_BELOW; else l = OB_STACKING_LAYER_NORMAL; @@ -2116,23 +2331,6 @@ gboolean client_should_show(ObClient *self) return FALSE; if (client_normal(self) && screen_showing_desktop) return FALSE; - /* - if (self->transient_for) { - if (self->transient_for != OB_TRAN_GROUP) - return client_should_show(self->transient_for); - else { - GSList *it; - - for (it = self->group->members; it; it = g_slist_next(it)) { - ObClient *c = it->data; - if (c != self && !c->transient_for) { - if (client_should_show(c)) - return TRUE; - } - } - } - } - */ if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL) return TRUE; @@ -2189,6 +2387,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? */ @@ -2606,7 +2810,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 */ @@ -2619,22 +2824,24 @@ static void client_iconify_recursive(ObClient *self, if (changed) { client_change_state(self); + if (ob_state() != OB_STATE_STARTING && config_animate_iconify) + frame_begin_iconify_animation(self->frame, iconic); + /* do this after starting the animation so it doesn't flash */ client_showhide(self); - if (STRUT_EXISTS(self->strut)) - screen_update_areas(); } - /* iconify all direct transients */ + /* iconify all direct transients, and deiconify all transients + (non-direct too) */ for (it = self->transients; it; it = g_slist_next(it)) if (it->data != self) - if (client_is_direct_child(self, it->data)) + if (client_is_direct_child(self, it->data) || !iconic) client_iconify_recursive(it->data, iconic, curdesk); } void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk) { /* move up the transient chain as far as possible first */ - self = client_search_top_parent(self); + self = client_search_top_normal_parent(self); client_iconify_recursive(self, iconic, curdesk); } @@ -2775,11 +2982,13 @@ void client_hilite(ObClient *self, gboolean hilite) /* don't allow focused windows to hilite */ self->demands_attention = hilite && !client_focused(self); - if (self->demands_attention) - frame_flash_start(self->frame); - else - frame_flash_stop(self->frame); - client_change_state(self); + if (self->frame != NULL) { /* if we're mapping, just set the state */ + if (self->demands_attention) + frame_flash_start(self->frame); + else + frame_flash_stop(self->frame); + client_change_state(self); + } } void client_set_desktop_recursive(ObClient *self, @@ -2816,6 +3025,12 @@ void client_set_desktop_recursive(ObClient *self, focus_order_to_top(self); else focus_order_to_bottom(self); + + /* call the notifies */ + for (it = client_desktop_notifies; it; it = g_slist_next(it)) { + ClientCallback *d = it->data; + d->func(self, d->data); + } } /* move all transients */ @@ -2827,7 +3042,7 @@ void client_set_desktop_recursive(ObClient *self, void client_set_desktop(ObClient *self, guint target, gboolean donthide) { - self = client_search_top_parent(self); + self = client_search_top_normal_parent(self); client_set_desktop_recursive(self, target, donthide); } @@ -2944,7 +3159,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) action = self->demands_attention ? prop_atoms.net_wm_state_remove : prop_atoms.net_wm_state_add; - else if (state == prop_atoms.ob_wm_state_undecorated) + else if (state == prop_atoms.openbox_wm_state_undecorated) action = undecorated ? prop_atoms.net_wm_state_remove : prop_atoms.net_wm_state_add; } @@ -2974,7 +3189,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) self->below = TRUE; } else if (state == prop_atoms.net_wm_state_demands_attention) { demands_attention = TRUE; - } else if (state == prop_atoms.ob_wm_state_undecorated) { + } else if (state == prop_atoms.openbox_wm_state_undecorated) { undecorated = TRUE; } @@ -3001,7 +3216,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) self->below = FALSE; } else if (state == prop_atoms.net_wm_state_demands_attention) { demands_attention = FALSE; - } else if (state == prop_atoms.ob_wm_state_undecorated) { + } else if (state == prop_atoms.openbox_wm_state_undecorated) { undecorated = FALSE; } } @@ -3143,6 +3358,47 @@ gboolean client_focus(ObClient *self) return TRUE; } +/*! Present the client to the user. + @param raise If the client should be raised or not. You should only set + raise to false if you don't care if the window is completely + hidden. +*/ +static void client_present(ObClient *self, gboolean here, gboolean raise) +{ + /* if using focus_delay, stop the timer now so that focus doesn't + go moving on us */ + event_halt_focus_delay(); + + if (client_normal(self) && screen_showing_desktop) + screen_show_desktop(FALSE, FALSE); + if (self->iconic) + client_iconify(self, FALSE, here); + if (self->desktop != DESKTOP_ALL && + self->desktop != screen_desktop) + { + if (here) + client_set_desktop(self, screen_desktop, FALSE); + else + screen_set_desktop(self->desktop, FALSE); + } else if (!self->frame->visible) + /* if its not visible for other reasons, then don't mess + with it */ + return; + if (self->shaded) + client_shade(self, FALSE); + + client_focus(self); + + if (raise) { + /* we do this as an action here. this is rather important. this is + because we want the results from the focus change to take place + BEFORE we go about raising the window. when a fullscreen window + loses focus, we need this or else the raise wont be able to raise + above the to-lose-focus fullscreen window. */ + client_raise(self); + } +} + void client_activate(ObClient *self, gboolean here, gboolean user) { guint32 last_time = focus_client ? focus_client->user_time : CurrentTime; @@ -3165,35 +3421,7 @@ void client_activate(ObClient *self, gboolean here, gboolean user) if (event_curtime != CurrentTime) self->user_time = event_curtime; - /* if using focus_delay, stop the timer now so that focus doesn't - go moving on us */ - event_halt_focus_delay(); - - if (client_normal(self) && screen_showing_desktop) - screen_show_desktop(FALSE); - if (self->iconic) - client_iconify(self, FALSE, here); - if (self->desktop != DESKTOP_ALL && - self->desktop != screen_desktop) { - if (here) - client_set_desktop(self, screen_desktop, FALSE); - else - screen_set_desktop(self->desktop); - } else if (!self->frame->visible) - /* if its not visible for other reasons, then don't mess - with it */ - return; - if (self->shaded) - client_shade(self, FALSE); - - client_focus(self); - - /* we do this as an action here. this is rather important. this is - because we want the results from the focus change to take place - BEFORE we go about raising the window. when a fullscreen window - loses focus, we need this or else the raise wont be able to raise - above the to-lose-focus fullscreen window. */ - client_raise(self); + client_present(self, here, TRUE); } } @@ -3305,20 +3533,24 @@ guint client_monitor(ObClient *self) return screen_find_monitor(&self->frame->area); } -ObClient *client_search_top_parent(ObClient *self) +ObClient *client_search_top_normal_parent(ObClient *self) { while (self->transient_for && self->transient_for != OB_TRAN_GROUP && - client_normal(self)) + client_normal(self->transient_for)) self = self->transient_for; return self; } -GSList *client_search_all_top_parents(ObClient *self) +static GSList *client_search_all_top_parents_internal(ObClient *self, + gboolean bylayer, + ObStackingLayer layer) { GSList *ret = NULL; - + /* move up the direct transient chain as far as possible */ - while (self->transient_for && self->transient_for != OB_TRAN_GROUP) + while (self->transient_for && self->transient_for != OB_TRAN_GROUP && + (!bylayer || self->transient_for->layer == layer) && + client_normal(self->transient_for)) self = self->transient_for; if (!self->transient_for) @@ -3331,8 +3563,11 @@ GSList *client_search_all_top_parents(ObClient *self) for (it = self->group->members; it; it = g_slist_next(it)) { ObClient *c = it->data; - if (!c->transient_for && client_normal(c)) + if (!c->transient_for && client_normal(c) && + (!bylayer || c->layer == layer)) + { ret = g_slist_prepend(ret, c); + } } if (ret == NULL) /* no group parents */ @@ -3342,6 +3577,16 @@ GSList *client_search_all_top_parents(ObClient *self) return ret; } +GSList *client_search_all_top_parents(ObClient *self) +{ + return client_search_all_top_parents_internal(self, FALSE, 0); +} + +GSList *client_search_all_top_parents_layer(ObClient *self) +{ + return client_search_all_top_parents_internal(self, TRUE, self->layer); +} + ObClient *client_search_focus_parent(ObClient *self) { if (self->transient_for) { @@ -3401,21 +3646,10 @@ ObClient *client_search_transient(ObClient *self, ObClient *search) return NULL; } -void client_update_sm_client_id(ObClient *self) -{ - g_free(self->sm_client_id); - self->sm_client_id = NULL; - - if (!PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id) && - self->group) - PROP_GETS(self->group->leader, sm_client_id, locale, - &self->sm_client_id); -} - #define WANT_EDGE(cur, c) \ if(cur == c) \ continue; \ - if(!client_normal(cur)) \ + if(!client_normal(cur)) \ continue; \ if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \ continue; \ @@ -3607,7 +3841,10 @@ ObClient* client_under_pointer() if (WINDOW_IS_CLIENT(it->data)) { ObClient *c = WINDOW_AS_CLIENT(it->data); if (c->frame->visible && - RECT_CONTAINS(c->frame->area, x, y)) { + /* ignore all animating windows */ + !frame_iconify_animating(c->frame) && + RECT_CONTAINS(c->frame->area, x, y)) + { ret = c; break; } @@ -3621,3 +3858,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; +}