diff --git a/openbox/action.c b/openbox/action.c index b54e888e..2e348c7c 100644 --- a/openbox/action.c +++ b/openbox/action.c @@ -48,19 +48,8 @@ inline void client_action_start(union ActionData *data) inline void client_action_end(union ActionData *data) { if (config_focus_follow) - if (data->any.context != OB_FRAME_CONTEXT_CLIENT) { - if (!data->any.button) { - grab_pointer(FALSE, FALSE, OB_CURSOR_NONE); - } else { - ObClient *c; - - /* usually this is sorta redundant, but with a press action - the enter event will come as a GrabNotify which is - ignored, so this will handle that case */ - if ((c = client_under_pointer())) - event_enter_client(c); - } - } + if (data->any.context != OB_FRAME_CONTEXT_CLIENT && !data->any.button) + grab_pointer(FALSE, FALSE, OB_CURSOR_NONE); } typedef struct @@ -1275,7 +1264,7 @@ void action_raiselower(union ActionData *data) if (cit == c) break; if (client_normal(cit) == client_normal(c) && cit->layer == c->layer && - cit->frame->visible && + frame_visible(cit->frame) && !client_search_transient(c, cit)) { if (RECT_INTERSECTS_RECT(cit->frame->area, c->frame->area)) { diff --git a/openbox/client.c b/openbox/client.c index 658b05db..02bd74b4 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -2061,7 +2061,7 @@ static void client_change_wm_state(ObClient *self) old = self->wmstate; - if (self->shaded || self->iconic || !self->frame->visible) + if (self->shaded || self->iconic || !frame_visible(self->frame)) self->wmstate = IconicState; else self->wmstate = NormalState; @@ -2731,15 +2731,19 @@ static void client_iconify_recursive(ObClient *self, (self->frame, iconic, (ObFrameIconifyAnimateFunc)client_showhide, self); /* but focus a new window now please */ - focus_fallback(TRUE); + focus_fallback(FALSE); } else client_showhide(self); } else { if (config_animate_iconify) - /* start the animation then show it, this way the whole window - doesnt get shown, just the first step of the animation */ - frame_begin_iconify_animation(self->frame, iconic, NULL, NULL); - client_showhide(self); + /* the animation will show the window when it is hidden, + but the window state needs to be adjusted after the + animation finishes, so call showhide when it's done to make + sure everything is updated appropriately + */ + frame_begin_iconify_animation + (self->frame, iconic, + (ObFrameIconifyAnimateFunc)client_showhide, self); } } @@ -3184,7 +3188,7 @@ gboolean client_can_focus(ObClient *self) /* choose the correct target */ self = client_focus_target(self); - if (!self->frame->visible) + if (!frame_visible(self->frame)) return FALSE; if (!(self->can_focus || self->focus_notify)) @@ -3217,7 +3221,7 @@ gboolean client_focus(ObClient *self) self = client_focus_target(self); if (!client_can_focus(self)) { - if (!self->frame->visible) { + if (!frame_visible(self->frame)) { /* update the focus lists */ focus_order_to_top(self); } @@ -3302,7 +3306,7 @@ void client_activate(ObClient *self, gboolean here, gboolean user) client_set_desktop(self, screen_desktop, FALSE); else screen_set_desktop(self->desktop); - } else if (!self->frame->visible) + } else if (!frame_visible(self->frame)) /* if its not visible for other reasons, then don't mess with it */ return; @@ -3729,8 +3733,11 @@ ObClient* client_under_pointer() for (it = stacking_list; it; it = g_list_next(it)) { if (WINDOW_IS_CLIENT(it->data)) { ObClient *c = WINDOW_AS_CLIENT(it->data); - if (c->frame->visible && - RECT_CONTAINS(c->frame->area, x, y)) { + if (frame_visible(c->frame) && + /* ignore all animating windows */ + !frame_iconify_animating(c->frame) && + RECT_CONTAINS(c->frame->area, x, y)) + { ret = c; break; } diff --git a/openbox/event.c b/openbox/event.c index a0202bf3..ec0789d1 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -83,6 +83,7 @@ static void event_handle_dock(ObDock *s, XEvent *e); static void event_handle_dockapp(ObDockApp *app, XEvent *e); static void event_handle_client(ObClient *c, XEvent *e); static void event_handle_group(ObGroup *g, XEvent *e); +static void event_handle_user_input(ObClient *client, XEvent *e); static void focus_delay_dest(gpointer data); static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2); @@ -530,49 +531,13 @@ static void event_process(const XEvent *ec, gpointer data) } #endif - /* user input (action-bound) events */ if (e->type == ButtonPress || e->type == ButtonRelease || e->type == MotionNotify || e->type == KeyPress || e->type == KeyRelease) { - gboolean useevent = TRUE; - - if (menu_frame_visible) { - if (event_handle_menu(e)) - /* don't use the event if the menu used it, but if the menu - didn't use it and it's a keypress that is bound, it will - close the menu and be used */ - useevent = FALSE; - } - - if (useevent) { - /* if the keyboard interactive action uses the event then dont - use it for bindings. likewise is moveresize uses the event. */ - if (!keyboard_process_interactive_grab(e, &client) && - !(moveresize_in_progress && moveresize_event(e))) - { - if (moveresize_in_progress) - /* make further actions work on the client being - moved/resized */ - client = moveresize_client; - - - menu_can_hide = FALSE; - ob_main_loop_timeout_add(ob_main_loop, - config_menu_hide_delay * 1000, - menu_hide_delay_func, - NULL, g_direct_equal, NULL); - - if (e->type == ButtonPress || e->type == ButtonRelease || - e->type == MotionNotify) { - mouse_event(client, e); - } else if (e->type == KeyPress) { - keyboard_event((focus_cycle_target ? focus_cycle_target : - (client ? client : focus_client)), e); - } - } - } + event_handle_user_input(client, e); } + /* if something happens and it's not from an XEvent, then we don't know the time */ event_curtime = CurrentTime; @@ -732,6 +697,12 @@ static void event_handle_client(ObClient *client, XEvent *e) frame_adjust_state(client->frame); break; case OB_FRAME_CONTEXT_FRAME: + /* When the mouse leaves an animating window, don't use the + corresponding enter events. Pretend like the animating window + doesn't even exist..! */ + if (frame_iconify_animating(client->frame)) + event_ignore_queued_enters(); + ob_debug_type(OB_DEBUG_FOCUS, "%sNotify mode %d detail %d on %lx\n", (e->type == EnterNotify ? "Enter" : "Leave"), @@ -1379,6 +1350,51 @@ static gboolean event_handle_menu(XEvent *ev) return ret; } +static void event_handle_user_input(ObClient *client, XEvent *e) +{ + g_assert(e->type == ButtonPress || e->type == ButtonRelease || + e->type == MotionNotify || e->type == KeyPress || + e->type == KeyRelease); + + if (menu_frame_visible) { + if (event_handle_menu(e)) + /* don't use the event if the menu used it, but if the menu + didn't use it and it's a keypress that is bound, it will + close the menu and be used */ + return; + } + + /* if the keyboard interactive action uses the event then dont + use it for bindings. likewise is moveresize uses the event. */ + if (!keyboard_process_interactive_grab(e, &client) && + !(moveresize_in_progress && moveresize_event(e))) + { + if (moveresize_in_progress) + /* make further actions work on the client being + moved/resized */ + client = moveresize_client; + + menu_can_hide = FALSE; + ob_main_loop_timeout_add(ob_main_loop, + config_menu_hide_delay * 1000, + menu_hide_delay_func, + NULL, g_direct_equal, NULL); + + if (e->type == ButtonPress || + e->type == ButtonRelease || + e->type == MotionNotify) + { + /* the frame may not be "visible" but they can still click on it + in the case where it is animating before disappearing */ + if (client && frame_visible(client->frame)) + mouse_event(client, e); + } else if (e->type == KeyPress) { + keyboard_event((focus_cycle_target ? focus_cycle_target : + (client ? client : focus_client)), e); + } + } +} + static gboolean menu_hide_delay_func(gpointer data) { menu_can_hide = TRUE; diff --git a/openbox/focus.c b/openbox/focus.c index 823435c9..94257205 100644 --- a/openbox/focus.c +++ b/openbox/focus.c @@ -499,7 +499,7 @@ static gboolean valid_focus_target(ObClient *ft, gboolean dock_windows) for (it = ft->transients; it; it = g_slist_next(it)) { ObClient *c = it->data; - if (c->frame->visible) + if (frame_visible(c->frame)) return FALSE; } return TRUE; diff --git a/openbox/frame.c b/openbox/frame.c index 6c4241aa..3af354c3 100644 --- a/openbox/frame.c +++ b/openbox/frame.c @@ -42,7 +42,8 @@ from the frame. */ #define INNER_EVENTMASK (ButtonPressMask) -#define FRAME_ANIMATE_ICONIFY_TIME 150000 /* .15 seconds */ +//#define FRAME_ANIMATE_ICONIFY_TIME 150000 /* .15 seconds */ +#define FRAME_ANIMATE_ICONIFY_TIME 2000000 #define FRAME_ANIMATE_ICONIFY_STEP_TIME (G_USEC_PER_SEC / 30) /* 30 Hz */ #define FRAME_HANDLE_Y(f) (f->innersize.top + f->client->area.height + \ @@ -481,17 +482,18 @@ void frame_adjust_area(ObFrame *self, gboolean moved, self->client->area.height); } - if (!fake && !self->iconify_animation_going) { - /* move and resize the top level frame. - shading can change without being moved or resized. - - but don't do this during an iconify animation. it will be - reflected afterwards. - */ - XMoveResizeWindow(ob_display, self->window, - self->area.x, self->area.y, - self->area.width - self->bwidth * 2, - self->area.height - self->bwidth * 2); + if (!fake) { + if (!frame_iconify_animating(self)) + /* move and resize the top level frame. + shading can change without being moved or resized. + + but don't do this during an iconify animation. it will be + reflected afterwards. + */ + XMoveResizeWindow(ob_display, self->window, + self->area.x, self->area.y, + self->area.width - self->bwidth * 2, + self->area.height - self->bwidth * 2); if (resized) { framerender_frame(self); @@ -839,10 +841,6 @@ ObFrameContext frame_context(ObClient *client, Window win) if (win == RootWindow(ob_display, ob_screen)) return OB_FRAME_CONTEXT_DESKTOP; if (client == NULL) return OB_FRAME_CONTEXT_NONE; - - self = client->frame; - if (self->iconify_animation_going) return OB_FRAME_CONTEXT_NONE; - if (win == client->window) { /* conceptually, this is the desktop, as far as users are concerned */ @@ -851,6 +849,7 @@ ObFrameContext frame_context(ObClient *client, Window win) return OB_FRAME_CONTEXT_CLIENT; } + self = client->frame; if (win == self->inner || win == self->plate) { /* conceptually, this is the desktop, as far as users are concerned */ @@ -1108,27 +1107,41 @@ static gboolean frame_animate_iconify(gpointer p) h = self->innersize.top; /* just the titlebar */ } - if (time == 0) { - /* call the callback when it's done */ - if (self->iconify_animation_cb) - self->iconify_animation_cb(self->iconify_animation_data); - /* we're not animating any more ! */ - self->iconify_animation_going = 0; + if (time == 0) + frame_end_iconify_animation(self); + else { + XMoveResizeWindow(ob_display, self->window, x, y, w, h); + XFlush(ob_display); } - /* move to the next spot (after the callback for the animation ending) */ - XMoveResizeWindow(ob_display, self->window, x, y, w, h); - XFlush(ob_display); - return time > 0; /* repeat until we're out of time */ } +void frame_end_iconify_animation(ObFrame *self) +{ + /* see if there is an animation going */ + if (self->iconify_animation_going == 0) return; + + /* call the callback when it's done */ + if (self->iconify_animation_cb) + self->iconify_animation_cb(self->iconify_animation_data); + /* we're not animating any more ! */ + self->iconify_animation_going = 0; + + /* move after the callback for the animation ending */ + XMoveResizeWindow(ob_display, self->window, + self->area.x, self->area.y, + self->area.width - self->bwidth * 2, + self->area.height - self->bwidth * 2); + XFlush(ob_display); +} + void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying, ObFrameIconifyAnimateFunc callback, gpointer data) { gulong time; - gboolean start_timer = TRUE; + gboolean new_anim = FALSE; gboolean set_end = TRUE; GTimeVal now; @@ -1151,8 +1164,8 @@ void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying, } else /* animation was already going in the same direction */ set_end = FALSE; - start_timer = FALSE; - } + } else + new_anim = TRUE; self->iconify_animation_going = iconifying ? 1 : -1; self->iconify_animation_cb = callback; @@ -1165,14 +1178,25 @@ void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying, g_time_val_add(&self->iconify_animation_end, time); } - if (start_timer) { + if (new_anim) { ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify, self, FALSE); ob_main_loop_timeout_add(ob_main_loop, FRAME_ANIMATE_ICONIFY_STEP_TIME, frame_animate_iconify, self, g_direct_equal, NULL); + /* do the first step */ frame_animate_iconify(self); + + if (!self->visible) + frame_show(self); } } + +gboolean frame_visible(ObFrame *self) +{ + /* if it is animating back from iconic state then it is considered + visible. but if it is iconifying then it is not visible. */ + return self->visible && self->iconify_animation_going <= 0; +} diff --git a/openbox/frame.h b/openbox/frame.h index 4ffc7df2..5f31b8c9 100644 --- a/openbox/frame.h +++ b/openbox/frame.h @@ -77,6 +77,9 @@ struct _ObFrame Strut size; Rect area; + /*! Is the frame visible? Don't read this directly ! Use frame_visible() + instead, because that takes into account if the frame is visible but + animating to the iconic (invisible) state. */ gboolean visible; guint decorations; @@ -195,5 +198,12 @@ void frame_flash_stop(ObFrame *self); void frame_begin_iconify_animation(ObFrame *self, gboolean iconifying, ObFrameIconifyAnimateFunc callback, gpointer data); +void frame_end_iconify_animation(ObFrame *self); + +/* Returns true if the frame is visible (but false if it is only visible + because it is animating */ +gboolean frame_visible(ObFrame *self); + +#define frame_iconify_animating(f) (f->iconify_animation_going != 0) #endif diff --git a/openbox/moveresize.c b/openbox/moveresize.c index 868c0c25..9f9d4dfd 100644 --- a/openbox/moveresize.c +++ b/openbox/moveresize.c @@ -153,12 +153,14 @@ void moveresize_start(ObClient *c, gint x, gint y, guint b, guint32 cnr) moving = (cnr == prop_atoms.net_wm_moveresize_move || cnr == prop_atoms.net_wm_moveresize_move_keyboard); - if (moveresize_in_progress || !c->frame->visible || + if (moveresize_in_progress || !frame_visible(c->frame) || !(moving ? (c->functions & OB_CLIENT_FUNC_MOVE) : (c->functions & OB_CLIENT_FUNC_RESIZE))) return; + frame_end_iconify_animation(c->frame); + moveresize_client = c; start_cx = c->area.x; start_cy = c->area.y; diff --git a/openbox/place.c b/openbox/place.c index 8003270e..eada385f 100644 --- a/openbox/place.c +++ b/openbox/place.c @@ -251,7 +251,8 @@ typedef enum } ObSmartType; #define SMART_IGNORE(placer, c) \ - (placer == c || !c->frame->visible || c->shaded || !client_normal(c) || \ + (placer == c || c->shaded || !client_normal(c) || \ + !frame_visible(c->frame) || \ (c->desktop != DESKTOP_ALL && \ c->desktop != (placer->desktop == DESKTOP_ALL ? \ screen_desktop : placer->desktop))) diff --git a/openbox/resist.c b/openbox/resist.c index fc293446..0c7ec87e 100644 --- a/openbox/resist.c +++ b/openbox/resist.c @@ -57,7 +57,7 @@ void resist_move_windows(ObClient *c, gint *x, gint *y) target = it->data; /* don't snap to self or non-visibles */ - if (!target->frame->visible || target == c) continue; + if (!frame_visible(target->frame) || target == c) continue; /* don't snap to windows in layers beneath */ if(target->layer < c->layer && !config_resist_layers_below) @@ -199,7 +199,7 @@ void resist_size_windows(ObClient *c, gint *w, gint *h, ObCorner corn) target = it->data; /* don't snap to invisibles or ourself */ - if (!target->frame->visible || target == c) continue; + if (!frame_visible(target->frame) || target == c) continue; /* don't snap to windows in layers beneath */ if(target->layer < c->layer && !config_resist_layers_below)