File: vnc_driver.go

package info (click to toggle)
packer 1.3.4%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 8,324 kB
  • sloc: python: 619; sh: 557; makefile: 111
file content (151 lines) | stat: -rw-r--r-- 3,348 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package bootcommand

import (
	"fmt"
	"log"
	"os"
	"strings"
	"time"
	"unicode"

	"github.com/hashicorp/packer/common"
)

const KeyLeftShift uint32 = 0xFFE1

type VNCKeyEvent interface {
	KeyEvent(uint32, bool) error
}

type vncDriver struct {
	c          VNCKeyEvent
	interval   time.Duration
	specialMap map[string]uint32
	// keyEvent can set this error which will prevent it from continuing
	err error
}

func NewVNCDriver(c VNCKeyEvent, interval time.Duration) *vncDriver {
	// 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
	}

	// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
	sMap := make(map[string]uint32)
	sMap["bs"] = 0xFF08
	sMap["del"] = 0xFFFF
	sMap["down"] = 0xFF54
	sMap["end"] = 0xFF57
	sMap["enter"] = 0xFF0D
	sMap["esc"] = 0xFF1B
	sMap["f1"] = 0xFFBE
	sMap["f2"] = 0xFFBF
	sMap["f3"] = 0xFFC0
	sMap["f4"] = 0xFFC1
	sMap["f5"] = 0xFFC2
	sMap["f6"] = 0xFFC3
	sMap["f7"] = 0xFFC4
	sMap["f8"] = 0xFFC5
	sMap["f9"] = 0xFFC6
	sMap["f10"] = 0xFFC7
	sMap["f11"] = 0xFFC8
	sMap["f12"] = 0xFFC9
	sMap["home"] = 0xFF50
	sMap["insert"] = 0xFF63
	sMap["left"] = 0xFF51
	sMap["leftalt"] = 0xFFE9
	sMap["leftctrl"] = 0xFFE3
	sMap["leftshift"] = 0xFFE1
	sMap["leftsuper"] = 0xFFEB
	sMap["menu"] = 0xFF67
	sMap["pagedown"] = 0xFF56
	sMap["pageup"] = 0xFF55
	sMap["return"] = 0xFF0D
	sMap["right"] = 0xFF53
	sMap["rightalt"] = 0xFFEA
	sMap["rightctrl"] = 0xFFE4
	sMap["rightshift"] = 0xFFE2
	sMap["rightsuper"] = 0xFFEC
	sMap["spacebar"] = 0x020
	sMap["tab"] = 0xFF09
	sMap["up"] = 0xFF52

	return &vncDriver{
		c:          c,
		interval:   keyInterval,
		specialMap: sMap,
	}
}

func (d *vncDriver) keyEvent(k uint32, down bool) error {
	if d.err != nil {
		return nil
	}
	if err := d.c.KeyEvent(k, down); err != nil {
		d.err = err
		return err
	}
	time.Sleep(d.interval)
	return nil
}

// Flush does nothing here
func (d *vncDriver) Flush() error {
	return nil
}

func (d *vncDriver) SendKey(key rune, action KeyAction) error {
	keyShift := unicode.IsUpper(key) || strings.ContainsRune(shiftedChars, key)
	keyCode := uint32(key)
	log.Printf("Sending char '%c', code 0x%X, shift %v", key, keyCode, keyShift)

	switch action {
	case KeyOn:
		if keyShift {
			d.keyEvent(KeyLeftShift, true)
		}
		d.keyEvent(keyCode, true)
	case KeyOff:
		if keyShift {
			d.keyEvent(KeyLeftShift, false)
		}
		d.keyEvent(keyCode, false)
	case KeyPress:
		if keyShift {
			d.keyEvent(KeyLeftShift, true)
		}
		d.keyEvent(keyCode, true)
		d.keyEvent(keyCode, false)
		if keyShift {
			d.keyEvent(KeyLeftShift, false)
		}
	}
	return d.err
}

func (d *vncDriver) 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: 0x%X", special, keyCode)

	switch action {
	case KeyOn:
		d.keyEvent(keyCode, true)
	case KeyOff:
		d.keyEvent(keyCode, false)
	case KeyPress:
		d.keyEvent(keyCode, true)
		d.keyEvent(keyCode, false)
	}

	return d.err
}