RPDeshaies
RPDeshaies

Reputation: 1874

Why does shortcut don't have the same behavior when applied on multiple keys

Here's my problem.

I wrote a little Volume Control Script to control my volume like windows do with it's Microsoft Keyboard's shortcut.

I have 3 functions. Two to controls volumes and the other one to control the Hold state of the key to continue rise or down my volume

Here's the code.

    ;//**************************************************
    ;// Volume Mouse Control
    ;//**************************************************
    VolumeUp(p_numberToDecrease, p_holdToDecrease = true)
    {
        Send {Volume_Up %p_numberToDecrease%} 

        if(p_holdtoDecrease)
        {
            VolumeHoldTreatment("Up")
        }
    }

    VolumeDown(p_numberToDecrease, p_holdToDecrease = true)
    {
        Send {Volume_Down %p_numberToDecrease%} 

        if(p_holdtoDecrease)
        {
            VolumeHoldTreatment("Down")
        }
    }

    VolumeHoldTreatment(p_treatment)
    {
        Count := 0  

        Sleep 300
        While GetKeyState(A_ThisHotkey,"P")
        {
            ++Count
            if(p_treatment == "Up")
            {   
                Send {Volume_Up %Count%}
            }
            else
            {
                Send {Volume_Down %Count%}
            }
            Sleep 25
        }
    }

When I call the methods in the shortcuts like this ↓, they work correctly.

I can rise up and down my volume.

If I hold the key more then 300ms, the volume will continue rising/downing.

    XButton1:: VolumeDown(1)
    XButton2:: VolumeUp(1)

But When I add my mute shortcut ↓

    XButton1 & XButton2:: Send {Volume_Mute}
    XButton2 & XButton1:: Send {Volume_Mute}

The hold behavior don't act normaly. I need to press my Button1/2 two times to call the hold behavior. Why ?

Thanks for your help

Upvotes: 0

Views: 176

Answers (1)

MCL
MCL

Reputation: 4065

The combinations

XButton1 & XButton2:: Send {Volume_Mute}
XButton2 & XButton1:: Send {Volume_Mute}

make XButton1 and XButton2 prefix keys. Prefix keys have a slightly different behaviour. They only fire upon release, which can't be avoided: For example, if you press XButton1, it is possible that you will press XButton2, causing the combination to be triggered. Only if you haven't pressed any additional button, XButton1:: will be triggered, and that on the other hand can't be determined before it was released.
To your VolumeHoldTreatment() function: In this case, a loop with GetKeyState() isn't necessary. A normal key will continue firing anyway when you hold it down. That's why some people are able to produce the word LOL with way too many O letters, with only three keystrokes.
Disregarding the mute function, something like this would do exactly the same:

XButton1::Send, {Volume_Down}
XButton2::Send, {Volume_Up}

Avoiding the use of a combination like XButton1 & XButton2 and therewith preventing the occurrence of prefix keys will save you a lot of trouble and can e.g. be accomplished like this:

XButton1::
    if( GetKeyState("XButton2") ) {
        Send, {Volume_Mute}
    } else {
        Send, {Volume_Down}
    }
return

XButton2::
    if( GetKeyState("XButton1") ) {
        Send, {Volume_Mute}
    } else {
        Send, {Volume_Up}
    }
return

This will work almost flawlessly: If you hold just one button, it will be triggered again and again, continuously sending Volume_Up or Volume_Down. As soon as you press both keys, two things will happen:

  1. The key you pressed first will stop firing
  2. The other key will send the {Volume_Mute}

Unfortunately, this comes with two downsides when muting: a) There will always be at least one Volume_Up or Volume_Down (even more when you take a really long time pressing the second button) sent before the {Volume_Mute} is sent. b) If you hold both keys for a long time, {Volume_Mute} will be sent over and over again; but this at least won't change the outcome.

Update:

The XButtons indeed show a strange behaviour, namely they don't keep firing when they are held down. Check out this code:

XButton1::
    if( GetKeyState("XButton2", "P") ) {
        SetTimer, FireVolumeUp, Off
        Send, {Volume_Mute}
    } else {
        SetTimer, FireVolumeDown, 100
    }
return

XButton1 Up::
     SetTimer, FireVolumeDown, Off
return

XButton2::
    if( GetKeyState("XButton1", "P") ) {
        SetTimer, FireVolumeDown, Off
        Send, {Volume_Mute}
    } else {
       SetTimer, FireVolumeUp, 100
    }
return

XButton2 Up::
     SetTimer, FireVolumeUp, Off
return


FireVolumeUp:
    Send, {Volume_Up}
return

FireVolumeDown:
    Send, {Volume_Down}
return

This has become more similar to your code, but it uses timers instead of loops, making the script able to execute other code in between timer runs. Also, I'm using XButtonN Up:: hotkeys, leading to more precision in terms of detecting the key release. The rest should be pretty self-explanatory. If you don't want the timers to wait 100 ms before their first run, add a GoSub, FireVolume... before each SetTimer.

Upvotes: 2

Related Questions