One-shot (sticky) keys

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
HighArn
Posts: 17
Joined: 20 Oct 2021, 08:25

One-shot (sticky) keys

Post by HighArn » 08 Aug 2023, 18:21

Hi, unexpectedly cant find an autohotkey implementation of this problem. So what actually One-shot (sticky) keys are

When you hold one of the modifiers Alt, Control, Shift, Command they work as usual, but when you tap it once they stick, so actually they stay pressed down, and wait for any other key to be pressed and then they releasing.
Example if i press Ctrl and release it and then press "a" it will act like shortcut Ctrl+A and after it script will send Send {Control up}.
If I press Ctrl and release, and Shift then release, and after it "a", it must work like Ctrl+Shift+A, and then send {Control up} and {Shift up}

So i try like a prototype this, but it didnt work.

Code: Select all

SC021:: 
Send {LControl Down}
KeyWait, a
Send {LControl Up}
return
[Mod edit: [code][/code] tags added.]
[Mod edit: Moved topic to AHk v1 help, since this is not v2 code.]

User avatar
mikeyww
Posts: 27366
Joined: 09 Sep 2014, 18:38

Re: One-shot (sticky) keys

Post by mikeyww » 08 Aug 2023, 21:16

Code: Select all

#Requires AutoHotkey v1.1.33
ih       := InputHook(), ih.KeyOpt("{All}", "E")
modifier := "Control|Shift|Alt|Win"

#If !prefix
Alt::
Ctrl::
Shift::
LWin Up::
If (A_PriorKey ~= modifier) {
 prefix := "{" A_PriorKey " down}"
 suffix := "{" A_PriorKey " up}"
 Loop {
  ih.Start(), ih.Wait()
  If match := ih.EndKey ~= modifier {
   prefix .= "{" ih.EndKey " down}"
   suffix .= "{" ih.EndKey " up}"
  }
 } Until !match
 Send % prefix "{" ih.EndKey "}" suffix
 prefix := ""
}
Return
#If
Various other options.... https://www.autohotkey.com/board/topic/6596-sticky-keys/

HighArn
Posts: 17
Joined: 20 Oct 2021, 08:25

Re: One-shot (sticky) keys

Post by HighArn » 09 Aug 2023, 07:18

Thanks, it works! How can i change this code if i want it to work only with remapped modifiers? So actually my left and right alt remapped to change keyboard layers like this:

Code: Select all

LAlt:: 
last := layer, layer := 3
KeyWait, LAlt
layer := A_Priorkey != "LAlt" ? last : last = 2 ? 1 : 2
Return

RAlt::
last := layer, layer := 5
KeyWait, RAlt
layer := A_Priorkey != "RAlt" ? last : last = 4 ? 1 : 4
Return
And my modifiers are remapped like this:

Code: Select all

 #If (layer = 2)

SC01E::LAlt 
SC01F::LWin 
SC020::LShift 
SC021::LCtrl

 #If (layer = 3)

SC01E::LAlt 
SC01F::LWin 
SC020::LShift 
SC021::LCtrl
Sorry for maybe dumb questions, i'm not very good at coding, just wanna improve my work with pc.

User avatar
mikeyww
Posts: 27366
Joined: 09 Sep 2014, 18:38

Re: One-shot (sticky) keys

Post by mikeyww » 09 Aug 2023, 08:47

That is a different question and potentially a different script. An idea is below to get you started. You can adjust the Switch block as needed. Enjoy!

Code: Select all

#Requires AutoHotkey v1.1.33
ih       := InputHook(), ih.KeyOpt("{All}", "E")
modifier := "Control|Shift|Alt|Win"

#If !prefix
Alt::
Ctrl::
Shift::
LWin Up::
If (A_PriorKey ~= modifier) {
 prefix := "{" A_PriorKey " down}"
 suffix := "{" A_PriorKey " up}"
 Switch A_PriorKey {
  Case "LAlt":
   SoundBeep 1500
  Case "RAlt":
   SoundBeep 1000
 }
 Loop {
  ih.Start(), ih.Wait()
  If match := ih.EndKey ~= modifier {
   prefix .= "{" ih.EndKey " down}"
   suffix .= "{" ih.EndKey " up}"
  }
 } Until !match
 Send % prefix "{" ih.EndKey "}" suffix
 prefix := ""
}
Return
#If

HighArn
Posts: 17
Joined: 20 Oct 2021, 08:25

Re: One-shot (sticky) keys

Post by HighArn » 09 Aug 2023, 10:48

thanks for advice! If i understand you I dont need to write different code, but just add something in switch block? Like something:

Case "SC01E": ; Remapped LAlt
SoundBeep 1200 ; Customize this action for the remapped key

