*
* Copyright (c) Tuomo Valkonen 1999-2007.
*
- * 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 <ctype.h>
+
#include <libtu/objp.h>
+#include <libextl/extl.h>
+#include <libmainloop/defer.h>
+
#include "common.h"
#include "key.h"
#include "binding.h"
#include "cursor.h"
#include "grab.h"
#include "regbind.h"
-#include <libextl/extl.h>
#include "strings.h"
#include "xwindow.h"
static bool waitrelease_handler(WRegion *reg, XEvent *ev)
{
- if(!ioncore_unmod(ev->xkey.state, ev->xkey.keycode))
- return TRUE;
- return FALSE;
+ return (ioncore_unmod(ev->xkey.state, ev->xkey.keycode)==0);
}
}
-static void free_subs(WSubmapState *p)
+static void free_sub(WSubmapState *p)
{
- WSubmapState *next;
+ /*extl_unref_fn(p->leave);
+ watch_reset(&p->leave_reg);
+ */
- while(p!=NULL){
- next=p->next;
- free(p);
- p=next;
+ free(p);
+}
+
+
+void region_free_submapstat(WRegion *reg)
+{
+ while(reg->submapstat!=NULL){
+ WSubmapState *p=reg->submapstat;
+ reg->submapstat=p->next;
+ free_sub(p);
}
}
+WHook *ioncore_submap_ungrab_hook=NULL;
+
+
+static void call_submap_ungrab_hook()
+{
+ hook_call_v(ioncore_submap_ungrab_hook);
+}
+
+
static void clear_subs(WRegion *reg)
{
- while(reg->submapstat!=NULL){
- WSubmapState *tmp=reg->submapstat;
- reg->submapstat=tmp->next;
- free(tmp);
+ region_free_submapstat(reg);
+ mainloop_defer_action(NULL, (WDeferredAction*)call_submap_ungrab_hook);
+/*
+ while(reg!=NULL && reg->submapstat!=NULL){
+ WSubmapState *p=reg->submapstat;
+ reg->submapstat=p->next;
+
+ if(p->leave!=extl_fn_none() && p->leave_reg.obj!=NULL){
+ Watch regw=WATCH_INIT;
+
+ watch_setup(®w, (Obj*)reg, NULL);
+
+ extl_call(p->leave, "o", NULL, p->leave_reg.obj);
+
+ reg=(WRegion*)regw.obj;
+
+ watch_reset(®w);
+ }
+
+ free_sub(p);
}
+*/
}
-static bool add_sub(WRegion *reg, uint key, uint state)
+static WSubmapState *add_sub(WRegion *reg, uint key, uint state)
{
WSubmapState **p;
WSubmapState *s;
s=ALLOC(WSubmapState);
if(s==NULL)
- return FALSE;
+ return NULL;
s->key=key;
s->state=state;
+ /*s->leave=extl_fn_none();
+ watch_init(&s->leave_reg);*/
*p=s;
- return TRUE;
+ return s;
}
}
-/* Return value TRUE = grab needed */
-static bool do_key(WRegion *reg, XKeyEvent *ev)
+enum{GRAB_NONE, GRAB_NONE_SUBMAP, GRAB_SUBMAP, GRAB_WAITRELEASE};
+
+
+static WBinding *lookup_binding_(WRegion *reg,
+ int act, uint state, uint kcb,
+ WSubmapState *st,
+ WRegion **binding_owner, WRegion **subreg)
{
- WBinding *binding=NULL;
- WRegion *oreg=NULL, *binding_owner=NULL, *subreg=NULL;
- bool grabbed;
+ WBinding *binding;
- oreg=reg;
- grabbed=(oreg->flags®ION_BINDINGS_ARE_GRABBED);
+ *subreg=NULL;
- if(grabbed){
- /* Find the deepest nested active window grabbing this key. */
- while(reg->active_sub!=NULL)
- reg=reg->active_sub;
+ do{
+ binding=region_lookup_keybinding(reg, act, state, kcb, st,
+ binding_owner);
- do{
- binding=region_lookup_keybinding(reg, ev, oreg->submapstat,
- &binding_owner);
+ if(binding!=NULL)
+ break;
- if(binding!=NULL)
- break;
- if(OBJ_IS(reg, WRootWin))
- break;
-
- subreg=reg;
- reg=REGION_PARENT_REG(reg);
- }while(reg!=NULL);
+ if(OBJ_IS(reg, WRootWin))
+ break;
+
+ *subreg=reg;
+ reg=REGION_PARENT_REG(reg);
+ }while(reg!=NULL);
+
+ return binding;
+}
+
+static WBinding *lookup_binding(WRegion *oreg,
+ int act, uint state, uint kcb,
+ WRegion **binding_owner, WRegion **subreg)
+{
+ WRegion *reg=oreg;
+
+ /* Find the deepest nested active window grabbing this key. */
+ while(reg->active_sub!=NULL)
+ reg=reg->active_sub;
+
+ return lookup_binding_(reg, act, state, kcb, oreg->submapstat,
+ binding_owner, subreg);
+}
+
+
+static void do_call_binding(WBinding *binding, WRegion *reg, WRegion *subreg)
+{
+ WRegion *mgd=region_managed_within(reg, subreg);
+
+ /* TODO: having to pass both mgd and subreg for some handlers
+ * to work is ugly and complex.
+ */
+ extl_call(binding->func, "ooo", NULL, reg, mgd, subreg);
+}
+
+
+static int do_key(WRegion *oreg, XKeyEvent *ev)
+{
+ WBinding *binding=NULL;
+ WRegion *binding_owner=NULL, *subreg=NULL;
+ bool grabbed=(oreg->flags®ION_BINDINGS_ARE_GRABBED);
+ int ret=GRAB_NONE;
+
+ if(grabbed){
+ binding=lookup_binding(oreg, BINDING_KEYPRESS, ev->state, ev->keycode,
+ &binding_owner, &subreg);
}else{
- binding=region_lookup_keybinding(oreg, ev, oreg->submapstat,
+ binding=region_lookup_keybinding(oreg, BINDING_KEYPRESS,
+ ev->state, ev->keycode,
+ oreg->submapstat,
&binding_owner);
}
if(binding!=NULL){
+ bool subs=(oreg->submapstat!=NULL);
+ WBinding *call=NULL;
+
if(binding->submap!=NULL){
- if(add_sub(oreg, ev->keycode, ev->state))
- return grabbed;
- else
- clear_subs(oreg);
- }else if(binding_owner!=NULL){
- WRegion *mgd=region_managed_within(binding_owner, subreg);
- bool subs=(oreg->submapstat!=NULL);
-
- clear_subs(oreg);
+ WSubmapState *s=add_sub(oreg, ev->keycode, ev->state);
+ if(s!=NULL){
+ /*WRegion *own2, *subreg2;
+
+ call=lookup_binding(binding_owner, BINDING_SUBMAP_LEAVE, 0, 0,
+ oreg->submapstat, &own2, &subreg2);
+
+ if(call!=NULL){
+ s->leave=extl_ref_fn(call->func);
+ watch_setup(&s->leave_reg, (Obj*)own2, NULL);
+ }*/
+
+ call=lookup_binding_(binding_owner, BINDING_SUBMAP_ENTER, 0, 0,
+ oreg->submapstat,
+ &binding_owner, &subreg);
+
+ ret=(grabbed ? GRAB_SUBMAP : GRAB_NONE_SUBMAP);
+ }
+ }else{
+ call=binding;
if(grabbed)
XUngrabKeyboard(ioncore_g.dpy, CurrentTime);
+ if(ev->state!=0 && !subs && binding->wait)
+ ret=GRAB_WAITRELEASE;
+ }
+
+ if(call!=NULL){
current_kcb=ev->keycode;
current_state=ev->state;
current_submap=subs;
- /* TODO: having to pass both mgd and subreg for some handlers
- * to work is ugly and complex.
- */
- extl_call(binding->func, "ooo", NULL, binding_owner, mgd, subreg);
+ do_call_binding(call, binding_owner, subreg);
current_kcb=0;
-
- if(ev->state!=0 && !subs && binding->wait)
- waitrelease(oreg);
}
- }else if(oreg->submapstat!=NULL){
- clear_subs(oreg);
- }else if(OBJ_IS(oreg, WWindow)){
+ }else if(oreg->submapstat==NULL && OBJ_IS(oreg, WWindow)){
insstr((WWindow*)oreg, ev);
}
- return FALSE;
+ return ret;
}
static bool submapgrab_handler(WRegion* reg, XEvent *xev)
{
XKeyEvent *ev=&xev->xkey;
- if(ev->type!=KeyPress)
+ if(ev->type!=KeyPress){
+ if(ioncore_unmod(ev->state, ev->keycode)==0){
+ WBinding *binding;
+ WRegion *binding_owner, *subreg;
+
+ binding=lookup_binding(reg,
+ BINDING_SUBMAP_RELEASEMOD, 0, 0,
+ &binding_owner, &subreg);
+
+ if(binding!=NULL)
+ do_call_binding(binding, binding_owner, subreg);
+ }
return FALSE;
+ }
+
if(ioncore_ismod(ev->keycode))
return FALSE;
- return !do_key(reg, ev);
+ if(do_key(reg, ev)!=GRAB_SUBMAP){
+ clear_subs(reg);
+ return TRUE;
+ }else{
+ return FALSE;
+ }
}
+
+
static void submapgrab(WRegion *reg)
{
ioncore_grab_establish(reg, submapgrab_handler, clear_subs, 0);
WRegion *reg=(WRegion*)XWINDOW_REGION_OF(ev->window);
if(reg!=NULL){
- if(do_key(reg, ev))
- submapgrab(reg);
+ Watch w=WATCH_INIT;
+ int grab;
+
+ /* reg might be destroyed by binding handlers */
+ watch_setup(&w, (Obj*)reg, NULL);
+
+ grab=do_key(reg, ev);
+
+ reg=(WRegion*)w.obj;
+
+ if(reg!=NULL){
+ if(grab==GRAB_SUBMAP)
+ submapgrab(reg);
+ else if(grab==GRAB_WAITRELEASE)
+ waitrelease(reg);
+ else if(grab==GRAB_NONE_SUBMAP)
+ /* nothing */;
+ else if(grab==GRAB_NONE && reg->submapstat!=NULL)
+ clear_subs(reg);
+ }
+
+ watch_reset(&w);
}
}