github.com/HashDataInc/packer@v1.3.2/common/bootcommand/pc_xt_driver.go (about) 1 package bootcommand 2 3 import ( 4 "fmt" 5 "log" 6 "os" 7 "strings" 8 "time" 9 "unicode" 10 "unicode/utf8" 11 12 "github.com/hashicorp/packer/common" 13 ) 14 15 // SendCodeFunc will be called to send codes to the VM 16 type SendCodeFunc func([]string) error 17 type scMap map[string]*scancode 18 19 type pcXTDriver struct { 20 interval time.Duration 21 sendImpl SendCodeFunc 22 specialMap scMap 23 scancodeMap map[rune]byte 24 buffer [][]string 25 // TODO: set from env 26 scancodeChunkSize int 27 } 28 29 type scancode struct { 30 make []string 31 break_ []string 32 } 33 34 func (sc *scancode) makeBreak() []string { 35 return append(sc.make, sc.break_...) 36 } 37 38 // NewPCXTDriver creates a new boot command driver for VMs that expect PC-XT 39 // keyboard codes. `send` should send its argument to the VM. `chunkSize` should 40 // be the maximum number of keyboard codes to send to `send` at one time. 41 func NewPCXTDriver(send SendCodeFunc, chunkSize int, interval time.Duration) *pcXTDriver { 42 // We delay (default 100ms) between each input event to allow for CPU or 43 // network latency. See PackerKeyEnv for tuning. 44 keyInterval := common.PackerKeyDefault 45 if delay, err := time.ParseDuration(os.Getenv(common.PackerKeyEnv)); err == nil { 46 keyInterval = delay 47 } 48 // Override interval based on builder-specific override 49 if interval > time.Duration(0) { 50 keyInterval = interval 51 } 52 // Scancodes reference: https://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html 53 // https://www.win.tue.nl/~aeb/linux/kbd/scancodes-10.html 54 // 55 // Scancodes are recorded here in pairs. The first entry represents 56 // the key press and the second entry represents the key release and is 57 // derived from the first by the addition of 0x80. 58 sMap := make(scMap) 59 sMap["bs"] = &scancode{[]string{"0e"}, []string{"8e"}} 60 sMap["del"] = &scancode{[]string{"e0", "53"}, []string{"e0", "d3"}} 61 sMap["down"] = &scancode{[]string{"e0", "50"}, []string{"e0", "d0"}} 62 sMap["end"] = &scancode{[]string{"e0", "4f"}, []string{"e0", "cf"}} 63 sMap["enter"] = &scancode{[]string{"1c"}, []string{"9c"}} 64 sMap["esc"] = &scancode{[]string{"01"}, []string{"81"}} 65 sMap["f1"] = &scancode{[]string{"3b"}, []string{"bb"}} 66 sMap["f2"] = &scancode{[]string{"3c"}, []string{"bc"}} 67 sMap["f3"] = &scancode{[]string{"3d"}, []string{"bd"}} 68 sMap["f4"] = &scancode{[]string{"3e"}, []string{"be"}} 69 sMap["f5"] = &scancode{[]string{"3f"}, []string{"bf"}} 70 sMap["f6"] = &scancode{[]string{"40"}, []string{"c0"}} 71 sMap["f7"] = &scancode{[]string{"41"}, []string{"c1"}} 72 sMap["f8"] = &scancode{[]string{"42"}, []string{"c2"}} 73 sMap["f9"] = &scancode{[]string{"43"}, []string{"c3"}} 74 sMap["f10"] = &scancode{[]string{"44"}, []string{"c4"}} 75 sMap["f11"] = &scancode{[]string{"57"}, []string{"d7"}} 76 sMap["f12"] = &scancode{[]string{"58"}, []string{"d8"}} 77 sMap["home"] = &scancode{[]string{"e0", "47"}, []string{"e0", "c7"}} 78 sMap["insert"] = &scancode{[]string{"e0", "52"}, []string{"e0", "d2"}} 79 sMap["left"] = &scancode{[]string{"e0", "4b"}, []string{"e0", "cb"}} 80 sMap["leftalt"] = &scancode{[]string{"38"}, []string{"b8"}} 81 sMap["leftctrl"] = &scancode{[]string{"1d"}, []string{"9d"}} 82 sMap["leftshift"] = &scancode{[]string{"2a"}, []string{"aa"}} 83 sMap["leftsuper"] = &scancode{[]string{"e0", "5b"}, []string{"e0", "db"}} 84 sMap["menu"] = &scancode{[]string{"e0", "5d"}, []string{"e0", "dd"}} 85 sMap["pagedown"] = &scancode{[]string{"e0", "51"}, []string{"e0", "d1"}} 86 sMap["pageup"] = &scancode{[]string{"e0", "49"}, []string{"e0", "c9"}} 87 sMap["return"] = &scancode{[]string{"1c"}, []string{"9c"}} 88 sMap["right"] = &scancode{[]string{"e0", "4d"}, []string{"e0", "cd"}} 89 sMap["rightalt"] = &scancode{[]string{"e0", "38"}, []string{"e0", "b8"}} 90 sMap["rightctrl"] = &scancode{[]string{"e0", "1d"}, []string{"e0", "9d"}} 91 sMap["rightshift"] = &scancode{[]string{"36"}, []string{"b6"}} 92 sMap["rightsuper"] = &scancode{[]string{"e0", "5c"}, []string{"e0", "dc"}} 93 sMap["spacebar"] = &scancode{[]string{"39"}, []string{"b9"}} 94 sMap["tab"] = &scancode{[]string{"0f"}, []string{"8f"}} 95 sMap["up"] = &scancode{[]string{"e0", "48"}, []string{"e0", "c8"}} 96 97 scancodeIndex := make(map[string]byte) 98 scancodeIndex["1234567890-="] = 0x02 99 scancodeIndex["!@#$%^&*()_+"] = 0x02 100 scancodeIndex["qwertyuiop[]"] = 0x10 101 scancodeIndex["QWERTYUIOP{}"] = 0x10 102 scancodeIndex["asdfghjkl;'`"] = 0x1e 103 scancodeIndex[`ASDFGHJKL:"~`] = 0x1e 104 scancodeIndex[`\zxcvbnm,./`] = 0x2b 105 scancodeIndex["|ZXCVBNM<>?"] = 0x2b 106 scancodeIndex[" "] = 0x39 107 108 scancodeMap := make(map[rune]byte) 109 for chars, start := range scancodeIndex { 110 var i byte = 0 111 for len(chars) > 0 { 112 r, size := utf8.DecodeRuneInString(chars) 113 chars = chars[size:] 114 scancodeMap[r] = start + i 115 i += 1 116 } 117 } 118 119 return &pcXTDriver{ 120 interval: keyInterval, 121 sendImpl: send, 122 specialMap: sMap, 123 scancodeMap: scancodeMap, 124 scancodeChunkSize: chunkSize, 125 } 126 } 127 128 // Flush send all scanecodes. 129 func (d *pcXTDriver) Flush() error { 130 defer func() { 131 d.buffer = nil 132 }() 133 sc, err := chunkScanCodes(d.buffer, d.scancodeChunkSize) 134 if err != nil { 135 return err 136 } 137 for _, b := range sc { 138 if err := d.sendImpl(b); err != nil { 139 return err 140 } 141 time.Sleep(d.interval) 142 } 143 return nil 144 } 145 146 func (d *pcXTDriver) SendKey(key rune, action KeyAction) error { 147 keyShift := unicode.IsUpper(key) || strings.ContainsRune(shiftedChars, key) 148 149 var sc []string 150 151 if action&(KeyOn|KeyPress) != 0 { 152 scInt := d.scancodeMap[key] 153 if keyShift { 154 sc = append(sc, "2a") 155 } 156 sc = append(sc, fmt.Sprintf("%02x", scInt)) 157 } 158 159 if action&(KeyOff|KeyPress) != 0 { 160 scInt := d.scancodeMap[key] + 0x80 161 if keyShift { 162 sc = append(sc, "aa") 163 } 164 sc = append(sc, fmt.Sprintf("%02x", scInt)) 165 } 166 167 log.Printf("Sending char '%c', code '%s', shift %v", 168 key, strings.Join(sc, ""), keyShift) 169 170 d.send(sc) 171 return nil 172 } 173 174 func (d *pcXTDriver) SendSpecial(special string, action KeyAction) error { 175 keyCode, ok := d.specialMap[special] 176 if !ok { 177 return fmt.Errorf("special %s not found.", special) 178 } 179 log.Printf("Special code '%s' '<%s>' found, replacing with: %v", action.String(), special, keyCode) 180 181 switch action { 182 case KeyOn: 183 d.send(keyCode.make) 184 case KeyOff: 185 d.send(keyCode.break_) 186 case KeyPress: 187 d.send(keyCode.makeBreak()) 188 } 189 return nil 190 } 191 192 // send stores the codes in an internal buffer. Use Flush to send them. 193 func (d *pcXTDriver) send(codes []string) { 194 d.buffer = append(d.buffer, codes) 195 } 196 197 func chunkScanCodes(sc [][]string, size int) (out [][]string, err error) { 198 var running []string 199 for _, codes := range sc { 200 if size > 0 { 201 if len(codes) > size { 202 return nil, fmt.Errorf("chunkScanCodes: size cannot be smaller than sc width.") 203 } 204 if len(running)+len(codes) > size { 205 out = append(out, running) 206 running = nil 207 } 208 } 209 running = append(running, codes...) 210 } 211 if running != nil { 212 out = append(out, running) 213 } 214 return 215 }