Passing array as args (robotgo keytoggle) (original) (raw)
November 10, 2025, 10:51am 1
I am trying to toggle a key with modifiers using robotgo. The function I am calling is KeyToggle; for me takes something like robotgo.KeyToggle(“a”, “up”, “alt”, “cmd”).
I have the code below that compiles and builds an array of strings, but I don’t know how to expand this in Go - I have tried using slices (as shown) but that fails at runtime. My previous approach was to call KeyToggle for each of the modifiers separately, but this is unreliable in applying the modifers
I’m hoping I’m missing some obvious way to do this. I might otherwise need to unpack the array or rely on KeyToggle allowing for empty strings
which seems a dangerous approach.
Thank you in advance - Andy
func press_release(key keyJSON) {
modifiers := []string{}
if key.Shift {
modifiers = append(modifiers, "shift")
}
if key.Ctrl {
modifiers = append(modifiers, "ctrl")
}
if key.Alt {
modifiers = append(modifiers, "alt")
}
state := "up"
if key.Press {
state = "down"
}
robotgo.KeyToggle(key.Key, state, modifiers[:])
}
Andrew_Stratton (Andrew Stratton) November 10, 2025, 12:07pm 2
I spotted an earlier error using “control” and needed to use “ctrl”, which fixes my original issue.
I still don’t know how to do this using a simpler slice way - my code is now
func press_release(key keyJSON) {
modifiers := []string{}
if key.Shift {
modifiers = append(modifiers, "shift")
}
if key.Ctrl {
modifiers = append(modifiers, "ctrl")
}
if key.Alt {
modifiers = append(modifiers, "alt")
}
state := "up"
if key.Press { // press modifiers before pressing key
state = "down"
press_release_modifiers(modifiers, state)
}
robotgo.KeyToggle(key.Key, state)
if !key.Press { // release modifiers after releasing key
press_release_modifiers(modifiers, state)
}
}
Where press_release_modifiers calls keyToggle for any key modifiers…
Dean_Davidson (Dean Davidson) November 10, 2025, 4:41pm 3
I don’t really see a problem with your code. Is it that you are trying to reduce the repetitive nature of your if key.Shift { checks? You could use reflection here. Something like this:
type keyJSON struct {
Key string
Shift bool `key:"shift"`
Ctrl bool `key:"ctrl"`
Alt bool `key:"alt"`
Press bool
}
func (k keyJSON) toKeyArgs() []string {
// We will build up a list of modifiers based on our struct values
modifiers := []string{}
// Get the type and value of the instance
v := reflect.ValueOf(k)
t := v.Type()
// Iterate over all available fields
for i := 0; i < t.NumField(); i++ {
// Get the field type info (for name and tag)
fieldType := t.Field(i)
// Get the field value info (for IsZero check)
fieldValue := v.Field(i)
// Check for the existence of our "key" tag
keyTag := fieldType.Tag.Get("key")
// Check if the field's value is its zero value
isZero := fieldValue.IsZero()
// We have a non-zero value and a key tag so add to our modifiers
if !isZero && len(keyTag) > 0 {
modifiers = append(modifiers, keyTag)
}
}
return modifiers
}
Then to use it:
func main() {
key := keyJSON{
Key: "enter",
Shift: true,
}
fmt.Println(key.Key, key.toKeyArgs())
key = keyJSON{
Key: "ctrl",
Alt: true,
Shift: true,
}
fmt.Println(key.Key, key.toKeyArgs())
}
// Output:
// enter [shift]
// ctrl [shift alt]
But I don’t know if that is easier to reason about / maintain. You can run / modify this on the playground:
Andrew_Stratton (Andrew Stratton) November 10, 2025, 4:56pm 4
Sorry - I have obviously been unclear - I was trying to pass the arguments to KeyToggle in one go.
i.e. Robotgo allows this call KeyToggle('tab', 'down', 'alt') - and this will press the target and modifier virtual keys in the right order, in one call. Also ‘up’ could be passed to release in the right order.
My current working solution instead calls KeyToggle multiple times - in a different order based on whether ‘down’ or ‘up’ is passed. So the modifier keys (e.g. ‘alt’) are pressed down before the target key, or released ‘up’ after the target key is released.
But I don’t know how to get Go to compile the request to KeyToggle('tab', 'down', modifiers), where modifiers can be 0 to 3 strings.
I hope this makes a little more sense - Andy
mje (Jeff Emanuel) November 10, 2025, 5:39pm 5
Dean_Davidson (Dean Davidson) November 10, 2025, 5:57pm 6
I was going to type something up but I’ve been busy with work this morning. But this is exactly what you need to grok to figure out your solution.
Andrew_Stratton (Andrew Stratton) November 11, 2025, 10:01am 7
Unfortunately I can’t change the KeyToggle parameters - they are in an external package with signature of func robotgo.KeyToggle(key string, args ...interface{}) error
Below is a compiling version that runs then throws interface conversion: interface {} is []string, not string, i.e. it’s expecting a string not []string. I’m not even sure this should compile?!
I think I’m going to have to give up on this and use my less simple way - since that actually works 
Thank you for the feedback - Andy
func press_release(key keyJSON) {
state := "up"
if key.Press { // press modifiers before pressing key
state = "down"
}
modifiers := []string{}
modifiers = append(modifiers, state)
if key.Shift {
modifiers = append(modifiers, "shift")
}
if key.Ctrl {
modifiers = append(modifiers, "ctrl")
}
if key.Alt {
modifiers = append(modifiers, "alt")
}
robotgo.KeyToggle(key.Key, modifiers)
}
Andrew_Stratton (Andrew Stratton) November 11, 2025, 1:45pm 8
I solved it - by using any (interface{}) instead of string - also had to take a slice and pass that expanded - there’s a Go playground sample here Go Playground - The Go Programming Language and my code below.
func press_release(key keyJSON) {
modifiers := []any{} // REPLACED STRING
state := "up"
if key.Press {
state = "down"
}
modifiers = append(modifiers, state)
if key.Shift {
modifiers = append(modifiers, "shift")
}
if key.Ctrl {
modifiers = append(modifiers, "ctrl")
}
if key.Alt {
modifiers = append(modifiers, "alt")
}
robotgo.KeyToggle(key.Key, modifiers[:]...) // USING SLICE EXPANDED
}
Thank you for all the feedback - I would have given up otherwise - Andy
lemarkar (Leo Emar-Kar) November 12, 2025, 11:18am 9
Why do you use reslicing with `modifiers`?
Andrew_Stratton (Andrew Stratton) November 12, 2025, 11:21am 10
It was the only way I managed to get it to compile and run without an error 
lemarkar (Leo Emar-Kar) November 12, 2025, 11:22am 11
Reslicing is `[:]` after your slice, it re-creates ptr to underlaying data. Nothing will happen if you remove it
Andrew_Stratton (Andrew Stratton) November 12, 2025, 11:25am 12
If you mean putting robotgo.KeyToggle(key.Key, modifiers), this fails at runtime
2025/11/12 11:24:07 http: panic serving 127.0.0.1:52146: interface conversion: interface {} is []interface {}, not string
goroutine 22 [running]:
net/http.(*conn).serve.func1()
C:/Program Files/Go/src/net/http/server.go:1943 +0x14b
panic({0x7ff788015920?, 0xc00027aff0?})
C:/Program Files/Go/src/runtime/panic.go:783 +0x136
github.com/go-vgo/robotgo.ToStrings({0xc0002953b8, 0x1, 0x1})
C:/Users/Andy Stratton/go/pkg/mod/github.com/go-vgo/robotgo@v0.110.8/key.go:467 +0x245
github.com/go-vgo/robotgo.KeyToggle({0x7ff7880e76b0, 0x1}, {0xc0002953b8, 0x1, 0x1})
C:/Users/Andy Stratton/go/pkg/mod/github.com/go-vgo/robotgo@v0.110.8/key.go:562 +0x531
quando/internal/server/devices/keyboard.press_release({{0x7ff7880e76b0, 0x1}, 0x1, 0x0, 0x1, 0x0})
c:/Users/Andy Stratton/Dropbox/projects/quando/internal/server/devices/keyboard/keyboard.go:50 +0x425
lemarkar (Leo Emar-Kar) November 12, 2025, 11:26am 13
I didn’t mean that. I said you can type modifiers… without any modifiers[:]…
lemarkar (Leo Emar-Kar) November 12, 2025, 11:27am 14
Andrew_Stratton (Andrew Stratton) November 12, 2025, 11:27am 15
Ok - immediately realised you meant robotgo.KeyToggle(key.Key, modifiers...)
yes - this works 
It’s been a long journey to get to where I am now