예제 #1
0
        /// <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;
                }
            }
        }