/*
* ion/ioncore/focus.c
*
- * Copyright (c) Tuomo Valkonen 1999-2006.
+ * Copyright (c) Tuomo Valkonen 1999-2008.
*
- * Ion is free software; you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
+ * See the included file LICENSE for details.
*/
#include <libmainloop/hooks.h>
WHook *region_do_warp_alt=NULL;
-WHook *region_activated_hook=NULL;
-WHook *region_inactivated_hook=NULL;
/*}}}*/
/*{{{ Focus list */
-void region_focuslist_remove(WRegion *reg)
+void region_focuslist_remove_with_mgrs(WRegion *reg)
{
+ WRegion *mgrp=region_manager_or_parent(reg);
+
UNLINK_ITEM(ioncore_g.focus_current, reg, active_next, active_prev);
+
+ if(mgrp!=NULL)
+ region_focuslist_remove_with_mgrs(mgrp);
}
void region_focuslist_push(WRegion *reg)
{
- region_focuslist_remove(reg);
+ region_focuslist_remove_with_mgrs(reg);
LINK_ITEM_FIRST(ioncore_g.focus_current, reg, active_next, active_prev);
}
void region_focuslist_move_after(WRegion *reg, WRegion *after)
{
- region_focuslist_remove(reg);
+ region_focuslist_remove_with_mgrs(reg);
LINK_ITEM_AFTER(ioncore_g.focus_current, after, reg,
active_next, active_prev);
}
if(replace!=NULL)
region_focuslist_move_after(replace, reg);
-
- region_focuslist_remove(reg);
+
+ UNLINK_ITEM(ioncore_g.focus_current, reg, active_next, active_prev);
}
}
+/*EXTL_DOC
+ * Iterate over focus history until \var{iterfn} returns \code{false}.
+ * The function is called in protected mode.
+ * This routine returns \code{true} if it reaches the end of list
+ * without this happening.
+ */
+EXTL_EXPORT
+bool ioncore_focushistory_i(ExtlFn iterfn)
+{
+ WRegion *next;
+
+ if(ioncore_g.focus_current==NULL)
+ return FALSE;
+
+ /* Find the first region on focus history list that isn't currently
+ * active.
+ */
+ for(next=ioncore_g.focus_current->active_next;
+ next!=NULL;
+ next=next->active_next){
+
+ if(!extl_iter_obj(iterfn, (Obj*)next))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
/*}}}*/
}
-static bool region_is_await(WRegion *reg)
+static bool region_is_parent(WRegion *reg, WRegion *aw)
{
- WRegion *aw=(WRegion*)await_watch.obj;
-
while(aw!=NULL){
if(aw==reg)
return TRUE;
}
+static bool region_is_await(WRegion *reg)
+{
+ return region_is_parent(reg, (WRegion*)await_watch.obj);
+}
+
+
+static bool region_is_focusnext(WRegion *reg)
+{
+ return region_is_parent(reg, ioncore_g.focus_next);
+}
+
+
/* Only keep await status if focus event is to an ancestor of the await
* region.
*/
}
-bool ioncore_await_focus()
+WRegion *ioncore_await_focus()
{
- return (await_watch.obj!=NULL);
+ return (WRegion*)(await_watch.obj);
}
void region_got_focus(WRegion *reg)
{
- WRegion *par, *mgr, *tmp;
+ WRegion *par;
check_clear_await(reg);
if(!REGION_IS_ACTIVE(reg)){
D(fprintf(stderr, "got focus (inact) %s [%p]\n", OBJ_TYPESTR(reg), reg);)
+
reg->flags|=REGION_ACTIVE;
+ region_set_manager_pseudoactivity(reg);
par=REGION_PARENT_REG(reg);
if(par!=NULL){
par->active_sub=reg;
region_update_owned_grabs(par);
}
-
- region_activated(reg);
- mgr=REGION_MANAGER(reg);
- tmp=reg;
- while(mgr!=NULL){
- /* We need to loop over managing non-windows (workspaces) here to
- * signal their managers.
- */
- region_managed_activated(mgr, tmp);
-
- if(REGION_PARENT_REG(reg)==mgr)
- break;
-
- tmp=mgr;
- mgr=REGION_MANAGER(mgr);
- }
+ region_activated(reg);
+ region_notify_change(reg, ioncore_g.notifies.activated);
}else{
D(fprintf(stderr, "got focus (act) %s [%p]\n", OBJ_TYPESTR(reg), reg);)
}
*/
if(reg->active_sub==NULL && !OBJ_IS(reg, WClientWin))
rootwin_install_colormap(region_rootwin_of(reg), None);
-
- extl_protect(NULL);
- hook_call_o(region_activated_hook, (Obj*)reg);
- extl_unprotect(NULL);
}
void region_lost_focus(WRegion *reg)
{
- WRegion *r, *par;
+ WRegion *par;
if(!REGION_IS_ACTIVE(reg)){
D(fprintf(stderr, "lost focus (inact) %s [%p:]\n", OBJ_TYPESTR(reg), reg);)
D(fprintf(stderr, "lost focus (act) %s [%p:]\n", OBJ_TYPESTR(reg), reg);)
reg->flags&=~REGION_ACTIVE;
- region_inactivated(reg);
- r=REGION_MANAGER(reg);
- if(r!=NULL)
- region_managed_inactivated(r, reg);
+ region_unset_manager_pseudoactivity(reg);
- extl_protect(NULL);
- hook_call_o(region_inactivated_hook, (Obj*)reg);
- extl_unprotect(NULL);
+ region_inactivated(reg);
+ region_notify_change(reg, ioncore_g.notifies.inactivated);
}
*/
EXTL_SAFE
EXTL_EXPORT_MEMBER
-bool region_is_active(WRegion *reg)
+bool region_is_active(WRegion *reg, bool pseudoact_ok)
{
- return REGION_IS_ACTIVE(reg);
+ return (REGION_IS_ACTIVE(reg) ||
+ (pseudoact_ok && REGION_IS_PSEUDOACTIVE(reg)));
+}
+
+
+bool region_manager_is_focusnext(WRegion *reg)
+{
+ if(reg==NULL || ioncore_g.focus_next==NULL)
+ return FALSE;
+
+ if(reg==ioncore_g.focus_next)
+ return TRUE;
+
+ return region_manager_is_focusnext(REGION_MANAGER(reg));
}
bool region_may_control_focus(WRegion *reg)
{
- WRegion *par, *r2;
-
if(OBJ_IS_BEING_DESTROYED(reg))
return FALSE;
- if(REGION_IS_ACTIVE(reg))
+ if(REGION_IS_ACTIVE(reg) || REGION_IS_PSEUDOACTIVE(reg))
return TRUE;
-
- if(region_is_await(reg))
+
+ if(region_is_await(reg) || region_is_focusnext(reg))
+ return TRUE;
+
+ if(region_manager_is_focusnext(reg))
return TRUE;
-
- par=REGION_PARENT_REG(reg);
-
- if(par==NULL || !REGION_IS_ACTIVE(par))
- return FALSE;
-
- r2=par->active_sub;
- while(r2!=NULL && r2!=par){
- if(r2==reg)
- return TRUE;
- r2=REGION_MANAGER(r2);
- }
return FALSE;
}
if(warp)
region_do_warp(reg);
+ if(REGION_IS_ACTIVE(reg) && ioncore_await_focus()==NULL)
+ return;
+
region_set_await_focus(reg);
/*xwindow_do_set_focus(win);*/
XSetInputFocus(ioncore_g.dpy, win, RevertToParent,
void region_maybewarp(WRegion *reg, bool warp)
{
ioncore_g.focus_next=reg;
+ ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_OTHER;
ioncore_g.warp_next=(warp && ioncore_g.warp_enabled);
}
+void region_maybewarp_now(WRegion *reg, bool warp)
+{
+ ioncore_g.focus_next=NULL;
+ /* TODO: what if focus isn't set? Should focus_next be reset then? */
+ region_do_set_focus(reg, warp && ioncore_g.warp_enabled);
+}
+
+
void region_set_focus(WRegion *reg)
{
region_maybewarp(reg, FALSE);
return ioncore_g.focus_current;
}
+
/*}}}*/
+
+
+/*{{{ Pointer focus hack */
+
+
+/* This ugly hack tries to prevent focus change, when the pointer is
+ * in a window to be unmapped (or destroyed), and that does not have
+ * the focus, or should not soon have it.
+ */
+void region_pointer_focus_hack(WRegion *reg)
+{
+ WRegion *act;
+
+ if(ioncore_g.opmode!=IONCORE_OPMODE_NORMAL)
+ return;
+
+ if(ioncore_g.focus_next!=NULL &&
+ ioncore_g.focus_next_source<=IONCORE_FOCUSNEXT_POINTERHACK){
+ return;
+ }
+
+ act=ioncore_await_focus();
+
+ if((REGION_IS_ACTIVE(reg) && act==NULL) || !region_is_fully_mapped(reg))
+ return;
+
+ if(act==NULL)
+ act=ioncore_g.focus_current;
+
+ if(act==NULL ||
+ OBJ_IS_BEING_DESTROYED(act) ||
+ !region_is_fully_mapped(act) ||
+ region_skip_focus(act)){
+ return;
+ }
+
+ region_set_focus(act);
+ ioncore_g.focus_next_source=IONCORE_FOCUSNEXT_POINTERHACK;
+}
+
+
+/*}}}*/
+