| 2989 | |
| 2990 | |
| 2991 | ToggleValueType ToggleKeyState(vk_type aVK, ToggleValueType aToggleValue) |
| 2992 | // Toggle the given aVK into another state. For performance, it is the caller's responsibility to |
| 2993 | // ensure that aVK is a toggleable key such as capslock, numlock, insert, or scrolllock. |
| 2994 | // Returns the state the key was in before it was changed. |
| 2995 | { |
| 2996 | // Can't use IsKeyDownAsync/GetAsyncKeyState() because it doesn't have this info: |
| 2997 | ToggleValueType starting_state = IsKeyToggledOn(aVK) ? TOGGLED_ON : TOGGLED_OFF; |
| 2998 | if (aToggleValue != TOGGLED_ON && aToggleValue != TOGGLED_OFF) // Shouldn't be called this way. |
| 2999 | return starting_state; |
| 3000 | if (starting_state == aToggleValue) // It's already in the desired state, so just return the state. |
| 3001 | return starting_state; |
| 3002 | //if (aVK == VK_NUMLOCK) // v1.1.22.05: This also applies to CapsLock and ScrollLock. |
| 3003 | { |
| 3004 | // If the key is being held down, sending a KEYDOWNANDUP won't change its toggle |
| 3005 | // state unless the key is "released" first. This has been confirmed for NumLock, |
| 3006 | // CapsLock and ScrollLock on Windows 2000 (in a VM) and Windows 10. |
| 3007 | // Examples where problems previously occurred: |
| 3008 | // ~CapsLock & x::Send abc ; Produced "ABC" |
| 3009 | // ~CapsLock::Send abc ; Alternated between "abc" and "ABC", even without {Blind} |
| 3010 | // ~ScrollLock::SetScrollLockState Off ; Failed to change state |
| 3011 | // The behaviour can still be observed by sending the keystrokes manually: |
| 3012 | // ~NumLock::Send {NumLock} ; No effect |
| 3013 | // ~NumLock::Send {NumLock up}{NumLock} ; OK |
| 3014 | // OLD COMMENTS: |
| 3015 | // Sending an extra up-event first seems to prevent the problem where the Numlock |
| 3016 | // key's indicator light doesn't change to reflect its true state (and maybe its |
| 3017 | // true state doesn't change either). This problem tends to happen when the key |
| 3018 | // is pressed while the hook is forcing it to be either ON or OFF (or it suppresses |
| 3019 | // it because it's a hotkey). Needs more testing on diff. keyboards & OSes: |
| 3020 | if (IsKeyDown(aVK)) |
| 3021 | KeyEvent(KEYUP, aVK); |
| 3022 | } |
| 3023 | // Since it's not already in the desired state, toggle it: |
| 3024 | KeyEvent(KEYDOWNANDUP, aVK); |
| 3025 | // Fix for v1.0.40: IsKeyToggledOn()'s call to GetKeyState() relies on our thread having |
| 3026 | // processed messages. Confirmed necessary 100% of the time if our thread owns the active window. |
| 3027 | // v1.0.43: Extended the above fix to include all toggleable keys (not just Capslock) and to apply |
| 3028 | // to both directions (ON and OFF) since it seems likely to be needed for them all. |
| 3029 | bool our_thread_is_foreground; |
| 3030 | if (our_thread_is_foreground = (GetWindowThreadProcessId(GetForegroundWindow(), NULL) == g_MainThreadID)) // GetWindowThreadProcessId() tolerates a NULL hwnd. |
| 3031 | SLEEP_WITHOUT_INTERRUPTION(-1); |
| 3032 | if (aVK == VK_CAPITAL && aToggleValue == TOGGLED_OFF && IsKeyToggledOn(aVK)) |
| 3033 | { |
| 3034 | // Fix for v1.0.36.06: Since it's Capslock and it didn't turn off as attempted, it's probably because |
| 3035 | // the OS is configured to turn Capslock off only in response to pressing the SHIFT key (via Ctrl Panel's |
| 3036 | // Regional settings). So send shift to do it instead: |
| 3037 | KeyEvent(KEYDOWNANDUP, VK_SHIFT); |
| 3038 | if (our_thread_is_foreground) // v1.0.43: Added to try to achieve 100% reliability in all situations. |
| 3039 | SLEEP_WITHOUT_INTERRUPTION(-1); // Check msg queue to put SHIFT's turning off of Capslock into effect from our thread's POV. |
| 3040 | } |
| 3041 | return starting_state; |
| 3042 | } |
| 3043 | |
| 3044 | |
| 3045 | /* |
no test coverage detected