main
1// Package rfb implements the RFB (Remote Framebuffer) protocol for VNC.
2package rfb
3
4import (
5 "encoding/binary"
6 "fmt"
7)
8
9// Server-to-client message types
10const (
11 MsgTypeFramebufferUpdate = 0
12 MsgTypeSetColourMapEntries = 1
13 MsgTypeBell = 2
14 MsgTypeServerCutText = 3
15)
16
17// Client-to-server message types
18const (
19 MsgTypeSetPixelFormat = 0
20 MsgTypeSetEncodings = 2
21 MsgTypeFramebufferUpdateRequest = 3
22 MsgTypeKeyEvent = 4
23 MsgTypePointerEvent = 5
24 MsgTypeClientCutText = 6
25)
26
27// Common VNC key codes (X11 keysyms)
28const (
29 KeyBackspace = 0xff08
30 KeyTab = 0xff09
31 KeyReturn = 0xff0d
32 KeyEscape = 0xff1b
33 KeyInsert = 0xff63
34 KeyDelete = 0xffff
35 KeyHome = 0xff50
36 KeyEnd = 0xff57
37 KeyPageUp = 0xff55
38 KeyPageDown = 0xff56
39 KeyLeft = 0xff51
40 KeyUp = 0xff52
41 KeyRight = 0xff53
42 KeyDown = 0xff54
43 KeyF1 = 0xffbe
44 KeyF2 = 0xffbf
45 KeyF3 = 0xffc0
46 KeyF4 = 0xffc1
47 KeyF5 = 0xffc2
48 KeyF6 = 0xffc3
49 KeyF7 = 0xffc4
50 KeyF8 = 0xffc5
51 KeyF9 = 0xffc6
52 KeyF10 = 0xffc7
53 KeyF11 = 0xffc8
54 KeyF12 = 0xffc9
55 KeyShiftL = 0xffe1
56 KeyShiftR = 0xffe2
57 KeyControlL = 0xffe3
58 KeyControlR = 0xffe4
59 KeyMetaL = 0xffe7
60 KeyMetaR = 0xffe8
61 KeyAltL = 0xffe9
62 KeyAltR = 0xffea
63 KeySuperL = 0xffeb
64 KeySuperR = 0xffec
65)
66
67// Standard encodings
68const (
69 EncodingRaw = 0
70 EncodingCopyRect = 1
71 EncodingRRE = 2
72 EncodingHextile = 5
73 EncodingZRLE = 16
74 EncodingTight = 7
75 EncodingTightPNG = -260
76
77 // Pseudo-encodings
78 EncodingCursor = -239
79 EncodingDesktopSize = -223
80 EncodingLastRect = -224
81 EncodingDesktopName = -307
82 EncodingExtendedDesktopSize = -308
83)
84
85// DefaultEncodings returns the default encoding list for noVNC compatibility.
86func DefaultEncodings() []int32 {
87 return []int32{
88 EncodingTight,
89 EncodingZRLE,
90 EncodingHextile,
91 EncodingCopyRect,
92 EncodingRaw,
93 EncodingDesktopSize,
94 EncodingLastRect,
95 EncodingCursor,
96 }
97}
98
99// EncodeSetEncodings creates a SetEncodings message.
100func EncodeSetEncodings(encodings []int32) []byte {
101 msg := make([]byte, 4+4*len(encodings))
102 msg[0] = MsgTypeSetEncodings
103 // msg[1] padding
104 binary.BigEndian.PutUint16(msg[2:4], uint16(len(encodings)))
105 for i, enc := range encodings {
106 binary.BigEndian.PutUint32(msg[4+i*4:], uint32(enc))
107 }
108 return msg
109}
110
111// EncodeFramebufferUpdateRequest creates a FramebufferUpdateRequest message.
112func EncodeFramebufferUpdateRequest(incremental bool, x, y, width, height uint16) []byte {
113 msg := make([]byte, 10)
114 msg[0] = MsgTypeFramebufferUpdateRequest
115 if incremental {
116 msg[1] = 1
117 }
118 binary.BigEndian.PutUint16(msg[2:4], x)
119 binary.BigEndian.PutUint16(msg[4:6], y)
120 binary.BigEndian.PutUint16(msg[6:8], width)
121 binary.BigEndian.PutUint16(msg[8:10], height)
122 return msg
123}
124
125// EncodeKeyEvent creates a KeyEvent message.
126func EncodeKeyEvent(down bool, key uint32) []byte {
127 msg := make([]byte, 8)
128 msg[0] = MsgTypeKeyEvent
129 if down {
130 msg[1] = 1
131 }
132 // msg[2:4] padding
133 binary.BigEndian.PutUint32(msg[4:8], key)
134 return msg
135}
136
137// EncodePointerEvent creates a PointerEvent message.
138func EncodePointerEvent(buttonMask uint8, x, y uint16) []byte {
139 msg := make([]byte, 6)
140 msg[0] = MsgTypePointerEvent
141 msg[1] = buttonMask
142 binary.BigEndian.PutUint16(msg[2:4], x)
143 binary.BigEndian.PutUint16(msg[4:6], y)
144 return msg
145}
146
147// EncodeClientCutText creates a ClientCutText message.
148func EncodeClientCutText(text string) []byte {
149 msg := make([]byte, 8+len(text))
150 msg[0] = MsgTypeClientCutText
151 // msg[1:4] padding
152 binary.BigEndian.PutUint32(msg[4:8], uint32(len(text)))
153 copy(msg[8:], text)
154 return msg
155}
156
157// ServerMessage represents a parsed server-to-client message.
158type ServerMessage struct {
159 Type uint8
160 Raw []byte
161}
162
163// FramebufferUpdate contains parsed framebuffer update data.
164type FramebufferUpdate struct {
165 NumRects uint16
166 Raw []byte
167}
168
169// ServerCutText contains clipboard data from the server.
170type ServerCutText struct {
171 Text string
172}
173
174// ParseServerMessage parses a raw server message.
175func ParseServerMessage(data []byte) (*ServerMessage, error) {
176 if len(data) == 0 {
177 return nil, fmt.Errorf("empty message")
178 }
179 return &ServerMessage{
180 Type: data[0],
181 Raw: data,
182 }, nil
183}
184
185// ParseFramebufferUpdate parses a FramebufferUpdate message.
186func ParseFramebufferUpdate(data []byte) (*FramebufferUpdate, error) {
187 if len(data) < 4 {
188 return nil, fmt.Errorf("framebuffer update too short: %d", len(data))
189 }
190 return &FramebufferUpdate{
191 NumRects: binary.BigEndian.Uint16(data[2:4]),
192 Raw: data,
193 }, nil
194}
195
196// ParseServerCutText parses a ServerCutText message.
197func ParseServerCutText(data []byte) (*ServerCutText, error) {
198 if len(data) < 8 {
199 return nil, fmt.Errorf("server cut text too short: %d", len(data))
200 }
201 textLen := binary.BigEndian.Uint32(data[4:8])
202 if len(data) < 8+int(textLen) {
203 return nil, fmt.Errorf("server cut text incomplete: need %d, got %d", 8+textLen, len(data))
204 }
205 return &ServerCutText{
206 Text: string(data[8 : 8+textLen]),
207 }, nil
208}
209
210// RuneToKeysym converts a rune to a VNC keysym.
211func RuneToKeysym(r rune) uint32 {
212 switch r {
213 case '\n':
214 return KeyReturn
215 case '\t':
216 return KeyTab
217 case '\b':
218 return KeyBackspace
219 case 0x1b:
220 return KeyEscape
221 default:
222 // For ASCII characters, the keysym equals the Unicode code point
223 return uint32(r)
224 }
225}