or it need to be different code, just based on this idea?

User avatar
mikeyww
Posts: 27366
Joined: 09 Sep 2014, 18:38

Re: One-shot (sticky) keys

Post by mikeyww » 09 Aug 2023, 11:05

That is correct, at least worth exploring, depending on your specific goal. It looks like you want to change the value of layer, so this could be done in that block if you wish. You will need to decide what you want the hotkey subroutine to do, or whether you need a separate one. These subroutines simply execute the code line by line, so follow the script line by line to understand what it does.

HighArn
Posts: 17
Joined: 20 Oct 2021, 08:25

Re: One-shot (sticky) keys

Post by HighArn » 09 Aug 2023, 13:32

So i follow the script line by line to understand what it does, and mostly I understand what it does but I stuck with some syntax misunderstanding. For example:

1) Switch section in this code for now doing that Alt key is an exception, so when Alt pressed the section after with if statement, loop and other is not executed. So how in this case a prefix at the end cleaned? Because empty prefix is needed at beginning to start a script. Or I dont understand how switch block works in this script.
2) In layer 2 my SC021::LCtrl so when i press alt i'm in layer 2, and my SC021 key is now Ctrl, and this key works like normal control, but why then it does not work like
sticky key? What the difference? This was the reason why I cant figure out the solution.

Edit: so it doesnt work because when I am in layer 2 the #If !prefix doesnt triggers because now im in this #If (layer = 2) ? But if so i tried to combine those to this
#If (!prefix && layer = 2) but this work same as previous.

Edit 2: about switch section, so to be more clear i actually dont understand why Alt is treated like exception if only what it does in switch section is producing sound, so if it empty is would the same.

User avatar
mikeyww
Posts: 27366
Joined: 09 Sep 2014, 18:38

Re: One-shot (sticky) keys

Post by mikeyww » 09 Aug 2023, 21:00

Unfortunately, I do not understand your post. You might want to consider using Google Translate if you are having difficulty in getting your idea across in English. It is up to you.

Switch simply executes commands according to specific cases that are defined in the block.
If present, SwitchValue is evaluated once and compared to each case value until a match is found, and then that case is executed. Otherwise, the first case which evaluates to true (non-zero and non-empty) is executed. If there is no matching case and a Default is present, it is executed.
As I mentioned, you would need to revise that block to insert your own code.

If you are asking a question about a revised script that you have not posted, please note that I am unable to debug a script that I cannot see.

HighArn
Posts: 17
Joined: 20 Oct 2021, 08:25

Re: One-shot (sticky) keys

Post by HighArn » 10 Aug 2023, 10:59

I'm really sorry, my english is not very good. I will try to write better.
So I'm stopped to use Notepad as my code editor, download a VsCode with debugger, and things go a little better.
So now this is my version of code, that works but have few bugs, that I can't fix.

Code: Select all

#InstallKeybdHook

ih       := InputHook(), ih.KeyOpt("{All}", "E")
modifier := "LCtr|Shift|LAlt|LWin"

layer = 1 ; Auto-execute section

#If (layer = 2)

SC01E:: LAlt
SC01F:: LWin
SC020:: Shift
SC021:: LCtrl


#If !prefix && layer != 1
SC01E::
Send {Alt Down}
      If (A_PriorKey ~= NULL) {
 prefix := "{Alt down}"
 suffix := "{Alt up}"
 
 Loop {
  ih.Start(), ih.Wait()
  If match := ih.EndKey ~= modifier {
   prefix .= "{" ih.EndKey " down}"
   suffix .= "{" ih.EndKey " up}"
  }
 } Until !match
 Key := % prefix "{" ih.EndKey "}" suffix
 Send % prefix "{" ih.EndKey "}" suffix
 prefix := ""
}
Return

SC01F::
Send {Lwin Down}
    If (A_PriorKey ~= NULL) {
 prefix := "{LWin down}"
 suffix := "{LWin up}"
 
 Loop {
  ih.Start(), ih.Wait()
  If match := ih.EndKey ~= modifier {
   prefix .= "{" ih.EndKey " down}"
   suffix .= "{" ih.EndKey " up}"
  }
 } Until !match
 Key := % prefix "{" ih.EndKey "}" suffix
 Send % prefix "{" ih.EndKey "}" suffix
 prefix := ""
}
Return

SC020::
 Send {Shift Down}
  If (A_PriorKey ~= NULL) {
 prefix := "{Shift down}"
 suffix := "{Shift up}"
 
 Loop {
  ih.Start(), ih.Wait()
  If match := ih.EndKey ~= modifier {
   prefix .= "{" ih.EndKey " down}"
   suffix .= "{" ih.EndKey " up}"
  }
 } Until !match
 Key := % prefix "{" ih.EndKey "}" suffix
 Send % prefix "{" ih.EndKey "}" suffix
 prefix := ""
}
Return

