コード例 #1
0
        internal static Key VkToKey(VK vk, SC sc, LLKHF flags, bool has_shift,
                                    bool has_altgr, bool has_capslock)
        {
            byte[] keystate = new byte[256];
            NativeMethods.GetKeyboardState(keystate);
            keystate[(int)VK.SHIFT]   = (byte)(has_shift ? 0x80 : 0x00);
            keystate[(int)VK.CONTROL] = (byte)(has_altgr ? 0x80 : 0x00);
            keystate[(int)VK.MENU]    = (byte)(has_altgr ? 0x80 : 0x00);
            keystate[(int)VK.CAPITAL] = (byte)(has_capslock ? 0x01 : 0x00);

            // These two calls must be done together and in this order.
            string str_if_normal = VkToUnicode(vk, sc, keystate, flags);
            string str_if_dead   = VkToUnicode(VK.SPACE);

            // This indicates that vk was a dead key
            if (str_if_dead != "" && str_if_dead != " ")
            {
                return(new Key(str_if_dead));
            }

            // Special case: we don't consider characters such as Esc as printable
            // otherwise they are not properly serialised in the config file.
            if (str_if_normal == "" || str_if_normal[0] < ' ')
            {
                return(new Key(vk));
            }

            return(new Key(str_if_normal));
        }
コード例 #2
0
        private static string KeyToUnicode(VK vk, SC sc, byte[] keystate, LLKHF flags)
        {
            const int buflen = 4;

            byte[] buf = new byte[2 * buflen];
            int    ret = NativeMethods.ToUnicode(vk, sc, keystate, buf, buflen, flags);

            if (ret > 0 && ret < buflen)
            {
                return(Encoding.Unicode.GetString(buf, 0, ret * 2));
            }
            return("");
        }
コード例 #3
0
ファイル: Composer.cs プロジェクト: milotype/wincompose
        /// <summary>
        /// Get input from the keyboard hook; return true if the key was handled
        /// and needs to be removed from the input chain.
        /// </summary>
        public static bool OnKey(WM ev, VK vk, SC sc, LLKHF flags)
        {
            // Remember when the user touched a key for the last time
            m_last_key_time = DateTime.Now;

            // We need to check the keyboard layout before we save the dead
            // key, otherwise we may be saving garbage.
            KeyboardLayout.CheckForChanges();

            KeyboardLayout.SaveDeadKey();
            bool ret = OnKeyInternal(ev, vk, sc, flags);

            KeyboardLayout.RestoreDeadKey();

            return(ret);
        }
コード例 #4
0
    /// <summary>
    /// Get input from the keyboard hook; return true if the key was handled
    /// and needs to be removed from the input chain.
    /// </summary>
    public static bool OnKey(WM ev, VK vk, SC sc, LLKHF flags)
    {
        // Remember when the user touched a key for the last time
        m_last_key_time = DateTime.Now;

        // Do nothing if we are disabled
        if (m_disabled)
        {
            return false;
        }

        int dead_key = SaveDeadKey();
        bool ret = OnKeyInternal(ev, vk, sc, flags);
        RestoreDeadKey(dead_key);

        return ret;
    }
コード例 #5
0
        /// <summary>
        /// Get input from the keyboard hook; return true if the key was handled
        /// and needs to be removed from the input chain.
        /// </summary>
        public static bool OnKey(WM ev, VK vk, SC sc, LLKHF flags)
        {
            // Remember when the user touched a key for the last time
            m_last_key_time = DateTime.Now;

            // Do nothing if we are disabled
            if (m_disabled)
            {
                return(false);
            }

            int  dead_key = SaveDeadKey();
            bool ret      = OnKeyInternal(ev, vk, sc, flags);

            RestoreDeadKey(dead_key);

            return(ret);
        }
コード例 #6
0
        /// <summary>
        /// Get input from the keyboard hook; return true if the key was handled
        /// and needs to be removed from the input chain.
        /// </summary>
        public static bool OnKey(WM ev, VK vk, SC sc, LLKHF flags)
        {
            // Remember when the user touched a key for the last time
            m_last_key_time = DateTime.Now;

            // Do nothing if we are disabled; NOTE: this disables stats, too
            if (Settings.Disabled.Value)
            {
                return(false);
            }

            // We need to check the keyboard layout before we save the dead
            // key, otherwise we may be saving garbage.
            CheckKeyboardLayout();

            int  dead_key = SaveDeadKey();
            bool ret      = OnKeyInternal(ev, vk, sc, flags);

            RestoreDeadKey(dead_key);

            return(ret);
        }
コード例 #7
0
        private static bool OnKeyInternal(WM ev, VK vk, SC sc, LLKHF flags)
        {
            CheckKeyboardLayout();

            bool is_keydown = (ev == WM.KEYDOWN || ev == WM.SYSKEYDOWN);
            bool is_keyup   = !is_keydown;

            bool has_shift = (NativeMethods.GetKeyState(VK.SHIFT) & 0x80) != 0;
            bool has_altgr = (NativeMethods.GetKeyState(VK.LCONTROL) &
                              NativeMethods.GetKeyState(VK.RMENU) & 0x80) != 0;
            bool has_lrshift = (NativeMethods.GetKeyState(VK.LSHIFT) &
                                NativeMethods.GetKeyState(VK.RSHIFT) & 0x80) != 0;
            bool has_capslock = NativeMethods.GetKeyState(VK.CAPITAL) != 0;

            // Guess what key was just pressed. If we can not find a printable
            // representation for the key, default to its virtual key code.
            Key key = new Key(vk);

            byte[] keystate = new byte[256];
            NativeMethods.GetKeyboardState(keystate);
            keystate[(int)VK.SHIFT]   = (byte)(has_shift ? 0x80 : 0x00);
            keystate[(int)VK.CONTROL] = (byte)(has_altgr ? 0x80 : 0x00);
            keystate[(int)VK.MENU]    = (byte)(has_altgr ? 0x80 : 0x00);
            keystate[(int)VK.CAPITAL] = (byte)(has_capslock ? 0x01 : 0x00);



            string str_if_normal = KeyToUnicode(vk, sc, keystate, flags);
            string str_if_dead   = KeyToUnicode(VK.SPACE);

            if (str_if_normal != "")
            {
                // This appears to be a normal, printable key
                key = new Key(str_if_normal);
            }
            else if (str_if_dead != " ")
            {
                // This appears to be a dead key
                key = new Key(str_if_dead);
            }

            // Special case: we don't consider characters such as Esc as printable
            // otherwise they are not properly serialised in the config file.
            if (key.IsPrintable() && key.ToString()[0] < ' ')
            {
                key = new Key(vk);
            }

            Log("WM.{0} {1} (VK:0x{2:X02} SC:0x{3:X02})",
                ev.ToString(), key.FriendlyName, (int)vk, (int)sc);

            // FIXME: we don’t properly support compose keys that also normally
            // print stuff, such as `.
            if (key == Settings.ComposeKey.Value)
            {
                if (is_keyup)
                {
                    // If we receive a keyup for the compose key, but we hadn't
                    // previously marked it as down, it means we're in emulation
                    // mode and we need to cancel it.
                    if (!m_compose_down)
                    {
                        Log("Fallback Off");
                        SendKeyUp(Settings.ComposeKey.Value.VirtualKey);
                    }

                    m_compose_down = false;
                }
                else if (is_keydown && !m_compose_down)
                {
                    // FIXME: we don't want compose + compose to disable composing,
                    // since there are compose sequences that use Multi_key.
                    // FIXME: also, if a sequence was in progress, print it!
                    m_compose_down = true;
                    m_composing    = !m_composing;
                    if (!m_composing)
                    {
                        m_sequence.Clear();
                    }

                    Log("{0} Composing", m_composing ? "Now" : "No Longer");

                    // Lauch the sequence reset expiration thread
                    // FIXME: do we need to launch a new thread each time the
                    // compose key is pressed? Let's have a dormant thread instead
                    if (m_composing && Settings.ResetDelay.Value > 0)
                    {
                        new Thread(() =>
                        {
                            while (m_composing && DateTime.Now < m_last_key_time.AddMilliseconds(Settings.ResetDelay.Value))
                            {
                                Thread.Sleep(50);
                            }
                            ResetSequence();
                        }).Start();
                    }
                }

                Changed(null, new EventArgs());

                return(true);
            }

            // Feature: emulate capslock key with both shift keys, and optionally
            // disable capslock using only one shift key.
            if (key.VirtualKey == VK.LSHIFT || key.VirtualKey == VK.RSHIFT)
            {
                if (is_keyup && has_lrshift && Settings.EmulateCapsLock.Value)
                {
                    SendKeyPress(VK.CAPITAL);
                    return(false);
                }

                if (is_keydown && has_capslock && Settings.ShiftDisablesCapsLock.Value)
                {
                    SendKeyPress(VK.CAPITAL);
                    return(false);
                }
            }

            // If we are not currently composing a sequence, do nothing. But if
            // this was a dead key, eat it.
            if (!m_composing)
            {
                return(false);
            }

            // If the compose key is down, maybe there is a key combination
            // going on, such as Alt+Tab or Windows+Up, so we abort composing
            // and tell the OS that the key is down.
            if (m_compose_down && (Settings.KeepOriginalKey.Value ||
                                   !Settings.IsUsableKey(key)))
            {
                Log("Fallback On");
                ResetSequence();
                SendKeyDown(Settings.ComposeKey.Value.VirtualKey);
                return(false);
            }

            // If the key can't be used in a sequence, just ignore it.
            if (!Settings.IsUsableKey(key))
            {
                return(false);
            }

            // If we reached this point, everything else ignored this key, so it
            // is a key we must add to the current sequence.
            if (is_keydown)
            {
                return(AddToSequence(key));
            }

            return(true);
        }
