/// <summary> /// Attempt to enumerate all dead keys available on the current keyboard /// layout and cache the results in <see cref="m_possible_dead_keys"/>. /// </summary> private static void AnalyzeLayout() { // Clear key buffer VkToUnicode(VK.SPACE); VkToUnicode(VK.SPACE); // Compute an input locale identifier suitable for ToUnicodeEx(). This is // necessary because some IMEs interfer with ToUnicodeEx, e.g. Japanese, // so instead we pretend we use an English US keyboard. // I could check that Korean (Microsoft IME) and Chinese Simplified // (Microsoft Pinyin) are not affected. // High bytes are the device ID, low bytes are the language ID var current_device_id = (ulong)m_current_layout >> 16; var japanese_lang_id = NativeMethods.MAKELANG(LANG.JAPANESE, SUBLANG.JAPANESE_JAPAN); if (current_device_id == japanese_lang_id) { var english_lang_id = NativeMethods.MAKELANG(LANG.ENGLISH, SUBLANG.DEFAULT); m_transformed_hkl = (IntPtr)((english_lang_id << 16) | english_lang_id); } // Check that the transformed HKL actually works; otherwise, revert. if (VkToUnicode(VK.SPACE) != " ") { m_transformed_hkl = m_current_layout; } // Try every keyboard key followed by space to see which ones are // dead keys. This way, when later we want to know if a dead key is // currently buffered, we just call VkToUnicode(VK.SPACE) and match // the result with what we found here. This code also precomputes // characters obtained with AltGr. m_possible_dead_keys = new Dictionary <string, int>(); m_possible_altgr_keys = new Dictionary <string, string>(); string[] no_altgr = new string[0x200]; byte[] state = new byte[256]; for (int i = 0; i < 0x400; ++i) { VK vk = (VK)(i & 0xff); bool has_shift = (i & 0x100) != 0; bool has_altgr = (i & 0x200) != 0; state[(int)VK.SHIFT] = (byte)(has_shift ? 0x80 : 0x00); state[(int)VK.CONTROL] = (byte)(has_altgr ? 0x80 : 0x00); state[(int)VK.MENU] = (byte)(has_altgr ? 0x80 : 0x00); // First the key we’re interested in, then the space key string str_if_normal = VkToUnicode(vk, (SC)0, state, (LLKHF)0); string str_if_dead = VkToUnicode(VK.SPACE); VkToUnicode(VK.SPACE); // Additional safety to clear buffer bool has_dead = str_if_dead != "" && str_if_dead != " "; // If the AltGr gives us a result and it’s different from without // AltGr, we need to remember it. string str = has_dead ? str_if_dead : str_if_normal; if (has_altgr) { if (no_altgr[i - 0x200] != "" && str != "" && no_altgr[i - 0x200] != str) { Log.Debug("VK {0} is “{1}” but “{2}” with AltGr", vk.ToString(), no_altgr[i - 0x200], str); m_possible_altgr_keys[no_altgr[i - 0x200]] = str; } } else { no_altgr[i] = str; } // If the resulting string is not the space character, it means // that it was a dead key. Good! if (has_dead) { Log.Debug("VK {0} is dead key “{1}”", vk.ToString(), str_if_dead); m_possible_dead_keys[str_if_dead] = i; } } }