X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=da38843b74e210b56f9694fe644fbc2fed360ed1;hb=b8f207892a11b53365c47b4828254269fca5c8e9;hp=6bc2b630f00046ccf5b7f9d10e7bb0c13d971a0e;hpb=f1f946ea02814bc06f23d6b402d3e901cce13ee1;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index 6bc2b630..da38843b 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -64,7 +64,7 @@ typedef struct GList *client_list = NULL; -static GSList *client_destructors = NULL; +static GSList *client_destroy_notifies = NULL; static void client_get_all(ObClient *self, gboolean real); static void client_toggle_border(ObClient *self, gboolean show); @@ -79,7 +79,7 @@ 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); @@ -91,6 +91,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) { @@ -104,23 +105,34 @@ 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; } } @@ -229,7 +241,6 @@ void client_manage(Window window) XWMHints *wmhint; gboolean activate = FALSE; ObAppSettings *settings; - gint newx, newy; grab_server(TRUE); @@ -282,30 +293,13 @@ void client_manage(Window window) /* non-zero defaults */ self->wmstate = WithdrawnState; /* make sure it gets updated first time */ + 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_setup_decor_and_functions(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); @@ -315,11 +309,34 @@ 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. the returned settings is a shallow copy, + that needs to be freed with g_free(). */ + settings = client_get_settings_state(self); + /* the session should get the last say 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); /* do this to add ourselves to the stacking list in a non-intrusive way */ client_calc_layer(self); @@ -342,20 +359,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 @@ -373,19 +391,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(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 ? @@ -453,16 +484,13 @@ void client_manage(Window window) 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); @@ -479,7 +507,11 @@ void client_manage(Window window) /* update the list hints */ client_set_list(); - ob_debug("Managed window 0x%lx (%s)\n", window, self->class); + /* free the ObAppSettings shallow copy */ + g_free(settings); + + ob_debug("Managed window 0x%lx plate 0x%x (%s)\n", + window, self->frame->plate, self->class); return; } @@ -499,7 +531,7 @@ ObClient *client_fake_manage(Window window) client_get_all(self, FALSE); /* per-app settings override stuff, and return the settings for other - uses too */ + uses too. this returns a shallow copy that needs to be freed */ settings = client_get_settings_state(self); client_setup_decor_and_functions(self); @@ -507,6 +539,10 @@ ObClient *client_fake_manage(Window window) /* 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); + + /* free the ObAppSettings shallow copy */ + g_free(settings); + return self; } @@ -521,7 +557,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); @@ -536,7 +573,7 @@ void client_unmanage(ObClient *self) /* ignore enter events from the unmap so it doesnt mess with the focus */ - event_ignore_queued_enters(); + event_ignore_all_queued_enters(); mouse_grab_for_client(self, FALSE); @@ -562,10 +599,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 */ @@ -594,7 +628,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; @@ -609,9 +648,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 */ @@ -666,30 +702,39 @@ void client_fake_unmanage(ObClient *self) g_free(self); } +/*! Returns a new structure containing the per-app settings for this client. + The returned structure needs to be freed with g_free. */ static ObAppSettings *client_get_settings_state(ObClient *self) { - ObAppSettings *settings = NULL; + ObAppSettings *settings; GSList *it; + settings = config_create_app_settings(); + for (it = config_per_app_settings; it; it = g_slist_next(it)) { ObAppSettings *app = it->data; - - if ((app->name && !app->class && !strcmp(app->name, self->name)) - || (app->class && !app->name && !strcmp(app->class, self->class)) - || (app->class && app->name && !strcmp(app->class, self->class) - && !strcmp(app->name, self->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; - } + gboolean match = TRUE; + + g_assert(app->name != NULL || app->class != NULL); + + /* we know that either name or class is not NULL so it will have to + match to use the rule */ + if (app->name && + !g_pattern_match(app->name, strlen(self->name), self->name, NULL)) + match = FALSE; + if (app->class && + !g_pattern_match(app->class, strlen(self->class),self->class,NULL)) + match = FALSE; + if (app->role && + !g_pattern_match(app->role, strlen(self->role), self->role, NULL)) + match = FALSE; + + if (match) { + ob_debug("Window matching: %s\n", app->name); + + /* copy the settings to our struct, overriding the existing + settings if they are not defaults */ + config_app_settings_copy_non_defaults(app, settings); } } @@ -825,9 +870,11 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, gint ox = *x, oy = *y; gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude; gint fw, fh; + Rect desired; + RECT_SET(desired, *x, *y, w, h); all_a = screen_area(self->desktop); - mon_a = screen_area_monitor(self->desktop, client_monitor(self)); + mon_a = screen_area_monitor(self->desktop, screen_find_monitor(&desired)); /* get where the frame would be */ frame_client_gravity(self->frame, x, y, w, h); @@ -1005,6 +1052,9 @@ static void client_get_all(ObClient *self, gboolean real) if (!real) return; + /* get this early so we have it for debugging */ + client_update_title(self); + client_update_protocols(self); client_update_wmhints(self); @@ -1030,7 +1080,6 @@ static void client_get_all(ObClient *self, gboolean real) #endif client_get_colormap(self); - client_update_title(self); client_update_strut(self); client_update_icons(self); client_update_user_time_window(self); @@ -1134,7 +1183,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; } @@ -1220,19 +1269,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); } @@ -1304,7 +1368,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) @@ -1536,7 +1602,10 @@ 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 | + OB_CLIENT_FUNC_UNDECORATE); if (!(self->min_size.width < self->max_size.width || self->min_size.height < self->max_size.height)) @@ -1568,10 +1637,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; } @@ -1616,7 +1690,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) && @@ -1630,6 +1704,11 @@ void client_setup_decor_and_functions(ObClient *self) if (self->max_vert && self->max_horz) self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS); + /* If there are no decorations to remove, don't allow the user to try + toggle the state */ + if (self->decorations == 0) + self->functions &= ~OB_CLIENT_FUNC_UNDECORATE; + /* finally, the user can have requested no decorations, which overrides everything (but doesnt give it a border if it doesnt have one) */ if (self->undecorated) { @@ -1661,7 +1740,7 @@ void client_setup_decor_and_functions(ObClient *self) static void client_change_allowed_actions(ObClient *self) { - gulong actions[9]; + gulong actions[12]; gint num = 0; /* desktop windows are kept on all desktops */ @@ -1684,6 +1763,12 @@ 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; + if (self->functions & OB_CLIENT_FUNC_UNDECORATE) + actions[num++] = prop_atoms.ob_wm_action_undecorate; PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num); @@ -1694,7 +1779,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) { @@ -2221,7 +2306,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) @@ -2279,13 +2364,14 @@ static ObStackingLayer calc_layer(ObClient *self) 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)))))) && + /* No decorations and fills the monitor = oldskool fullscreen. + But not for undecorated windows, because the user can do that + */ + (self->decorations == 0 && + !self->undecorated && + 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; @@ -2341,11 +2427,13 @@ gboolean client_should_show(ObClient *self) return FALSE; } -void client_show(ObClient *self) +gboolean client_show(ObClient *self) { + gboolean show = FALSE; if (client_should_show(self)) { frame_show(self->frame); + show = TRUE; } /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it @@ -2353,12 +2441,28 @@ void client_show(ObClient *self) desktop! */ client_change_wm_state(self); + return show; } -void client_hide(ObClient *self) +gboolean client_hide(ObClient *self) { + gboolean hide = FALSE; + if (!client_should_show(self)) { + if (self == focus_client) { + /* 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(); + } + frame_hide(self->frame); + hide = TRUE; } /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it @@ -2366,17 +2470,13 @@ void client_hide(ObClient *self) desktop! */ client_change_wm_state(self); + return hide; } void client_showhide(ObClient *self) { - - if (client_should_show(self)) { - frame_show(self->frame); - } - else { - frame_hide(self->frame); - } + if (!client_show(self)) + client_hide(self); /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it needs to be in IconicState. This includes when it is on another @@ -2414,17 +2514,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); @@ -2432,12 +2523,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; @@ -2455,27 +2545,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: @@ -2656,15 +2731,16 @@ 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) +void client_configure(ObClient *self, gint x, gint y, gint w, gint h, + 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; + gboolean fmoved, fresized; guint fdecor = self->frame->decorations; gboolean fhorz = self->frame->max_horz; + gboolean fvert = self->frame->max_vert; gint logicalw, logicalh; /* find the new x, y, width, and height (and logical size) */ @@ -2690,29 +2766,25 @@ 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); } /* find the frame's dimensions and move/resize it */ - if (self->decorations != fdecor || self->max_horz != fhorz) - moved = resized = TRUE; - 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)) + fmoved = moved; + fresized = resized; + if (self->decorations != fdecor || + self->max_horz != fhorz || self->max_vert != fvert) + { + fmoved = fresized = TRUE; + } + if (fmoved || fresized) + frame_adjust_area(self->frame, fmoved, fresized, FALSE); + + if ((!user || (user && final)) && !resized) { XEvent event; @@ -2727,6 +2799,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; @@ -2740,11 +2815,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); @@ -2759,7 +2835,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; @@ -2774,40 +2849,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; @@ -2847,7 +2923,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); @@ -2858,15 +2934,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); } } @@ -2888,8 +2966,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; @@ -2907,34 +2985,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); } } @@ -3100,10 +3167,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; } } @@ -3173,7 +3240,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; } @@ -3203,7 +3270,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; } @@ -3230,11 +3297,12 @@ 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; } } } + if (max_horz != self->max_horz || max_vert != self->max_vert) { if (max_horz != self->max_horz && max_vert != self->max_vert) { /* toggling both */ @@ -3260,24 +3328,30 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) client_shade(self, shaded); if (undecorated != self->undecorated) client_set_undecorated(self, undecorated); + if (above != self->above || below != self->below) { + self->above = above; + self->below = below; + client_calc_layer(self); + } + if (modal != self->modal) { self->modal = modal; /* when a window changes modality, then its stacking order with its transients needs to change */ stacking_raise(CLIENT_AS_WINDOW(self)); + + /* it also may get focused. if something is focused that shouldn't + be focused anymore, then move the focus */ + if (focus_client && client_focus_target(focus_client) != focus_client) + client_focus(focus_client); } + 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); - if (above != self->above || below != self->below) { - self->above = above; - self->below = below; - client_calc_layer(self); - } - client_change_state(self); /* change the hint to reflect these changes */ } @@ -3292,8 +3366,6 @@ ObClient *client_focus_target(ObClient *self) gboolean client_can_focus(ObClient *self) { - XEvent ev; - /* choose the correct target */ self = client_focus_target(self); @@ -3303,24 +3375,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; } @@ -3341,13 +3395,24 @@ 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(); + + xerror_set_ignore(TRUE); + xerror_occured = FALSE; + 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. */ - xerror_set_ignore(TRUE); XSetInputFocus(ob_display, self->window, RevertToPointerRoot, event_curtime); - xerror_set_ignore(FALSE); } if (self->focus_notify) { @@ -3365,17 +3430,9 @@ gboolean client_focus(ObClient *self) XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce); } -#ifdef DEBUG_FOCUS - ob_debug("%sively focusing %lx at %d\n", - (self->can_focus ? "act" : "pass"), - self->window, (gint) event_curtime); -#endif + xerror_set_ignore(FALSE); - /* Cause the FocusIn to come back to us. Important for desktop switches, - since otherwise we'll have no FocusIn on the queue and send it off to - the focus_backup. */ - XSync(ob_display, FALSE); - return TRUE; + return !xerror_occured; } /*! Present the client to the user. @@ -3390,9 +3447,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) { @@ -3435,12 +3492,9 @@ void client_activate(ObClient *self, gboolean here, gboolean user) self->window, event_curtime, last_time, (user ? "user" : "application"), allow); - if (allow) { - if (event_curtime != CurrentTime) - self->user_time = event_curtime; - + if (allow) client_present(self, here, TRUE); - } else + else /* don't focus it but tell the user it wants attention */ client_hilite(self, TRUE); } @@ -3473,9 +3527,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; @@ -3498,20 +3550,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) @@ -3544,16 +3599,13 @@ void client_set_layer(ObClient *self, gint layer) void client_set_undecorated(ObClient *self, gboolean undecorated) { - if (self->undecorated != undecorated) { + if (self->undecorated != undecorated && + /* don't let it undecorate if the function is missing, but let + it redecorate */ + (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated)) + { self->undecorated = undecorated; client_setup_decor_and_functions(self); - /* Make sure the client knows it might have moved. Maybe there is a - * better way of doing this so only one client_configure is sent, but - * since 125 of these are sent per second when moving the window (with - * user = FALSE) i doubt it matters much. - */ - client_configure(self, self->area.x, self->area.y, - self->area.width, self->area.height, TRUE, TRUE); client_change_state(self); /* reflect this in the state hints */ } } @@ -3684,8 +3736,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) \