4
votes

I'm looking to use AutoHotKey to modify the functionality of my shift keys. The functionality is described in Steve Losh's Blog entry here. Specifically, I'd like my shift keys to do the following:

  • If LShift or RShift is pressed and released in under 300 ms with no other keys being pressed in between, send ( or ), respectively.
  • If LShift and RShift are "rolled" together (press LShift, press RShift, release LShift, release RShift, etc.) in under 300ms, send () or )(.
  • If a shift key is used improperly (LShift and S, RShift and K, etc.) then nothing happens.

I've been having issues with the 300ms requirement and the "rolling" functionality. Specifically, I'm having issues with only being able to detect when the key is released due to the hotkey combos such as:

LShift &  0:: return

This is where I'm at so far:

LShift::
    Send {LShift Down}
    KeyWait, LShift
    Send {LShift Up}
    if (A_TimeSinceThisHotkey < 300){
        if (A_PriorKey = "LShift")
        {
            Send {)}
        }
    }
return
4

4 Answers

3
votes

I don't see a reason to use a 300 ms timeout anyway, it seems unreliable and unnecessary.
Have a look at this commented code, it is short and efficient, and seems to meet all of your requirements:

LShift::Send, (
RShift::Send, )
LShift & RShift:: Send, ()
RShift & LShift:: Send, )(

/* Put your unwanted combinations here
 * and let them do nothing
*/
LShift & q::return
RShift & y::return

Edit:
Since LShift and RShift already are prefix hotkeys, I left out the trick described here.

1
votes

MCL's answer is close but when I tested it I found that shift-clicking didn't select text. Here's a version with a passthrough to allow shift-clicking to work.

;shift to parens
LShift::Send, (
RShift::Send, )
LShift & RShift:: Send, ()
RShift & LShift:: Send, )(

;passthrough for shift-click
LShift & LButton::
  Send, {LShift Down}{LButton}
  Send, {LShift Up}
RShift & LButton::
  Send, {RShift Down}{LButton}
  Send, {RShift Up}

I don't think the 300ms timeout is possible without either very deep understanding of autohotkey's implementation or actual modification to autohotkey. When I tried to get it to work (using http://www.autohotkey.com/board/topic/98742-remapping-shift-key/) I found that A_PriorHotkey was not consistently populated. I don't think that variable was meant to work with modifier keys this way.

0
votes

I felt compelled to figure this one out. Here you go!

I basically created a hotkey for every Shift + Letter key combination in order to send the correct key case and also set the Abort value. The Abort value is then referenced whenever one of the Shift keys is pressed in order to determine whether or not to send the corresponding ( or ).

The "Rolling" was accomplished by creating a Hotkey for LShift + RShift (and the opposite). It then looks to see which key is released first to determine () or )(.

Accept if this was what you were looking for!

Loop 26
{
    Hotkey, % "~LShift & " Chr(A_Index+96), LetterKey ; Hotkeys for A-Z
    Hotkey, % "~RShift & " Chr(A_Index+96), LetterKey ; Hotkeys for A-Z
}
Return

RShift::
LShift::
    Abort := 0
    keyDown := A_TickCount
    Keywait, % A_ThisHotkey
    duration := A_TickCount - keyDown
    If (duration < 200) and (Abort = 0)
        SendInput % (A_ThisHotkey = "LShift") ? "(" : ")"
    Send {LShift Up}
    Return

RShift & LShift::
LShift & RShift::
    Keywait, LShift
    If GetKeyState("RShift", "P")
    {
        Keywait, RShift
        Send ()
    }
    Else
        Send )(
    Return

LetterKey:
    SendInput, % "+" SubStr(A_ThisHotKey, 0, 1)
    Abort := 1
Return

EDIT: Hmm, I seem to be having the same problem as you. I always get a duration of 0 due to the hotkeys.

0
votes

I found and modified this script on the AutoHotKey Forums. (The original script was prone to type "K(" when you meant to type "K" if you type too quickly, so I've modified it so that shouldn't happen any more)

$LShift Up::send, % getkeystate("LShift") ? "{LShift Up}" : ""
$RShift Up::send, % getkeystate("RShift") ? "{RShift Up}" : ""

~$LShift::
    KeyWait, LShift, T0.1 ; wait 100ms to check shift state
    if (A_PriorKey = "LShift")
    {
        send, % getkeystate("LShift") ? "{LShift Down}" : "("
    }
    KeyWait, LShift
return

~$RShift::
    KeyWait, RShift, T0.1 ; wait 100ms to check shift state
    if (A_PriorKey = "RShift")
    {
        send, % getkeystate("RShift") ? "{RShift Down}" : ")"
    }
    KeyWait, RShift
return