[PATCH libX11] XIM: Allow modifier releases in compose sequences (#26705)

Andreas Wettstein wettstein509 at solnet.ch
Tue Nov 8 12:18:09 PST 2011


Currently, only non-modifier keys (actually, keysyms) can be part of a compose
sequence, and they are matched against the defined compose sequences at the
time the key is pressed.  The patch allows to use modifier keys an well, but
matches them on key release, and only if no other key has been pressed after
the modifier.

Releasing a non-matched modifier during an ongoing compose sequence only aborts
the sequence if any modifier release would have matched.  In particular, if no
compose sequences with modifiers are specified, the compose mechanism works
exactly as without this patch.

Even if modifiers are part of a compose sequence, they are not filtered.  This
is because modifiers affect the keyboard state no matter what we do here and,
therefore, filtering them only could confuse clients.

The purpose is this extension to the compose mechanism is to allow to make
better use of keys in convenient reach for touch typing.

Signed-off-by: Andreas Wettstein <wettstein509 at solnet.ch>
---
 modules/im/ximcp/imLcFlt.c |   52 +++++++++++++++++++++++++++++++++++--------
 1 files changed, 42 insertions(+), 10 deletions(-)

diff --git a/modules/im/ximcp/imLcFlt.c b/modules/im/ximcp/imLcFlt.c
index 06aa998..a706090 100644
--- a/modules/im/ximcp/imLcFlt.c
+++ b/modules/im/ximcp/imLcFlt.c
@@ -43,18 +43,17 @@ _XimLocalFilter(Display *d, Window w, XEvent *ev, XPointer client_data)
     Xic		 ic = (Xic)client_data;
     KeySym	 keysym;
     static char	 buf[256];
+    static unsigned prevcode = 0, prevstate = 0;
+    unsigned    currstate;
     DefTree	*b = ic->private.local.base.tree;
     DTIndex	 t;
-    Bool	 braille = False;
+    Bool	 braille = False, anymodifier = False;
 
     if(ev->xkey.keycode == 0)
 	return (False);
 
     XLookupString((XKeyEvent *)ev, buf, sizeof(buf), &keysym, NULL);
 
-    if(IsModifierKey(keysym))
-	return (False);
-
     if(keysym >= XK_braille_dot_1 && keysym <= XK_braille_dot_8) {
 	if(ev->type == KeyPress) {
 	    ic->private.local.brl_pressed |=
@@ -78,38 +77,71 @@ _XimLocalFilter(Display *d, Window w, XEvent *ev, XPointer client_data)
 	}
     }
 
-    if(   (ev->type != KeyPress)
-       || (((Xim)ic->core.im)->private.local.top == 0 ) )
+    if(((Xim)ic->core.im)->private.local.top == 0 )
 	goto emit_braille;
 
+    currstate = ev->xkey.state;
+    if(ev->type == KeyPress) {
+	prevcode = ev->xkey.keycode;
+	prevstate = currstate;
+
+	if(IsModifierKey(keysym))
+	    return(False);
+	prevcode = 0;
+    } else {
+	if(prevcode != ev->xkey.keycode)
+	    return False;
+
+	/* For lookup, we use the state at the time when the key was pressed, */
+	/* because this state was not affected by the modifier that is mapped */
+	/* to the key. */
+	ev->xkey.state = prevstate;
+	XLookupString((XKeyEvent *)ev, buf, sizeof(buf), &keysym, NULL);
+    }
+
     for(t = ic->private.local.context; t; t = b[t].next) {
+	if(IsModifierKey(b[t].keysym))
+	    anymodifier = True;
 	if(((ev->xkey.state & b[t].modifier_mask) == b[t].modifier) &&
 	   (keysym == b[t].keysym))
 	    break;
     }
 
+    /* Restore the state */
+    ev->xkey.state = currstate;
+
     if(t) { /* Matched */
 	if(b[t].succession) { /* Intermediate */
 	    ic->private.local.context = b[t].succession;
-	    return(True);
+	    return (ev->type == KeyPress);
 	} else { /* Terminate (reached to leaf) */
 	    ic->private.local.composed = t;
 	    ic->private.local.brl_committed = 0;
 	    /* return back to client KeyPressEvent keycode == 0 */
 	    ev->xkey.keycode = 0;
+	    ev->xkey.type = KeyPress;
 	    XPutBackEvent(d, ev);
+	    if(prevcode){
+		/* For modifier key releases, restore the event, as we do not */
+		/* filter it.  */
+		ev->xkey.type = KeyRelease;
+		ev->xkey.keycode = prevcode;
+	    }
 	    /* initialize internal state for next key sequence */
 	    ic->private.local.context = ((Xim)ic->core.im)->private.local.top;
-	    return(True);
+	    return (ev->type == KeyPress);
 	}
     } else { /* Unmatched */
-	if(ic->private.local.context == ((Xim)ic->core.im)->private.local.top) {
+	/* Unmatched modifier key releases abort matching only in the case that */
+	/* there was any modifier that would have matched */
+	if((ic->private.local.context == ((Xim)ic->core.im)->private.local.top) ||
+	   (ev->type == KeyRelease && !anymodifier)) {
 	    goto emit_braille;
 	}
 	/* Error (Sequence Unmatch occured) */
 	/* initialize internal state for next key sequence */
 	ic->private.local.context = ((Xim)ic->core.im)->private.local.top;
-	return(True);
+	return (ev->type == KeyPress);
     }
 
 emit_braille:
-- 
1.7.6



More information about the xorg-devel mailing list