Skip to content

NCK sometimes fails. Start menu pops up, and some modifier keys are stuck in "pressed" -state #11

@comesuccingfuccsloot

Description

@comesuccingfuccsloot

first of all, a huge thank you for making this program. it's a life saver.

title describes the issue i was having.
i pasted your main.cpp code to claude, and after a few rounds of fixing/building/testing, i do believe i have a solution. at least i can't reproduce the problem anymore.

the issue was that sometimes (maybe ~1/20 keypresses, and not consistently) my computer would send LShift before LWin. this caused the suppression logic to not work correctly. if you want, here's the last message i got from claude, including the fix:

The problem is the same as before — LShift arrived before LWin at 772034109. But the fix I gave you has a bug: the LWin handler calls ReplaySuppressedKeys2() unconditionally, which replays the already-suppressed LShift before recognizing that LWin is the second key of the pair.
Here's a clean rewrite of the entire press handling:

if (pressed)
{
    if (keyCode == VK_LWIN)
    {
        if (pressState == STATE::Idle)
        {
            ReplaySuppressedKeys2();
            leftWindowsSuppressed = true;
            SetPressState(STATE::LeftWindows);
            EnsureTimer();
            return -1;
        }
        else if (pressState == STATE::LeftWindows && leftShiftSuppressed && !leftWindowsSuppressed)
        {
            // LShift came first, LWin completes the pair
            leftWindowsSuppressed = true;
            SetPressState(STATE::LeftShift);
            return -1;
        }
        else
        {
            ReplaySuppressedKeys2();
            SetPressState(STATE::Idle);
        }
    }
    else if (keyCode == VK_LSHIFT)
    {
        if (pressState == STATE::LeftWindows && leftWindowsSuppressed && !leftShiftSuppressed)
        {
            // LWin came first, LShift completes the pair
            leftShiftSuppressed = true;
            SetPressState(STATE::LeftShift);
            return -1;
        }
        else if (pressState == STATE::Idle)
        {
            ReplaySuppressedKeys2();
            leftShiftSuppressed = true;
            SetPressState(STATE::LeftWindows);
            EnsureTimer();
            return -1;
        }
        else
        {
            ReplaySuppressedKeys2();
            SetPressState(STATE::Idle);
        }
    }
    else if (keyCode == VK_F23 && pressState == STATE::LeftShift)
    {
        SetPressState(STATE::Idle);
        SetReleaseState(STATE::F23);
        CancelTimer();
        leftShiftSuppressed = false;
        leftWindowsSuppressed = false;
        if (!rightCtrlDown)
        {
            InjectKeyDown(VK_RCONTROL);
        }
        return -1;
    }
    else
    {
        if (pressState != STATE::Idle)
        {
            ReplaySuppressedKeys2();
            SetPressState(STATE::Idle);
        }
    }
}

The critical change: when we're in LeftWindows state (meaning "got one modifier") and the second modifier arrives, we check the suppression flags to know which key came first, and don't call ReplaySuppressedKeys2(). We just suppress the second key and advance to LeftShift state (meaning "got both modifiers").
The state names are overloaded now — LeftWindows really means "got one of the two" and LeftShift means "got both" — but it avoids changing the enum and the release logic.

if you don't want to use that AI-slop, then don't. i'm not a cpp programmer, so this is kinda the only solution i got. made this issue in case you wanna use this OR other people happen to have the same issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions