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)); }
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(""); }
/// <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); }
/// <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; }
/// <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); }
/// <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); }
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); }
/// <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; }
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 ""; }
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); }
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; }
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); }
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; }
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); }
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; }
public static extern int ToUnicode(VK wVirtKey, SC wScanCode, byte[] lpKeyState, byte[] pwszBuff, int cchBuff, LLKHF flags);
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); }