コード例 #8
0
ファイル: Composer.cs プロジェクト: samhocevar/wincompose
    /// <summary>
    /// Get input from the keyboard hook; return true if the key was handled
    /// and needs to be removed from the input chain.
    /// </summary>
    public static bool OnKey(WM ev, VK vk, SC sc, LLKHF flags)
    {
        // Remember when the user touched a key for the last time
        m_last_key_time = DateTime.Now;

        // Do nothing if we are disabled; NOTE: this disables stats, too
        if (Settings.Disabled.Value)
        {
            return false;
        }

        // We need to check the keyboard layout before we save the dead
        // key, otherwise we may be saving garbage.
        CheckKeyboardLayout();

        int dead_key = SaveDeadKey();
        bool ret = OnKeyInternal(ev, vk, sc, flags);
        RestoreDeadKey(dead_key);

        return ret;
    }
コード例 #9
0
ファイル: Composer.cs プロジェクト: samhocevar/wincompose
 private static string VkToUnicode(VK vk, SC sc, byte[] keystate, LLKHF flags)
 {
     const int buflen = 4;
     byte[] buf = new byte[2 * buflen];
     int ret = NativeMethods.ToUnicode(vk, sc, keystate, buf, buflen, flags);
     if (ret > 0 && ret < buflen)
     {
         return Encoding.Unicode.GetString(buf, 0, ret * 2);
     }
     return "";
 }
コード例 #10
0
ファイル: Composer.cs プロジェクト: samhocevar/wincompose
    private static Key VkToKey(VK vk, SC sc, LLKHF flags, bool has_shift,
                               bool has_altgr, bool has_capslock)
    {
        byte[] keystate = new byte[256];
        NativeMethods.GetKeyboardState(keystate);
        keystate[(int)VK.SHIFT] = (byte)(has_shift ? 0x80 : 0x00);
        keystate[(int)VK.CONTROL] = (byte)(has_altgr ? 0x80 : 0x00);
        keystate[(int)VK.MENU] = (byte)(has_altgr ? 0x80 : 0x00);
        keystate[(int)VK.CAPITAL] = (byte)(has_capslock ? 0x01 : 0x00);

        // These two calls must be done together and in this order.
        string str_if_normal = VkToUnicode(vk, sc, keystate, flags);
        string str_if_dead = VkToUnicode(VK.SPACE);

        if (str_if_dead != " ")
            return new Key(str_if_dead);

        // Special case: we don't consider characters such as Esc as printable
        // otherwise they are not properly serialised in the config file.
        if (str_if_normal == "" || str_if_normal[0] < ' ')
            return new Key(vk);

        return new Key(str_if_normal);
    }
コード例 #11
0
ファイル: Composer.cs プロジェクト: samhocevar/wincompose
    private static bool OnKeyInternal(WM ev, VK vk, SC sc, LLKHF flags)
    {
        bool is_keydown = (ev == WM.KEYDOWN || ev == WM.SYSKEYDOWN);
        bool is_keyup = !is_keydown;
        bool add_to_sequence = is_keydown;
        bool is_capslock_hack = false;
        bool compose_is_altgr = m_possible_altgr_keys.Count > 0
                       && Settings.ComposeKey.Value.VirtualKey == VK.RMENU;

        bool has_shift = (NativeMethods.GetKeyState(VK.SHIFT) & 0x80) != 0;
        bool has_altgr = (NativeMethods.GetKeyState(VK.LCONTROL) &
                          NativeMethods.GetKeyState(VK.RMENU) & 0x80) != 0;
        bool has_lrshift = (NativeMethods.GetKeyState(VK.LSHIFT) &
                            NativeMethods.GetKeyState(VK.RSHIFT) & 0x80) != 0;
        bool has_capslock = NativeMethods.GetKeyState(VK.CAPITAL) != 0;

        // Guess what the system would print if we weren’t interfering. If
        // a printable representation exists, use that. Otherwise, default
        // to its virtual key code.
        Key key = VkToKey(vk, sc, flags, has_shift, has_altgr, has_capslock);

        // If Caps Lock is on, and the Caps Lock hack is enabled, we check
        // whether this key without Caps Lock gives a non-ASCII alphabetical
        // character. If so, we replace “result” with the lowercase or
        // uppercase variant of that character.
        if (has_capslock && Settings.CapsLockCapitalizes.Value)
        {
            Key alt_key = VkToKey(vk, sc, flags, has_shift, has_altgr, false);

            if (alt_key.IsPrintable() && alt_key.ToString()[0] > 0x7f)
            {
                string str_upper = alt_key.ToString().ToUpper();
                string str_lower = alt_key.ToString().ToLower();

                // Hack for German keyboards: it seems that ToUpper() does
                // not properly change ß into ẞ.
                if (str_lower == "ß")
                    str_upper = "ẞ";

                if (str_upper != str_lower)
                {
                    key = new Key(has_shift ? str_lower : str_upper);
                    is_capslock_hack = true;
                }
            }
        }

        // Update statistics
        if (is_keydown)
        {
            // Update single key statistics
            Stats.AddKey(key);

            // Update key pair statistics if applicable
            if (DateTime.Now < m_last_key_time.AddMilliseconds(2000)
                 && m_last_key != null)
            {
                Stats.AddPair(m_last_key, key);
            }

            // Remember when we pressed a key for the last time
            m_last_key_time = DateTime.Now;
            m_last_key = key;
        }

        // If the special Synergy window has focus, we’re actually sending
        // keystrokes to another computer; disable WinCompose.
        if (m_window_is_synergy)
        {
            return false;
        }

        // If we receive a keyup for the compose key while in emulation
        // mode, we’re done. Send a KeyUp event and exit emulation mode.
        if (is_keyup && key == Settings.ComposeKey.Value
             && CurrentState == State.Combination)
        {
            Log.Debug("Combination Off");
            CurrentState = State.Idle;
            m_compose_counter = 0;

            // If relevant, send an additional KeyUp for the opposite
            // key; experience indicates that it helps unstick some
            // applications such as mintty.exe.
            switch (Settings.ComposeKey.Value.VirtualKey)
            {
                case VK.LMENU: SendKeyUp(VK.RMENU); break;
                case VK.RMENU: SendKeyUp(VK.LMENU);
                               if (compose_is_altgr)
                                   SendKeyUp(VK.LCONTROL);
                               break;
                case VK.LSHIFT: SendKeyUp(VK.RSHIFT); break;
                case VK.RSHIFT: SendKeyUp(VK.LSHIFT); break;
                case VK.LCONTROL: SendKeyUp(VK.RCONTROL); break;
                case VK.RCONTROL: SendKeyUp(VK.LCONTROL); break;
            }

            return false;
        }

        // If this is the compose key and we’re idle, enter Sequence mode
        if (is_keydown && key == Settings.ComposeKey.Value
             && m_compose_counter == 0 && CurrentState == State.Idle)
        {
            Log.Debug("Now Composing");
            CurrentState = State.Sequence;
            ++m_compose_counter;

            // Lauch the sequence reset expiration thread
            // FIXME: do we need to launch a new thread each time the
            // compose key is pressed? Let's have a dormant thread instead
            if (Settings.ResetDelay.Value > 0)
            {
                new Thread(() =>
                {
                    while (CurrentState == State.Sequence &&
                            DateTime.Now < m_last_key_time.AddMilliseconds(Settings.ResetDelay.Value))
                        Thread.Sleep(50);
                    ResetSequence();
                }).Start();
            }

            return true;
        }

        // If this is a compose key KeyDown event and it’s already down, or it’s
        // a KeyUp and it’s already up, eat this event without forwarding it.
        if (key == Settings.ComposeKey.Value
             && is_keydown == ((m_compose_counter & 1) != 0))
        {
            return true;
        }

        // Escape and backspace cancel the current sequence
        if (is_keydown && CurrentState == State.Sequence
             && (key.VirtualKey == VK.ESCAPE || key.VirtualKey == VK.BACK))
        {
            // FIXME: if a sequence was in progress, maybe print it!
            Log.Debug("No Longer Composing");
            ResetSequence();
            return true;
        }

        // Feature: emulate capslock key with both shift keys, and optionally
        // disable capslock using only one shift key.
        if (key.VirtualKey == VK.LSHIFT || key.VirtualKey == VK.RSHIFT)
        {
            if (is_keyup && has_lrshift && Settings.EmulateCapsLock.Value)
            {
                SendKeyPress(VK.CAPITAL);
                return false;
            }

            if (is_keydown && has_capslock && Settings.ShiftDisablesCapsLock.Value)
            {
                SendKeyPress(VK.CAPITAL);
                return false;
            }
        }

        // If we are not currently composing a sequence, do nothing unless
        // one of our hacks forces us to send the key as a string (for
        // instance the Caps Lock capitalisation feature).
        if (CurrentState != State.Sequence)
        {
            if (is_capslock_hack && is_keydown)
            {
                SendString(key.ToString());
                return true;
            }

            // If this was a dead key, it will be completely ignored. But
            // it’s okay since we stored it.
            Log.Debug("Forwarding Key to System (not composing)");
            return false;
        }

        // If the compose key is down and the user pressed a new key, maybe
        // instead of composing they want to do a key combination, such as
        // Alt+Tab or Windows+Up. So we abort composing and send the KeyDown
        // event for the Compose key that we previously discarded. The same
        // goes for characters that need AltGr when AltGr is the compose key.
        //
        // Never do this if the event is KeyUp.
        // Never do this if we already started a sequence
        // Never do this if the key is a modifier key such as shift or alt.
        if (m_compose_counter == 1 && is_keydown
             && m_sequence.Count == 0 && !key.IsModifier())
        {
            bool keep_original = Settings.KeepOriginalKey.Value;
            bool key_unusable = !key.IsUsable();
            bool altgr_combination = compose_is_altgr &&
                        m_possible_altgr_keys.ContainsKey(key.ToString());

            if (keep_original || key_unusable || altgr_combination)
            {
                Log.Debug("Combination On");
                ResetSequence();
                if (compose_is_altgr)
                {
                    // It’s necessary to use KEYEVENTF_EXTENDEDKEY otherwise the system
                    // does not understand that we’re sending AltGr.
                    SendKeyDown(VK.LCONTROL);
                    SendKeyDown(VK.RMENU, KEYEVENTF.EXTENDEDKEY);
                }
                else
                {
                    SendKeyDown(Settings.ComposeKey.Value.VirtualKey);
                }
                CurrentState = State.Combination;
                return false;
            }
        }

        // If this is the compose key again, use our custom virtual key
        // FIXME: we don’t properly support compose keys that also normally
        // print stuff, such as `.
        if (key == Settings.ComposeKey.Value)
        {
            ++m_compose_counter;
            key = new Key(VK.COMPOSE);

            // If the compose key is AltGr, we only add it to the sequence
            // if it’s a KeyUp event, otherwise we may be adding Multi_key to the
            // sequence when the user actually wants to enter an AltGr char.
            if (compose_is_altgr && m_compose_counter > 2)
                add_to_sequence = is_keyup;
        }

        // If the compose key is AltGr and it’s down, check whether the current
        // key needs translating.
        if (compose_is_altgr && (m_compose_counter & 1) != 0)
        {
            string altgr_variant;
            if (m_possible_altgr_keys.TryGetValue(key.ToString(), out altgr_variant))
            {
                key = new Key(altgr_variant);
                // Do as if we already released Compose, otherwise the next KeyUp
                // event will cause VK.COMPOSE to be added to the sequence…
                ++m_compose_counter;
            }
        }

        // If the key can't be used in a sequence, just ignore it.
        if (!key.IsUsable())
        {
            Log.Debug("Forwarding Key to System (no possible sequence uses it)");
            return false;
        }

        // If we reached this point, everything else ignored this key, so it
        // is a key we must add to the current sequence.
        if (add_to_sequence)
        {
            Log.Debug("Adding to Sequence: {0}", key.FriendlyName);
            return AddToSequence(key);
        }

        return true;
    }
コード例 #12
0
        private static bool OnKeyInternal(WM ev, VK vk, SC sc, LLKHF flags)
        {
            bool is_keydown       = (ev == WM.KEYDOWN || ev == WM.SYSKEYDOWN);
            bool is_keyup         = !is_keydown;
            bool add_to_sequence  = is_keydown;
            bool is_capslock_hack = false;
            bool compose_is_altgr = m_possible_altgr_keys.Count > 0 &&
                                    Settings.ComposeKey.Value.VirtualKey == VK.RMENU;

            bool has_shift = (NativeMethods.GetKeyState(VK.SHIFT) & 0x80) != 0;
            bool has_altgr = (NativeMethods.GetKeyState(VK.LCONTROL) &
                              NativeMethods.GetKeyState(VK.RMENU) & 0x80) != 0;
            bool has_lrshift = (NativeMethods.GetKeyState(VK.LSHIFT) &
                                NativeMethods.GetKeyState(VK.RSHIFT) & 0x80) != 0;
            bool has_capslock = NativeMethods.GetKeyState(VK.CAPITAL) != 0;

            // Guess what the system would print if we weren’t interfering. If
            // a printable representation exists, use that. Otherwise, default
            // to its virtual key code.
            Key key = VkToKey(vk, sc, flags, has_shift, has_altgr, has_capslock);

            // If Caps Lock is on, and the Caps Lock hack is enabled, we check
            // whether this key without Caps Lock gives a non-ASCII alphabetical
            // character. If so, we replace “result” with the lowercase or
            // uppercase variant of that character.
            if (has_capslock && Settings.CapsLockCapitalizes.Value)
            {
                Key alt_key = VkToKey(vk, sc, flags, has_shift, has_altgr, false);

                if (alt_key.IsPrintable() && alt_key.ToString()[0] > 0x7f)
                {
                    string str_upper = alt_key.ToString().ToUpper();
                    string str_lower = alt_key.ToString().ToLower();
                    if (str_upper != str_lower)
                    {
                        key = new Key(has_shift ? str_lower : str_upper);
                        is_capslock_hack = true;
                    }
                }
            }

            // Update statistics
            if (is_keydown)
            {
                // Update single key statistics
                Stats.AddKey(key);

                // Update key pair statistics if applicable
                if (DateTime.Now < m_last_key_time.AddMilliseconds(2000) &&
                    m_last_key != null)
                {
                    Stats.AddPair(m_last_key, key);
                }

                // Remember when we pressed a key for the last time
                m_last_key_time = DateTime.Now;
                m_last_key      = key;
            }

            // If the special Synergy window has focus, we’re actually sending
            // keystrokes to another computer; disable WinCompose.
            if (m_window_is_synergy)
            {
                return(false);
            }

            // If we receive a keyup for the compose key while in emulation
            // mode, we’re done. Send a KeyUp event and exit emulation mode.
            if (is_keyup && key == Settings.ComposeKey.Value &&
                CurrentState == State.Combination)
            {
                Log.Debug("Combination Off");
                CurrentState      = State.Idle;
                m_compose_counter = 0;

                // If relevant, send an additional KeyUp for the opposite
                // key; experience indicates that it helps unstick some
                // applications such as mintty.exe.
                switch (Settings.ComposeKey.Value.VirtualKey)
                {
                case VK.LMENU: SendKeyUp(VK.RMENU); break;

                case VK.RMENU: SendKeyUp(VK.LMENU);
                    if (compose_is_altgr)
                    {
                        SendKeyUp(VK.LCONTROL);
                    }
                    break;

                case VK.LSHIFT: SendKeyUp(VK.RSHIFT); break;

                case VK.RSHIFT: SendKeyUp(VK.LSHIFT); break;

                case VK.LCONTROL: SendKeyUp(VK.RCONTROL); break;

                case VK.RCONTROL: SendKeyUp(VK.LCONTROL); break;
                }

                return(false);
            }

            // If this is the compose key and we’re idle, enter Sequence mode
            if (is_keydown && key == Settings.ComposeKey.Value &&
                m_compose_counter == 0 && CurrentState == State.Idle)
            {
                Log.Debug("Now Composing");
                CurrentState = State.Sequence;
                ++m_compose_counter;

                // Lauch the sequence reset expiration thread
                // FIXME: do we need to launch a new thread each time the
                // compose key is pressed? Let's have a dormant thread instead
                if (Settings.ResetDelay.Value > 0)
                {
                    new Thread(() =>
                    {
                        while (CurrentState == State.Sequence &&
                               DateTime.Now < m_last_key_time.AddMilliseconds(Settings.ResetDelay.Value))
                        {
                            Thread.Sleep(50);
                        }
                        ResetSequence();
                    }).Start();
                }

                return(true);
            }

            // If this is a compose key KeyDown event and it’s already down, or it’s
            // a KeyUp and it’s already up, eat this event without forwarding it.
            if (key == Settings.ComposeKey.Value &&
                is_keydown == ((m_compose_counter & 1) != 0))
            {
                return(true);
            }

            // Escape and backspace cancel the current sequence
            if (is_keydown && CurrentState == State.Sequence &&
                (key.VirtualKey == VK.ESCAPE || key.VirtualKey == VK.BACK))
            {
                // FIXME: if a sequence was in progress, maybe print it!
                Log.Debug("No Longer Composing");
                ResetSequence();
                return(true);
            }

            // Feature: emulate capslock key with both shift keys, and optionally
            // disable capslock using only one shift key.
            if (key.VirtualKey == VK.LSHIFT || key.VirtualKey == VK.RSHIFT)
            {
                if (is_keyup && has_lrshift && Settings.EmulateCapsLock.Value)
                {
                    SendKeyPress(VK.CAPITAL);
                    return(false);
                }

                if (is_keydown && has_capslock && Settings.ShiftDisablesCapsLock.Value)
                {
                    SendKeyPress(VK.CAPITAL);
                    return(false);
                }
            }

            // If we are not currently composing a sequence, do nothing unless
            // one of our hacks forces us to send the key as a string (for
            // instance the Caps Lock capitalisation feature).
            if (CurrentState != State.Sequence)
            {
                if (is_capslock_hack && is_keydown)
                {
                    SendString(key.ToString());
                    return(true);
                }

                // If this was a dead key, it will be completely ignored. But
                // it’s okay since we stored it.
                Log.Debug("Forwarding Key to System (not composing)");
                return(false);
            }

            // If the compose key is down and the user pressed a new key, maybe
            // they want to do a key combination instead of composing, such as
            // Alt+Tab or Windows+Up. So we abort composing and send the KeyDown
            // event for the Compose key that we previously discarded. The same
            // goes for characters that need AltGr when AltGr is the compose key.
            //
            // Never do this if the event is KeyUp.
            // Never do this if we already started a sequence
            // Never do this if the key is a modifier key such as shift or alt.
            if (m_compose_counter == 1 && is_keydown &&
                m_sequence.Count == 0 && !key.IsModifier())
            {
                bool keep_original     = Settings.KeepOriginalKey.Value;
                bool key_unusable      = !key.IsUsable();
                bool altgr_combination = compose_is_altgr &&
                                         m_possible_altgr_keys.ContainsKey(key.ToString());

                if (keep_original || key_unusable || altgr_combination)
                {
                    Log.Debug("Combination On");
                    ResetSequence();
                    if (compose_is_altgr)
                    {
                        // It’s necessary to use KEYEVENTF_EXTENDEDKEY otherwise the system
                        // does not understand that we’re sending AltGr.
                        SendKeyDown(VK.LCONTROL);
                        SendKeyDown(VK.RMENU, KEYEVENTF.EXTENDEDKEY);
                    }
                    else
                    {
                        SendKeyDown(Settings.ComposeKey.Value.VirtualKey);
                    }
                    CurrentState = State.Combination;
                    return(false);
                }
            }

            // If this is the compose key again, use our custom virtual key
            // FIXME: we don’t properly support compose keys that also normally
            // print stuff, such as `.
            if (key == Settings.ComposeKey.Value)
            {
                ++m_compose_counter;
                key = new Key(VK.COMPOSE);

                // If the compose key is AltGr, we only add it to the sequence
                // if it’s a KeyUp event, otherwise we may be adding Multi_key to the
                // sequence when the user actually wants to enter an AltGr char.
                if (compose_is_altgr && m_compose_counter > 2)
                {
                    add_to_sequence = is_keyup;
                }
            }

            // If the compose key is AltGr and it’s down, check whether the current
            // key needs translating.
            if (compose_is_altgr && (m_compose_counter & 1) != 0)
            {
                string altgr_variant;
                if (m_possible_altgr_keys.TryGetValue(key.ToString(), out altgr_variant))
                {
                    key = new Key(altgr_variant);
                    // Do as if we already released Compose, otherwise the next KeyUp
                    // event will cause VK.COMPOSE to be added to the sequence…
                    ++m_compose_counter;
                }
            }

            // If the key can't be used in a sequence, just ignore it.
            if (!key.IsUsable())
            {
                Log.Debug("Forwarding Key to System (no possible sequence uses it)");
                return(false);
            }

            // If we reached this point, everything else ignored this key, so it
            // is a key we must add to the current sequence.
            if (add_to_sequence)
            {
                Log.Debug("Adding To Sequence: {0}", key.FriendlyName);
                return(AddToSequence(key));
            }

            return(true);
        }
コード例 #13
0
ファイル: Composer.cs プロジェクト: DarkDare/wincompose
    private static bool OnKeyInternal(WM ev, VK vk, SC sc, LLKHF flags)
    {
        //CheckKeyboardLayout();

        bool is_keydown = (ev == WM.KEYDOWN || ev == WM.SYSKEYDOWN);
        bool is_keyup = !is_keydown;
        bool is_capslock_hack = false;

        bool has_shift = (NativeMethods.GetKeyState(VK.SHIFT) & 0x80) != 0;
        bool has_altgr = (NativeMethods.GetKeyState(VK.LCONTROL) &
                          NativeMethods.GetKeyState(VK.RMENU) & 0x80) != 0;
        bool has_lrshift = (NativeMethods.GetKeyState(VK.LSHIFT) &
                            NativeMethods.GetKeyState(VK.RSHIFT) & 0x80) != 0;
        bool has_capslock = NativeMethods.GetKeyState(VK.CAPITAL) != 0;

        // Guess what the system would print if we weren’t interfering. If
        // a printable representation exists, use that. Otherwise, default
        // to its virtual key code.
        Key key = VkToKey(vk, sc, flags, has_shift, has_altgr, has_capslock);

        // If Caps Lock is on, and the Caps Lock hack is enabled, we check
        // whether this key without Caps Lock gives a non-ASCII alphabetical
        // character. If so, we replace “result” with the lowercase or
        // uppercase variant of that character.
        if (has_capslock && Settings.CapsLockCapitalizes.Value)
        {
            Key alt_key = VkToKey(vk, sc, flags, has_shift, has_altgr, false);

            if (alt_key.IsPrintable() && alt_key.ToString()[0] > 0x7f)
            {
                string str_upper = alt_key.ToString().ToUpper();
                string str_lower = alt_key.ToString().ToLower();
                if (str_upper != str_lower)
                {
                    key = new Key(has_shift ? str_lower : str_upper);
                    is_capslock_hack = true;
                }
            }
        }

        // Update statistics
        if (is_keydown)
        {
            // Update single key statistics
            Stats.AddKey(key);

            // Update key pair statistics if applicable
            if (DateTime.Now < m_last_key_time.AddMilliseconds(2000)
                 && m_last_key != null)
            {
                Stats.AddPair(m_last_key, key);
            }

            // Remember when we pressed a key for the last time
            m_last_key_time = DateTime.Now;
            m_last_key = key;
        }

        // FIXME: we don’t properly support compose keys that also normally
        // print stuff, such as `.
        if (key == Settings.ComposeKey.Value)
        {
            // If we receive a keyup for the compose key while in emulation
            // mode, we’re done. Send a KeyUp event and exit emulation mode.
            if (is_keyup && CurrentState == State.Combination)
            {
                Log.Debug("Combination Off");
                CurrentState = State.Idle;
                m_compose_down = false;

                // If relevant, send an additional KeyUp for the opposite
                // key; experience indicates that it helps unstick some
                // applications such as mintty.exe.
                switch (Settings.ComposeKey.Value.VirtualKey)
                {
                    case VK.LMENU: SendKeyUp(VK.RMENU); break;
                    case VK.RMENU: SendKeyUp(VK.LMENU); break;
                    case VK.LSHIFT: SendKeyUp(VK.RSHIFT); break;
                    case VK.RSHIFT: SendKeyUp(VK.LSHIFT); break;
                    case VK.LCONTROL: SendKeyUp(VK.RCONTROL); break;
                    case VK.RCONTROL: SendKeyUp(VK.LCONTROL); break;
                }

                return false;
            }

            if (is_keydown && !m_compose_down)
            {
                // FIXME: we don't want compose + compose to disable composing,
                // since there are compose sequences that use Multi_key.
                switch (CurrentState)
                {
                    case State.Sequence:
                        // FIXME: also, if a sequence was in progress, print it!
                        CurrentState = State.Idle;
                        m_sequence.Clear();
                        break;
                    case State.Idle:
                        CurrentState = State.Sequence;
                        // Lauch the sequence reset expiration thread
                        // FIXME: do we need to launch a new thread each time the
                        // compose key is pressed? Let's have a dormant thread instead
                        if (Settings.ResetDelay.Value > 0)
                        {
                            new Thread(() =>
                            {
                                while (CurrentState == State.Sequence &&
                                        DateTime.Now < m_last_key_time.AddMilliseconds(Settings.ResetDelay.Value))
                                    Thread.Sleep(50);
                                ResetSequence();
                            }).Start();
                        }
                        break;
                }

                Log.Debug("{0} Composing", IsComposing() ? "Now" : "No Longer");
            }

            m_compose_down = is_keydown;

            return true;
        }

        // Feature: emulate capslock key with both shift keys, and optionally
        // disable capslock using only one shift key.
        if (key.VirtualKey == VK.LSHIFT || key.VirtualKey == VK.RSHIFT)
        {
            if (is_keyup && has_lrshift && Settings.EmulateCapsLock.Value)
            {
                SendKeyPress(VK.CAPITAL);
                return false;
            }

            if (is_keydown && has_capslock && Settings.ShiftDisablesCapsLock.Value)
            {
                SendKeyPress(VK.CAPITAL);
                return false;
            }
        }

        // If we are not currently composing a sequence, do nothing unless
        // one of our hacks forces us to send the key as a string (for
        // instance the Caps Lock capitalisation feature).
        if (CurrentState != State.Sequence)
        {
            if (is_capslock_hack && is_keydown)
            {
                SendString(key.ToString());
                return true;
            }

            // If this was a dead key, it will be completely ignored. But
            // it’s okay since we stored it.
            return false;
        }

        // If the compose key is down and the user pressed a new key, maybe
        // they want to do a key combination instead of composing, such as
        // Alt+Tab or Windows+Up. So we abort composing and send the KeyDown
        // event for the Compose key that we previously discarded.
        //
        // Never do this if the event is KeyUp.
        // Never do this if we already started a sequence
        // Never do this if the key is a modifier key such as shift or alt.
        if (m_compose_down && is_keydown
             && m_sequence.Count == 0 && !key.IsModifier()
             && (Settings.KeepOriginalKey.Value || !key.IsUsable()))
        {
            Log.Debug("Combination On");
            ResetSequence();
            SendKeyDown(Settings.ComposeKey.Value.VirtualKey);
            CurrentState = State.Combination;
            return false;
        }

        // If the key can't be used in a sequence, just ignore it.
        if (!key.IsUsable())
        {
            return false;
        }

        // If we reached this point, everything else ignored this key, so it
        // is a key we must add to the current sequence.
        if (is_keydown)
        {
            Log.Debug("Adding To Sequence: {0}", key.FriendlyName);
            return AddToSequence(key);
        }

        return true;
    }
コード例 #14
0
        private static bool OnKeyInternal(WM ev, VK vk, SC sc, LLKHF flags)
        {
            bool is_keydown       = (ev == WM.KEYDOWN || ev == WM.SYSKEYDOWN);
            bool is_keyup         = !is_keydown;
            bool add_to_sequence  = is_keydown;
            bool is_capslock_hack = false;

            bool has_shift = (NativeMethods.GetKeyState(VK.SHIFT) & 0x80) != 0;
            bool has_altgr = (NativeMethods.GetKeyState(VK.LCONTROL) &
                              NativeMethods.GetKeyState(VK.RMENU) & 0x80) != 0;
            bool has_lrshift = (NativeMethods.GetKeyState(VK.LSHIFT) &
                                NativeMethods.GetKeyState(VK.RSHIFT) & 0x80) != 0;
            bool has_capslock = NativeMethods.GetKeyState(VK.CAPITAL) != 0;

            // Guess what the system would print if we weren’t interfering. If
            // a printable representation exists, use that. Otherwise, default
            // to its virtual key code.
            Key key = KeyboardLayout.VkToKey(vk, sc, flags, has_shift, has_altgr, has_capslock);

            // If Caps Lock is on, and the Caps Lock hack is enabled, we check
            // whether this key without Caps Lock gives a non-ASCII alphabetical
            // character. If so, we replace “result” with the lowercase or
            // uppercase variant of that character.
            if (has_capslock && Settings.CapsLockCapitalizes.Value)
            {
                Key alt_key = KeyboardLayout.VkToKey(vk, sc, flags, has_shift, has_altgr, false);

                if (alt_key.IsPrintable && alt_key.ToString()[0] > 0x7f)
                {
                    string str_upper = alt_key.ToString().ToUpper();
                    string str_lower = alt_key.ToString().ToLower();

                    // Hack for German keyboards: it seems that ToUpper() does
                    // not properly change ß into ẞ.
                    if (str_lower == "ß")
                    {
                        str_upper = "ẞ";
                    }

                    if (str_upper != str_lower)
                    {
                        key = new Key(has_shift ? str_lower : str_upper);
                        is_capslock_hack = true;
                    }
                }
            }

            // If we are being used to capture a key, send the resulting key
            if (Captured != null)
            {
                if (!is_keydown)
                {
                    Captured.Invoke(key);
                }
                return(true);
            }

            // Update statistics
            if (is_keydown)
            {
                // Update single key statistics
                Stats.AddKey(key);

                // Update key pair statistics if applicable
                if (DateTime.Now < m_last_key_time.AddMilliseconds(2000) &&
                    m_last_key != null)
                {
                    Stats.AddPair(m_last_key, key);
                }

                // Remember when we pressed a key for the last time
                m_last_key_time = DateTime.Now;
                m_last_key      = key;
            }

            // If the special Synergy window has focus, we’re actually sending
            // keystrokes to another computer; disable WinCompose. Same if it is
            // a Cygwin X window.
            if (KeyboardLayout.Window.IsOtherDesktop)
            {
                return(false);
            }

            // Sanity check in case the configuration changed between two
            // key events.
            if (m_current_compose_key.VirtualKey != VK.NONE &&
                !Settings.ComposeKeys.Value.Contains(m_current_compose_key))
            {
                CurrentState          = State.Idle;
                m_current_compose_key = new Key(VK.NONE);
            }

            // If we receive a keyup for the compose key while in emulation
            // mode, we’re done. Send a KeyUp event and exit emulation mode.
            if (is_keyup && CurrentState == State.KeyCombination &&
                key == m_current_compose_key)
            {
                bool compose_key_was_altgr = m_compose_key_is_altgr;
                Key  old_compose_key       = m_current_compose_key;
                CurrentState           = State.Idle;
                m_current_compose_key  = new Key(VK.NONE);
                m_compose_key_is_altgr = false;
                m_compose_counter      = 0;

                Log.Debug("KeyCombination ended (state: {0})", m_state);

                // If relevant, send an additional KeyUp for the opposite
                // key; experience indicates that it helps unstick some
                // applications such as mintty.exe.
                switch (old_compose_key.VirtualKey)
                {
                case VK.LMENU: SendKeyUp(VK.RMENU); break;

                case VK.RMENU: SendKeyUp(VK.LMENU);
                    // If keyup is RMENU and we have AltGr on this
                    // keyboard layout, send LCONTROL up too.
                    if (compose_key_was_altgr)
                    {
                        SendKeyUp(VK.LCONTROL);
                    }
                    break;

                case VK.LSHIFT: SendKeyUp(VK.RSHIFT); break;

                case VK.RSHIFT: SendKeyUp(VK.LSHIFT); break;

                case VK.LCONTROL: SendKeyUp(VK.RCONTROL); break;

                case VK.RCONTROL: SendKeyUp(VK.LCONTROL); break;
                }

                return(false);
            }

            // If this is the compose key and we’re idle, enter Sequence mode
            if (is_keydown && Settings.ComposeKeys.Value.Contains(key) &&
                m_compose_counter == 0 && CurrentState == State.Idle)
            {
                CurrentState           = State.Sequence;
                m_current_compose_key  = key;
                m_compose_key_is_altgr = key.VirtualKey == VK.RMENU &&
                                         KeyboardLayout.HasAltGr;
                ++m_compose_counter;

                Log.Debug("Now composing (state: {0}) (altgr: {1})",
                          m_state, m_compose_key_is_altgr);

                // Lauch the sequence reset expiration timer
                if (Settings.ResetDelay.Value > 0)
                {
                    m_timeout.Change(TimeSpan.FromMilliseconds(Settings.ResetDelay.Value), NEVER);
                }

                return(true);
            }

            // If this is a compose key KeyDown event and it’s already down, or it’s
            // a KeyUp and it’s already up, eat this event without forwarding it.
            if (key == m_current_compose_key &&
                is_keydown == ((m_compose_counter & 1) != 0))
            {
                return(true);
            }

            // Escape and backspace cancel the current sequence
            if (is_keydown && CurrentState == State.Sequence &&
                (key.VirtualKey == VK.ESCAPE || key.VirtualKey == VK.BACK))
            {
                // FIXME: if a sequence was in progress, maybe print it!
                ResetSequence();
                Log.Debug("No longer composing (state: {0})", m_state);
                return(true);
            }

            // Feature: emulate capslock key with both shift keys, and optionally
            // disable capslock using only one shift key.
            if (key.VirtualKey == VK.LSHIFT || key.VirtualKey == VK.RSHIFT)
            {
                if (is_keyup && has_lrshift && Settings.EmulateCapsLock.Value)
                {
                    SendKeyPress(VK.CAPITAL);
                    return(false);
                }

                if (is_keydown && has_capslock && Settings.ShiftDisablesCapsLock.Value)
                {
                    SendKeyPress(VK.CAPITAL);
                    return(false);
                }
            }

            // If we are not currently composing a sequence, do nothing unless
            // one of our hacks forces us to send the key as a string (for
            // instance the Caps Lock capitalisation feature).
            if (CurrentState != State.Sequence)
            {
                if (is_capslock_hack && is_keydown)
                {
                    SendString(key.ToString());
                    return(true);
                }

                // If this was a dead key, it will be completely ignored. But
                // it’s okay since we stored it.
                Log.Debug("Forwarding {0} “{1}” to system (state: {2})",
                          is_keydown ? "⭝" : "⭜", key.FriendlyName, m_state);
                return(false);
            }

            //
            // From this point we know we are composing
            //

            // If the compose key is down and the user pressed a new key, maybe
            // instead of composing they want to do a key combination, such as
            // Alt+Tab or Windows+Up. So we abort composing and send the KeyDown
            // event for the Compose key that we previously discarded. The same
            // goes for characters that need AltGr when AltGr is the compose key.
            //
            // Never do this if the event is KeyUp.
            // Never do this if we already started a sequence
            // Never do this if the key is a modifier key such as shift or alt.
            if (m_compose_counter == 1 && is_keydown &&
                m_sequence.Count == 0 && !key.IsModifier())
            {
                bool keep_original     = Settings.KeepOriginalKey.Value;
                bool key_unusable      = !key.IsUsable();
                bool altgr_combination = m_compose_key_is_altgr &&
                                         KeyboardLayout.KeyToAltGrVariant(key) != null;

                if (keep_original || key_unusable || altgr_combination)
                {
                    bool compose_key_was_altgr = m_compose_key_is_altgr;
                    ResetSequence();
                    if (compose_key_was_altgr)
                    {
                        // It’s necessary to use KEYEVENTF_EXTENDEDKEY otherwise the system
                        // does not understand that we’re sending AltGr.
                        SendKeyDown(VK.LCONTROL);
                        SendKeyDown(VK.RMENU, KEYEVENTF.EXTENDEDKEY);
                    }
                    else
                    {
                        SendKeyDown(m_current_compose_key.VirtualKey);
                    }
                    CurrentState = State.KeyCombination;
                    Log.Debug("KeyCombination started (state: {0})", m_state);
                    return(false);
                }
            }

            // If this is the compose key again, use our custom virtual key
            // FIXME: we don’t properly support compose keys that also normally
            // print stuff, such as `.
            if (key == m_current_compose_key)
            {
                ++m_compose_counter;
                key = new Key(VK.COMPOSE);

                // If the compose key is AltGr, we only add it to the sequence when
                // it’s a KeyUp event, otherwise we may be adding Multi_key to the
                // sequence while the user actually wants to enter an AltGr char.
                if (m_compose_key_is_altgr && m_compose_counter > 2)
                {
                    add_to_sequence = is_keyup;
                }
            }

            // If the compose key is AltGr and it’s down, check whether the current
            // key needs translating.
            if (m_compose_key_is_altgr && (m_compose_counter & 1) != 0)
            {
                Key altgr_variant = KeyboardLayout.KeyToAltGrVariant(key);
                if (altgr_variant != null)
                {
                    key = altgr_variant;
                    // Do as if we already released Compose, otherwise the next KeyUp
                    // event will cause VK.COMPOSE to be added to the sequence…
                    ++m_compose_counter;
                }
            }

            // If the key can't be used in a sequence, just ignore it.
            if (!key.IsUsable())
            {
                Log.Debug("Forwarding unusable {0} “{1}” to system (state: {2})",
                          is_keydown ? "⭝" : "⭜", key.FriendlyName, m_state);
                return(false);
            }

            // If we reached this point, everything else ignored this key, so it
            // is a key we must add to the current sequence.
            if (add_to_sequence)
            {
                Log.Debug("Adding to sequence: {0}", key.FriendlyName);
                return(AddToSequence(key));
            }

            return(true);
        }
