 : exclude-key-wm-char? ( n -- bool )
     exclude-keys-wm-char key? ;
 
-: keystroke>gesture ( n -- mods sym ? )
-    dup wm-keydown-codes at* [
-        nip >r key-modifiers r> t
-    ] [
-        drop 1string >r key-modifiers r>
-        C+ pick member? >r A+ pick member? r> or [
-            shift? [ >lower ] unless f
-        ] [
-            switch-case? [ switch-case ] when t
-        ] if
-    ] if ;
+: keystroke>gesture ( n -- mods sym )
+    wm-keydown-codes at* [ key-modifiers swap ] [ drop f f ] if ;
+
+: send-key-gesture ( sym action? quot hWnd -- )
+    [ [ key-modifiers ] 3dip call ] dip
+    window-focus propagate-gesture ; inline
+
+: send-key-down ( sym action? hWnd -- )
+    [ [ <key-down> ] ] dip send-key-gesture ;
+
+: send-key-up ( sym action? hWnd -- )
+    [ [ <key-up> ] ] dip send-key-gesture ;
+
+: key-sym ( wParam -- string/f action? )
+    {
+        {
+            [ dup LETTER? ]
+            [ shift? caps-lock? xor [ CHAR: a + CHAR: A - ] unless 1string f ]
+        }
+        { [ dup digit? ] [ 1string f ] }
+        [ wm-keydown-codes at t ]
+    } cond ;
 
 :: handle-wm-keydown ( hWnd uMsg wParam lParam -- )
     wParam exclude-key-wm-keydown? [
-        wParam keystroke>gesture <key-down>
-        hWnd window-focus propagate-gesture
+        wParam key-sym over [
+            dup ctrl? alt? xor or [
+                hWnd send-key-down
+            ] [ 2drop ] if
+        ] [ 2drop ] if
     ] unless ;
 
 :: handle-wm-char ( hWnd uMsg wParam lParam -- )
-    wParam exclude-key-wm-char? ctrl? alt? xor or [
-        wParam 1string
-        hWnd window-focus user-input
+    wParam exclude-key-wm-char? [
+        ctrl? alt? xor [
+            wParam 1string
+            [ f hWnd send-key-down ]
+            [ hWnd window-focus user-input ] bi
+        ] unless
     ] unless ;
 
 :: handle-wm-keyup ( hWnd uMsg wParam lParam -- )
-    wParam keystroke>gesture <key-up>
-    hWnd window-focus propagate-gesture ;
+    wParam exclude-key-wm-keydown? [
+        wParam key-sym over [
+            hWnd send-key-up
+        ] [ 2drop ] if
+    ] unless ;
 
 :: set-window-active ( hwnd uMsg wParam lParam ? -- n )
     ? hwnd window (>>active?)

: alt? ( -- ? ) left-alt? right-alt? or ;
: caps-lock? ( -- ? ) VK_CAPITAL GetKeyState zero? not ;

: switch-case ( seq -- seq )
    dup first CHAR: a >= [ >upper ] [ >lower ] if ;

: switch-case? ( -- ? ) shift? caps-lock? xor not ;

: key-modifiers ( -- seq )
    [
        shift? [ S+ , ] when


: message>button ( uMsg -- button down? )
    {
        { WM_LBUTTONDOWN   [ 1 t ] }
        { WM_LBUTTONUP     [ 1 f ] }
        { WM_MBUTTONDOWN   [ 2 t ] }
        { WM_MBUTTONUP     [ 2 f ] }
        { WM_RBUTTONDOWN   [ 3 t ] }
        { WM_RBUTTONUP     [ 3 f ] }

        { WM_NCLBUTTONDOWN [ 1 t ] }
        { WM_NCLBUTTONUP   [ 1 f ] }
        { WM_NCMBUTTONDOWN [ 2 t ] }
        { WM_NCMBUTTONUP   [ 2 f ] }
        { WM_NCRBUTTONDOWN [ 3 t ] }
        { WM_NCRBUTTONUP   [ 3 f ] }
    } case ;

! If the user clicks in the window border ("non-client area")
! Windows sends us an NC[LMR]BUTTONDOWN message; but if the
