X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=16c249378c3ec85de487e72b5b1c8b64e817bb25;hb=2e7359a2fd4fa7cad349651392348553c9623053;hp=9fbd0b2a4ad63a8aaa061b8542747b31e3a77e96;hpb=f03eef279530d271640a6e146dc4d651921af3dd;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index 9fbd0b2a..16c24937 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -64,7 +64,8 @@ typedef struct GList *client_list = NULL; -static GSList *client_destructors = NULL; +static GSList *client_destroy_notifies = NULL; +static GSList *client_hide_notifies = NULL; static void client_get_all(ObClient *self, gboolean real); static void client_toggle_border(ObClient *self, gboolean show); @@ -73,14 +74,13 @@ 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); -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_colormap(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_apply_startup_state(ObClient *self); static void client_restore_session_state(ObClient *self); static gboolean client_restore_session_stacking(ObClient *self); static ObAppSettings *client_get_settings_state(ObClient *self); @@ -92,6 +92,7 @@ static void client_present(ObClient *self, gboolean here, gboolean raise); static GSList *client_search_all_top_parents_internal(ObClient *self, gboolean bylayer, ObStackingLayer layer); +static void client_call_notifies(ObClient *self, GSList *list); void client_startup(gboolean reconfig) { @@ -105,23 +106,57 @@ void client_shutdown(gboolean reconfig) if (reconfig) return; } -void client_add_destructor(ObClientCallback func, gpointer data) +static void client_call_notifies(ObClient *self, GSList *list) +{ + GSList *it; + + for (it = list; it; it = g_slist_next(it)) { + ClientCallback *d = it->data; + d->func(self, d->data); + } +} + +void client_add_destroy_notify(ObClientCallback func, gpointer data) { ClientCallback *d = g_new(ClientCallback, 1); d->func = func; d->data = data; - client_destructors = g_slist_prepend(client_destructors, d); + client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d); } -void client_remove_destructor(ObClientCallback func) +void client_remove_destroy_notify(ObClientCallback func) { GSList *it; - for (it = client_destructors; it; it = g_slist_next(it)) { + for (it = client_destroy_notifies; it; it = g_slist_next(it)) { ClientCallback *d = it->data; if (d->func == func) { g_free(d); - client_destructors = g_slist_delete_link(client_destructors, it); + client_destroy_notifies = + g_slist_delete_link(client_destroy_notifies, it); + break; + } + } +} + +void client_add_hide_notify(ObClientCallback func, gpointer data) +{ + ClientCallback *d = g_new(ClientCallback, 1); + d->func = func; + d->data = data; + client_hide_notifies = g_slist_prepend(client_hide_notifies, d); +} + +void client_remove_hide_notify(ObClientCallback func) +{ + GSList *it; + + for (it = client_hide_notifies; it; it = g_slist_next(it)) { + ClientCallback *d = it->data; + if (d->func == func) { + g_free(d); + client_hide_notifies = + g_slist_delete_link(client_hide_notifies, it); break; } } @@ -230,7 +265,6 @@ void client_manage(Window window) XWMHints *wmhint; gboolean activate = FALSE; ObAppSettings *settings; - gint newx, newy; grab_server(TRUE); @@ -283,31 +317,13 @@ void client_manage(Window window) /* non-zero defaults */ self->wmstate = WithdrawnState; /* make sure it gets updated first time */ - self->layer = -1; + self->gravity = NorthWestGravity; self->desktop = screen_num_desktops; /* always an invalid value */ self->user_time = focus_client ? focus_client->user_time : CurrentTime; + /* get all the stuff off the window */ client_get_all(self, TRUE); - /* per-app settings override stuff, and return the settings for other - uses too */ - settings = client_get_settings_state(self); - /* the session should get the last say */ - client_restore_session_state(self); - - client_calc_layer(self); - - { - Time t = sn_app_started(self->startup_id, self->class); - if (t) self->user_time = t; - } - /* update the focus lists, do this before the call to change_state or - it can end up in the list twice! */ - focus_order_add_new(self); - - /* remove the client's border (and adjust re gravity) */ - client_toggle_border(self, FALSE); - /* specify that if we exit, the window should not be destroyed and should be reparented back to root automatically */ XChangeSaveSet(ob_display, window, SetModeInsert); @@ -317,13 +333,36 @@ void client_manage(Window window) frame_grab_client(self->frame); + /* we've grabbed everything and set everything that we need to at mapping + time now */ + grab_server(FALSE); + + /* per-app settings override stuff from client_get_all, and return the + settings for other uses too */ + settings = client_get_settings_state(self); + /* the session should get the last say thought */ + client_restore_session_state(self); + + /* now we have all of the window's information so we can set this up */ + client_setup_decor_and_functions(self); + + /* remove the client's border (and adjust re gravity) */ + client_toggle_border(self, FALSE); + + { + Time t = sn_app_started(self->startup_id, self->class); + if (t) self->user_time = t; + } + /* do this after we have a frame.. it uses the frame to help determine the WM_STATE to apply. */ client_change_state(self); - grab_server(FALSE); + /* add ourselves to the focus order */ + focus_order_add_new(self); - stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); + /* do this to add ourselves to the stacking list in a non-intrusive way */ + client_calc_layer(self); /* focus the new window? */ if (ob_state() != OB_STATE_STARTING && @@ -343,20 +382,21 @@ void client_manage(Window window) activate = TRUE; } - /* get the current position */ - newx = self->area.x; - newy = self->area.y; - /* figure out placement for the window */ if (ob_state() == OB_STATE_RUNNING) { gboolean transient; - transient = place_client(self, &newx, &newy, settings); + ob_debug("Positioned: %s @ %d %d\n", + (!self->positioned ? "no" : + (self->positioned == PPosition ? "program specified" : + (self->positioned == USPosition ? "user specified" : + "BADNESS !?"))), self->area.x, self->area.y); + + transient = place_client(self, &self->area.x, &self->area.y, settings); /* make sure the window is visible. */ - client_find_onscreen(self, &newx, &newy, - self->area.width, - self->area.height, + client_find_onscreen(self, &self->area.x, &self->area.y, + 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 @@ -374,19 +414,32 @@ void client_manage(Window window) !self->session)); } - /* do this after the window is placed, so the premax/prefullscreen numbers - won't be all wacko!! - also, this moves the window to the position where it has been placed - */ 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); + self->window, self->area.x, self->area.y, + self->area.width, self->area.height); if (self->session) - ob_debug("session requested %d %d\n", + ob_debug(" but session requested %d %d instead, overriding\n", self->session->x, self->session->y); - client_apply_startup_state(self, newx, newy); + /* adjust the frame to the client's size before showing the window */ + frame_adjust_area(self->frame, FALSE, TRUE, FALSE); + frame_adjust_client_area(self->frame); - mouse_grab_for_client(self, TRUE); + + /* move the client to its placed position, or it it's already there, + generate a ConfigureNotify telling the client where it is. + + do this after adjusting the frame. otherwise it gets all weird and + clients don't work right */ + client_configure_full(self, self->area.x, self->area.y, + self->area.width, self->area.height, + FALSE, TRUE); + + /* do this after the window is placed, so the premax/prefullscreen numbers + won't be all wacko!! + also, this moves the window to the position where it has been placed + */ + client_apply_startup_state(self); if (activate) { guint32 last_time = focus_client ? @@ -451,19 +504,16 @@ void client_manage(Window window) raised to the top. Legacy begets legacy I guess? */ if (!client_restore_session_stacking(self)) - client_raise(self); + stacking_raise(CLIENT_AS_WINDOW(self)); } + mouse_grab_for_client(self, TRUE); + /* this has to happen before we try focus the window, but we want it to happen after the client's stacking has been determined or it looks bad */ client_show(self); - /* use client_focus instead of client_activate cuz client_activate does - stuff like switch desktops etc and I'm not interested in all that when - 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) { gboolean stacked = client_restore_session_stacking(self); client_present(self, FALSE, !stacked); @@ -480,7 +530,8 @@ void client_manage(Window window) /* update the list hints */ client_set_list(); - ob_debug("Managed window 0x%lx (%s)\n", window, self->class); + ob_debug("Managed window 0x%lx plate 0x%x (%s)\n", + window, self->frame->plate, self->class); return; } @@ -503,8 +554,11 @@ ObClient *client_fake_manage(Window window) uses too */ settings = client_get_settings_state(self); - /* create the decoration frame for the client window */ + client_setup_decor_and_functions(self); + + /* create the decoration frame for the client window and adjust its size */ self->frame = frame_new(self); + frame_adjust_area(self->frame, FALSE, TRUE, TRUE); return self; } @@ -519,7 +573,8 @@ void client_unmanage(ObClient *self) guint j; GSList *it; - ob_debug("Unmanaging window: %lx (%s) (%s)\n", self->window, + ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)\n", + self->window, self->frame->plate, self->class, self->title ? self->title : ""); g_assert(self != NULL); @@ -560,10 +615,7 @@ void client_unmanage(ObClient *self) if (STRUT_EXISTS(self->strut)) screen_update_areas(); - for (it = client_destructors; it; it = g_slist_next(it)) { - ClientCallback *d = it->data; - d->func(self, d->data); - } + client_call_notifies(self, client_destroy_notifies); /* tell our parent(s) that we're gone */ if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */ @@ -592,7 +644,12 @@ void client_unmanage(ObClient *self) /* restore the window's original geometry so it is not lost */ { - Rect a = self->area; + Rect a; + + /* give the client its border back */ + client_toggle_border(self, TRUE); + + a = self->area; if (self->fullscreen) a = self->pre_fullscreen_area; @@ -607,9 +664,6 @@ void client_unmanage(ObClient *self) } } - /* give the client its border back */ - client_toggle_border(self, TRUE); - self->fullscreen = self->max_horz = self->max_vert = FALSE; self->decorations = 0; /* unmanaged windows have no decor */ @@ -993,51 +1047,50 @@ static void client_get_all(ObClient *self, gboolean real) /* this can change the mwmhints for special cases */ client_get_type_and_transientness(self); client_get_state(self); - client_update_protocols(self); client_update_normal_hints(self); - /* got the type, the mwmhints, the protocols, and the normal hints - (min/max sizes), so we're ready to set up the decorations/functions */ - client_setup_decor_and_functions(self); + /* get the session related properties, these can change decorations + from per-app settings */ + client_get_session_ids(self); - if (real) { - client_update_wmhints(self); - /* this may have already been called from client_update_wmhints */ - if (self->transient_for == NULL) - client_update_transient_for(self); + /* now we got everything that can affect the decorations */ + if (!real) + return; - client_get_startup_id(self); - client_get_desktop(self);/* uses transient data/group/startup id if a - desktop is not specified */ - client_get_shaped(self); + /* get this early so we have it for debugging */ + client_update_title(self); - client_get_layer(self); /* if layer hasn't been specified, get it from - other sources if possible */ + client_update_protocols(self); - { - /* a couple type-based defaults for new windows */ + client_update_wmhints(self); + /* this may have already been called from client_update_wmhints */ + if (self->transient_for == NULL) + client_update_transient_for(self); - /* this makes sure that these windows appear on all desktops */ - if (self->type == OB_CLIENT_TYPE_DESKTOP) - self->desktop = DESKTOP_ALL; - } + client_get_startup_id(self); + client_get_desktop(self);/* uses transient data/group/startup id if a + desktop is not specified */ + client_get_shaped(self); + + { + /* a couple type-based defaults for new windows */ + + /* this makes sure that these windows appear on all desktops */ + if (self->type == OB_CLIENT_TYPE_DESKTOP) + self->desktop = DESKTOP_ALL; + } #ifdef SYNC - client_update_sync_request_counter(self); + client_update_sync_request_counter(self); #endif - /* get the session related properties */ - client_get_session_ids(self); - - client_get_colormap(self); - client_update_title(self); - client_update_strut(self); - client_update_icons(self); - client_update_user_time_window(self); - if (!self->user_time_window) /* check if this would have been called */ - client_update_user_time(self); - client_update_icon_geometry(self); - } + client_get_colormap(self); + client_update_strut(self); + client_update_icons(self); + client_update_user_time_window(self); + if (!self->user_time_window) /* check if this would have been called */ + client_update_user_time(self); + client_update_icon_geometry(self); } static void client_get_startup_id(ObClient *self) @@ -1105,41 +1158,6 @@ static void client_get_desktop(ObClient *self) } } -static void client_get_layer(ObClient *self) -{ - if (!(self->above || self->below)) { - if (self->group) { - /* apply stuff from the group */ - GSList *it; - gint layer = -2; - - for (it = self->group->members; it; it = g_slist_next(it)) { - ObClient *c = it->data; - if (c != self && !client_search_transient(self, c) && - client_normal(self) && client_normal(c)) - { - layer = MAX(layer, - (c->above ? 1 : (c->below ? -1 : 0))); - } - } - switch (layer) { - case -1: - self->below = TRUE; - break; - case -2: - case 0: - break; - case 1: - self->above = TRUE; - break; - default: - g_assert_not_reached(); - break; - } - } - } -} - static void client_get_state(ObClient *self) { guint32 *state; @@ -1170,7 +1188,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.openbox_wm_state_undecorated) + else if (state[i] == prop_atoms.ob_wm_state_undecorated) self->undecorated = TRUE; } @@ -1256,19 +1274,34 @@ static void client_update_transient_tree(ObClient *self, GSList *it, *next; ObClient *c; + /* * * + Group transient windows are not allowed to have other group + transient windows as their children. + * * */ + + /* 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 */ + then any group transients which were our children and are now becoming + our parents need to stop being our children. + + Group transients can't be children of group transients already, but + we could have any number of direct parents above up, any of which could + be transient for the group, and we need to remove it from our children. + */ 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); + ObClient *look = newparent; + do { + self->transients = g_slist_remove(self->transients, look); + look = look->transient_for; + } while (look != NULL && look != OB_TRAN_GROUP); } @@ -1340,7 +1373,9 @@ static void client_update_transient_tree(ObClient *self, 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 + B is transient for A + C is transient for B + A can't be transient for C or we have a cycle */ if (oldgroup != newgroup && newgroup != NULL && newparent != OB_TRAN_GROUP) @@ -1572,7 +1607,9 @@ void client_setup_decor_and_functions(ObClient *self) OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_MAXIMIZE | OB_CLIENT_FUNC_SHADE | - OB_CLIENT_FUNC_CLOSE); + OB_CLIENT_FUNC_CLOSE | + OB_CLIENT_FUNC_BELOW | + OB_CLIENT_FUNC_ABOVE); if (!(self->min_size.width < self->max_size.width || self->min_size.height < self->max_size.height)) @@ -1604,10 +1641,15 @@ void client_setup_decor_and_functions(ObClient *self) self->functions = OB_CLIENT_FUNC_MOVE; case OB_CLIENT_TYPE_DESKTOP: - case OB_CLIENT_TYPE_DOCK: /* these windows are not manipulated by the window manager */ self->decorations = 0; self->functions = 0; + + case OB_CLIENT_TYPE_DOCK: + /* these windows are not manipulated by the window manager, but they + can set below layer which has a special meaning */ + self->decorations = 0; + self->functions = OB_CLIENT_FUNC_BELOW; break; } @@ -1652,7 +1694,7 @@ void client_setup_decor_and_functions(ObClient *self) if (!(self->functions & OB_CLIENT_FUNC_ICONIFY)) self->decorations &= ~OB_FRAME_DECOR_ICONIFY; if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) - self->decorations &= ~OB_FRAME_DECOR_GRIPS; + self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE); /* can't maximize without moving/resizing */ if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) && @@ -1664,7 +1706,7 @@ void client_setup_decor_and_functions(ObClient *self) /* kill the handle on fully maxed windows */ if (self->max_vert && self->max_horz) - self->decorations &= ~OB_FRAME_DECOR_HANDLE; + self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS); /* finally, the user can have requested no decorations, which overrides everything (but doesnt give it a border if it doesnt have one) */ @@ -1697,7 +1739,7 @@ void client_setup_decor_and_functions(ObClient *self) static void client_change_allowed_actions(ObClient *self) { - gulong actions[9]; + gulong actions[11]; gint num = 0; /* desktop windows are kept on all desktops */ @@ -1720,6 +1762,10 @@ static void client_change_allowed_actions(ObClient *self) actions[num++] = prop_atoms.net_wm_action_maximize_horz; actions[num++] = prop_atoms.net_wm_action_maximize_vert; } + if (self->functions & OB_CLIENT_FUNC_ABOVE) + actions[num++] = prop_atoms.net_wm_action_above; + if (self->functions & OB_CLIENT_FUNC_BELOW) + actions[num++] = prop_atoms.net_wm_action_below; PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num); @@ -1730,7 +1776,7 @@ static void client_change_allowed_actions(ObClient *self) else self->shaded = FALSE; } if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) { - if (self->frame) client_iconify(self, FALSE, TRUE); + if (self->frame) client_iconify(self, FALSE, TRUE, FALSE); else self->iconic = FALSE; } if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) { @@ -2257,7 +2303,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.openbox_wm_state_undecorated; + netstate[num++] = prop_atoms.ob_wm_state_undecorated; PROP_SETA32(self->window, net_wm_state, atom, netstate, num); if (self->frame) @@ -2321,9 +2367,12 @@ static ObStackingLayer calc_layer(ObClient *self) 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; + (client_monitor(self))))))) { + if (client_focused(self) || client_search_focus_tree(self)) + l = OB_STACKING_LAYER_FULLSCREEN; + else + l = OB_STACKING_LAYER_FULLSCREEN_BELOW; + } else if (self->above) l = OB_STACKING_LAYER_ABOVE; else if (self->below) l = OB_STACKING_LAYER_BELOW; else l = OB_STACKING_LAYER_NORMAL; @@ -2332,7 +2381,7 @@ static ObStackingLayer calc_layer(ObClient *self) } static void client_calc_layer_recursive(ObClient *self, ObClient *orig, - ObStackingLayer min, gboolean raised) + ObStackingLayer min) { ObStackingLayer old, own; GSList *it; @@ -2341,16 +2390,14 @@ static void client_calc_layer_recursive(ObClient *self, ObClient *orig, own = calc_layer(self); self->layer = MAX(own, min); + if (self->layer != old) { + stacking_remove(CLIENT_AS_WINDOW(self)); + stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); + } + for (it = self->transients; it; it = g_slist_next(it)) client_calc_layer_recursive(it->data, orig, - self->layer, - raised ? raised : self->layer != old); - - if (!raised && self->layer != old) - if (orig->frame) { /* only restack if the original window is managed */ - stacking_remove(CLIENT_AS_WINDOW(self)); - stacking_add(CLIENT_AS_WINDOW(self)); - } + self->layer); } void client_calc_layer(ObClient *self) @@ -2364,7 +2411,7 @@ void client_calc_layer(ObClient *self) it = client_search_all_top_parents(self); for (; it; it = g_slist_next(it)) - client_calc_layer_recursive(it->data, orig, 0, FALSE); + client_calc_layer_recursive(it->data, orig, 0); } gboolean client_should_show(ObClient *self) @@ -2397,6 +2444,8 @@ void client_hide(ObClient *self) { if (!client_should_show(self)) { frame_hide(self->frame); + + client_call_notifies(self, client_hide_notifies); } /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it @@ -2414,6 +2463,8 @@ void client_showhide(ObClient *self) } else { frame_hide(self->frame); + + client_call_notifies(self, client_hide_notifies); } /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it @@ -2452,17 +2503,8 @@ gboolean client_enter_focusable(ObClient *self) } -static void client_apply_startup_state(ObClient *self, gint x, gint y) +static void client_apply_startup_state(ObClient *self) { - gboolean pos = FALSE; /* has the window's position been configured? */ - gint ox, oy; - - /* save the position, and set self->area for these to use */ - ox = self->area.x; - oy = self->area.y; - self->area.x = x; - self->area.y = y; - /* set the desktop hint, to make sure that it always exists */ PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop); @@ -2470,12 +2512,11 @@ static void client_apply_startup_state(ObClient *self, gint x, gint y) if (self->iconic) { self->iconic = FALSE; - client_iconify(self, TRUE, FALSE); + client_iconify(self, TRUE, FALSE, TRUE); } if (self->fullscreen) { self->fullscreen = FALSE; client_fullscreen(self, TRUE); - pos = TRUE; } if (self->undecorated) { self->undecorated = FALSE; @@ -2493,27 +2534,12 @@ static void client_apply_startup_state(ObClient *self, gint x, gint y) if (self->max_vert && self->max_horz) { self->max_vert = self->max_horz = FALSE; client_maximize(self, TRUE, 0); - pos = TRUE; } else if (self->max_vert) { self->max_vert = FALSE; client_maximize(self, TRUE, 2); - pos = TRUE; } else if (self->max_horz) { self->max_horz = FALSE; client_maximize(self, TRUE, 1); - pos = TRUE; - } - - /* if the client didn't get positioned yet, then do so now. - call client_move even if the window is not being moved anywhere, because - when we reparent it and decorate it, it is getting moved and we need to - be telling it so with a ConfigureNotify event. - */ - if (!pos) { - /* use the saved position */ - self->area.x = ox; - self->area.y = oy; - client_move(self, x, y); } /* nothing to do for the other states: @@ -2695,12 +2721,11 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h, - gboolean user, gboolean final, - gboolean force_reply) + gboolean user, gboolean final) { - gint oldw, oldh, oldrx, oldry; + gint oldw, oldh; gboolean send_resize_client; - gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE; + gboolean moved = FALSE, resized = FALSE; guint fdecor = self->frame->decorations; gboolean fhorz = self->frame->max_horz; gint logicalw, logicalh; @@ -2728,8 +2753,9 @@ void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h, (resized && config_resize_redraw)))); /* if the client is enlarging, then resize the client before the frame */ - if (send_resize_client && user && (w > oldw || h > oldh)) { - XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh)); + if (send_resize_client && (w > oldw || h > oldh)) { + XResizeWindow(ob_display, self->window, + MAX(w, oldw), MAX(h, oldh)); /* resize the plate to show the client padding color underneath */ frame_adjust_client_area(self->frame); } @@ -2740,17 +2766,7 @@ void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h, if (moved || resized) frame_adjust_area(self->frame, moved, resized, FALSE); - /* find the client's position relative to the root window */ - oldrx = self->root_pos.x; - oldry = self->root_pos.y; - rootmoved = (oldrx != (signed)(self->frame->area.x + - self->frame->size.left - - self->border_width) || - oldry != (signed)(self->frame->area.y + - self->frame->size.top - - self->border_width)); - - if (force_reply || ((!user || (user && final)) && rootmoved)) + if ((!user || (user && final)) && !resized) { XEvent event; @@ -2765,6 +2781,9 @@ void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h, event.xconfigure.event = self->window; event.xconfigure.window = self->window; + ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d\n", + self->title, self->root_pos.x, self->root_pos.y, w, h); + /* root window real coords */ event.xconfigure.x = self->root_pos.x; event.xconfigure.y = self->root_pos.y; @@ -2778,11 +2797,12 @@ void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h, } /* if the client is shrinking, then resize the frame before the client */ - if (send_resize_client && (!user || (w <= oldw || h <= oldh))) { + if (send_resize_client && (w <= oldw || h <= oldh)) { /* resize the plate to show the client padding color underneath */ frame_adjust_client_area(self->frame); - XResizeWindow(ob_display, self->window, w, h); + if (send_resize_client) + XResizeWindow(ob_display, self->window, w, h); } XFlush(ob_display); @@ -2797,7 +2817,6 @@ void client_fullscreen(ObClient *self, gboolean fs) self->fullscreen = fs; client_change_state(self); /* change the state hints on the client */ - client_calc_layer(self); /* and adjust out layer/stacking */ if (fs) { self->pre_fullscreen_area = self->area; @@ -2812,40 +2831,41 @@ void client_fullscreen(ObClient *self, gboolean fs) self->pre_fullscreen_area.height = self->pre_max_area.height; } - /* these are not actually used cuz client_configure will set them - as appropriate when the window is fullscreened */ - x = y = w = h = 0; + /* these will help configure_full figure out where to fullscreen + the window */ + x = self->area.x; + y = self->area.y; + w = self->area.width; + h = self->area.height; } else { - Rect *a; + g_assert(self->pre_fullscreen_area.width > 0 && + self->pre_fullscreen_area.height > 0); - if (self->pre_fullscreen_area.width > 0 && - self->pre_fullscreen_area.height > 0) - { - x = self->pre_fullscreen_area.x; - y = self->pre_fullscreen_area.y; - w = self->pre_fullscreen_area.width; - h = self->pre_fullscreen_area.height; - RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0); - } else { - /* pick some fallbacks... */ - a = screen_area_monitor(self->desktop, 0); - x = a->x + a->width / 4; - y = a->y + a->height / 4; - w = a->width / 2; - h = a->height / 2; - } + x = self->pre_fullscreen_area.x; + y = self->pre_fullscreen_area.y; + w = self->pre_fullscreen_area.width; + h = self->pre_fullscreen_area.height; + RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0); } client_setup_decor_and_functions(self); client_move_resize(self, x, y, w, h); - /* try focus us when we go into fullscreen mode */ - client_focus(self); + /* and adjust our layer/stacking. do this after resizing the window, + and applying decorations, because windows which fill the screen are + considered "fullscreen" and it affects their layer */ + client_calc_layer(self); + + if (fs) { + /* try focus us when we go into fullscreen mode */ + client_focus(self); + } } static void client_iconify_recursive(ObClient *self, - gboolean iconic, gboolean curdesk) + gboolean iconic, gboolean curdesk, + gboolean hide_animation) { GSList *it; gboolean changed = FALSE; @@ -2885,7 +2905,7 @@ static void client_iconify_recursive(ObClient *self, if (changed) { client_change_state(self); - if (ob_state() != OB_STATE_STARTING && config_animate_iconify) + if (config_animate_iconify && !hide_animation) frame_begin_iconify_animation(self->frame, iconic); /* do this after starting the animation so it doesn't flash */ client_showhide(self); @@ -2896,15 +2916,17 @@ static void client_iconify_recursive(ObClient *self, for (it = self->transients; it; it = g_slist_next(it)) if (it->data != self) if (client_is_direct_child(self, it->data) || !iconic) - client_iconify_recursive(it->data, iconic, curdesk); + client_iconify_recursive(it->data, iconic, curdesk, + hide_animation); } -void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk) +void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk, + gboolean hide_animation) { if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) { /* move up the transient chain as far as possible first */ self = client_search_top_normal_parent(self); - client_iconify_recursive(self, iconic, curdesk); + client_iconify_recursive(self, iconic, curdesk, hide_animation); } } @@ -2926,8 +2948,8 @@ void client_maximize(ObClient *self, gboolean max, gint dir) if (dir == 2 && !self->max_vert) return; } - /* we just tell it to configure in the same place and client_configure - worries about filling the screen with the window */ + /* these will help configure_full figure out which screen to fill with + the window */ x = self->area.x; y = self->area.y; w = self->area.width; @@ -2945,34 +2967,23 @@ void client_maximize(ObClient *self, gboolean max, gint dir) self->pre_max_area.width, self->area.height); } } else { - Rect *a; - - a = screen_area_monitor(self->desktop, 0); if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */ - if (self->pre_max_area.width > 0) { - x = self->pre_max_area.x; - w = self->pre_max_area.width; + g_assert(self->pre_max_area.width > 0); - RECT_SET(self->pre_max_area, 0, self->pre_max_area.y, - 0, self->pre_max_area.height); - } else { - /* pick some fallbacks... */ - x = a->x + a->width / 4; - w = a->width / 2; - } + x = self->pre_max_area.x; + w = self->pre_max_area.width; + + RECT_SET(self->pre_max_area, 0, self->pre_max_area.y, + 0, self->pre_max_area.height); } if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */ - if (self->pre_max_area.height > 0) { - y = self->pre_max_area.y; - h = self->pre_max_area.height; + g_assert(self->pre_max_area.height > 0); - RECT_SET(self->pre_max_area, self->pre_max_area.x, 0, - self->pre_max_area.width, 0); - } else { - /* pick some fallbacks... */ - y = a->y + a->height / 4; - h = a->height / 2; - } + y = self->pre_max_area.y; + h = self->pre_max_area.height; + + RECT_SET(self->pre_max_area, self->pre_max_area.x, 0, + self->pre_max_area.width, 0); } } @@ -3077,7 +3088,7 @@ void client_set_desktop_recursive(ObClient *self, client_showhide(self); /* raise if it was not already on the desktop */ if (old != DESKTOP_ALL) - client_raise(self); + stacking_raise(CLIENT_AS_WINDOW(self)); if (STRUT_EXISTS(self->strut)) screen_update_areas(); } @@ -3138,10 +3149,10 @@ void client_set_wm_state(ObClient *self, glong state) switch (state) { case IconicState: - client_iconify(self, TRUE, TRUE); + client_iconify(self, TRUE, TRUE, FALSE); break; case NormalState: - client_iconify(self, FALSE, TRUE); + client_iconify(self, FALSE, TRUE, FALSE); break; } } @@ -3211,7 +3222,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.openbox_wm_state_undecorated) + else if (state == prop_atoms.ob_wm_state_undecorated) action = undecorated ? prop_atoms.net_wm_state_remove : prop_atoms.net_wm_state_add; } @@ -3241,7 +3252,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) below = TRUE; } else if (state == prop_atoms.net_wm_state_demands_attention) { demands_attention = TRUE; - } else if (state == prop_atoms.openbox_wm_state_undecorated) { + } else if (state == prop_atoms.ob_wm_state_undecorated) { undecorated = TRUE; } @@ -3268,7 +3279,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) below = FALSE; } else if (state == prop_atoms.net_wm_state_demands_attention) { demands_attention = FALSE; - } else if (state == prop_atoms.openbox_wm_state_undecorated) { + } else if (state == prop_atoms.ob_wm_state_undecorated) { undecorated = FALSE; } } @@ -3302,10 +3313,10 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) self->modal = modal; /* when a window changes modality, then its stacking order with its transients needs to change */ - client_raise(self); + stacking_raise(CLIENT_AS_WINDOW(self)); } if (iconic != self->iconic) - client_iconify(self, iconic, FALSE); + client_iconify(self, iconic, FALSE, FALSE); if (demands_attention != self->demands_attention) client_hilite(self, demands_attention); @@ -3330,8 +3341,6 @@ ObClient *client_focus_target(ObClient *self) gboolean client_can_focus(ObClient *self) { - XEvent ev; - /* choose the correct target */ self = client_focus_target(self); @@ -3341,24 +3350,6 @@ gboolean client_can_focus(ObClient *self) if (!(self->can_focus || self->focus_notify)) return FALSE; - /* do a check to see if the window has already been unmapped or destroyed - do this intelligently while watching out for unmaps we've generated - (ignore_unmaps > 0) */ - if (XCheckTypedWindowEvent(ob_display, self->window, - DestroyNotify, &ev)) { - XPutBackEvent(ob_display, &ev); - return FALSE; - } - while (XCheckTypedWindowEvent(ob_display, self->window, - UnmapNotify, &ev)) { - if (self->ignore_unmaps) { - self->ignore_unmaps--; - } else { - XPutBackEvent(ob_display, &ev); - return FALSE; - } - } - return TRUE; } @@ -3379,6 +3370,16 @@ gboolean client_focus(ObClient *self) "Focusing client \"%s\" at time %u\n", self->title, event_curtime); + /* if there is a grab going on, then we need to cancel it. if we move + focus during the grab, applications will get NotifyWhileGrabbed events + and ignore them ! + + actions should not rely on being able to move focus during an + interactive grab. + */ + if (keyboard_interactively_grabbed()) + keyboard_interactive_cancel(); + if (self->can_focus) { /* This can cause a BadMatch error with CurrentTime, or if an app passed in a bad time for _NET_WM_ACTIVE_WINDOW. */ @@ -3428,9 +3429,9 @@ static void client_present(ObClient *self, gboolean here, gboolean raise) event_halt_focus_delay(); if (client_normal(self) && screen_showing_desktop) - screen_show_desktop(FALSE, FALSE); + screen_show_desktop(FALSE, self); if (self->iconic) - client_iconify(self, FALSE, here); + client_iconify(self, FALSE, here, FALSE); if (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop) { @@ -3444,17 +3445,10 @@ static void client_present(ObClient *self, gboolean here, gboolean raise) return; if (self->shaded) client_shade(self, FALSE); + if (raise) + stacking_raise(CLIENT_AS_WINDOW(self)); 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) @@ -3510,16 +3504,6 @@ void client_bring_helper_windows(ObClient *self) client_bring_helper_windows_recursive(self, self->desktop); } -void client_raise(ObClient *self) -{ - action_run_string("Raise", self, CurrentTime); -} - -void client_lower(ObClient *self) -{ - action_run_string("Lower", self, CurrentTime); -} - gboolean client_focused(ObClient *self) { return self == focus_client; @@ -3528,9 +3512,7 @@ gboolean client_focused(ObClient *self) static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h) { guint i; - /* si is the smallest image >= req */ - /* li is the largest image < req */ - gulong size, smallest = 0xffffffff, largest = 0, si = 0, li = 0; + gulong min_diff, min_i; if (!self->nicons) { ObClientIcon *parent = NULL; @@ -3553,20 +3535,23 @@ static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h) return parent; } - for (i = 0; i < self->nicons; ++i) { - size = self->icons[i].width * self->icons[i].height; - if (size < smallest && size >= (unsigned)(w * h)) { - smallest = size; - si = i; - } - if (size > largest && size <= (unsigned)(w * h)) { - largest = size; - li = i; + /* some kind of crappy approximation to find the icon closest in size to + what we requested, but icons are generally all the same ratio as + eachother so it's good enough. */ + + min_diff = ABS(self->icons[0].width - w) + ABS(self->icons[0].height - h); + min_i = 0; + + for (i = 1; i < self->nicons; ++i) { + gulong diff; + + diff = ABS(self->icons[0].width - w) + ABS(self->icons[0].height - h); + if (diff < min_diff) { + min_diff = diff; + min_i = i; } } - if (largest == 0) /* didnt find one smaller than the requested size */ - return &self->icons[si]; - return &self->icons[li]; + return &self->icons[min_i]; } const ObClientIcon* client_icon(ObClient *self, gint w, gint h) @@ -3739,8 +3724,6 @@ ObClient *client_search_transient(ObClient *self, ObClient *search) if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \ continue; \ if(cur->iconic) \ - continue; \ - if(cur->layer < c->layer && !config_resist_layers_below) \ continue; #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \