GetKeyState() private method

private GetKeyState ( VK nVirtKey ) : short
nVirtKey VK
return short
Ejemplo n.º 1
0
        private static void SendString(string str)
        {
            List <VK> modifiers = new List <VK>();
            bool      use_gtk_hack = false, use_office_hack = false;

            const int     len  = 256;
            StringBuilder buf  = new StringBuilder(len);
            var           hwnd = NativeMethods.GetForegroundWindow();

            if (NativeMethods.GetClassName(hwnd, buf, len) > 0)
            {
                string wclass = buf.ToString();

                /* HACK: GTK+ applications behave differently with Unicode, and some
                 * applications such as XChat for Windows rename their own top-level
                 * window, so we parse through the names we know in order to detect
                 * a GTK+ application. */
                if (wclass == "gdkWindowToplevel" || wclass == "xchatWindowToplevel")
                {
                    use_gtk_hack = true;
                }

                /* HACK: in MS Office, some symbol insertions change the text font
                 * without returning to the original font. To avoid this, we output
                 * a space character, then go left, insert our actual symbol, then
                 * go right and backspace. */
                /* These are the actual window class names for Outlook and Word…
                 * TODO: PowerPoint ("PP(7|97|9|10)FrameClass") */
                if (wclass == "rctrl_renwnd32" || wclass == "OpusApp")
                {
                    use_office_hack = true && Settings.InsertZwsp.Value;
                }
            }

            /* Clear keyboard modifiers if we need one of our custom hacks */
            if (use_gtk_hack || use_office_hack)
            {
                VK[] all_modifiers = new VK[]
                {
                    VK.LSHIFT, VK.RSHIFT,
                    VK.LCONTROL, VK.RCONTROL,
                    VK.LMENU, VK.RMENU,
                };

                foreach (VK vk in all_modifiers)
                {
                    if ((NativeMethods.GetKeyState(vk) & 0x80) == 0x80)
                    {
                        modifiers.Add(vk);
                    }
                }

                foreach (VK vk in modifiers)
                {
                    SendKeyUp(vk);
                }
            }

            if (use_gtk_hack)
            {
                foreach (var ch in str)
                {
                    if (false)
                    {
                        /* FIXME: there is a possible optimisation here where we do
                         * not have to send the whole unicode sequence for regular
                         * ASCII characters. However, SendKeyPress() needs a VK, so
                         * we need an ASCII to VK conversion method, together with
                         * the proper keyboard modifiers. Maybe not worth it.
                         * Also, we cannot use KeySequence because GTK+ seems to
                         * ignore SendInput(). */
                        //SendKeyPress((VK)char.ToUpper(ch));
                    }
                    else
                    {
                        /* Wikipedia says Ctrl+Shift+u, release, then type the four
                         * hex digits, and press Enter.
                         * (http://en.wikipedia.org/wiki/Unicode_input). */
                        SendKeyDown(VK.LCONTROL);
                        SendKeyDown(VK.LSHIFT);
                        SendKeyPress((VK)'U');
                        SendKeyUp(VK.LSHIFT);
                        SendKeyUp(VK.LCONTROL);

                        foreach (var key in String.Format("{0:X04} ", (short)ch))
                        {
                            SendKeyPress((VK)key);
                        }
                    }
                }
            }
            else
            {
                InputSequence Seq = new InputSequence();

                if (use_office_hack)
                {
                    Seq.AddInput((ScanCodeShort)'\u200b');
                    Seq.AddInput((VirtualKeyShort)VK.LEFT);
                }

                foreach (char ch in str)
                {
                    Seq.AddInput((ScanCodeShort)ch);
                }

                if (use_office_hack)
                {
                    Seq.AddInput((VirtualKeyShort)VK.RIGHT);
                }

                Seq.Send();
            }

            /* Restore keyboard modifiers if we needed one of our custom hacks */
            if (use_gtk_hack || use_office_hack)
            {
                foreach (VK vk in modifiers)
                {
                    SendKeyDown(vk);
                }
            }
        }
Ejemplo n.º 2
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 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);
        }
