/* Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package vm import ( "context" "flag" "fmt" "sort" "strconv" "strings" "github.com/vmware/govmomi/govc/cli" "github.com/vmware/govmomi/govc/flags" "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/vim25/types" ) type hidKey struct { Code int32 ShiftPressed bool } // stolen from // https://gist.github.com/MightyPork/6da26e382a7ad91b5496ee55fdc73db2#file-usb_hid_keys-h-L110 const ( KEY_MOD_LCTRL = 0x01 KEY_MOD_LSHIFT = 0x02 KEY_MOD_LALT = 0x04 KEY_MOD_LMETA = 0x08 KEY_MOD_RCTRL = 0x10 KEY_MOD_RSHIFT = 0x20 KEY_MOD_RALT = 0x40 KEY_MOD_RMETA = 0x80 KEY_NONE = 0x00 KEY_ERR_OVF = 0x01 KEY_A = 0x04 KEY_B = 0x05 KEY_C = 0x06 KEY_D = 0x07 KEY_E = 0x08 KEY_F = 0x09 KEY_G = 0x0a KEY_H = 0x0b KEY_I = 0x0c KEY_J = 0x0d KEY_K = 0x0e KEY_L = 0x0f KEY_M = 0x10 KEY_N = 0x11 KEY_O = 0x12 KEY_P = 0x13 KEY_Q = 0x14 KEY_R = 0x15 KEY_S = 0x16 KEY_T = 0x17 KEY_U = 0x18 KEY_V = 0x19 KEY_W = 0x1a KEY_X = 0x1b KEY_Y = 0x1c KEY_Z = 0x1d KEY_1 = 0x1e KEY_2 = 0x1f KEY_3 = 0x20 KEY_4 = 0x21 KEY_5 = 0x22 KEY_6 = 0x23 KEY_7 = 0x24 KEY_8 = 0x25 KEY_9 = 0x26 KEY_0 = 0x27 KEY_ENTER = 0x28 KEY_ESC = 0x29 KEY_BACKSPACE = 0x2a KEY_TAB = 0x2b KEY_SPACE = 0x2c KEY_MINUS = 0x2d KEY_EQUAL = 0x2e KEY_LEFTBRACE = 0x2f KEY_RIGHTBRACE = 0x30 KEY_BACKSLASH = 0x31 KEY_HASHTILDE = 0x32 KEY_SEMICOLON = 0x33 KEY_APOSTROPHE = 0x34 KEY_GRAVE = 0x35 KEY_COMMA = 0x36 KEY_DOT = 0x37 KEY_SLASH = 0x38 KEY_CAPSLOCK = 0x39 KEY_F1 = 0x3a KEY_F2 = 0x3b KEY_F3 = 0x3c KEY_F4 = 0x3d KEY_F5 = 0x3e KEY_F6 = 0x3f KEY_F7 = 0x40 KEY_F8 = 0x41 KEY_F9 = 0x42 KEY_F10 = 0x43 KEY_F11 = 0x44 KEY_F12 = 0x45 KEY_SYSRQ = 0x46 KEY_SCROLLLOCK = 0x47 KEY_PAUSE = 0x48 KEY_INSERT = 0x49 KEY_HOME = 0x4a KEY_PAGEUP = 0x4b KEY_DELETE = 0x4c KEY_END = 0x4d KEY_PAGEDOWN = 0x4e KEY_RIGHT = 0x4f KEY_LEFT = 0x50 KEY_DOWN = 0x51 KEY_UP = 0x52 KEY_NUMLOCK = 0x53 KEY_KPSLASH = 0x54 KEY_KPASTERISK = 0x55 KEY_KPMINUS = 0x56 KEY_KPPLUS = 0x57 KEY_KPENTER = 0x58 KEY_KP1 = 0x59 KEY_KP2 = 0x5a KEY_KP3 = 0x5b KEY_KP4 = 0x5c KEY_KP5 = 0x5d KEY_KP6 = 0x5e KEY_KP7 = 0x5f KEY_KP8 = 0x60 KEY_KP9 = 0x61 KEY_KP0 = 0x62 KEY_KPDOT = 0x63 KEY_102ND = 0x64 KEY_COMPOSE = 0x65 KEY_POWER = 0x66 KEY_KPEQUAL = 0x67 KEY_F13 = 0x68 KEY_F14 = 0x69 KEY_F15 = 0x6a KEY_F16 = 0x6b KEY_F17 = 0x6c KEY_F18 = 0x6d KEY_F19 = 0x6e KEY_F20 = 0x6f KEY_F21 = 0x70 KEY_F22 = 0x71 KEY_F23 = 0x72 KEY_F24 = 0x73 KEY_OPEN = 0x74 KEY_HELP = 0x75 KEY_PROPS = 0x76 KEY_FRONT = 0x77 KEY_STOP = 0x78 KEY_AGAIN = 0x79 KEY_UNDO = 0x7a KEY_CUT = 0x7b KEY_COPY = 0x7c KEY_PASTE = 0x7d KEY_FIND = 0x7e KEY_MUTE = 0x7f KEY_VOLUMEUP = 0x80 KEY_VOLUMEDOWN = 0x81 KEY_KPCOMMA = 0x85 KEY_RO = 0x87 KEY_KATAKANAHIRAGANA = 0x88 KEY_YEN = 0x89 KEY_HENKAN = 0x8a KEY_MUHENKAN = 0x8b KEY_KPJPCOMMA = 0x8c KEY_HANGEUL = 0x90 KEY_HANJA = 0x91 KEY_KATAKANA = 0x92 KEY_HIRAGANA = 0x93 KEY_ZENKAKUHANKAKU = 0x94 KEY_KPLEFTPAREN = 0xb6 KEY_KPRIGHTPAREN = 0xb7 KEY_LEFTCTRL = 0xe0 KEY_LEFTSHIFT = 0xe1 KEY_LEFTALT = 0xe2 KEY_LEFTMETA = 0xe3 KEY_RIGHTCTRL = 0xe4 KEY_RIGHTSHIFT = 0xe5 KEY_RIGHTALT = 0xe6 KEY_RIGHTMETA = 0xe7 KEY_MEDIA_PLAYPAUSE = 0xe8 KEY_MEDIA_STOPCD = 0xe9 KEY_MEDIA_PREVIOUSSONG = 0xea KEY_MEDIA_NEXTSONG = 0xeb KEY_MEDIA_EJECTCD = 0xec KEY_MEDIA_VOLUMEUP = 0xed KEY_MEDIA_VOLUMEDOWN = 0xee KEY_MEDIA_MUTE = 0xef KEY_MEDIA_WWW = 0xf0 KEY_MEDIA_BACK = 0xf1 KEY_MEDIA_FORWARD = 0xf2 KEY_MEDIA_STOP = 0xf3 KEY_MEDIA_FIND = 0xf4 KEY_MEDIA_SCROLLUP = 0xf5 KEY_MEDIA_SCROLLDOWN = 0xf6 KEY_MEDIA_EDIT = 0xf7 KEY_MEDIA_SLEEP = 0xf8 KEY_MEDIA_COFFEE = 0xf9 KEY_MEDIA_REFRESH = 0xfa KEY_MEDIA_CALC = 0xfb ) var hidKeyMap = map[string]int32{ "KEY_MOD_LCTRL": KEY_MOD_LCTRL, "KEY_MOD_LSHIFT": KEY_MOD_LSHIFT, "KEY_MOD_LALT": KEY_MOD_LALT, "KEY_MOD_LMETA": KEY_MOD_LMETA, "KEY_MOD_RCTRL": KEY_MOD_RCTRL, "KEY_MOD_RSHIFT": KEY_MOD_RSHIFT, "KEY_MOD_RALT": KEY_MOD_RALT, "KEY_MOD_RMETA": KEY_MOD_RMETA, "KEY_NONE": KEY_NONE, "KEY_ERR_OVF": KEY_ERR_OVF, "KEY_A": KEY_A, "KEY_B": KEY_B, "KEY_C": KEY_C, "KEY_D": KEY_D, "KEY_E": KEY_E, "KEY_F": KEY_F, "KEY_G": KEY_G, "KEY_H": KEY_H, "KEY_I": KEY_I, "KEY_J": KEY_J, "KEY_K": KEY_K, "KEY_L": KEY_L, "KEY_M": KEY_M, "KEY_N": KEY_N, "KEY_O": KEY_O, "KEY_P": KEY_P, "KEY_Q": KEY_Q, "KEY_R": KEY_R, "KEY_S": KEY_S, "KEY_T": KEY_T, "KEY_U": KEY_U, "KEY_V": KEY_V, "KEY_W": KEY_W, "KEY_X": KEY_X, "KEY_Y": KEY_Y, "KEY_Z": KEY_Z, "KEY_1": KEY_1, "KEY_2": KEY_2, "KEY_3": KEY_3, "KEY_4": KEY_4, "KEY_5": KEY_5, "KEY_6": KEY_6, "KEY_7": KEY_7, "KEY_8": KEY_8, "KEY_9": KEY_9, "KEY_0": KEY_0, "KEY_ENTER": KEY_ENTER, "KEY_ESC": KEY_ESC, "KEY_BACKSPACE": KEY_BACKSPACE, "KEY_TAB": KEY_TAB, "KEY_SPACE": KEY_SPACE, "KEY_MINUS": KEY_MINUS, "KEY_EQUAL": KEY_EQUAL, "KEY_LEFTBRACE": KEY_LEFTBRACE, "KEY_RIGHTBRACE": KEY_RIGHTBRACE, "KEY_BACKSLASH": KEY_BACKSLASH, "KEY_HASHTILDE": KEY_HASHTILDE, "KEY_SEMICOLON": KEY_SEMICOLON, "KEY_APOSTROPHE": KEY_APOSTROPHE, "KEY_GRAVE": KEY_GRAVE, "KEY_COMMA": KEY_COMMA, "KEY_DOT": KEY_DOT, "KEY_SLASH": KEY_SLASH, "KEY_CAPSLOCK": KEY_CAPSLOCK, "KEY_F1": KEY_F1, "KEY_F2": KEY_F2, "KEY_F3": KEY_F3, "KEY_F4": KEY_F4, "KEY_F5": KEY_F5, "KEY_F6": KEY_F6, "KEY_F7": KEY_F7, "KEY_F8": KEY_F8, "KEY_F9": KEY_F9, "KEY_F10": KEY_F10, "KEY_F11": KEY_F11, "KEY_F12": KEY_F12, "KEY_SYSRQ": KEY_SYSRQ, "KEY_SCROLLLOCK": KEY_SCROLLLOCK, "KEY_PAUSE": KEY_PAUSE, "KEY_INSERT": KEY_INSERT, "KEY_HOME": KEY_HOME, "KEY_PAGEUP": KEY_PAGEUP, "KEY_DELETE": KEY_DELETE, "KEY_END": KEY_END, "KEY_PAGEDOWN": KEY_PAGEDOWN, "KEY_RIGHT": KEY_RIGHT, "KEY_LEFT": KEY_LEFT, "KEY_DOWN": KEY_DOWN, "KEY_UP": KEY_UP, "KEY_NUMLOCK": KEY_NUMLOCK, "KEY_KPSLASH": KEY_KPSLASH, "KEY_KPASTERISK": KEY_KPASTERISK, "KEY_KPMINUS": KEY_KPMINUS, "KEY_KPPLUS": KEY_KPPLUS, "KEY_KPENTER": KEY_KPENTER, "KEY_KP1": KEY_KP1, "KEY_KP2": KEY_KP2, "KEY_KP3": KEY_KP3, "KEY_KP4": KEY_KP4, "KEY_KP5": KEY_KP5, "KEY_KP6": KEY_KP6, "KEY_KP7": KEY_KP7, "KEY_KP8": KEY_KP8, "KEY_KP9": KEY_KP9, "KEY_KP0": KEY_KP0, "KEY_KPDOT": KEY_KPDOT, "KEY_102ND": KEY_102ND, "KEY_COMPOSE": KEY_COMPOSE, "KEY_POWER": KEY_POWER, "KEY_KPEQUAL": KEY_KPEQUAL, "KEY_F13": KEY_F13, "KEY_F14": KEY_F14, "KEY_F15": KEY_F15, "KEY_F16": KEY_F16, "KEY_F17": KEY_F17, "KEY_F18": KEY_F18, "KEY_F19": KEY_F19, "KEY_F20": KEY_F20, "KEY_F21": KEY_F21, "KEY_F22": KEY_F22, "KEY_F23": KEY_F23, "KEY_F24": KEY_F24, "KEY_OPEN": KEY_OPEN, "KEY_HELP": KEY_HELP, "KEY_PROPS": KEY_PROPS, "KEY_FRONT": KEY_FRONT, "KEY_STOP": KEY_STOP, "KEY_AGAIN": KEY_AGAIN, "KEY_UNDO": KEY_UNDO, "KEY_CUT": KEY_CUT, "KEY_COPY": KEY_COPY, "KEY_PASTE": KEY_PASTE, "KEY_FIND": KEY_FIND, "KEY_MUTE": KEY_MUTE, "KEY_VOLUMEUP": KEY_VOLUMEUP, "KEY_VOLUMEDOWN": KEY_VOLUMEDOWN, "KEY_KPCOMMA": KEY_KPCOMMA, "KEY_RO": KEY_RO, "KEY_KATAKANAHIRAGANA": KEY_KATAKANAHIRAGANA, "KEY_YEN": KEY_YEN, "KEY_HENKAN": KEY_HENKAN, "KEY_MUHENKAN": KEY_MUHENKAN, "KEY_KPJPCOMMA": KEY_KPJPCOMMA, "KEY_HANGEUL": KEY_HANGEUL, "KEY_HANJA": KEY_HANJA, "KEY_KATAKANA": KEY_KATAKANA, "KEY_HIRAGANA": KEY_HIRAGANA, "KEY_ZENKAKUHANKAKU": KEY_ZENKAKUHANKAKU, "KEY_KPLEFTPAREN": KEY_KPLEFTPAREN, "KEY_KPRIGHTPAREN": KEY_KPRIGHTPAREN, "KEY_LEFTCTRL": KEY_LEFTCTRL, "KEY_LEFTSHIFT": KEY_LEFTSHIFT, "KEY_LEFTALT": KEY_LEFTALT, "KEY_LEFTMETA": KEY_LEFTMETA, "KEY_RIGHTCTRL": KEY_RIGHTCTRL, "KEY_RIGHTSHIFT": KEY_RIGHTSHIFT, "KEY_RIGHTALT": KEY_RIGHTALT, "KEY_RIGHTMETA": KEY_RIGHTMETA, "KEY_MEDIA_PLAYPAUSE": KEY_MEDIA_PLAYPAUSE, "KEY_MEDIA_STOPCD": KEY_MEDIA_STOPCD, "KEY_MEDIA_PREVIOUSSONG": KEY_MEDIA_PREVIOUSSONG, "KEY_MEDIA_NEXTSONG": KEY_MEDIA_NEXTSONG, "KEY_MEDIA_EJECTCD": KEY_MEDIA_EJECTCD, "KEY_MEDIA_VOLUMEUP": KEY_MEDIA_VOLUMEUP, "KEY_MEDIA_VOLUMEDOWN": KEY_MEDIA_VOLUMEDOWN, "KEY_MEDIA_MUTE": KEY_MEDIA_MUTE, "KEY_MEDIA_WWW": KEY_MEDIA_WWW, "KEY_MEDIA_BACK": KEY_MEDIA_BACK, "KEY_MEDIA_FORWARD": KEY_MEDIA_FORWARD, "KEY_MEDIA_STOP": KEY_MEDIA_STOP, "KEY_MEDIA_FIND": KEY_MEDIA_FIND, "KEY_MEDIA_SCROLLUP": KEY_MEDIA_SCROLLUP, "KEY_MEDIA_SCROLLDOWN": KEY_MEDIA_SCROLLDOWN, "KEY_MEDIA_EDIT": KEY_MEDIA_EDIT, "KEY_MEDIA_SLEEP": KEY_MEDIA_SLEEP, "KEY_MEDIA_COFFEE": KEY_MEDIA_COFFEE, "KEY_MEDIA_REFRESH": KEY_MEDIA_REFRESH, "KEY_MEDIA_CALC": KEY_MEDIA_CALC, } var hidCharacterMap = map[string]hidKey{ "a": {KEY_A, false}, "b": {KEY_B, false}, "c": {KEY_C, false}, "d": {KEY_D, false}, "e": {KEY_E, false}, "f": {KEY_F, false}, "g": {KEY_G, false}, "h": {KEY_H, false}, "i": {KEY_I, false}, "j": {KEY_J, false}, "k": {KEY_K, false}, "l": {KEY_L, false}, "m": {KEY_M, false}, "n": {KEY_N, false}, "o": {KEY_O, false}, "p": {KEY_P, false}, "q": {KEY_Q, false}, "r": {KEY_R, false}, "s": {KEY_S, false}, "t": {KEY_T, false}, "u": {KEY_U, false}, "v": {KEY_V, false}, "w": {KEY_W, false}, "x": {KEY_X, false}, "y": {KEY_Y, false}, "z": {KEY_Z, false}, "1": {KEY_1, false}, "2": {KEY_2, false}, "3": {KEY_3, false}, "4": {KEY_4, false}, "5": {KEY_5, false}, "6": {KEY_6, false}, "7": {KEY_7, false}, "8": {KEY_8, false}, "9": {KEY_9, false}, "0": {KEY_0, false}, "A": {KEY_A, true}, "B": {KEY_B, true}, "C": {KEY_C, true}, "D": {KEY_D, true}, "E": {KEY_E, true}, "F": {KEY_F, true}, "G": {KEY_G, true}, "H": {KEY_H, true}, "I": {KEY_I, true}, "J": {KEY_J, true}, "K": {KEY_K, true}, "L": {KEY_L, true}, "M": {KEY_M, true}, "N": {KEY_N, true}, "O": {KEY_O, true}, "P": {KEY_P, true}, "Q": {KEY_Q, true}, "R": {KEY_R, true}, "S": {KEY_S, true}, "T": {KEY_T, true}, "U": {KEY_U, true}, "V": {KEY_V, true}, "W": {KEY_W, true}, "X": {KEY_X, true}, "Y": {KEY_Y, true}, "Z": {KEY_Z, true}, "!": {KEY_1, true}, "@": {KEY_2, true}, "#": {KEY_3, true}, "$": {KEY_4, true}, "%": {KEY_5, true}, "^": {KEY_6, true}, "&": {KEY_7, true}, "*": {KEY_8, true}, "(": {KEY_9, true}, ")": {KEY_0, true}, " ": {KEY_SPACE, false}, "-": {KEY_MINUS, false}, "_": {KEY_MINUS, true}, "=": {KEY_EQUAL, false}, "+": {KEY_EQUAL, true}, "[": {KEY_LEFTBRACE, false}, "{": {KEY_LEFTBRACE, true}, "]": {KEY_RIGHTBRACE, false}, "}": {KEY_RIGHTBRACE, true}, `\`: {KEY_BACKSLASH, false}, "|": {KEY_BACKSLASH, true}, ";": {KEY_SEMICOLON, false}, ":": {KEY_SEMICOLON, true}, "'": {KEY_APOSTROPHE, false}, `"`: {KEY_APOSTROPHE, true}, "`": {KEY_GRAVE, false}, "~": {KEY_GRAVE, true}, ",": {KEY_COMMA, false}, "<": {KEY_COMMA, true}, ".": {KEY_DOT, false}, ">": {KEY_DOT, true}, "/": {KEY_SLASH, false}, "?": {KEY_SLASH, true}, } type keystrokes struct { *flags.VirtualMachineFlag UsbHidCodeValue int32 UsbHidCodes string UsbHidString string LeftControl bool LeftShift bool LeftAlt bool LeftGui bool RightControl bool RightShift bool RightAlt bool RightGui bool } func init() { cli.Register("vm.keystrokes", &keystrokes{}) } func (cmd *keystrokes) Register(ctx context.Context, f *flag.FlagSet) { cmd.VirtualMachineFlag, ctx = flags.NewVirtualMachineFlag(ctx) cmd.VirtualMachineFlag.Register(ctx, f) f.StringVar(&cmd.UsbHidString, "s", "", "Raw String to Send") f.StringVar(&cmd.UsbHidCodes, "c", "", "USB HID Codes (hex) or aliases, comma separated") f.Var(flags.NewInt32(&cmd.UsbHidCodeValue), "r", "Raw USB HID Code Value (int32)") f.BoolVar(&cmd.LeftControl, "lc", false, "Enable/Disable Left Control") f.BoolVar(&cmd.LeftShift, "ls", false, "Enable/Disable Left Shift") f.BoolVar(&cmd.LeftAlt, "la", false, "Enable/Disable Left Alt") f.BoolVar(&cmd.LeftGui, "lg", false, "Enable/Disable Left Gui") f.BoolVar(&cmd.RightControl, "rc", false, "Enable/Disable Right Control") f.BoolVar(&cmd.RightShift, "rs", false, "Enable/Disable Right Shift") f.BoolVar(&cmd.RightAlt, "ra", false, "Enable/Disable Right Alt") f.BoolVar(&cmd.RightGui, "rg", false, "Enable/Disable Right Gui") } func (cmd *keystrokes) Usage() string { return "VM" } func (cmd *keystrokes) Description() string { description := `Send Keystrokes to VM. Examples: Default Scenario govc vm.keystrokes -vm $vm -s "root" # writes 'root' to the console govc vm.keystrokes -vm $vm -c 0x15 # writes an 'r' to the console govc vm.keystrokes -vm $vm -r 1376263 # writes an 'r' to the console govc vm.keystrokes -vm $vm -c 0x28 # presses ENTER on the console govc vm.keystrokes -vm $vm -c 0x4c -la=true -lc=true # sends CTRL+ALT+DEL to console govc vm.keystrokes -vm $vm -c 0x15,KEY_ENTER # writes an 'r' to the console and press ENTER List of available aliases: ` keys := make([]string, 0) for key, _ := range hidKeyMap { keys = append(keys, key) } sort.Strings(keys) for i, key := range keys { if i > 0 { description += ", " } description += key } return description + "\n" } func (cmd *keystrokes) Process(ctx context.Context) error { if err := cmd.VirtualMachineFlag.Process(ctx); err != nil { return err } return nil } func (cmd *keystrokes) Run(ctx context.Context, f *flag.FlagSet) error { vm, err := cmd.VirtualMachine() if err != nil { return err } if vm == nil { return flag.ErrHelp } err = cmd.processUserInput(ctx, vm) if err != nil { return err } return nil } func (cmd *keystrokes) processUserInput(ctx context.Context, vm *object.VirtualMachine) error { if err := cmd.checkValidInputs(); err != nil { return err } codes, err := cmd.processUsbCode() if err != nil { return err } var keyEventArray []types.UsbScanCodeSpecKeyEvent for _, code := range codes { leftShiftSetting := false if code.ShiftPressed || cmd.LeftShift { leftShiftSetting = true } modifiers := types.UsbScanCodeSpecModifierType{ LeftControl: &cmd.LeftControl, LeftShift: &leftShiftSetting, LeftAlt: &cmd.LeftAlt, LeftGui: &cmd.LeftGui, RightControl: &cmd.RightControl, RightShift: &cmd.RightShift, RightAlt: &cmd.RightAlt, RightGui: &cmd.RightGui, } keyEvent := types.UsbScanCodeSpecKeyEvent{ UsbHidCode: code.Code, Modifiers: &modifiers, } keyEventArray = append(keyEventArray, keyEvent) } spec := types.UsbScanCodeSpec{ KeyEvents: keyEventArray, } _, err = vm.PutUsbScanCodes(ctx, spec) return err } func (cmd *keystrokes) processUsbCode() ([]hidKey, error) { if cmd.rawCodeProvided() { return []hidKey{{cmd.UsbHidCodeValue, false}}, nil } if cmd.hexCodeProvided() { var retKeyArray []hidKey for _, c := range strings.Split(cmd.UsbHidCodes, ",") { var s int32 lookupvalue, ok := hidKeyMap[c] if ok { s = intToHidCode(lookupvalue) } else { var err error s, err = hexStringToHidCode(c) if err != nil { return nil, err } } retKeyArray = append(retKeyArray, hidKey{s, false}) } return retKeyArray, nil } if cmd.stringProvided() { var retKeyArray []hidKey for _, c := range cmd.UsbHidString { lookupValue, ok := hidCharacterMap[string(c)] if !ok { return nil, fmt.Errorf("invalid Character %s in String: %s", string(c), cmd.UsbHidString) } lookupValue.Code = intToHidCode(lookupValue.Code) retKeyArray = append(retKeyArray, lookupValue) } return retKeyArray, nil } return nil, nil } func hexStringToHidCode(hex string) (int32, error) { s, err := strconv.ParseInt(hex, 0, 32) if err != nil { return 0, err } return intToHidCode(int32(s)), nil } func intToHidCode(v int32) int32 { var s int32 = v << 16 s |= 7 return s } func (cmd *keystrokes) checkValidInputs() error { // poor man's boolean XOR -> A xor B xor C = A'BC' + AB'C' + A'B'C + ABC if (!cmd.rawCodeProvided() && cmd.hexCodeProvided() && !cmd.stringProvided()) || // A'BC' (cmd.rawCodeProvided() && !cmd.hexCodeProvided() && !cmd.stringProvided()) || // AB'C' (!cmd.rawCodeProvided() && !cmd.hexCodeProvided() && cmd.stringProvided()) || // A'B'C (cmd.rawCodeProvided() && cmd.hexCodeProvided() && cmd.stringProvided()) { // ABC return nil } return fmt.Errorf("specify only 1 argument") } func (cmd keystrokes) rawCodeProvided() bool { return cmd.UsbHidCodeValue != 0 } func (cmd keystrokes) hexCodeProvided() bool { return cmd.UsbHidCodes != "" } func (cmd keystrokes) stringProvided() bool { return cmd.UsbHidString != "" }