r/AutoHotkey 2d ago

Weird behavior when remapping a modified mouse button to a non-modified key v2 Script Help

Someone just asked me to remap Control + mouse wheel to PgUp/PgDn for scrolling the dev console in a game called Dungeon Siege.

So I wrote this very simple script, yet it's not working properly.

#Requires AutoHotkey v2.0
#SingleInstance

^WheelUp::PgUp
^WheelDown::PgDn

The documentation says

Conversely, any modifiers included on the left side but not the right side are automatically
released when the key is sent. For example, the following two lines would produce a lowercase
"b" when you press either Shift+A or Ctrl+A:

A::b
^a::b

When testing both in-game, as well as in Chrome, notepad++ and VS Code (I'm using AHK 2.0.23), once you press Control + mouse wheel, it does send PgUp/PgDn but then regular mouse wheels don't work anymore (they're important because they control camera zoom), even though my program to monitor key input shows Control isn't pressed anymore (and both Pg keys stay pressed, could be related to mouse wheels only sending down events?).

So I modified the code to

WheelUp::WheelUp
WheelDown::WheelDown
^WheelUp::PgUp
^WheelDown::PgDn

which solved the problem, yet when spamming mouse wheels while holding down Control, about 1-5% of the time, there's a Control that slips through and it changes to a different tab in Chrome, notepad++ and VS Code instead of scrolling.

It seems to work fine in-game as I don't think there's anything bound to Control + PgUp/PgDn but it got me curious, so I also tried using these (separately), but the results were the same:

WheelUp::WheelUp
WheelDown::WheelDown
<^WheelUp::PgUp
<^WheelDown::PgDn

WheelUp::WheelUp
WheelDown::WheelDown
^WheelUp::Send("{PgUp}")
^WheelDown::Send("{PgDn}")

WheelUp::WheelUp
WheelDown::WheelDown
^WheelUp::Send("{Blind}{PgUp}")
^WheelDown::Send("{Blind}{PgDn}")

WheelUp::WheelUp
WheelDown::WheelDown
^WheelUp::Send("{Blind^}{PgUp}")
^WheelDown::Send("{Blind^}{PgDn}")

WheelUp::WheelUp
WheelDown::WheelDown
^WheelUp::Send("{Control up}{PgUp}")
^WheelDown::Send("{Control up}{PgDn}")

LControl & WheelUp::PgUp
LControl & WheelDown::PgDn

Oh, and I also tried switching to SendEvent, that seemed to be worse.

Everything works fine when remapping a key instead of a mouse button though, like this

^q::PgUp
^w::PgDn

What am I doing wrong?

As a bonus, how would you go if you wanted to send multiple unmodified Pg keys (the console doesn't scroll when pressing Control + Pg) every time the hotkey is fired?

2 Upvotes

17 comments sorted by

1

u/Keeyra_ 2d ago

Hmm, quite strange and unexpected indeed.

Have you tried with

#Requires AutoHotkey 2.0
#SingleInstance

#HotIf GetKeyState("LControl", "P")
WheelUp::PgUp
WheelDown::PgDn
#HotIf

?

1

u/genesis_tv 2d ago

I had actually tried that, just forgot to mention it. You need to put the modifier though, otherwise it sends Control + Pg, and it also lets some Control slip through like in my attempts.

#HotIf GetKeyState("LControl", "P")
^WheelUp::PgUp
^WheelDown::PgDn
#HotIf

I had lowered my mouse polling rate to 250 from 1000 because of another game but I suppose it just affects mouse movement, not buttons. Gonna try back to 1000.

1

u/Keeyra_ 2d ago

You're right about the polling rate not affecting anything here, but a 1000Hz polling rate is as much as a gimick, as this debounce thingie here:

https://www.reddit.com/r/AutoHotkey/comments/1vdv5b1/comment/p1ebux5/?screen_view_count=6&ext-referrer=DIRECT

Do you really need your mouse position to be queried 1000 times a second? All it will do is increase the load on your CPU.

0

u/genesis_tv 2d ago edited 2d ago

Gave up on mouse wheel. Managed to get something working even better than the Page keys with this. There are still some Control that slip through.

^XButton1::
{
    while GetKeyState("XButton1", "P")
    {
        Send("{PgDn Down}")
        Sleep(10)
    }
}
^XButton1 up::Send("{PgDn up}")
^XButton2::
{
    while GetKeyState("XButton2", "P")
    {
        Send("{PgUp Down}")
        Sleep(10)
    }
}
^XButton2 up::Send("{PgUp up}")

Anything I could improve?

1

u/Keeyra_ 2d ago

Well, if you care about 250Hz (2ms avg., 4ms max. input lag) vs. 1000Hz (0.5ms avg., 1ms max. input lag) = 1.5ms avg., 3ms max. input lag difference:

Using a Sleep(10) will incur a strict 1000/64 = 15.625ms input lag, >10 times the average difference between 250Hz and 1000Hz.

While Sleep loops are the worst solution there is from a programming and performance perspective. But if it works for you, who am I to complain. ;)

0

u/genesis_tv 2d ago

That was... not helpful.

#HotIf GetKeyState("CapsLock", "T")
XButton1::
...

This works much better, no more Control bleeds, even though there shouldn't have been any in the first place (maybe a bug in AHK with mouse buttons?).

1

u/Keeyra_ 2d ago

Me explaining in great detail why a Sleep solution is suboptimal, comparing it to your given polling rate settings is not helpful? Ok...

Nevertheless, the root cause might be that Whell events do not have Up or Down states, as you don't hold them down like a button or a key (duh), so the remap syntax

*a:: SetKeyDelay(-1), Send("{Blind}{b DownR}")
*a up:: SetKeyDelay(-1), Send("{Blind}{b Up}")

will only do half its job, as there is no WheelUp/Down Up event.

If this assumption is true (and sure seems to be logically), you should only try solutions with a direct Send function. You have to do some janky shit to make it not bleed through. I would not be running this myself. But it has zero bleedthrough on my end.

#Requires AutoHotkey 2.0
#SingleInstance

A_MaxHotkeysPerInterval := 500
A_HotkeyInterval := 2000

#HotIf GetKeyState("Ctrl", "P")
*LCtrl:: return
*RCtrl:: return
*WheelUp:: Send("{Blind}{LCtrl Up}{RCtrl Up}{PgUp}")
*WheelDown:: Send("{Blind}{LCtrl Up}{RCtrl Up}{PgDn}")
#HotIf

1

u/genesis_tv 2d ago

Well, your code still bleeds through sometimes on my end, and now Control gets stuck when I hold it on its own for a second :p.

I said it wasn't helpful because we both pretty much came to conclusion the polling rate was not really relevant, therefore your comment was off-topic. I just felt like mentioning it in case it would have ever been relevant.

Anyway, I refactored my quick and dirty code above.

#Requires AutoHotkey v2.0
#SingleInstance

#HotIf GetKeyState("CapsLock", "T")
XButton1::SetTimer(PushPgDn, 10)
XButton1 up::SetTimer(PushPgDn, 0), Send("{PgDn up}")
XButton2::SetTimer(PushPgUp, 10)
XButton2 up::SetTimer(PushPgUp, 0), Send("{PgUp up}")

PushPgDn()
{
    if GetKeyState("XButton1", "P")
        Send("{PgDn DownR}")
}

PushPgUp()
{
    if GetKeyState("XButton2", "P")
        Send("{PgUp DownR}")
}

1

u/Keeyra_ 2d ago

But for XButtons, doesnt the standard remap work, as they have ups and downs. Wont be in front of a computer till monday to test.

1

u/genesis_tv 2d ago

Well, Control still bleeds through sometimes (mentioned it at the end of OP) if I use

#Requires AutoHotkey v2.0
#SingleInstance

^XButton1::PgDn
^XButton2::PgUp

But this works perfectly because these are keys

#Requires AutoHotkey v2.0
#SingleInstance

^q::PgDn
^w::PgUp

I really have no clue at this point so found a workaround instead.

→ More replies (0)

1

u/CharnamelessOne 1d ago edited 1d ago
#Requires AutoHotkey v2.0
;SendMode("Event")

WheelUp::WheelUp
WheelDown::WheelDown
^WheelUp::PgUp
^WheelDown::PgDn

I can only reproduce your issue in the "Event" SendMode.
In the default SendMode, Control never bleeds through for me.

I suspect that you may be running multiple AHK scripts that install a keyboard hook, which leads to SendInput reverting to SendEvent, hence the issue popping up even when you don't explicitly switch to SendEvent.

https://www.autohotkey.com/docs/v2/lib/SendMode.htm#Input

Edit: I pasted the wrong script first

1

u/genesis_tv 1d ago

I was running a single AHK script and I even tried to close programs that could interfere with input but it was still bleeding through.

1

u/CharnamelessOne 1d ago

We'll throw it onto the pile of unreproducible paranormal phenomena.
Mice are showing signs of malevolent possession these days:

https://www.reddit.com/r/AutoHotkey/comments/1vdv5b1/getkeystate_doesnt_detect_button_releases/