Sticky Shift

Post your working scripts, libraries and tools
guest3456
Posts: 2419
Joined: 09 Oct 2013, 10:31

Sticky Shift

Post by guest3456 » 15 Feb 2017, 22:57

Sticky Shift allows you to press Shift, and then release, and then the next letter will be shifted. This is substantially easier on your hands and fingers. Pressing Shift+Letter at the same time (as a chord) is the normal method, and its way less ergonomic, and timing is also an issue, because you have to press and release both keys at the correct time, otherwise you may get typos, especially at higher speeds.

I didn't like the built-in Windows feature for Sticky Keys, it kept pissing me off: I just wanted to use it for Shift, not the other modifiers such as Ctrl or Alt. I found another script and have modified it, and added the option that a double Shift = CAPSLOCK

Code: Select all


; Sticky Shift
;
; ==================================
; OPTIONS
; ==================================

timeout := 1
                                     ; How long the Shift should stay active 
                                     ; this value is in seconds. use 0.5 for half second
                                     ; if you want a capital A, then press and release Shift, then press 'a' within two seconds
                                     ; after the timeout has elapsed, the shift is no longer in effect

; ----------------------------------

show_indicator := true
                                     ; Whether or not to show a grahpic while the Shift is in progress
                                     ; this value is either 'true' or 'false' with no quotes

; ----------------------------------

double_shift_for_caps := false
                                     ; Should tapping shift twice turn on Capslock?
                                     ; this value is either 'true' or 'false' with no quotes
                                     ; default is 'false', which means a double tap of Shift will cancel the shift

; ----------------------------------

skip_spacebar := false
                                     ; Allow the sequence "Shift,Space,Key" to still shift the Key
                                     ; this value is either 'true' or 'false' with no quotes
                                     ; this means that the Spacebar will not cancel the shift
                                     ; a user wanted to press Shift+Space to be able to shift the next char after Space

; ==================================
; END OF OPTIONS
; ==================================


#NoEnv
#SingleInstance, force
#KeyHistory, 0
ListLines, Off
SetBatchLines, -1
SendMode, Input
Hotkey, LShift,    Shift_Down, on
Hotkey, RShift,    Shift_Down, on
Hotkey, LShift Up, Shift_Up  , on
Hotkey, RShift Up, Shift_Up  , on
; we don't want Control as end key, because we need to be able to press Shift,Ctrl+Left
; we don't want Alt as end key, because Shift+Alt+F should still open up File menu bars
; we don't want Win as end key, because it Shift+Win+D should still send Shift+D
; don't want CapsLock as end key, because Shift down, Caps down/up, Shift still down + M, should invert CapsLock
end_keys := "{F1}{F2}{F3}{F4}{F5}{F6}{F7}{F8}{F9}{F10}{F11}{F12}{Left}{Right}{Up}{Down}"
          . "{Home}{End}{PgUp}{PgDn}{Del}{Ins}{BS}{PrintScreen}{Pause}{AppsKey}{LShift}{RShift}"
; test := 0
if (show_indicator)
{
   HBITMAP := Create_shift_key_png()
   GUIH := Bitmap_GetHeight(HBITMAP)
   GUIW := Bitmap_GetWidth(HBITMAP)
   GUIY := A_ScreenHeight-150
   Gui, -DPIScale -Caption +AlwaysOnTop +ToolWindow
   Gui, Margin, 0, 0
   Gui, Add, Text, x0 y0 w%GUIW% h%GUIH% hwndHPic1
   Bitmap_SetImage(HPic1, HBITMAP)
   Gui, Show, Hide y%GUIY%, StickyShift indicator image
}
return


;F11::ListHotkeys
;Esc::ExitApp