SC021::
Send {Control Down}
If (A_PriorKey ~= NULL) {
 prefix := "{Control down}"
 suffix := "{Control up}"
 
 Loop {
  ih.Start(), ih.Wait()
  If match := ih.EndKey ~= modifier {
   prefix .= "{" ih.EndKey " down}"
   suffix .= "{" ih.EndKey " up}"
  }
 } Until !match
 Key := % prefix "{" ih.EndKey "}" suffix
 Send % prefix "{" ih.EndKey "}" suffix
 prefix := ""
}
Return
    

#If
So code is bad designed, and it actually repeats almost the same block of code 4 times and i think If (A_PriorKey ~= NULL) is not necessary here. I tried make a function like below, but it didn't work.
And I didn't figure out how to implement Switch block to this so i did it this way. Now it work with remapped modifiers like i need.
Script works with any two keys hotkeys almost well. When i press any Modifier once and then press any non modifier key, for example: If i press Ctrl and release and then press 'a' it produce Ctrl-a . So bugs are:

1) It not work with 3 keys hotkeys at all. For example if press Alt+CTRL+DEL at one time like usual, it doesnt work. More interesting when i press those buttons in order, It not work too, but for debug purposes I create that variable "Key" at the end of each code block, to see what output is generated, and when i press Alt Ctrl Del in order, the value of this variable Key : "{Control down}{LAlt down}{Delete}{Control up}{LAlt up}" So it looks like it should work, but it doesn't.

2) If I press and hold for example CTRL and then press many times for example "v" it produce Ctrl+V only after first "v" press, and then it just produce letter "v" many times.


This is a function that i tried to do

Code: Select all

SC021::
Send {Control Down}
Sticky("{Control down}" , "{Control up}")

Sticky(prefix , suffix)
{
   global modifier
  If (A_PriorKey ~= NULL) {

 Loop {
  ih.Start(), ih.Wait()
  If match := ih.EndKey ~= modifier {
   prefix .= "{" ih.EndKey " down}"
   suffix .= "{" ih.EndKey " up}"
  }
 } Until !match
 Key := Send prefix "{" ih.EndKey "}" suffix
 
 prefix := ""
}
Return Key
      
}

User avatar
mikeyww
Posts: 27366
Joined: 09 Sep 2014, 18:38

Re: One-shot (sticky) keys

Post by mikeyww » 10 Aug 2023, 15:48

I am not able to debug your entire script. I do recommend that you investigate the following RegExMatch.

Code: Select all

MsgBox % A_PriorKey ~= NULL
This probably does not behave as you expect. Displaying the value of the RegExMatch function (or equivalent operation) as shown here may shed some light on the issue. NULL is actually not a special word in AHK. It would be considered to be a variable name. My understanding is that RegExMatch always succeeds with a value of 1 when matching against the null string. Thus, even if the prior key is not null, this function or operation would still return 1 if NULL is null.

Other suggestions for proceeding: add one small step at a time, and retest, instead of adding multiple new hotkeys or subroutines at once.

Best of luck!

hunterhyena1
Posts: 1
Joined: 23 Sep 2023, 07:04

Re: One-shot (sticky) keys

Post by hunterhyena1 » 23 Sep 2023, 07:12

mikeyww wrote:
08 Aug 2023, 21:16

Code: Select all

#Requires AutoHotkey v1.1.33
ih       := InputHook(), ih.KeyOpt("{All}", "E")
modifier := "Control|Shift|Alt|Win"

#If !prefix
Alt::
Ctrl::
Shift::
LWin Up::
If (A_PriorKey ~= modifier) {
 prefix := "{" A_PriorKey " down}"
 suffix := "{" A_PriorKey " up}"
 Loop {
  ih.Start(), ih.Wait()
  If match := ih.EndKey ~= modifier {
   prefix .= "{" ih.EndKey " down}"
   suffix .= "{" ih.EndKey " up}"
  }
 } Until !match
 Send % prefix "{" ih.EndKey "}" suffix
 prefix := ""
}
Return
#If
Various other options.... https://www.autohotkey.com/board/topic/6596-sticky-keys/
thanks for u answer, how to combine with this script https://www.autohotkey.com/boards/viewtopic.php?p=361191#p361191 ?

User avatar
mikeyww
Posts: 27366
Joined: 09 Sep 2014, 18:38

Re: One-shot (sticky) keys

Post by mikeyww » 24 Sep 2023, 10:00

This script already shows how to make the modifier sticky.

Post Reply

Return to “Ask for Help (v1)”