]> git.decadent.org.uk Git - ion3.git/blobdiff - ioncore/key.c
[svn-upgrade] Integrating new upstream version, ion3 (20070506)
[ion3.git] / ioncore / key.c
index 57ada801f313f85d8f3515f6611e638876e62a2c..4134ab77f2abdd158181472e9ff471d38613b5cb 100644 (file)
@@ -3,14 +3,15 @@
  *
  * 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"
@@ -19,7 +20,6 @@
 #include "cursor.h"
 #include "grab.h"
 #include "regbind.h"
-#include <libextl/extl.h>
 #include "strings.h"
 #include "xwindow.h"
 
@@ -97,9 +97,7 @@ void clientwin_quote_next(WClientWin *cwin)
 
 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);
 }
 
 
@@ -120,29 +118,63 @@ static void waitrelease(WRegion *reg)
 }
 
 
-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(&regw, (Obj*)reg, NULL);
+                
+            extl_call(p->leave, "o", NULL, p->leave_reg.obj);
+            
+            reg=(WRegion*)regw.obj;
+            
+            watch_reset(&regw);
+        }
+        
+        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;
@@ -159,14 +191,16 @@ static bool add_sub(WRegion *reg, uint key, uint state)
     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;
 
 }
 
@@ -189,88 +223,159 @@ bool ioncore_current_key(uint *kcb, uint *state, bool *sub)
 }
 
 
-/* Return value TRUE = grab needed */
-static bool do_key(WRegion *reg, XKeyEvent *ev)
+enum{GRAB_NONE, 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&REGION_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&REGION_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);
+            }
+        }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);
@@ -283,7 +388,25 @@ void ioncore_do_handle_keypress(XKeyEvent *ev)
     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 && reg->submapstat!=NULL)
+                clear_subs(reg);
+        }
+        
+        watch_reset(&w);
     }
 }