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 :frowning: 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 :slight_smile:

It’s been a long journey to get to where I am now