Skip to content

Commit

Permalink
Changed ~x & y:: to not affect suppression of x when disabled by #If.
Browse files Browse the repository at this point in the history
  • Loading branch information
Lexikos committed Apr 26, 2021
1 parent cfcb70e commit a6a1a58
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 149 deletions.
155 changes: 60 additions & 95 deletions source/hook.cpp
Expand Up @@ -762,6 +762,8 @@ LRESULT LowLevelCommon(const HHOOK aHook, int aCode, WPARAM wParam, LPARAM lPara
GetModifierLRState(true);
}

bool fire_with_no_suppress = false; // Set default.

///////////////////////////////////////////////////////////////////////////////////////
// CASE #1 of 4: PREFIX key has been pressed down. But use it in this capacity only if
// no other prefix is already in effect or if this key isn't a suffix. Update: Or if
Expand All @@ -780,9 +782,10 @@ LRESULT LowLevelCommon(const HHOOK aHook, int aCode, WPARAM wParam, LPARAM lPara
// introduce a new hotkey modifier such as an "up2" keyword that makes any key into a prefix
// key even if it never acts as a prefix for other keys, which in turn has the benefit of firing
// on key-up, but only if the no other key was pressed while the user was holding it down.
bool suppress_this_prefix = false;// !(this_key.no_suppress & NO_SUPPRESS_PREFIX); // Set default.
bool has_no_enabled_suffixes;
if ( !(has_no_enabled_suffixes = (this_key.used_as_prefix == PREFIX_ACTUAL)
&& Hotkey::PrefixHasNoEnabledSuffixes(sc_takes_precedence ? aSC : aVK, sc_takes_precedence)) )
&& Hotkey::PrefixHasNoEnabledSuffixes(sc_takes_precedence ? aSC : aVK, sc_takes_precedence, suppress_this_prefix)) )
{
// This check is necessary in cases such as the following, in which the "A" key continues
// to repeat because pressing a mouse button (unlike pressing a keyboard key) does not
Expand Down Expand Up @@ -838,24 +841,55 @@ LRESULT LowLevelCommon(const HHOOK aHook, int aCode, WPARAM wParam, LPARAM lPara
if (this_key.as_modifiersLR) // This will always be false if our caller is the mouse hook.
// Hotkeys are not defined to modify themselves, so look for a match accordingly.
modifiersLRnew &= ~this_key.as_modifiersLR;
// For this case to be checked, there must be at least one modifier key currently down (other
// than this key itself if it's a modifier), because if there isn't and this prefix is also
// a suffix, its suffix action should only fire on key-up (i.e. not here, but later on).
// UPDATE: In v1.0.41, an exception to the above is when a prefix is disabled via
// has_no_enabled_suffixes, in which case it seems desirable for most uses to have its
// suffix action fire on key-down rather than key-up.
// UPDATE: Another exception was added so that the no-suppress prefix allows the key to function
// as if the custom combination wasn't defined. For example, ~x & y:: allows x:: to retain its
// normal behaviour, firing the subroutine on key-down and blocking the keystroke. This is more
// useful and intuitive/consistent than the old behaviour, which was to fire the suffix hotkey
// on key-up even though the key-down wasn't suppressed (unless either of the first two conditions
// below were met).
if (modifiersLRnew || has_no_enabled_suffixes || (this_key.no_suppress & NO_SUPPRESS_PREFIX))

// This prefix key's hotkey needs to be checked even if it will ultimately fire only on release.
// If suppress_this_prefix == false, this prefix key's key-down hotkey should fire immediately.
// If suppress_this_prefix == true, its final value can only be confirmed by verifying whether
// this prefix key's hotkey has the no-suppress prefix (which should cause the hotkey to fire
// immediately and not be suppressed).
// This prefix key's hotkey should also be fired immediately if there are any modifiers down.
// Check hook type too in case a script ever explicitly specifies scan code zero as a hotkey:
hotkey_id_with_flags = (aHook == g_KeybdHook && sc_takes_precedence)
? Kscm(modifiersLRnew, aSC) : Kvkm(modifiersLRnew, aVK);
hotkey_id_temp = hotkey_id_with_flags & HOTKEY_ID_MASK;
if (IS_ALT_TAB(hotkey_id_temp))
hotkey_id_with_flags = HOTKEY_ID_INVALID; // Let it be rediscovered when the key is released.
else
{
// Check hook type too in case a script every explicitly specifies scan code zero as a hotkey:
hotkey_id_with_flags = (aHook == g_KeybdHook && sc_takes_precedence)
? Kscm(modifiersLRnew, aSC) : Kvkm(modifiersLRnew, aVK);
if (hotkey_id_with_flags & HOTKEY_KEY_UP) // And it's okay even if it's is HOTKEY_ID_INVALID.
if (suppress_this_prefix && !modifiersLRnew) // So far, it looks like the prefix should be suppressed.
{
UCHAR unused_no_suppress; // Leave this_key.no_suppress unchanged in case !firing_is_certain.
firing_is_certain = Hotkey::CriterionFiringIsCertain(hotkey_id_with_flags, aKeyUp, aExtraInfo, unused_no_suppress, fire_with_no_suppress, NULL);
if (firing_is_certain && fire_with_no_suppress)
suppress_this_prefix = false; // Just to avoid making it fire on release below.
else // Hotkey is ineligible to fire or lacks the no-suppress prefix.
{
// Resetting the ID is necessary to avoid the following cases:
// 1) A key-down hotkey which isn't eligible to fire prevents the prefix key from being suppressed.
// 2) A key-down hotkey which isn't eligible to fire causes its key-up counterpart to fire even if
// the prefix key was used to activate a custom combo.
// 3) A key-down hotkey without ~ fires immediately instead of on release.
// 4) A key-up hotkey without ~ fires even if the prefix key was used to activate a custom combo.
if (hotkey_id_with_flags < Hotkey::sHotkeyCount && hotkey_up[hotkey_id_with_flags] != HOTKEY_ID_INVALID)
{
// This key-down hotkey has a key-up counterpart.
fire_with_no_suppress = false; // Reset for the call below.
auto firing_up = Hotkey::CriterionFiringIsCertain(hotkey_up[hotkey_id_with_flags], aKeyUp, aExtraInfo, unused_no_suppress, fire_with_no_suppress, NULL);
if ( !(firing_up && fire_with_no_suppress) ) // Both key-down and key-up are either ineligible or lack the no-suppress prefix.
hotkey_id_with_flags = HOTKEY_ID_INVALID; // See comments above about resetting the ID.
else if (firing_is_certain) // Both key-down and key-up are eligible, but key-down should be suppressed.
fire_with_no_suppress = false; // For backward-compatibility, suppress the key-down but leave hotkey_id_with_flags set so it fires immediately.
else // Key-down is not eligible, but key-up is.
{
firing_is_certain = firing_up;
hotkey_id_with_flags = hotkey_up[hotkey_id_with_flags];
}
}
else
hotkey_id_with_flags = HOTKEY_ID_INVALID; // See comments above about resetting the ID.
}
}
if (hotkey_id_with_flags & HOTKEY_ID_INVALID) // A key-up hotkey or HOTKEY_ID_INVALID.
{
// Queue it for later, which is done here rather than upon release of the key so that
// the user can release the key's modifiers before releasing the key itself, which
Expand All @@ -865,9 +899,8 @@ LRESULT LowLevelCommon(const HHOOK aHook, int aCode, WPARAM wParam, LPARAM lPara
this_key.hotkey_to_fire_upon_release = hotkey_id_with_flags;
hotkey_id_with_flags = HOTKEY_ID_INVALID;
}
else // hotkey_id_with_flags is either HOTKEY_ID_INVALID or a valid key-down hotkey.
else // hotkey_id_with_flags is a valid key-down hotkey.
{
hotkey_id_temp = hotkey_id_with_flags & HOTKEY_ID_MASK;
if (hotkey_id_temp < Hotkey::sHotkeyCount)
this_key.hotkey_to_fire_upon_release = hotkey_up[hotkey_id_temp]; // Might assign HOTKEY_ID_INVALID.
// Since this prefix key is being used in its capacity as a suffix instead,
Expand All @@ -886,25 +919,18 @@ LRESULT LowLevelCommon(const HHOOK aHook, int aCode, WPARAM wParam, LPARAM lPara
if (hotkey_id_with_flags == HOTKEY_ID_INVALID)
{
if (has_no_enabled_suffixes)
{
this_key.no_suppress |= NO_SUPPRESS_NEXT_UP_EVENT; // Since the "down" is non-suppressed, so should the "up".
pKeyHistoryCurr->event_type = _T('#'); // '#' to indicate this prefix key is disabled due to #IfWin criterion.
}
// In this case, a key-down event can't trigger a suffix, so return immediately.
// If our caller is the mouse hook, both of the following will always be false:
// this_key.as_modifiersLR
// this_toggle_key_can_be_toggled
if (this_key.as_modifiersLR || (this_key.no_suppress & NO_SUPPRESS_PREFIX)
|| this_toggle_key_can_be_toggled || has_no_enabled_suffixes)
if (this_key.as_modifiersLR || !suppress_this_prefix || this_toggle_key_can_be_toggled)
{
this_key.no_suppress |= NO_SUPPRESS_NEXT_UP_EVENT;
return AllowKeyToGoToSystem;
}
// Mark this key as having been suppressed. This currently doesn't have any known effect
// since the change to tilde (~) handling in v1.0.95 (commit 161162b8), but may in future.
// Search for "SEND_NOSUPPRESS_PREFIX_KEY_ON_RELEASE" for related comments.
//#define SEND_NOSUPPRESS_PREFIX_KEY_ON_RELEASE
// Without this next assignment, the following issues occur if the above line is uncommented:
// 1) ~prefixkey:: allows just a key-up to pass through, without first sending a key-down as
// originally intended.
// 2) #if false .. ~prefixkey:: causes the key-up to pass through when it should be suppressed.
this_key.hotkey_down_was_suppressed = true;
return SuppressThisKey;
}
Expand Down Expand Up @@ -1069,7 +1095,7 @@ LRESULT LowLevelCommon(const HHOOK aHook, int aCode, WPARAM wParam, LPARAM lPara
if (this_key.as_modifiersLR) // Always false if our caller is the mouse hook.
return AllowKeyToGoToSystem; // Win/Alt will be disguised if needed.
// Otherwise:
return (this_key.no_suppress & NO_SUPPRESS_PREFIX) ? AllowKeyToGoToSystem : SuppressThisKey;
return SuppressThisKey;
}

// v1.0.41: This spot cannot be reached when a disabled prefix key's up-action fires on
Expand All @@ -1088,8 +1114,7 @@ LRESULT LowLevelCommon(const HHOOK aHook, int aCode, WPARAM wParam, LPARAM lPara
// If our caller is the mouse hook, both of the following will always be false:
// this_key.as_modifiersLR
// this_toggle_key_can_be_toggled
return (this_key.as_modifiersLR || (this_key.no_suppress & NO_SUPPRESS_PREFIX)
// The order on this line important; it relies on short-circuit boolean:
return (this_key.as_modifiersLR
|| this_toggle_key_can_be_toggled) ? AllowKeyToGoToSystem : SuppressThisKey;

// Since the above didn't return, this key is both a prefix and a suffix, but
Expand Down Expand Up @@ -1120,7 +1145,6 @@ LRESULT LowLevelCommon(const HHOOK aHook, int aCode, WPARAM wParam, LPARAM lPara
// it fell through from CASE #3 or #2 above). This case can also happen if it fell through from
// case #1 (i.e. it already determined the value of hotkey_id_with_flags).
////////////////////////////////////////////////////////////////////////////////////////////////////
bool fire_with_no_suppress = false; // Set default.

if (pPrefixKey && (!aKeyUp || this_key.used_as_key_up) && hotkey_id_with_flags == HOTKEY_ID_INVALID) // Helps performance by avoiding all the below checking.
{
Expand Down Expand Up @@ -1350,8 +1374,6 @@ LRESULT LowLevelCommon(const HHOOK aHook, int aCode, WPARAM wParam, LPARAM lPara
// Fix for v1.0.28: If the ID isn't an alt-tab type, don't consider it to be valid.
// Someone pointed out that pressing Alt-Tab and then pressing ESC while still holding
// down ALT fired the ~Esc hotkey even when it should just dismiss the alt-tab menu.
// Note: Both of the below checks must be done because the high-order bits of the
// hotkey_id_with_flags might be set to indicate no-suppress, etc:
hotkey_id_temp = hotkey_id_with_flags & HOTKEY_ID_MASK;
if (!IS_ALT_TAB(hotkey_id_temp))
hotkey_id_with_flags = HOTKEY_ID_INVALID; // Since it's not an Alt-tab action, don't fire this hotkey.
Expand Down Expand Up @@ -1440,9 +1462,7 @@ LRESULT LowLevelCommon(const HHOOK aHook, int aCode, WPARAM wParam, LPARAM lPara
// toggled, just allow this up-event to go through because the
// previous down-event for it (in its role as a prefix) would not
// have been suppressed:
// NO_SUPPRESS_PREFIX can occur if it fell through from Case #3 but the right
// modifier keys aren't down to have triggered a key-up hotkey:
return (this_key.as_modifiersLR || (this_key.no_suppress & NO_SUPPRESS_PREFIX)
return (this_key.as_modifiersLR
// The following line was added for v1.0.37.02 to take into account key-up hotkeys,
// the release of which should never be suppressed if it didn't actually fire the
// up-hotkey (due to the wrong modifiers being down):
Expand Down Expand Up @@ -1871,56 +1891,11 @@ LRESULT LowLevelCommon(const HHOOK aHook, int aCode, WPARAM wParam, LPARAM lPara
if (aHook == g_MouseHook)
return AllowKeyToGoToSystem;
// Otherwise, our caller is the keyboard hook.

// The following section is currently disabled because it hasn't been working as intended
// for quite some time, and doesn't seem to be what users expect. It also contains some
// contradictions; for instance, explicit key-up hotkeys such as the one in the example
// were excluded, apparently by design (since v1.0.36.02). Explicit key-up hotkeys which
// are turned on after the key is pressed were erroneously included, but this has been
// fixed in the code below. Implicit key-up hotkeys (which act on key-up because the key
// is used as a prefix key) did not work because this_key.hotkey_down_was_suppressed was
// not set when the prefix key was suppressed -- and later because it was not suppressed
// at all due to a change in v1.0.95 (commit 161162b8).
#ifdef SEND_NOSUPPRESS_PREFIX_KEY_ON_RELEASE
// Since this hotkey is firing on key-up but the user specified not to suppress its native
// function, send a down event to make up for the fact that the original down event was
// suppressed (since key-up hotkeys' down events are always suppressed because they
// are also prefix keys by definition). UPDATE: Now that it is possible for a prefix key
// to be non-suppressed, this is done only if the prior down event wasn't suppressed.
// Note that for a pair of hotkeys such as:
// *capslock::Send {Ctrl Down}
// *~capslock up:: Send {Ctrl Up} ; Specify tilde to allow caps lock to be toggled upon release.
// ... the following key history is produced (see note):
//14 03A h d 3.46 Caps Lock
//A2 01D i d 0.00 Ctrl
//14 03A h u 0.10 Caps Lock
//14 03A i d 0.00 Caps Lock <<< This actually came before the prior due to re-entrancy.
//A2 01D i u 0.00 Ctrl
// Can't use this_toggle_key_can_be_toggled in this case. Relies on short-circuit boolean order:
bool suppress_to_prevent_toggle = this_key.pForceToggle && *this_key.pForceToggle != NEUTRAL;
// The following isn't checked as part of the above because this_key.was_just_used would
// never be true with hotkeys such as the Capslock pair shown above. That's because
// Capslock isn't a prefix in that case, it's just a suffix. Even if it were a prefix, it would
// never reach this point in the execution because places higher above return early if the value of
// this_key.was_just_used is AS_PREFIX/AS_PREFIX_FOR_HOTKEY.
// Used as either a prefix for a hotkey or just a plain modifier for another key.
// ... && (*this_key.pForceToggle != NEUTRAL || this_key.was_just_used);
if (this_key.hotkey_down_was_suppressed // Down was suppressed.
&& !(hotkey_id_with_flags & HOTKEY_KEY_UP) // v1.0.36.02: Prevents a hotkey such as "~5 up::" from generating double characters, regardless of whether it's paired with a "~5::" hotkey.
&& !suppress_to_prevent_toggle) // Mouse vs. keybd hook was already checked higher above.
KeyEvent(KEYDOWN, aVK, aSC); // Substitute this to make up for the suppression (a check higher above has already determined that no_supress==true).
// Now allow the up-event to go through. The DOWN should always wind up taking effect
// before the UP because the above should already have "finished" by now, since
// it resulted in a recursive call to this function (using our hook-thread
// rather than our main thread or some other thread):
return suppress_to_prevent_toggle ? SuppressThisKey : AllowKeyToGoToSystem;
#else
// Although it seems more sensible to suppress the key-up if the key-down was suppressed,
// it probably does no harm to let the key-up pass through, and in this case, it's exactly
// what the script is asking to happen (by prefixing the key-up hotkey with '~').
// this_key.pForceToggle isn't checked because AllowIt() handles that.
return AllowKeyToGoToSystem;
#endif
} // No suppression.
}
else // Key Down
Expand Down Expand Up @@ -3770,16 +3745,6 @@ void ChangeHookState(Hotkey *aHK[], int aHK_count, HookType aWhichHook, HookType
pThisKey->first_hotkey = hk.mID;
continue;
}
#ifndef SEND_NOSUPPRESS_PREFIX_KEY_ON_RELEASE // Search for this symbol for details.
else
{
// If this hotkey is a lone key with ~ prefix such as "~a::", the following ensures that
// the ~ prefix is respected even if the key is also used as a prefix in a custom combo,
// such as "a & b::". This is consistent with the behaviour of "~a & b::".
if (!hk.mModifiersConsolidatedLR && (hk.mNoSuppress & AT_LEAST_ONE_VARIANT_HAS_TILDE))
pThisKey->no_suppress |= NO_SUPPRESS_PREFIX;
}
#endif

// At this point, since the above didn't "continue", this hotkey is one without a ModifierVK/SC.
// Put it into a temporary array, which will be later sorted:
Expand Down
2 changes: 1 addition & 1 deletion source/hook.h
Expand Up @@ -130,7 +130,7 @@ struct key_type
UCHAR used_as_prefix; // Whether a given virtual key or scan code is even used by a hotkey.
bool used_as_suffix; //
bool used_as_key_up; // Whether this suffix also has an enabled key-up hotkey.
UCHAR no_suppress; // Contains bitwise flags such as NO_SUPPRESS_PREFIX.
UCHAR no_suppress; // Contains bitwise flags such as NO_SUPPRESS_NEXT_UP_EVENT.
bool is_down; // this key is currently down.
bool it_put_alt_down; // this key resulted in ALT being pushed down (due to alt-tab).
bool it_put_shift_down; // this key resulted in SHIFT being pushed down (due to shift-alt-tab).
Expand Down

0 comments on commit a6a1a58

Please sign in to comment.