Shift_Down:
   ; tooltip, % "[" A_ThisHotkey "]" ++test ;"`nA_PriorKey=" A_PriorKey    ; if you hold down shift, you'll see this loops after each timeout, so Shift is a repeat key just like any letter, it keeps getting spammed
   if (caps_on)
   {
      SetCapsLockState, Off
      caps_on := false
      return
   }

   trigger_shift_key := A_ThisHotkey        ; needs to be set because A_ThisHotkey gets overwritten by the Up hotkey
   , waiting_for_input := 1

   if (show_indicator)
      Gui, Show, NA

   Send, {%trigger_shift_key% down}
   Input, key, V M L1 T%timeout%, %end_keys%
   ; inputErrorLevel := ErrorLevel

   if (ErrorLevel == "EndKey:LShift") || (ErrorLevel == "EndKey:RShift")
   {
      KeyWait, % SubStr(ErrorLevel, 8)   ; careful, keywait sets its own errorlevel from this point forward
      if (double_shift_for_caps)
      {
         ; tooltip, a second shift press occurred.`ntrigger_shift_key: %trigger_shift_key%`ninputErrorLevel: %inputErrorLevel%
         ; sleep, 1000
         SetCapsLockState, On
         caps_on := true
      }
   }

   ; allow Shift,Space,Space,Space(etc),Letter to work:
   if (skip_spacebar)
      while (key = " ")
         Input key, V M L1 T%timeout%, %end_keys%

    ; tooltip, key=[%key%]`nA_ThisHotkey=[%A_ThisHotkey%]`ntrigger_shift_key=[%trigger_shift_key%]`ninputErrorlevel=%inputErrorLevel%`nhotkeys back on

   if (show_indicator)
      Gui, Hide

   if !(GetKeyState(A_ThisHotkey))
      Send, {%trigger_shift_key% Up}
   waiting_for_input := 0          ; the order of this line could possibly matter, this seems like the best order
   Hotkey, LShift, on
   Hotkey, RShift, on
   Hotkey, LShift Up, on
   Hotkey, RShift Up, on
return