コード例 #15
0
    private static bool OnKeyInternal(WM ev, VK vk, SC sc, LLKHF flags)
    {
        CheckKeyboardLayout();

        bool is_keydown = (ev == WM.KEYDOWN || ev == WM.SYSKEYDOWN);
        bool is_keyup = !is_keydown;

        bool has_shift = (NativeMethods.GetKeyState(VK.SHIFT) & 0x80) != 0;
        bool has_altgr = (NativeMethods.GetKeyState(VK.LCONTROL) &
                          NativeMethods.GetKeyState(VK.RMENU) & 0x80) != 0;
        bool has_lrshift = (NativeMethods.GetKeyState(VK.LSHIFT) &
                            NativeMethods.GetKeyState(VK.RSHIFT) & 0x80) != 0;
        bool has_capslock = NativeMethods.GetKeyState(VK.CAPITAL) != 0;

        // Guess what key was just pressed. If we can not find a printable
        // representation for the key, default to its virtual key code.
        Key key = new Key(vk);

        byte[] keystate = new byte[256];
        NativeMethods.GetKeyboardState(keystate);
        keystate[(int)VK.SHIFT] = (byte)(has_shift ? 0x80 : 0x00);
        keystate[(int)VK.CONTROL] = (byte)(has_altgr ? 0x80 : 0x00);
        keystate[(int)VK.MENU] = (byte)(has_altgr ? 0x80 : 0x00);
        keystate[(int)VK.CAPITAL] = (byte)(has_capslock ? 0x01 : 0x00);



        string str_if_normal = KeyToUnicode(vk, sc, keystate, flags);
        string str_if_dead = KeyToUnicode(VK.SPACE);
        if (str_if_normal != "")
        {
            // This appears to be a normal, printable key
            key = new Key(str_if_normal);
        }
        else if (str_if_dead != " ")
        {
            // This appears to be a dead key
            key = new Key(str_if_dead);
        }

        // Special case: we don't consider characters such as Esc as printable
        // otherwise they are not properly serialised in the config file.
        if (key.IsPrintable() && key.ToString()[0] < ' ')
        {
            key = new Key(vk);
        }

        Log("WM.{0} {1} (VK:0x{2:X02} SC:0x{3:X02})",
            ev.ToString(), key.FriendlyName, (int)vk, (int)sc);

        // FIXME: we don’t properly support compose keys that also normally
        // print stuff, such as `.
        if (key == Settings.ComposeKey.Value)
        {
            if (is_keyup)
            {
                // If we receive a keyup for the compose key, but we hadn't
                // previously marked it as down, it means we're in emulation
                // mode and we need to cancel it.
                if (!m_compose_down)
                {
                    Log("Fallback Off");
                    SendKeyUp(Settings.ComposeKey.Value.VirtualKey);
                }

                m_compose_down = false;
            }
            else if (is_keydown && !m_compose_down)
            {
                // FIXME: we don't want compose + compose to disable composing,
                // since there are compose sequences that use Multi_key.
                // FIXME: also, if a sequence was in progress, print it!
                m_compose_down = true;
                m_composing = !m_composing;
                if (!m_composing)
                    m_sequence.Clear();

                Log("{0} Composing", m_composing ? "Now" : "No Longer");

                // Lauch the sequence reset expiration thread
                // FIXME: do we need to launch a new thread each time the
                // compose key is pressed? Let's have a dormant thread instead
                if (m_composing && Settings.ResetDelay.Value > 0)
                {
                    new Thread(() =>
                    {
                        while (m_composing && DateTime.Now < m_last_key_time.AddMilliseconds(Settings.ResetDelay.Value))
                            Thread.Sleep(50);
                        ResetSequence();
                    }).Start();
                }
            }

            Changed(null, new EventArgs());

            return true;
        }

        // Feature: emulate capslock key with both shift keys, and optionally
        // disable capslock using only one shift key.
        if (key.VirtualKey == VK.LSHIFT || key.VirtualKey == VK.RSHIFT)
        {
            if (is_keyup && has_lrshift && Settings.EmulateCapsLock.Value)
            {
                SendKeyPress(VK.CAPITAL);
                return false;
            }

            if (is_keydown && has_capslock && Settings.ShiftDisablesCapsLock.Value)
            {
                SendKeyPress(VK.CAPITAL);
                return false;
            }
        }

        // If we are not currently composing a sequence, do nothing. But if
        // this was a dead key, eat it.
        if (!m_composing)
        {
            return false;
        }

        // If the compose key is down, maybe there is a key combination
        // going on, such as Alt+Tab or Windows+Up, so we abort composing
        // and tell the OS that the key is down.
        if (m_compose_down && (Settings.KeepOriginalKey.Value
                                || !Settings.IsUsableKey(key)))
        {
            Log("Fallback On");
            ResetSequence();
            SendKeyDown(Settings.ComposeKey.Value.VirtualKey);
            return false;
        }

        // If the key can't be used in a sequence, just ignore it.
        if (!Settings.IsUsableKey(key))
        {
            return false;
        }

        // If we reached this point, everything else ignored this key, so it
        // is a key we must add to the current sequence.
        if (is_keydown)
        {
            return AddToSequence(key);
        }

        return true;
    }