Ejemplo n.º 3
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);

            // Special handling of Left Control on keyboards with AltGr
            if (KeyboardLayout.HasAltGr && key.VirtualKey == VK.LCONTROL)
            {
                // If this is a key down event with LLKHF_ALTDOWN but no Alt key
                // is down, it is actually AltGr.
                if (is_keydown && (flags & LLKHF.ALTDOWN) != 0 &&
                    ((NativeMethods.GetKeyState(VK.LMENU) |
                      NativeMethods.GetKeyState(VK.RMENU)) & 0x80) == 0)
                {
                    m_control_key_was_altgr = true;
                    return(true);
                }

                // If this is a key up event bug Left Control is not down, it is
                // actually AltGr.
                if (is_keyup && m_control_key_was_altgr)
                {
                    m_control_key_was_altgr = false;
                    return(true);
                }

                m_control_key_was_altgr = false;
            }

            // 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_keyup)
                {
                    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 (m_compose_counter == 0 && CurrentState == State.Idle &&
                is_keydown && Settings.ComposeKeys.Value.Contains(key))
            {
                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 this is the compose key again, replace its value with 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 ||
                (Settings.AlwaysCompose.Value && Settings.ComposeKeys.Value.Contains(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 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 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);
        }
Ejemplo n.º 4
0
        private static void SendString(string str)
        {
            List <VK> modifiers = new List <VK>();

            /* HACK: GTK+ applications behave differently with Unicode, and some
             * applications such as XChat for Windows rename their own top-level
             * window, so we parse through the names we know in order to detect
             * a GTK+ application. */
            bool use_gtk_hack = KeyboardLayout.Window.IsGtk;

            /* HACK: Notepad++ and LibreOffice are unable to output high plane
             * Unicode characters, so we rely on clipboard hacking when the
             * composed string contains such characters. */
            bool use_clipboard_hack = KeyboardLayout.Window.IsNPPOrLO &&
                                      HasSurrogates(str);

            /* HACK: in MS Office, some symbol insertions change the text font
             * without returning to the original font. To avoid this, we output
             * a space character, then go left, insert our actual symbol, then
             * go right and backspace. */
            /* These are the actual window class names for Outlook and Word…
             * TODO: PowerPoint ("PP(7|97|9|10)FrameClass") */
            bool use_office_hack = KeyboardLayout.Window.IsOffice &&
                                   Settings.InsertZwsp.Value;

            /* Clear keyboard modifiers if we need one of our custom hacks */
            if (use_gtk_hack || use_office_hack)
            {
                VK[] all_modifiers =
                {
                    VK.LSHIFT,   VK.RSHIFT,
                    VK.LCONTROL, VK.RCONTROL,
                    VK.LMENU,    VK.RMENU,

                    /* Needs to be released, too, otherwise Caps Lock + é on
                     * a French keyboard will print garbage if Caps Lock is
                     * not released soon enough. See note below. */
                    VK.CAPITAL,
                };

                foreach (VK vk in all_modifiers)
                {
                    if ((NativeMethods.GetKeyState(vk) & 0x80) == 0x80)
                    {
                        modifiers.Add(vk);
                    }
                }

                foreach (VK vk in modifiers)
                {
                    SendKeyUp(vk);
                }
            }

            if (use_gtk_hack)
            {
                /* XXX: We need to disable caps lock because GTK’s Shift-Ctrl-U
                 * input mode (see below) doesn’t work when Caps Lock is on. */
                bool has_capslock = NativeMethods.GetKeyState(VK.CAPITAL) != 0;
                if (has_capslock)
                {
                    SendKeyPress(VK.CAPITAL);
                }

                foreach (var ch in str)
                {
                    if (false)
                    {
                        /* FIXME: there is a possible optimisation here where we do
                         * not have to send the whole unicode sequence for regular
                         * ASCII characters. However, SendKeyPress() needs a VK, so
                         * we need an ASCII to VK conversion method, together with
                         * the proper keyboard modifiers. Maybe not worth it.
                         * Also, we cannot use KeySequence because GTK+ seems to
                         * ignore SendInput(). */
                        //SendKeyPress((VK)char.ToUpper(ch));
                    }
                    else
                    {
                        /* Wikipedia says Ctrl+Shift+u, release, then type the four
                         * hex digits, and press Enter.
                         * (http://en.wikipedia.org/wiki/Unicode_input). */
                        SendKeyDown(VK.LCONTROL);
                        SendKeyDown(VK.LSHIFT);
                        SendKeyPress((VK)'U');
                        SendKeyUp(VK.LSHIFT);
                        SendKeyUp(VK.LCONTROL);

                        foreach (var key in $"{(short)ch:X04} ")
                        {
                            SendKeyPress((VK)key);
                        }
                    }
                }

                if (has_capslock)
                {
                    SendKeyPress(VK.CAPITAL);
                }
            }
            else if (use_clipboard_hack)
            {
                // We do not use Clipboard.GetDataObject because I have been
                // unable to restore the clipboard properly. This is reasonable
                // and has been tested with several clipboard content types.
                var backup_text  = Clipboard.GetText();
                var backup_image = Clipboard.GetImage();
                var backup_audio = Clipboard.GetAudioStream();
                var backup_files = Clipboard.GetFileDropList();

                // Use Shift+Insert instead of Ctrl-V because Ctrl-V will misbehave
                // if a Shift key is held down. Using Shift+Insert even works if the
                // compose key is Insert.
                Clipboard.SetText(str);
                SendKeyDown(VK.SHIFT);
                SendKeyPress(VK.INSERT);
                SendKeyUp(VK.SHIFT);
                Clipboard.Clear();

                if (!string.IsNullOrEmpty(backup_text))
                {
                    Clipboard.SetText(backup_text);
                }
                if (backup_image != null)
                {
                    Clipboard.SetImage(backup_image);
                }
                if (backup_audio != null)
                {
                    Clipboard.SetAudio(backup_audio);
                }
                if (backup_files != null && backup_files.Count > 0)
                {
                    Clipboard.SetFileDropList(backup_files);
                }
            }
            else
            {
                InputSequence Seq = new InputSequence();

                if (use_office_hack)
                {
                    Seq.AddInput((ScanCodeShort)'\u200b');
                    Seq.AddInput((VirtualKeyShort)VK.LEFT);
                }

                foreach (char ch in str)
                {
                    Seq.AddInput((ScanCodeShort)ch);
                }

                if (use_office_hack)
                {
                    Seq.AddInput((VirtualKeyShort)VK.RIGHT);
                }

                Seq.Send();
            }

            /* Restore keyboard modifiers if we needed one of our custom hacks */
            if (use_gtk_hack || use_office_hack)
            {
                foreach (VK vk in modifiers)
                {
                    SendKeyDown(vk);
                }
            }
        }
Ejemplo n.º 5
0
        private static void SendString(string str)
        {
            List <VK> modifiers = new List <VK>();

            // HACK: GTK+ applications will crash when receiving surrogate pairs through VK.PACKET,
            // so we use the Ctrl-Shift-u special sequence.
            bool use_gtk_hack = KeyboardLayout.Window.IsGtk &&
                                str.Any(x => char.IsSurrogate(x));

            // HACK: in MS Office, some symbol insertions change the text font
            // without returning to the original font. To avoid this, we output
            // a space character, then go left, insert our actual symbol, then
            // go right and backspace.
            // These are the actual window class names for Outlook and Word…
            // TODO: PowerPoint ("PP(7|97|9|10)FrameClass")
            bool use_office_hack = KeyboardLayout.Window.IsOffice &&
                                   Settings.InsertZwsp.Value;

            InputSequence seq = new InputSequence();

            // Clear keyboard modifiers if we need one of our custom hacks
            if (use_gtk_hack || use_office_hack)
            {
                VK[] all_modifiers =
                {
                    VK.LSHIFT,   VK.RSHIFT,
                    VK.LCONTROL, VK.RCONTROL,
                    VK.LMENU,    VK.RMENU,
                    // Needs to be released, too, otherwise Caps Lock + é on
                    // a French keyboard will print garbage if Caps Lock is
                    // not released soon enough. See note below.
                    VK.CAPITAL,
                };

                modifiers = all_modifiers.Where(x => (NativeMethods.GetKeyState(x) & 0x80) == 0x80)
                            .ToList();
                modifiers.ForEach(vk => seq.AddKeyEvent(EventType.KeyUp, vk));
            }

            if (use_office_hack)
            {
                seq.AddUnicodeInput('\u200b');
                seq.AddKeyEvent(EventType.KeyUpDown, VK.LEFT);
            }

            for (int i = 0; i < str.Length; ++i)
            {
                char ch = str[i];

                if (ch == '\n' || ch == '\r')
                {
                    // On some applications (e.g. Chrome or PowerPoint), \n cannot be injected
                    // through its scancode, so we send the virtual key instead.
                    seq.AddKeyEvent(EventType.KeyUpDown, VK.RETURN);
                }
                else if (use_gtk_hack && char.IsSurrogate(ch))
                {
                    // Sanity check
                    if (i + 1 >= str.Length || !char.IsHighSurrogate(ch) || !char.IsLowSurrogate(str, i + 1))
                    {
                        continue;
                    }

                    var codepoint = char.ConvertToUtf32(ch, str[++i]);

                    // GTK+ hack:
                    //  - We need to disable Caps Lock
                    //  - Wikipedia says Ctrl+Shift+u, release, then type the four hex digits,
                    //    and press Enter (http://en.wikipedia.org/wiki/Unicode_input).
                    //  - The Gimp accepts either Enter or Space but stops immediately in both
                    //    cases.
                    //  - Inkscape stops after Enter, but allows to chain sequences using Space.
                    bool has_capslock = NativeMethods.GetKeyState(VK.CAPITAL) != 0;
                    if (has_capslock)
                    {
                        seq.AddKeyEvent(EventType.KeyUpDown, VK.CAPITAL);
                    }

                    seq.AddKeyEvent(EventType.KeyDown, VK.LCONTROL);
                    seq.AddKeyEvent(EventType.KeyDown, VK.LSHIFT);
                    seq.AddKeyEvent(EventType.KeyUpDown, VK.U);
                    seq.AddKeyEvent(EventType.KeyUp, VK.LSHIFT);
                    seq.AddKeyEvent(EventType.KeyUp, VK.LCONTROL);
                    foreach (var key in $"{codepoint:X04}")
                    {
                        seq.AddKeyEvent(EventType.KeyUpDown, (VK)key);
                    }
                    seq.AddKeyEvent(EventType.KeyUpDown, VK.RETURN);

                    if (has_capslock)
                    {
                        seq.AddKeyEvent(EventType.KeyUpDown, VK.CAPITAL);
                    }
                }
                else
                {
                    seq.AddUnicodeInput(ch);
                }
            }

            if (use_office_hack)
            {
                seq.AddKeyEvent(EventType.KeyUpDown, VK.RIGHT);
            }

            // Restore keyboard modifier state if we needed one of our custom hacks
            modifiers.ForEach(vk => seq.AddKeyEvent(EventType.KeyDown, vk));

            // Send the whole keyboard sequence
            seq.Send();
        }