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 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); }