Shift_Up:
   ; tooltip, [%A_ThisHotkey%]`nshift_key=%trigger_shift_key%

   ; if we pressed and released Shift, then the down hotkey is waiting for the next char input
   ; so we want to turn off these hotkeys, so that we can detect another LShift as the Input EndKey
   if (waiting_for_input)
   {
      ; tooltip, turning off hotkeys
      Hotkey, RShift,    off
      Hotkey, RShift Up, off
      Hotkey, LShift,    off
      Hotkey, LShift Up, off
   }

   ; else, the down hotkey is no longer waiting for input.
   ; this only fires if the Up hotkey was triggered AFTER the Input was complete
   ; which means shift was held down during the Input, resulting in a normal Shift+letter simultanous chord.
   ; this is needed because without it, then shift would get 'stuck' down.
   ; test: hold down Shift, and the down hotkey above fires. while still holding shift, we press a letter.
   ; the Input cmd captures the letter and then finally its supposed to Send Shift Up
   ; but we're still physically holding down Shift, so i don't think the virtual Send Shift Up ever goes through
   ; or if it does go through, it somehow blocks our real Shift up release
   else
   {
      Send, {%A_ThisHotkey%}
   }
return







; ##################################################################################
; # This was generated by Image2Include.ahk, you must not change it! #
; #
; # https://autohotkey.com/board/topic/93292-image2include-include-images-in-your-scripts/
; #
; ##################################################################################
Create_shift_key_png(NewHandle := False) {
Static hBitmap := 0
If (NewHandle)
   hBitmap := 0
If (hBitmap)
   Return hBitmap
VarSetCapacity(B64, 8944 << !!A_IsUnicode)
B64 := "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"
If !DllCall("Crypt32.dll\CryptStringToBinary", "Ptr", &B64, "UInt", 0, "UInt", 0x01, "Ptr", 0, "UIntP", DecLen, "Ptr", 0, "Ptr", 0)
   Return False
VarSetCapacity(Dec, DecLen, 0)
If !DllCall("Crypt32.dll\CryptStringToBinary", "Ptr", &B64, "UInt", 0, "UInt", 0x01, "Ptr", &Dec, "UIntP", DecLen, "Ptr", 0, "Ptr", 0)
   Return False
; Bitmap creation adopted from "How to convert Image data (JPEG/PNG/GIF) to hBITMAP?" by SKAN
; -> http://www.autohotkey.com/board/topic/21213-how-to-convert-image-data-jpegpnggif-to-hbitmap/?p=139257
hData := DllCall("Kernel32.dll\GlobalAlloc", "UInt", 2, "UPtr", DecLen, "UPtr")
pData := DllCall("Kernel32.dll\GlobalLock", "Ptr", hData, "UPtr")
DllCall("Kernel32.dll\RtlMoveMemory", "Ptr", pData, "Ptr", &Dec, "UPtr", DecLen)
DllCall("Kernel32.dll\GlobalUnlock", "Ptr", hData)
DllCall("Ole32.dll\CreateStreamOnHGlobal", "Ptr", hData, "Int", True, "PtrP", pStream)
hGdip := DllCall("Kernel32.dll\LoadLibrary", "Str", "Gdiplus.dll", "UPtr")
VarSetCapacity(SI, 16, 0), NumPut(1, SI, 0, "UChar")
DllCall("Gdiplus.dll\GdiplusStartup", "PtrP", pToken, "Ptr", &SI, "Ptr", 0)
DllCall("Gdiplus.dll\GdipCreateBitmapFromStream",  "Ptr", pStream, "PtrP", pBitmap)
DllCall("Gdiplus.dll\GdipCreateHBITMAPFromBitmap", "Ptr", pBitmap, "PtrP", hBitmap, "UInt", 0)
DllCall("Gdiplus.dll\GdipDisposeImage", "Ptr", pBitmap)
DllCall("Gdiplus.dll\GdiplusShutdown", "Ptr", pToken)
DllCall("Kernel32.dll\FreeLibrary", "Ptr", hGdip)
DllCall(NumGet(NumGet(pStream + 0, 0, "UPtr") + (A_PtrSize * 2), 0, "UPtr"), "Ptr", pStream)
Return hBitmap
}
; ----------------------------------------------------------------------------------------------------------------------
; Returns the width of a bitmap.
; ----------------------------------------------------------------------------------------------------------------------
Bitmap_GetWidth(hBitmap) {
   Static Size := (4 * 5) + A_PtrSize + (A_PtrSize - 4)
   VarSetCapacity(BITMAP, Size, 0)
   DllCall("Gdi32.dll\GetObject", "Ptr", hBitmap, "Int", Size, "Ptr", &BITMAP, "Int")
   Return NumGet(BITMAP, 4, "Int")
}
; ----------------------------------------------------------------------------------------------------------------------
; Returns the height of a bitmap.
; ----------------------------------------------------------------------------------------------------------------------
Bitmap_GetHeight(hBitmap) {
   Static Size := (4 * 5) + A_PtrSize + (A_PtrSize - 4)
   VarSetCapacity(BITMAP, Size, 0)
   DllCall("Gdi32.dll\GetObject", "Ptr", hBitmap, "Int", Size, "Ptr", &BITMAP, "Int")
   Return NumGet(BITMAP, 8, "Int")
}
; ----------------------------------------------------------------------------------------------------------------------
; Associates a new bitmap with a static control.
; Parameters:     hCtrl    -  Handle to the GUI control (Pic or Text).
;                 hBitmap  -  Handle to the bitmap to associate with the GUI control.
; Return value:   Handle to the image previously associated with the GUI control, if any; otherwise, NULL.
; ----------------------------------------------------------------------------------------------------------------------
Bitmap_SetImage(hCtrl, hBitmap) {
   ; STM_SETIMAGE = 0x172, IMAGE_BITMAP = 0x00, SS_BITMAP = 0x0E
   WinSet, Style, +0x0E, ahk_id %hCtrl%
   SendMessage, 0x172, 0x00, %hBitmap%, , ahk_id %hCtrl%
   Return ErrorLevel
}
Last edited by guest3456 on 15 Nov 2017, 23:39, edited 4 times in total.

Helgef
Posts: 3190
Joined: 17 Jul 2016, 01:02
Contact:

Re: Sticky Shift

Post by Helgef » 16 Feb 2017, 18:19

Very good! I did not manage to break it :lol:
Good commenting of the code, makes it really easy to follow.
I like the skip_spacebar option, perfect if you get over eager after terminating a sentece with a period, and then starts the next one with a shift before the space.
This post was made this Sticky Script :)

Edit: Additionally, I like that it is idle until you actually use it.
guest3456
Posts: 2419
Joined: 09 Oct 2013, 10:31

Re: Sticky Shift

Post by guest3456 » 16 Feb 2017, 20:18

Lol thanks. Yours will likely be way more advanced

Helgef
Posts: 3190
Joined: 17 Jul 2016, 01:02
Contact:

Re: Sticky Shift

Post by Helgef » 17 Feb 2017, 03:36

I don't think I'll continue with it, I think it's redundant, yours can easily modified to do sticky anykey, and as I mentioned, it is idle until you activate the sticky, I prefer that. The only thing that could warrant using other methods is if you want context sensitive specific keys. Eg, if you have both ctrl and shift as sticky key, then I wouldn't want ctlr to work with w in my browser, there would be to high risk that you hit ctrl instead of shift when typing, then you get ctrl+w, which closes the tab, at least in my browser. I would only want physically holding ctrl+w to work, not sticky ctrl+w.
guest3456
Posts: 2419
Joined: 09 Oct 2013, 10:31

Re: Sticky Shift

Post by guest3456 » 18 Feb 2017, 17:42

I've updated the script in the OP.

I've added the option to display an on-screen indicator showing while the Shift is active

Helgef wrote:I don't think I'll continue with it, I think it's redundant
Well others may be interested in another technique. You could always just post your work-in-progress in this thread

Helgef
Posts: 3190
Joined: 17 Jul 2016, 01:02
Contact:

Re: Sticky Shift

Post by Helgef » 18 Feb 2017, 18:13

guest3456 wrote: You could always just post your work-in-progress in this thread
Perhaps, but then I'd probably start poking at it again :|

Edit: Nice pop-up! :thumbup:
User avatar
jeeswg
Posts: 5132
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Sticky Shift

Post by jeeswg » 19 Feb 2017, 11:47

I use this sometimes so I can drink a cup of tea/coffee while typing.

Code: Select all

$Shift:: ;anywhere - capitalise without holding shift
Input, vText, L1 T1 I
if (ErrorLevel = "Timeout")
	Return
SendInput +{%vText%}
Return
I also use win+space sometimes to capitalise the last word:
(To change capitalisation for the last word.)

Code: Select all

+CapsLock:: ;anywhere - alternative hotkey - capslock
SetStoreCapslockMode, Off
SendInput {CapsLock}
SetStoreCapslockMode, On
Return

;==================================================

#Space:: ;anywhere (text functions) - case - capitalise last word
SendInput ^+{Left}
vText := JEE_GetSelectedText()

StringLower, vLower, vText
StringUpper, vUpper, vText
StringLower, vTitle, vText, T

;(random) --> [lower --> title --> upper -->]
;[same order as MS Word 2003:]
;if lower then title
;if title then upper
;if upper/random then lower

vOutput := vLower
if (vText == vLower)
	vOutput := vTitle
if (vText == vTitle)
	vOutput := vUpper
if (vText == vUpper)
	vOutput := vLower

Clipboard := vOutput
SendInput ^v
Return

;==================================================

JEE_GetSelectedText(vWait=3)
{
WinGet, hWnd, ID, A
ControlGetFocus, vCtlClassNN, ahk_id %hWnd%
if (RegExReplace(vCtlClassNN, "\d", "") = "Edit")
	ControlGet, vText, Selected, , %vCtlClassNN%, ahk_id %hWnd%
else
{
	ClipSaved := ClipboardAll
	Clipboard := ""
	SendInput ^c
	ClipWait, %vWait%
	if ErrorLevel
	{
		ToolTip, ClipWait failed (%A_ThisHotkey%)
		Clipboard := ClipSaved
		ClipSaved := ""
		Sleep 1000
		ToolTip
		Exit ;terminate the thread that launched this function
	}
	vText := Clipboard
	Clipboard := ClipSaved
	ClipSaved := ""
}

Return vText
}

;==================================================
guest3456
Posts: 2419
Joined: 09 Oct 2013, 10:31

Re: Sticky Shift

Post by guest3456 » 06 Mar 2017, 08:24

Helgef wrote:
guest3456 wrote: You could always just post your work-in-progress in this thread
Perhaps, but then I'd probably start poking at it again :|
I think your script would be worthwhile to post. Mine has the DOuble caps problem if you hit the keys too fast (such as almost simultaneously). I'm using Input, L1 but I still can't eliminate the problem. I think the time between the Send, {Shift down} and Send, {Shift up} is just too long. And other keypresses are getting caught. But I believe I tried Send {Shift Down}{%key%}{Shift Up} but that was causing the first char to be missed like tHis. Maybe you have an idea how to fix that and still keep it idle?

Helgef
Posts: 3190
Joined: 17 Jul 2016, 01:02
Contact:

Re: Sticky Shift

Post by Helgef » 06 Mar 2017, 11:36

Hi.
guest3456 wrote:I think your script would be worthwhile to post.
I'll have to review which version is most acceptable first. I will (probably) post it later then.
guest3456 wrote:Mine has the DOuble caps problem if you hit the keys too fast (such as almost simultaneously).
I actually manage to reproduce the problem now, with your current script. But it's not that easy.
guest3456 wrote: I think the time between the Send, {Shift down} and Send, {Shift up} is just too long
That would be my guess too.
guest3456 wrote:But I believe I tried Send {Shift Down}{%key%}{Shift Up} but that was causing the first char to be missed like tHis
I think that the Input,... line (disregarding the idle waiting) takes the most time between Send, {Shift down} and Send, {Shift up}. So I guess, in both cases, your are most likely hitting two buttons while the input command is either preparing for or handling your keyboard input. So in the DOuble case, shift is down, and in the tHis case shift is not down, t is just typed out, while key:=h, hence the result. Maybe that's not readable, sorry :crazy:
guest3456 wrote:Maybe you have an idea how to fix that and still keep it idle?
Not really. :cry:
jeeswg wrote:I also use win+space sometimes to capitalise the last word:
That is nice jeeswg :thumbup:
Helgef
Posts: 3190
Joined: 17 Jul 2016, 01:02
Contact:

Re: Sticky Shift

Post by Helgef » 06 Mar 2017, 18:25

@guest3456, if this version works, I think it is my best attempt, I have made it more idle, and tried to prevent the DOuble issue.

@Anyone who wants to try it, the class with examples showing how to use it are included in:
StickyKeys.ahk
(7.67 KiB) Downloaded 99 times
In summary:
  • Make any key sticky.
  • Combine sticky keys. That is, for example, you can press and release, shift then ctrl then hit a to get a shift+ctrl+a.
  • Combine sticky keys can be turned off, then turning on a sticky key turns off any sticky key previously turned on. :wtf:
Good luck.
guest3456
Posts: 2419
Joined: 09 Oct 2013, 10:31

Re: Sticky Shift

Post by guest3456 » 06 Mar 2017, 19:20

Helgef wrote:@guest3456, if this version works, I think it is my best attempt, I have made it more idle, and tried to prevent the DOuble issue.

@Anyone who wants to try it, the class with examples showing how to use it are included in:
StickyKeys.ahk
In summary:
  • Make any key sticky.
  • Combine sticky keys. That is, for example, you can press and release, shift then ctrl then hit a to get a shift+ctrl+a.
  • Combine sticky keys can be turned off, then turning on a sticky key turns off any sticky key previously turned on. :wtf:
Good luck.
It works well.

Minor issues:

1. I still got the DOuble issue, but much much less frequently than my script. I pretty much have to press the letters simulatenously, which is hard to do
2. When CAPS is on, the Shift should then be a lowercase letter

Helgef
Posts: 3190
Joined: 17 Jul 2016, 01:02
Contact:

Re: Sticky Shift

Post by Helgef » 07 Mar 2017, 03:14

guest3456 wrote: Minor issues:
1. I still got the DOuble issue, but much much less frequently than my script. I pretty much have to press the letters simulatenously, which is hard to do
2. When CAPS is on, the Shift should then be a lowercase letter
1) :thumbdown: That is annoying. If my understanding of my own script is correct ( :lol: ), the only possibillity I can think of is that you hit the o when the script is doing,

Code: Select all

SendInput, % {Shift down}{d down}{d up}{Shift up}
and, [docs]SendInput[/docs] is reverting to [docs]SendEvent[/docs], then your input is not postponed until the send is done.
So I would change, in modifiedSend(),

Code: Select all

SendInput, % prefix . "{" . A_ThisHotkey . " down}{" . A_ThisHotkey . " up}" . suffix
to

Code: Select all

;SetKeyDelay,-1,-1 ; Try with and without this.
BlockInput,Send
SendInput, % prefix . "{" . A_ThisHotkey . " down}{" . A_ThisHotkey . " up}" . suffix
BlockInput,Default
This isn't a good solution either, since you would now loose the o output, but if my therory is correct, you would instead get Duble as often as you got DOuble. A better solution would be to have BlockInput,SendPostpone, you have to guess what I mean.

2) That makes sense.

Edit: Remember that you might need to run the script as administrator in order to enable BlockInput to work.
User avatar
jeeswg
Posts: 5132
Joined: 19 Dec 2016, 01:58
Location: UK

Re: Sticky Shift

Post by jeeswg » 08 Mar 2017, 21:58

@Helgef Cheers.

For capitalisation I use:
- shift, letter - capitalise letter
- shift+capslock - capslock on/off
- win+space - capitalise last word (cycle: (random -->) lower --> title --> upper)
- ctrl+capslock - capitalise selection (cycle: (random -->) lower --> title --> upper)
- special hotstrings for capitalising i (with one exception: 'i.e.', and disabled on maths software e.g. Maxima CAS, TeXstudio)
- hotstrings for words e.g. 'ahk-' -> 'AutoHotkey', 'bush-' -> 'Bush', 'vtext' -> 'vText'
guest3456
Posts: 2419
Joined: 09 Oct 2013, 10:31

Re: Sticky Shift

Post by guest3456 » 15 Nov 2017, 23:36

fixed a bug to allow Shift+aa=AA

ozzii
Posts: 308
Joined: 30 Oct 2013, 06:04

Re: Sticky Shift

Post by ozzii » 16 Nov 2017, 03:15

jeeswg wrote: For capitalisation I use:
- shift, letter - capitalise letter
- shift+capslock - capslock on/off
- win+space - capitalise last word (cycle: (random -->) lower --> title --> upper)
- ctrl+capslock - capitalise selection (cycle: (random -->) lower --> title --> upper)
- special hotstrings for capitalising i (with one exception: 'i.e.', and disabled on maths software e.g. Maxima CAS, TeXstudio)
- hotstrings for words e.g. 'ahk-' -> 'AutoHotkey', 'bush-' -> 'Bush', 'vtext' -> 'vText'
How can I do to keep the selection active after 'ctrl+capslock'?
Because when you have to reselect the things for redoing the cycle it's not so convenient.
guest3456
Posts: 2419
Joined: 09 Oct 2013, 10:31

Re: Sticky Shift

Post by guest3456 » 29 Mar 2018, 07:42

Helgef wrote:and tried to prevent the DOuble issue.
i'm still using my script with my laptop, but just for reference, i'm investigating moving to a programmable mechanical keyboard, and doing this at the firmware/hardware level:

https://docs.qmk.fm
https://docs.qmk.fm/feature_advanced_ke ... -shot-keys

User avatar
BGM
Posts: 419
Joined: 20 Nov 2013, 20:56
GitHub: bgmCoder
Contact:

Re: Sticky Shift

Post by BGM » 29 Mar 2018, 11:02

I've used a compUSA SK-6200 ergonomic mechanical keyboard for decades. It was wearing out, so I actually found another one, unused and unopened, after all these years on ebay and bought it. I love it and never want to move to another keyboard, ever. I could test something if you like.
PCSsolutions
Posts: 2
Joined: 09 May 2018, 14:05

Re: Sticky Shift

Post by PCSsolutions » 14 May 2018, 01:17

Awesome script!!

You went above and beyond with the popup graphics. StickyShift will definitely help my pinky fingers from being sore toward the end of my work day.

What will help my pinkies even more is if I can totally not use them to Shift at all. Is it possible to trigger StickyShift with a tap of the ALT key while holding ALT+X should provide the normal ALT operations.

I just discover AHK and still learning its features and how to program more complex functionalities. If you can provide some suggestion on how to implement the ALT tap to Activate StickyShift, I much appreciated.

Thanks,
guest3456
Posts: 2419
Joined: 09 Oct 2013, 10:31

Re: Sticky Shift

Post by guest3456 » 14 May 2018, 09:22

PCSsolutions wrote: What will help my pinkies even more is if I can totally not use them to Shift at all. Is it possible to trigger StickyShift with a tap of the ALT key while holding ALT+X should provide the normal ALT operations.
I thought about this too. Using thumb for Alt keys to shift is another nice alternative. I think modifying these lines should do it:

Code: Select all

Hotkey, LShift,    Shift_Down, on
Hotkey, RShift,    Shift_Down, on
Hotkey, LShift Up, Shift_Up  , on
Hotkey, RShift Up, Shift_Up  , on

; ..... further down:

   if (ErrorLevel == "EndKey:LShift") || (ErrorLevel == "EndKey:RShift")
change those to:

Code: Select all

Hotkey, LAlt,    Shift_Down, on
Hotkey, RAlt,    Shift_Down, on
Hotkey, LAlt Up, Shift_Up  , on
Hotkey, RAlt Up, Shift_Up  , on

; ..... further down:

   if (ErrorLevel == "EndKey:LAlt") || (ErrorLevel == "EndKey:RAlt")
try it out and let me know if that works, and if it retains the normal Alt+X functionality when holding it down

Post Reply

Return to “Scripts and Functions”