コード例 #16
0
ファイル: WinApi.cs プロジェクト: DarkDare/wincompose
 public static extern int ToUnicode(VK wVirtKey, SC wScanCode,
                                    byte[] lpKeyState, byte[] pwszBuff,
                                    int cchBuff, LLKHF flags);
コード例 #17
0
ファイル: WinApi.cs プロジェクト: DarkDare/wincompose
 public static extern int ToUnicode(VK wVirtKey, SC wScanCode,
                                    byte[] lpKeyState, byte[] pwszBuff,
                                    int cchBuff, LLKHF flags);
コード例 #18
0
ファイル: Composer.cs プロジェクト: DarkDare/wincompose
        private static bool OnKeyInternal(WM ev, VK vk, SC sc, LLKHF flags)
        {
            //CheckKeyboardLayout();

            bool is_keydown       = (ev == WM.KEYDOWN || ev == WM.SYSKEYDOWN);
            bool is_keyup         = !is_keydown;
            bool is_capslock_hack = false;

            bool has_shift = (NativeMethods.GetKeyState(VK.SHIFT) & 0x80) != 0;
            bool has_altgr = (NativeMethods.GetKeyState(VK.LCONTROL) &
                              NativeMethods.GetKeyState(VK.RMENU) & 0x80) != 0;
            bool has_lrshift = (NativeMethods.GetKeyState(VK.LSHIFT) &
                                NativeMethods.GetKeyState(VK.RSHIFT) & 0x80) != 0;
            bool has_capslock = NativeMethods.GetKeyState(VK.CAPITAL) != 0;

            // Guess what the system would print if we weren’t interfering. If
            // a printable representation exists, use that. Otherwise, default
            // to its virtual key code.
            Key key = VkToKey(vk, sc, flags, has_shift, has_altgr, has_capslock);

            // If Caps Lock is on, and the Caps Lock hack is enabled, we check
            // whether this key without Caps Lock gives a non-ASCII alphabetical
            // character. If so, we replace “result” with the lowercase or
            // uppercase variant of that character.
            if (has_capslock && Settings.CapsLockCapitalizes.Value)
            {
                Key alt_key = VkToKey(vk, sc, flags, has_shift, has_altgr, false);

                if (alt_key.IsPrintable() && alt_key.ToString()[0] > 0x7f)
                {
                    string str_upper = alt_key.ToString().ToUpper();
                    string str_lower = alt_key.ToString().ToLower();
                    if (str_upper != str_lower)
                    {
                        key = new Key(has_shift ? str_lower : str_upper);
                        is_capslock_hack = true;
                    }
                }
            }

            // Update statistics
            if (is_keydown)
            {
                // Update single key statistics
                Stats.AddKey(key);

                // Update key pair statistics if applicable
                if (DateTime.Now < m_last_key_time.AddMilliseconds(2000) &&
                    m_last_key != null)
                {
                    Stats.AddPair(m_last_key, key);
                }

                // Remember when we pressed a key for the last time
                m_last_key_time = DateTime.Now;
                m_last_key      = key;
            }

            // FIXME: we don’t properly support compose keys that also normally
            // print stuff, such as `.
            if (key == Settings.ComposeKey.Value)
            {
                // If we receive a keyup for the compose key while in emulation
                // mode, we’re done. Send a KeyUp event and exit emulation mode.
                if (is_keyup && CurrentState == State.Combination)
                {
                    Log.Debug("Combination Off");
                    CurrentState   = State.Idle;
                    m_compose_down = false;

                    // If relevant, send an additional KeyUp for the opposite
                    // key; experience indicates that it helps unstick some
                    // applications such as mintty.exe.
                    switch (Settings.ComposeKey.Value.VirtualKey)
                    {
                    case VK.LMENU: SendKeyUp(VK.RMENU); break;

                    case VK.RMENU: SendKeyUp(VK.LMENU); break;

                    case VK.LSHIFT: SendKeyUp(VK.RSHIFT); break;

                    case VK.RSHIFT: SendKeyUp(VK.LSHIFT); break;

                    case VK.LCONTROL: SendKeyUp(VK.RCONTROL); break;

                    case VK.RCONTROL: SendKeyUp(VK.LCONTROL); break;
                    }

                    return(false);
                }

                if (is_keydown && !m_compose_down)
                {
                    // FIXME: we don't want compose + compose to disable composing,
                    // since there are compose sequences that use Multi_key.
                    switch (CurrentState)
                    {
                    case State.Sequence:
                        // FIXME: also, if a sequence was in progress, print it!
                        CurrentState = State.Idle;
                        m_sequence.Clear();
                        break;

                    case State.Idle:
                        CurrentState = State.Sequence;
                        // Lauch the sequence reset expiration thread
                        // FIXME: do we need to launch a new thread each time the
                        // compose key is pressed? Let's have a dormant thread instead
                        if (Settings.ResetDelay.Value > 0)
                        {
                            new Thread(() =>
                            {
                                while (CurrentState == State.Sequence &&
                                       DateTime.Now < m_last_key_time.AddMilliseconds(Settings.ResetDelay.Value))
                                {
                                    Thread.Sleep(50);
                                }
                                ResetSequence();
                            }).Start();
                        }
                        break;
                    }

                    Log.Debug("{0} Composing", IsComposing() ? "Now" : "No Longer");
                }

                m_compose_down = is_keydown;

                return(true);
            }

            // Feature: emulate capslock key with both shift keys, and optionally
            // disable capslock using only one shift key.
            if (key.VirtualKey == VK.LSHIFT || key.VirtualKey == VK.RSHIFT)
            {
                if (is_keyup && has_lrshift && Settings.EmulateCapsLock.Value)
                {
                    SendKeyPress(VK.CAPITAL);
                    return(false);
                }

                if (is_keydown && has_capslock && Settings.ShiftDisablesCapsLock.Value)
                {
                    SendKeyPress(VK.CAPITAL);
                    return(false);
                }
            }

            // If we are not currently composing a sequence, do nothing unless
            // one of our hacks forces us to send the key as a string (for
            // instance the Caps Lock capitalisation feature).
            if (CurrentState != State.Sequence)
            {
                if (is_capslock_hack && is_keydown)
                {
                    SendString(key.ToString());
                    return(true);
                }

                // If this was a dead key, it will be completely ignored. But
                // it’s okay since we stored it.
                return(false);
            }

            // If the compose key is down and the user pressed a new key, maybe
            // they want to do a key combination instead of composing, such as
            // Alt+Tab or Windows+Up. So we abort composing and send the KeyDown
            // event for the Compose key that we previously discarded.
            //
            // Never do this if the event is KeyUp.
            // Never do this if we already started a sequence
            // Never do this if the key is a modifier key such as shift or alt.
            if (m_compose_down && is_keydown &&
                m_sequence.Count == 0 && !key.IsModifier() &&
                (Settings.KeepOriginalKey.Value || !key.IsUsable()))
            {
                Log.Debug("Combination On");
                ResetSequence();
                SendKeyDown(Settings.ComposeKey.Value.VirtualKey);
                CurrentState = State.Combination;
                return(false);
            }

            // If the key can't be used in a sequence, just ignore it.
            if (!key.IsUsable())
            {
                return(false);
            }

            // If we reached this point, everything else ignored this key, so it
            // is a key we must add to the current sequence.
            if (is_keydown)
            {
                Log.Debug("Adding To Sequence: {0}", key.FriendlyName);
                return(AddToSequence(key));
            }

            return(true);
        }