package bootcommand import ( "fmt" "github.com/hashicorp/packer/builder/vsphere/driver" "github.com/hashicorp/packer/common" "golang.org/x/mobile/event/key" "log" "os" "strings" "time" "unicode" ) // SendUsbScanCodes will be called to send codes to the VM type SendUsbScanCodes func([]key.Code, []bool) error type usbDriver struct { vm *driver.VirtualMachine sendImpl SendUsbScanCodes interval time.Duration specialMap map[string]key.Code scancodeMap map[rune]key.Code codeBuffer []key.Code downBuffer []bool // keyEvent can set this error which will prevent it from continuing err error } func NewUSBDriver(send SendUsbScanCodes, interval time.Duration) *usbDriver { // We delay (default 100ms) between each key event to allow for CPU or // network latency. See PackerKeyEnv for tuning. keyInterval := common.PackerKeyDefault if delay, err := time.ParseDuration(os.Getenv(common.PackerKeyEnv)); err == nil { keyInterval = delay } // override interval based on builder-specific override. if interval > time.Duration(0) { keyInterval = interval } special := map[string]key.Code{ "enter": key.CodeReturnEnter, "esc": key.CodeEscape, "bs": key.CodeDeleteBackspace, "del": key.CodeDeleteForward, "tab": key.CodeTab, "f1": key.CodeF1, "f2": key.CodeF2, "f3": key.CodeF3, "f4": key.CodeF4, "f5": key.CodeF5, "f6": key.CodeF6, "f7": key.CodeF7, "f8": key.CodeF8, "f9": key.CodeF9, "f10": key.CodeF10, "f11": key.CodeF11, "f12": key.CodeF12, "insert": key.CodeInsert, "home": key.CodeHome, "end": key.CodeEnd, "pageUp": key.CodePageUp, "pageDown": key.CodePageDown, "left": key.CodeLeftArrow, "right": key.CodeRightArrow, "up": key.CodeUpArrow, "down": key.CodeDownArrow, "leftalt": key.CodeLeftAlt, "leftctrl": key.CodeLeftControl, "leftshift": key.CodeLeftShift, "rightalt": key.CodeRightAlt, "rightctrl": key.CodeRightControl, "rightshift": key.CodeRightShift, } scancodeIndex := make(map[string]key.Code) scancodeIndex["abcdefghijklmnopqrstuvwxyz"] = key.CodeA scancodeIndex["ABCDEFGHIJKLMNOPQRSTUVWXYZ"] = key.CodeA scancodeIndex["1234567890"] = key.Code1 scancodeIndex["!@#$%^&*()"] = key.Code1 scancodeIndex[" "] = key.CodeSpacebar scancodeIndex["-=[]\\"] = key.CodeHyphenMinus scancodeIndex["_+{}|"] = key.CodeHyphenMinus scancodeIndex[";'`,./"] = key.CodeSemicolon scancodeIndex[":\"~<>?"] = key.CodeSemicolon var scancodeMap = make(map[rune]key.Code) for chars, start := range scancodeIndex { for i, r := range chars { scancodeMap[r] = start + key.Code(i) } } return &usbDriver{ sendImpl: send, specialMap: special, interval: keyInterval, scancodeMap: scancodeMap, } } // Flush sends codes to the vm func (d *usbDriver) Flush() error { defer func() { d.codeBuffer = nil }() if err := d.sendImpl(d.codeBuffer, d.downBuffer); err != nil { return err } time.Sleep(d.interval) return nil } func (d *usbDriver) SendKey(k rune, action KeyAction) error { keyShift := unicode.IsUpper(k) || strings.ContainsRune(shiftedChars, k) keyCode := d.scancodeMap[k] log.Printf("Sending char '%c', code %s, shift %v", k, keyCode, keyShift) d.send(keyCode, keyShift) return d.err } func (d *usbDriver) SendSpecial(special string, action KeyAction) error { keyCode, ok := d.specialMap[special] if !ok { return fmt.Errorf("special %s not found.", special) } log.Printf("Special code '<%s>' found, replacing with: %s", special, keyCode) switch action { case KeyOn: d.send(keyCode, true) case KeyOff, KeyPress: d.send(keyCode, false) } return d.err } // send stores the codes in an internal buffer. Use Flush to send them. func (d *usbDriver) send(code key.Code, down bool) { // slices to keep the input order d.codeBuffer = append(d.codeBuffer, code) d.downBuffer = append(d.downBuffer, down) }