--- w32fns.c-sent2 2015-07-01 02:56:30.787672000 -0700 +++ w32fns.c 2015-07-08 16:32:11.187197700 -0700 @@ -2932,6 +2932,15 @@ get_wm_chars (HWND aWnd, int *buf, int b # define FPRINTF_WM_CHARS(ARG) 0 #endif +/* This is a heuristic only. This is supposed to track the state of the + finite automaton in the language environment of Windows. + + However, separate windows (if with the same different language + environments!) should have different values. Moreover, switching to a + non-Emacs window with the same language environment, and using (dead)keys + there would change the value stored in the kernel, but not this value. */ +static int after_deadkey = 0; + int deliver_wm_chars (int do_translate, HWND hwnd, UINT msg, UINT wParam, UINT lParam, int legacy_alt_meta) @@ -2940,7 +2949,7 @@ deliver_wm_chars (int do_translate, HWND points to a keypress. (However, the "old style" TranslateMessage() would deliver at most 16 of them.) Be on a safe side, and prepare to treat many more. */ - int ctrl_cnt, buf[1024], count, is_dead; + int ctrl_cnt, buf[1024], count, is_dead, after_dead = (after_deadkey != -1); /* Since the keypress processing logic of Windows has a lot of state, it is important to call TranslateMessage() for every keyup/keydown, AND @@ -2956,7 +2965,8 @@ deliver_wm_chars (int do_translate, HWND So, with the usual message pump, the following call to TranslateMessage() is not needed (and is going to be VERY harmful). With Emacs' message pump, the call is needed. */ - if (do_translate) { + if (do_translate) + { MSG windows_msg = { hwnd, msg, wParam, lParam, 0, {0,0} }; windows_msg.time = GetMessageTime (); @@ -2970,13 +2980,22 @@ deliver_wm_chars (int do_translate, HWND || modifier_set (VK_CONTROL), &ctrl_cnt, &is_dead, wParam, (lParam & 0x1000000L) != 0); - if (count) { + if (count) + { W32Msg wmsg; - int *b = buf, strip_Alt = 1; - - /* wParam is checked when converting CapsLock to Shift */ - wmsg.dwModifiers = do_translate - ? w32_get_key_modifiers (wParam, lParam) : 0; + DWORD console_modifiers = construct_console_modifiers (); + int *b = buf, strip_Alt = 1, strip_ExtraMods = 1, hairy = 0; + char *type_CtrlAlt = NULL; + + /* XXXX In fact, there may be another case when we need to do the same: + What happens if the string defined in the LIGATURES has length + 0? Probably, we will get count==0, but the state of the finite + automaton would reset to 0??? */ + after_deadkey = -1; + + /* wParam is checked when converting CapsLock to Shift; this is a clone + of w32_get_key_modifiers (). */ + wmsg.dwModifiers = w32_kbd_mods_to_emacs (console_modifiers, wParam); /* What follows is just heuristics; the correct treatement requires non-destructive ToUnicode(): @@ -2991,8 +3010,8 @@ deliver_wm_chars (int do_translate, HWND Example: assume that we know: (A) lCtrl+rCtrl+rAlt modifiers with VK_A key produce a Latin "f" - ("may be logical" with a JCUKEN-flavored Russian keyboard flavor); - (B) removing any one of lCtrl, rCtrl, rAlt changes the produced char; + ("may be logical" in JCUKEN-flavored Russian keyboard flavors); + (B) removing any of lCtrl, rCtrl, rAlt changes the produced char; (C) Win-modifier is not affecting the produced character (this is the common case: happens with all "standard" layouts). @@ -3000,7 +3019,7 @@ deliver_wm_chars (int do_translate, HWND What is the intent of the user? We need to guess the intent to decide which event to deliver to the application. - This looks like a reasonable logic: wince Win- modifier does not affect + This looks like a reasonable logic: since Win- modifier doesn't affect the output string, the user was pressing Win for SOME OTHER purpose. So the user wanted to generate Win-SOMETHING event. Now, what is something? If one takes the mantra that "character payload is more @@ -3008,38 +3027,196 @@ deliver_wm_chars (int do_translate, HWND payload", then one should ignore lCtrl+rCtrl+rAlt, ignore VK_A, and assume that the user wanted to generate Win-f. - Unfortunately, without non-destructive ToUnicode(), checking (B) and (C) - is out of question. So we use heuristics (hopefully, covering 99.9999% - of cases). + Unfortunately, without non-destructive ToUnicode(), checking (B),(C) + is out of question. So we use heuristics (hopefully, covering + 99.9999% of cases). */ - /* If ctrl-something delivers chars, ctrl and the rest should be hidden; - so the consumer of key-event won't interpret it as an accelerator. */ - if (wmsg.dwModifiers & ctrl_modifier) - wmsg.dwModifiers = wmsg.dwModifiers & shift_modifier; - /* In many keyboard layouts, (left) Alt is not changing the character. - Unless we are in this situation, strip Alt/Meta. */ - if (wmsg.dwModifiers & (alt_modifier | meta_modifier) - /* If alt-something delivers non-ASCIIchars, alt should be hidden */ - && count == 1 && *b < 0x10000) - { - SHORT r = VkKeyScanW( *b ); + /* Another thing to watch for is a possibility to use AltGr-* and + Ctrl-Alt-* with different semantic. - FPRINTF_WM_CHARS((stderr, "VkKeyScanW %#06x %#04x\n", (int)r, wParam)); - if ((r & 0xFF) == wParam && !(r & ~0x1FF)) - { - /* Char available without Alt modifier, so Alt is "on top" */ + Background: the layout defining the KLLF_ALTGR bit are treated + specially by the kernel: when VK_RMENU (=rightAlt, =AltGr) is pressed + (released), a press (release) of VK_LCONTROL is emulated (unless Ctrl + is already down). As a result, any press/release of AltGr is seen + by applications as a press/release of lCtrl AND rAlt. This is + applicable, in particular, to ToUnicode[Ex](). (Keyrepeat is covered + the same way!) + + NOTE: it IS possible to see bare rAlt even with KLLF_ALTGR; but this + requires a good finger coordination: doing (physically) + Down-lCtrl Down-rAlt Up-lCtrl Down-a + (doing quick enough, so that key repeat of rAlt [which would + generate new "fake" Down-lCtrl events] does not happens before 'a' + is down) results in no "fake" events, so the application will see + only rAlt down when 'a' is pressed. (However, fake Up-lCtrl WILL + be generated when rAlt goes UP.) + + In fact, note also that KLLF_ALTGR does not prohibit construction of + rCtrl-rAlt (just press them in this order!). + + Moreover: "traditional" layouts do not define distinct modifier-masks + for VK_LMENU and VK_RMENU (same for VK_L/RCONTROL). Instead, they + rely on the KLLF_ALTGR bit to make the behaviour of VK_LMENU and + VK_RMENU distinct. As a corollary, for such layouts, the produced + character is the same for AltGr-* (=rAlt-*) and Ctrl-Alt-* (in any + combination of handedness). For description of masks, see + + http://search.cpan.org/~ilyaz/UI-KeyboardLayout/lib/UI/KeyboardLayout.pm#Keyboard_input_on_Windows,_Part_I:_what_is_the_kernel_doing? + + By default, Emacs was using these coincidences via the following + heuristics: it was treating: + (*) keypresses with lCtrl-rAlt modifiers as if they are carrying + ONLY the character payload (no matter what the actual keyboard + was defining: if lCtrl-lAlt-b was delivering U+05df=beta, then + Emacs saw [beta]; if lCtrl-lAlt-b was undefined in the layout, + the keypress was completely ignored), and + (*) keypresses with the other combinations of handedness of Ctrl-Alt + modifiers (e.g., lCtrl-lAlt) as if they NEVER carry a character + payload (so they were reported "raw": if lCtrl-lAlt-b was + delivering beta, then Emacs saw event [C-A-b], and not [beta]). + This worked good for "traditional" layouts: users could type both + AltGr-x and Ctrl-Alt-x, and one was a character, another a bindable + event. + + However, for layouts which deliver different characters for AltGr-x + and lCtrl-lAlt-x, this scheme makes the latter character unaccessible + in Emacs. While it is easy to access functionality of [C-M-x] in + Emacs by other means (for example, by the `controlify' prefix, or + using lCtrl-rCtrl-x, or rCtrl-rAlt-x [in this order]), missing + characters cannot be reconstructed without a tedious manual work. */ + + /* These two cases are often going to be distinguishable, since at most + one of these character is defined with KBDCTRL | KBDMENU modifier + bitmap. (This heuristic breaks if both lCtrl-lAlt- AND lCtrl-rAlt- + are translated to modifier bitmaps distinct from KBDCTRL | KBDMENU, + or in the cases when lCtrl-lAlt-* and lCtrl-rAlt-* are generally + different, but lCtrl-lAlt-x and lCtrl-rAlt-x happen to deliver the + same character.) + + So we have 2 chunks of info: + (A) is it lCtrl-rAlt-, or lCtrl-lAlt, or some other combination? + (B) is the delivered character defined with KBDCTRL | KBDMENU bits? + Basing on (A) and (B), we should decide whether to ignore the + delivered character. (Before, Emacs was completely ignoring (B), and + was treating the 3-state of (A) as a bit.) This means that we have 6 + bits of customization. + + Additionally, a presence of two Ctrl down may be AltGr-rCtrl-.*/ + + /* Strip all non-Shift modifiers if: + - more than one UTF-16 code point delivered (can't call VkKeyScanW ()) + - or the character is a result of combining with a prefix key. */ + if (!after_dead && count == 1 && *b < 0x10000) + { + if (console_modifiers & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED) + && console_modifiers & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) + { + type_CtrlAlt = "bB"; /* generic bindable Ctrl-Alt- modifiers */ + if (console_modifiers & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) + == (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) + /* double-Ctrl: + e.g. AltGr-rCtrl on some layouts (in this order!) */ + type_CtrlAlt = "dD"; + else if (console_modifiers + & (LEFT_CTRL_PRESSED | LEFT_ALT_PRESSED) + == (LEFT_CTRL_PRESSED | LEFT_ALT_PRESSED)) + type_CtrlAlt = "lL"; /* Ctrl-Alt- modifiers on the left */ + else if (!NILP (Vw32_recognize_altgr) + && (console_modifiers + & (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)) + == (RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED)) + type_CtrlAlt = "gG"; /* modifiers as in AltGr */ + } + else if (wmsg.dwModifiers & (alt_modifier | meta_modifier) + || (console_modifiers + & (RIGHT_WIN_PRESSED | RIGHT_WIN_PRESSED + | APPS_PRESSED | SCROLLLOCK_ON))) + { + /* pure Alt (or combination of Alt, Win, APPS, scrolllock */ + type_CtrlAlt = "aA"; + } + if (type_CtrlAlt) + { + /* Out of bound bitmap: */ + SHORT r = VkKeyScanW( *b ), bitmap = 0x1FF; + + FPRINTF_WM_CHARS((stderr, "VkKeyScanW %#06x %#04x\n", (int)r, + wParam)); + if ((r & 0xFF) == wParam) + bitmap = r>>8; /* *b is reachable via simple interface */ + if (*type_CtrlAlt == 'a') /* Simple Alt seen */ + { + if ((bitmap & ~1) == 0) /* 1: KBDSHIFT */ + { + /* In "traditional" layouts, Alt without Ctrl does not + change the delivered character. This detects this + situation; it is safe to report this as Alt-something + - as opposed to delivering the reported character + without modifiers. */ if (legacy_alt_meta && *b > 0x7f && ('A' <= wParam && wParam <= 'Z')) /* For backward-compatibility with older Emacsen, let - this be processed by another branch below (which would convert - it to Alt-Latin char via wParam). */ + this be processed by another branch below (which + would convert it to Alt-Latin char via wParam). */ + return 0; + } + else + { + hairy = 1; + } + } + /* Check whether the delivered character(s) is accessible via + KBDCTRL | KBDALT ( | KBDSHIFT ) modifier mask (which is 7). */ + else if ((bitmap & ~1) != 6) + { + /* The character is not accessible via plain Ctrl-Alt(-Shift) + (which is, probably, same as AltGr) modifiers. + Either it was after a prefix key, or is combined with + modifier keys which we don't see, or there is an asymmetry + between left-hand and right-hand modifiers, or other hairy + stuff. */ + hairy = 1; + } + /* The best solution is to delegate these tough (but rarely + needed) choices to the user. Temporarily (???), it is + implemented as C macros. + + Essentially, there are 3 things to do: return 0 (handle to the + legacy processing code [ignoring the character payload]; keep + some modifiers (so that they will be processed by the binding + system [on top of the character payload]; strip modifiers [so + that `self-insert' is going to be triggered with the character + payload]). + + The default below should cover 99.9999% of cases: + (a) strip Alt- in the hairy case only; + (stripping = not ignoring) + (l) for lAlt-lCtrl, ignore the char in simple cases only; + (g) for what looks like AltGr, ignore the modifiers; + (d) for what looks like lCtrl-rCtrl-Alt (probably + AltGr-rCtrl), ignore the character in simple cases only; + (b) for other cases of Ctrl-Alt, ignore the character in + simple cases only. + + Essentially, in all hairy cases, and in looks-like-AltGr case, + we keep the character, ignoring the modifiers. In all the + other cases, we ignore the delivered character. + */ +#define S_TYPES_TO_IGNORE_CHARACTER_PAYLOAD "aldb" +#define S_TYPES_TO_REPORT_CHARACTER_PAYLOAD_WITH_MODIFIERS "" + if (strchr(S_TYPES_TO_IGNORE_CHARACTER_PAYLOAD, + type_CtrlAlt[hairy])) return 0; - strip_Alt = 0; + /* if in neither list, report all the modifiers we see COMBINED + WITH the reported character */ + if (strchr(S_TYPES_TO_REPORT_CHARACTER_PAYLOAD_WITH_MODIFIERS, + type_CtrlAlt[hairy])) + strip_ExtraMods = 0; } } - if (strip_Alt) - wmsg.dwModifiers = wmsg.dwModifiers & ~(alt_modifier | meta_modifier); + if (strip_ExtraMods) + wmsg.dwModifiers = wmsg.dwModifiers & shift_modifier; signal_user_input (); while (count--) @@ -3052,8 +3229,11 @@ deliver_wm_chars (int do_translate, HWND else FPRINTF_WM_CHARS((stderr, "extra ctrl char\n")); return -1; - } else if (is_dead >= 0) { + } + else if (is_dead >= 0) + { FPRINTF_WM_CHARS((stderr, "dead %#06x\n", is_dead)); + after_deadkey = is_dead; return 1; } return 0; @@ -3175,6 +3355,15 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARA /* Inform lisp thread of keyboard layout changes. */ my_post_msg (&wmsg, hwnd, msg, wParam, lParam); + /* The state of the finite automaton is separate per every input + language environment (so it does not change when one switches + to a different window with the same environment). Moreover, + the experiments show that the state is not remembered when + one switches back to the pre-previous environment. */ + after_deadkey = -1; + + /* XXXX??? What follows is a COMPLETE misunderstanding of Windows! */ + /* Clear dead keys in the keyboard state; for simplicity only preserve modifier key states. */ {