In Windows 10, I use the Dvorak keyboard layout. That means common keyboard shortcuts are in different places than usual, e.g. ctrl+v and ctrl+w are right next to each other, and x/c/v are not altogether.
I'm trying to use Autohotkey to "reassign" shortcuts. As a simple example, ctrl+c to ctrl+j.
Simply adding a shortcut:
^j::^c
But, that leaves ctrl+c itself active. ChatGPT says I can do:
^j::^c
^c::return
But, that just creates a chain that ultimately exits doing nothing.
I had a thought that led to this:
HandleCtrlQOrX(key) {
if (key = "x") {
MsgBox "Pressed x" ; Adding for debugging
return
}
Send "^x" ; Send ctrl+x only if ctrl+q was pressed
}
^q::HandleCtrlQOrX("q")
^x::HandleCtrlQOrX("x")
HandleCtrlJOrC(key) {
if (key = "c") {
MsgBox "Pressed c" ; Adding for debugging
return
}
Send "^c" ; Send ctrl+c only if ctrl+j was pressed
}
^j::HandleCtrlJOrC("j")
^c::HandleCtrlJOrC("c")
HandleCtrlKOrV(key) {
if (key = "v") {
MsgBox "Pressed v" ; Adding for debugging
return
}
Send "^v" ; Send ctrl+v only if ctrl+k was pressed
}
^k::HandleCtrlKOrV("k")
^v::HandleCtrlKOrV("v")
Logically, it seems like it might end up the same as above, depending what `Send "^c" actually does. But, it worked.
However... The more I use it, the more I see it only half works. It will work for a few minutes. Then, it will have a spell of seeming to act like I actually pressed ctrl+c. It does trigger the MsgBox during these times. Eventually, it goes back to working as I'd hoped, and also stops triggering the MsgBox.
NOTE: Since, q/j/k that I'm reassigning to are actually in the same spots on the keyboard in Dvorak as x/c/v in qwerty, I wondered if something was confusing or intermingling Dvorak and qwerty layouts. So, I tried assigning to different characters, but still get the MsgBox.
Can this script not actually work as I'm hoping?
(I know there is also this other question already: How do I change shortcut keys with autohotkey? But it doesn't address disabling the "originals".)
Also, there might be alternatives. For example:
^d::!F4
^w::return
But, alt+F4 is not strictly the same as ctrl+w, e.g. in VS Code or Google Chrome ctrl+w closes individual tabs, not the entire window.
If there is another way to accomplish sending ctrl+w while also disabling any action when pressing ctrl+w itself, that is perfectly acceptable, too.
Try this:-
; Edit: I discovered this doesn't work properly, see the edit
; given much further below.
FuncSend(thiskey) {
SendInput thiskey
}
SendAlt(slct) {
Hotkey slct,, "Off" ;Disable main hotkey
Hotkey "~" . slct, FuncSend(slct), "On" ;Enable temp. hotkey
Send slct
Hotkey "~" . slct,, "Off" ;Disable temp. hotkey
Hotkey slct,, "On" ;Enable main hotkey
}
HandleCtrlQOrX(key) {
if (key = "x") {
MsgBox "Pressed x" ; Adding for debugging
return
}
SendAlt "^x" ; Send ctrl+x only if ctrl+q was pressed
}
HandleCtrlJOrC(key) {
if (key = "c") {
MsgBox "Pressed c" ; Adding for debugging
return
}
SendAlt "^c" ; Send ctrl+c only if ctrl+j was pressed
}
HandleCtrlKOrV(key) {
if (key = "v") {
MsgBox "Pressed v" ; Adding for debugging
return
}
SendAlt "^v" ; Send ctrl+v only if ctrl+k was pressed
}
^q::HandleCtrlQOrX("q")
^x::HandleCtrlQOrX("x")
^j::HandleCtrlJOrC("j")
^c::HandleCtrlJOrC("c")
^k::HandleCtrlKOrV("k")
^v::HandleCtrlKOrV("v")
You can arrange the function definitions however you want.
Explained (for the ^j
and ^c
case):-
When ^j
is pressed:-
HandleCtrlJOrC
receives "j"
if (key = "c")
test failed; Interpreter moves onSendAlt
receives "^c"
^c
gets disabled; temporary hotkey ~^c
(watch the tilde) gets enabled, action is function FuncSend
with slct
variable^c
; FuncSend
receives "^c"
; SendInput presses ^c
(I think FuncSend
is useless here, but I really don't know…)~^c
gets disabled; hotkey ^c
gets re-enabledWhen ^c
is pressed:-
HandleCtrlJOrC
receives "c"
if (key = "c")
test passedMsgBox
opens up saying “Pressed c”Similar case for the other hotkeys.
So, a very overcomplicated solution, isn't it? (I have an idea for a more compact HandleCtrlJOrC
function, might add later)
Edit:- I did make a more compact HandleCtrlShortcut
function, and also fixed some bugs I found later. So, the above code was bugged… 😭
Updated code:-
FuncSend(thiskey) {
SendInput thiskey
}
SendAlt(slct) {
Hotkey slct,, "Off" ;Disable main hotkey
Send slct
Hotkey slct,, "On" ;Enable main hotkey
}
HandleCtrlShortcut(set, downkey) {
if (set = 1) {
defkey := "x"
newkey := "q"
}
else if (set = 2) {
defkey := "c"
newkey := "j"
}
else if (set = 3) {
defkey := "v"
newkey := "k"
}
if (downkey = defkey) {
MsgBox "Pressed " . downkey ; Adding for debugging
}
else {
SendAlt "^" . defkey ; Send the default key, NOT the input key
}
}
^q::HandleCtrlShortcut(1, "q")
^x::HandleCtrlShortcut(1, "x")
^j::HandleCtrlShortcut(2, "j")
^c::HandleCtrlShortcut(2, "c")
^k::HandleCtrlShortcut(3, "k")
^v::HandleCtrlShortcut(3, "v")
Additions:-
The only disadvantage to doing this is that the Windows Clipboard no longer works properly. When clicking on a menu item, it tries to send ^v
to put it to the clipboard and then paste it, triggering the test MsgBox
in return. So, do you really need this?
Edit 3:- Okay, FuncSend
was redundant, should've removed it muuuuuuuuuuch earlier.
New code (again):-
SendAlt(slct) {
Hotkey slct,, "Off" ;Disable main hotkey
Send slct
Hotkey slct,, "On" ;Enable main hotkey
}
HandleCtrlShortcut(set, downkey) {
if (set = 1) {
defkey := "x"
newkey := "q"
}
else if (set = 2) {
defkey := "c"
newkey := "j"
}
else if (set = 3) {
defkey := "v"
newkey := "k"
}
if (downkey = defkey) {
MsgBox "Pressed " . downkey ; Adding for debugging
}
else {
SendAlt "^" . defkey ; Send the default key, NOT the input key
}
}
^q::HandleCtrlShortcut(1, "q")
^x::HandleCtrlShortcut(1, "x")
^j::HandleCtrlShortcut(2, "j")
^c::HandleCtrlShortcut(2, "c")
^k::HandleCtrlShortcut(3, "k")
^v::HandleCtrlShortcut(3, "v")
Additions:-