not working example
Signed-off-by: Vasiliy Tolstov <v.tolstov@selfip.ru>
This commit is contained in:
parent
5139d1dd82
commit
a45bca15e3
2
.gitignore
vendored
2
.gitignore
vendored
@ -12,3 +12,5 @@
|
|||||||
|
|
||||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||||
.glide/
|
.glide/
|
||||||
|
example/client/client
|
||||||
|
example/server/server
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,5 +1,7 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2013 Mitchell Hashimoto
|
||||||
|
Copyright (c) 2016-2017 Kate Ward
|
||||||
Copyright (c) 2017 Vasiliy Tolstov
|
Copyright (c) 2017 Vasiliy Tolstov
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
46
button_string.go
Normal file
46
button_string.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Code generated by "stringer -type=Button"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package vnc
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const (
|
||||||
|
_Button_name_0 = "BtnNoneBtnLeftBtnMiddle"
|
||||||
|
_Button_name_1 = "BtnRight"
|
||||||
|
_Button_name_2 = "BtnFour"
|
||||||
|
_Button_name_3 = "BtnFive"
|
||||||
|
_Button_name_4 = "BtnSix"
|
||||||
|
_Button_name_5 = "BtnSeven"
|
||||||
|
_Button_name_6 = "BtnEight"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_Button_index_0 = [...]uint8{0, 7, 14, 23}
|
||||||
|
_Button_index_1 = [...]uint8{0, 8}
|
||||||
|
_Button_index_2 = [...]uint8{0, 7}
|
||||||
|
_Button_index_3 = [...]uint8{0, 7}
|
||||||
|
_Button_index_4 = [...]uint8{0, 6}
|
||||||
|
_Button_index_5 = [...]uint8{0, 8}
|
||||||
|
_Button_index_6 = [...]uint8{0, 8}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i Button) String() string {
|
||||||
|
switch {
|
||||||
|
case 0 <= i && i <= 2:
|
||||||
|
return _Button_name_0[_Button_index_0[i]:_Button_index_0[i+1]]
|
||||||
|
case i == 4:
|
||||||
|
return _Button_name_1
|
||||||
|
case i == 8:
|
||||||
|
return _Button_name_2
|
||||||
|
case i == 16:
|
||||||
|
return _Button_name_3
|
||||||
|
case i == 32:
|
||||||
|
return _Button_name_4
|
||||||
|
case i == 64:
|
||||||
|
return _Button_name_5
|
||||||
|
case i == 128:
|
||||||
|
return _Button_name_6
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("Button(%d)", i)
|
||||||
|
}
|
||||||
|
}
|
23
buttons.go
Normal file
23
buttons.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package vnc
|
||||||
|
|
||||||
|
// Button represents a mask of pointer presses/releases.
|
||||||
|
type Button uint8
|
||||||
|
|
||||||
|
//go:generate stringer -type=Button
|
||||||
|
|
||||||
|
// All available button mask components.
|
||||||
|
const (
|
||||||
|
BtnLeft Button = 1 << iota
|
||||||
|
BtnMiddle
|
||||||
|
BtnRight
|
||||||
|
BtnFour
|
||||||
|
BtnFive
|
||||||
|
BtnSix
|
||||||
|
BtnSeven
|
||||||
|
BtnEight
|
||||||
|
BtnNone Button = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
func Mask(button Button) uint8 {
|
||||||
|
return uint8(button)
|
||||||
|
}
|
453
client.go
Normal file
453
client.go
Normal file
@ -0,0 +1,453 @@
|
|||||||
|
package vnc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DefaultServerMessages = []ServerMessage{
|
||||||
|
&FramebufferUpdate{},
|
||||||
|
&SetColorMapEntries{},
|
||||||
|
&Bell{},
|
||||||
|
&ServerCutText{},
|
||||||
|
}
|
||||||
|
|
||||||
|
func Connect(ctx context.Context, c net.Conn, cfg *ClientConfig) (*ClientConn, error) {
|
||||||
|
conn, err := NewClientConn(c, cfg)
|
||||||
|
if err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := cfg.VersionHandler(cfg, conn); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := cfg.SecurityHandler(cfg, conn); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := cfg.ClientInitHandler(cfg, conn); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := cfg.ServerInitHandler(cfg, conn); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
// Send client-to-server messages.
|
||||||
|
encs := conn.encodings
|
||||||
|
if err := conn.SetEncodings(encs); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, Errorf("failure calling SetEncodings; %s", err)
|
||||||
|
}
|
||||||
|
pf := conn.pixelFormat
|
||||||
|
if err := conn.SetPixelFormat(pf); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
return nil, Errorf("failure calling SetPixelFormat; %s", err)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Conn = (*ClientConn)(nil)
|
||||||
|
|
||||||
|
func (c *ClientConn) Flush() error {
|
||||||
|
return c.bw.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) Close() error {
|
||||||
|
return c.c.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) Read(buf []byte) (int, error) {
|
||||||
|
return c.br.Read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) Write(buf []byte) (int, error) {
|
||||||
|
return c.bw.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) ColorMap() *ColorMap {
|
||||||
|
return c.colorMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientConn) SetColorMap(cm *ColorMap) {
|
||||||
|
c.colorMap = cm
|
||||||
|
}
|
||||||
|
func (c *ClientConn) DesktopName() string {
|
||||||
|
return c.desktopName
|
||||||
|
}
|
||||||
|
func (c *ClientConn) PixelFormat() *PixelFormat {
|
||||||
|
return c.pixelFormat
|
||||||
|
}
|
||||||
|
func (c *ClientConn) Encodings() []Encoding {
|
||||||
|
return c.encodings
|
||||||
|
}
|
||||||
|
func (c *ClientConn) Width() uint16 {
|
||||||
|
return c.fbWidth
|
||||||
|
}
|
||||||
|
func (c *ClientConn) Height() uint16 {
|
||||||
|
return c.fbHeight
|
||||||
|
}
|
||||||
|
func (c *ClientConn) Protocol() string {
|
||||||
|
return c.protocol
|
||||||
|
}
|
||||||
|
func (c *ClientConn) SetWidth(w uint16) {
|
||||||
|
c.fbWidth = w
|
||||||
|
}
|
||||||
|
func (c *ClientConn) SetHeight(h uint16) {
|
||||||
|
c.fbHeight = h
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ClientConn type holds client connection information.
|
||||||
|
type ClientConn struct {
|
||||||
|
c net.Conn
|
||||||
|
br *bufio.Reader
|
||||||
|
bw *bufio.Writer
|
||||||
|
cfg *ClientConfig
|
||||||
|
protocol string
|
||||||
|
|
||||||
|
// If the pixel format uses a color map, then this is the color
|
||||||
|
// map that is used. This should not be modified directly, since
|
||||||
|
// the data comes from the server.
|
||||||
|
// Definition in §5 - Representation of Pixel Data.
|
||||||
|
colorMap *ColorMap
|
||||||
|
|
||||||
|
// Name associated with the desktop, sent from the server.
|
||||||
|
desktopName string
|
||||||
|
|
||||||
|
// Encodings supported by the client. This should not be modified
|
||||||
|
// directly. Instead, SetEncodings() should be used.
|
||||||
|
encodings []Encoding
|
||||||
|
|
||||||
|
// Height of the frame buffer in pixels, sent from the server.
|
||||||
|
fbHeight uint16
|
||||||
|
|
||||||
|
// Width of the frame buffer in pixels, sent from the server.
|
||||||
|
fbWidth uint16
|
||||||
|
|
||||||
|
// The pixel format associated with the connection. This shouldn't
|
||||||
|
// be modified. If you wish to set a new pixel format, use the
|
||||||
|
// SetPixelFormat method.
|
||||||
|
pixelFormat *PixelFormat
|
||||||
|
|
||||||
|
quit chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientConn(c net.Conn, cfg *ClientConfig) (*ClientConn, error) {
|
||||||
|
if cfg.ServerMessages == nil {
|
||||||
|
return nil, fmt.Errorf("ServerMessages cannel is nil")
|
||||||
|
}
|
||||||
|
if len(cfg.Encodings) == 0 {
|
||||||
|
return nil, fmt.Errorf("client can't handle encodings")
|
||||||
|
}
|
||||||
|
return &ClientConn{
|
||||||
|
c: c,
|
||||||
|
cfg: cfg,
|
||||||
|
br: bufio.NewReader(c),
|
||||||
|
bw: bufio.NewWriter(c),
|
||||||
|
encodings: cfg.Encodings,
|
||||||
|
pixelFormat: cfg.PixelFormat,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientMessage represents a Client-to-Server RFB message type.
|
||||||
|
type ClientMessageType uint8
|
||||||
|
|
||||||
|
//go:generate stringer -type=ClientMessageType
|
||||||
|
|
||||||
|
// Client-to-Server message types.
|
||||||
|
const (
|
||||||
|
SetPixelFormatMsgType ClientMessageType = iota
|
||||||
|
_
|
||||||
|
SetEncodingsMsgType
|
||||||
|
FramebufferUpdateRequestMsgType
|
||||||
|
KeyEventMsgType
|
||||||
|
PointerEventMsgType
|
||||||
|
ClientCutTextMsgType
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetPixelFormat holds the wire format message.
|
||||||
|
type SetPixelFormat struct {
|
||||||
|
MsgType ClientMessageType
|
||||||
|
_ [3]byte // padding
|
||||||
|
PF PixelFormat // pixel-format
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SetPixelFormat) Type() ClientMessageType {
|
||||||
|
return msg.MsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SetPixelFormat) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.MsgType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pf := c.PixelFormat()
|
||||||
|
// Invalidate the color map.
|
||||||
|
if pf.TrueColor != 1 {
|
||||||
|
c.SetColorMap(&ColorMap{})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SetPixelFormat) Read(c Conn) error {
|
||||||
|
return binary.Read(c, binary.BigEndian, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEncodings holds the wire format message, sans encoding-type field.
|
||||||
|
type SetEncodings struct {
|
||||||
|
MsgType ClientMessageType
|
||||||
|
_ [1]byte // padding
|
||||||
|
EncNum uint16 // number-of-encodings
|
||||||
|
Encodings []Encoding
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SetEncodings) Type() ClientMessageType {
|
||||||
|
return msg.MsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SetEncodings) Read(c Conn) error {
|
||||||
|
if err := binary.Read(c, binary.BigEndian, msg.MsgType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Read(c, binary.BigEndian, msg.EncNum); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var enc Encoding
|
||||||
|
for i := uint16(0); i < msg.EncNum; i++ {
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &enc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.Encodings = append(msg.Encodings, enc)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SetEncodings) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.MsgType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Write(c, binary.BigEndian, pad); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if uint16(len(msg.Encodings)) > msg.EncNum {
|
||||||
|
msg.EncNum = uint16(len(msg.Encodings))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.EncNum); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, enc := range msg.Encodings {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, enc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FramebufferUpdateRequest holds the wire format message.
|
||||||
|
type FramebufferUpdateRequest struct {
|
||||||
|
MsgType ClientMessageType
|
||||||
|
Inc uint8 // incremental
|
||||||
|
X, Y uint16 // x-, y-position
|
||||||
|
Width, Height uint16 // width, height
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *FramebufferUpdateRequest) Type() ClientMessageType {
|
||||||
|
return msg.MsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *FramebufferUpdateRequest) Read(c Conn) error {
|
||||||
|
return binary.Read(c, binary.BigEndian, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *FramebufferUpdateRequest) Write(c Conn) error {
|
||||||
|
return binary.Write(c, binary.BigEndian, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyEvent holds the wire format message.
|
||||||
|
type KeyEvent struct {
|
||||||
|
MsgType ClientMessageType // message-type
|
||||||
|
Down uint8 // down-flag
|
||||||
|
_ [2]byte // padding
|
||||||
|
Key Key // key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *KeyEvent) Type() ClientMessageType {
|
||||||
|
return msg.MsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *KeyEvent) Read(c Conn) error {
|
||||||
|
return binary.Read(c, binary.BigEndian, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *KeyEvent) Write(c Conn) error {
|
||||||
|
return binary.Write(c, binary.BigEndian, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PointerEventMessage holds the wire format message.
|
||||||
|
type PointerEvent struct {
|
||||||
|
MsgType ClientMessageType // message-type
|
||||||
|
Mask uint8 // button-mask
|
||||||
|
X, Y uint16 // x-, y-position
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *PointerEvent) Type() ClientMessageType {
|
||||||
|
return msg.MsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *PointerEvent) Read(c Conn) error {
|
||||||
|
return binary.Read(c, binary.BigEndian, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *PointerEvent) Write(c Conn) error {
|
||||||
|
return binary.Write(c, binary.BigEndian, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientCutText holds the wire format message, sans the text field.
|
||||||
|
type ClientCutText struct {
|
||||||
|
MsgType ClientMessageType // message-type
|
||||||
|
_ [3]byte // padding
|
||||||
|
Length uint32 // length
|
||||||
|
Text []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *ClientCutText) Type() ClientMessageType {
|
||||||
|
return msg.MsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *ClientCutText) Read(c Conn) error {
|
||||||
|
if err := binary.Read(c, binary.BigEndian, msg.MsgType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pad [3]byte
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Read(c, binary.BigEndian, msg.Length); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
text := make([]uint8, msg.Length)
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &text); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.Text = text
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *ClientCutText) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.MsgType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pad [3]byte
|
||||||
|
if err := binary.Write(c, binary.BigEndian, &pad); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if uint32(len(msg.Text)) > msg.Length {
|
||||||
|
msg.Length = uint32(len(msg.Text))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Length); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Text); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndHandle listens to a VNC server and handles server messages.
|
||||||
|
func (c *ClientConn) Handle() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
serverMessages := make(map[ServerMessageType]ServerMessage)
|
||||||
|
for _, m := range c.cfg.ServerMessages {
|
||||||
|
serverMessages[m.Type()] = m
|
||||||
|
}
|
||||||
|
|
||||||
|
clientLoop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case msg := <-c.cfg.ServerMessageCh:
|
||||||
|
if err = msg.Write(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case <-c.quit:
|
||||||
|
break clientLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serverLoop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-c.quit:
|
||||||
|
break serverLoop
|
||||||
|
default:
|
||||||
|
var messageType ServerMessageType
|
||||||
|
if err = binary.Read(c, binary.BigEndian, &messageType); err != nil {
|
||||||
|
break serverLoop
|
||||||
|
}
|
||||||
|
msg, ok := serverMessages[messageType]
|
||||||
|
if !ok {
|
||||||
|
break serverLoop
|
||||||
|
}
|
||||||
|
if err = msg.Read(c); err != nil {
|
||||||
|
break serverLoop
|
||||||
|
}
|
||||||
|
if c.cfg.ServerMessageCh == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.cfg.ServerMessageCh <- msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientHandler func(*ClientConfig, Conn) error
|
||||||
|
|
||||||
|
// A ClientConfig structure is used to configure a ClientConn. After
|
||||||
|
// one has been passed to initialize a connection, it must not be modified.
|
||||||
|
type ClientConfig struct {
|
||||||
|
VersionHandler ClientHandler
|
||||||
|
SecurityHandler ClientHandler
|
||||||
|
SecurityHandlers []ClientHandler
|
||||||
|
ClientInitHandler ClientHandler
|
||||||
|
ServerInitHandler ClientHandler
|
||||||
|
Encodings []Encoding
|
||||||
|
PixelFormat *PixelFormat
|
||||||
|
ColorMap *ColorMap
|
||||||
|
ClientMessageCh chan ClientMessage
|
||||||
|
ServerMessageCh chan ServerMessage
|
||||||
|
Exclusive bool
|
||||||
|
ServerMessages []ServerMessage
|
||||||
|
}
|
27
clientmessagetype_string.go
Normal file
27
clientmessagetype_string.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Code generated by "stringer -type=ClientMessageType"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package vnc
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const (
|
||||||
|
_ClientMessageType_name_0 = "SetPixelFormatMsgType"
|
||||||
|
_ClientMessageType_name_1 = "SetEncodingsMsgTypeFramebufferUpdateRequestMsgTypeKeyEventMsgTypePointerEventMsgTypeClientCutTextMsgType"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ClientMessageType_index_0 = [...]uint8{0, 21}
|
||||||
|
_ClientMessageType_index_1 = [...]uint8{0, 19, 50, 65, 84, 104}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i ClientMessageType) String() string {
|
||||||
|
switch {
|
||||||
|
case i == 0:
|
||||||
|
return _ClientMessageType_name_0
|
||||||
|
case 2 <= i && i <= 6:
|
||||||
|
i -= 2
|
||||||
|
return _ClientMessageType_name_1[_ClientMessageType_index_1[i]:_ClientMessageType_index_1[i+1]]
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("ClientMessageType(%d)", i)
|
||||||
|
}
|
||||||
|
}
|
18
conn.go
Normal file
18
conn.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package vnc
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
type Conn interface {
|
||||||
|
io.ReadWriteCloser
|
||||||
|
Protocol() string
|
||||||
|
PixelFormat() *PixelFormat
|
||||||
|
ColorMap() *ColorMap
|
||||||
|
SetColorMap(*ColorMap)
|
||||||
|
Encodings() []Encoding
|
||||||
|
Width() uint16
|
||||||
|
Height() uint16
|
||||||
|
SetWidth(uint16)
|
||||||
|
SetHeight(uint16)
|
||||||
|
DesktopName() string
|
||||||
|
Flush() error
|
||||||
|
}
|
88
encoding.go
Normal file
88
encoding.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package vnc
|
||||||
|
|
||||||
|
// EncodingType represents a known VNC encoding type.
|
||||||
|
type EncodingType int32
|
||||||
|
|
||||||
|
//go:generate stringer -type=EncodingType
|
||||||
|
|
||||||
|
const (
|
||||||
|
EncRaw EncodingType = 0
|
||||||
|
EncCopyRect EncodingType = 1
|
||||||
|
EncRRE EncodingType = 2
|
||||||
|
EncHextile EncodingType = 5
|
||||||
|
EncTRLE EncodingType = 15
|
||||||
|
EncZRLE EncodingType = 16
|
||||||
|
EncColorPseudo EncodingType = -239
|
||||||
|
EncDesktopSizePseudo EncodingType = -223
|
||||||
|
)
|
||||||
|
|
||||||
|
type Encoding interface {
|
||||||
|
Type() EncodingType
|
||||||
|
Read(Conn, *Rectangle) error
|
||||||
|
Write(Conn, *Rectangle) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type RawEncoding struct {
|
||||||
|
Colors []Color
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *RawEncoding) Write(c Conn, rect *Rectangle) error {
|
||||||
|
/*
|
||||||
|
for _, cl := range enc.Colors {
|
||||||
|
bytes, err := cl.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(c, binary.BigEndian, bytes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read implements the Encoding interface.
|
||||||
|
func (enc *RawEncoding) Read(c Conn, rect *Rectangle) error {
|
||||||
|
/*
|
||||||
|
var buf bytes.Buffer
|
||||||
|
pf := c.PixelFormat()
|
||||||
|
cm := c.ColorMap()
|
||||||
|
bytesPerPixel := int(pf.BPP / 8)
|
||||||
|
n := rect.Area() * bytesPerPixel
|
||||||
|
if err := c.receiveN(&buf, n); err != nil {
|
||||||
|
return fmt.Errorf("unable to read rectangle with raw encoding: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
colors := make([]Color, rect.Area())
|
||||||
|
for y := uint16(0); y < rect.Height; y++ {
|
||||||
|
for x := uint16(0); x < rect.Width; x++ {
|
||||||
|
color := NewColor(pf, cm)
|
||||||
|
if err := color.Unmarshal(buf.Next(bytesPerPixel)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
colors[int(y)*int(rect.Width)+int(x)] = *color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &RawEncoding{colors}, nil
|
||||||
|
*/
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*RawEncoding) Type() EncodingType { return EncRaw }
|
||||||
|
|
||||||
|
// DesktopSizePseudoEncoding represents a desktop size message from the server.
|
||||||
|
type DesktopSizePseudoEncoding struct{}
|
||||||
|
|
||||||
|
// Read implements the Encoding interface.
|
||||||
|
func (*DesktopSizePseudoEncoding) Read(c Conn, rect *Rectangle) error {
|
||||||
|
c.SetWidth(rect.Width)
|
||||||
|
c.SetHeight(rect.Height)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (enc *DesktopSizePseudoEncoding) Write(c *ServerConn, rect *Rectangle) error {
|
||||||
|
return nil
|
||||||
|
}
|
39
encodingtype_string.go
Normal file
39
encodingtype_string.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Code generated by "stringer -type=EncodingType"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package vnc
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const (
|
||||||
|
_EncodingType_name_0 = "EncColorPseudo"
|
||||||
|
_EncodingType_name_1 = "EncDesktopSizePseudo"
|
||||||
|
_EncodingType_name_2 = "EncRawEncCopyRectEncRRE"
|
||||||
|
_EncodingType_name_3 = "EncHextile"
|
||||||
|
_EncodingType_name_4 = "EncTRLEEncZRLE"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_EncodingType_index_0 = [...]uint8{0, 14}
|
||||||
|
_EncodingType_index_1 = [...]uint8{0, 20}
|
||||||
|
_EncodingType_index_2 = [...]uint8{0, 6, 17, 23}
|
||||||
|
_EncodingType_index_3 = [...]uint8{0, 10}
|
||||||
|
_EncodingType_index_4 = [...]uint8{0, 7, 14}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i EncodingType) String() string {
|
||||||
|
switch {
|
||||||
|
case i == -239:
|
||||||
|
return _EncodingType_name_0
|
||||||
|
case i == -223:
|
||||||
|
return _EncodingType_name_1
|
||||||
|
case 0 <= i && i <= 2:
|
||||||
|
return _EncodingType_name_2[_EncodingType_index_2[i]:_EncodingType_index_2[i+1]]
|
||||||
|
case i == 5:
|
||||||
|
return _EncodingType_name_3
|
||||||
|
case 15 <= i && i <= 16:
|
||||||
|
i -= 15
|
||||||
|
return _EncodingType_name_4[_EncodingType_index_4[i]:_EncodingType_index_4[i+1]]
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("EncodingType(%d)", i)
|
||||||
|
}
|
||||||
|
}
|
60
example/client/main.go
Normal file
60
example/client/main.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
vnc "github.com/kward/go-vnc"
|
||||||
|
"github.com/kward/go-vnc/logging"
|
||||||
|
"github.com/kward/go-vnc/messages"
|
||||||
|
"github.com/kward/go-vnc/rfbflags"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
logging.V(logging.FnDeclLevel)
|
||||||
|
// Establish TCP connection to VNC server.
|
||||||
|
nc, err := net.Dial("tcp", "127.0.0.1:5923")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error connecting to VNC host. %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negotiate connection with the server.
|
||||||
|
ch := make(chan vnc.ServerMessage)
|
||||||
|
vc, err := vnc.Connect(context.Background(), nc,
|
||||||
|
&vnc.ClientConfig{
|
||||||
|
Auth: []vnc.ClientAuth{&vnc.ClientAuthNone{}},
|
||||||
|
ServerMessageCh: ch,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error negotiating connection to VNC host. %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Periodically request framebuffer updates.
|
||||||
|
go func() {
|
||||||
|
w, h := vc.FramebufferWidth(), vc.FramebufferHeight()
|
||||||
|
for {
|
||||||
|
if err := vc.FramebufferUpdateRequest(rfbflags.RFBTrue, 0, 0, w, h); err != nil {
|
||||||
|
log.Printf("error requesting framebuffer update: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Listen and handle server messages.
|
||||||
|
go vc.ListenAndHandle()
|
||||||
|
|
||||||
|
// Process messages coming in on the ServerMessage channel.
|
||||||
|
for {
|
||||||
|
msg := <-ch
|
||||||
|
switch msg.Type() {
|
||||||
|
case messages.FramebufferUpdate:
|
||||||
|
log.Println("Received FramebufferUpdate message.")
|
||||||
|
default:
|
||||||
|
log.Printf("Received message type:%v msg:%v\n", msg.Type(), msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
70
example/proxy/main.go
Normal file
70
example/proxy/main.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
vnc "github.com/kward/go-vnc"
|
||||||
|
"github.com/kward/go-vnc/logging"
|
||||||
|
"github.com/kward/go-vnc/messages"
|
||||||
|
"github.com/kward/go-vnc/rfbflags"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
logging.V(logging.FnDeclLevel)
|
||||||
|
ln, err := net.Listen("tcp", os.Args[1])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error listen. %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negotiate connection with the server.
|
||||||
|
sch := make(chan vnc.ClientMessage)
|
||||||
|
|
||||||
|
// handle client messages.
|
||||||
|
vcc := vnc.NewServerConfig()
|
||||||
|
vcc.Auth = []vnc.ServerAuth{&vnc.ServerAuthNone{}}
|
||||||
|
vcc.ClientMessageCh = sch
|
||||||
|
go vnc.Serve(context.Background(), ln, vcc)
|
||||||
|
|
||||||
|
nc, err := net.Dial("tcp", os.Args[1])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error connecting to VNC host. %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negotiate connection with the server.
|
||||||
|
cch := make(chan vnc.ServerMessage)
|
||||||
|
vc, err := vnc.Connect(context.Background(), nc,
|
||||||
|
&vnc.ClientConfig{
|
||||||
|
Auth: []vnc.ClientAuth{&vnc.ClientAuthNone{}},
|
||||||
|
ServerMessageCh: cch,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error negotiating connection to VNC host. %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen and handle server messages.
|
||||||
|
go vc.ListenAndHandle()
|
||||||
|
|
||||||
|
// Process messages coming in on the ServerMessage channel.
|
||||||
|
for {
|
||||||
|
msg := <-ch
|
||||||
|
switch msg.Type() {
|
||||||
|
case messages.FramebufferUpdate:
|
||||||
|
log.Println("Received FramebufferUpdate message.")
|
||||||
|
default:
|
||||||
|
log.Printf("Received message type:%v msg:%v\n", msg.Type(), msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process messages coming in on the ClientMessage channel.
|
||||||
|
for {
|
||||||
|
msg := <-ch
|
||||||
|
msg.Write(
|
||||||
|
}
|
||||||
|
}
|
42
example/server/main.go
Normal file
42
example/server/main.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
vnc "github.com/vtolstov/go-vnc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ln, err := net.Listen("tcp", ":5900")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error listen. %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
chServer := make(chan vnc.ClientMessage)
|
||||||
|
chClient := make(chan vnc.ServerMessage)
|
||||||
|
|
||||||
|
cfg := &vnc.ServerConfig{
|
||||||
|
VersionHandler: vnc.ServerVersionHandler,
|
||||||
|
SecurityHandler: vnc.ServerSecurityHandler,
|
||||||
|
SecurityHandlers: []vnc.ServerHandler{vnc.ServerSecurityNoneHandler},
|
||||||
|
ClientInitHandler: vnc.ServerClientInitHandler,
|
||||||
|
ServerInitHandler: vnc.ServerServerInitHandler,
|
||||||
|
Encodings: []vnc.Encoding{&vnc.RawEncoding{}},
|
||||||
|
PixelFormat: vnc.PixelFormat32bit,
|
||||||
|
ClientMessageCh: chServer,
|
||||||
|
ServerMessageCh: chClient,
|
||||||
|
ClientMessages: vnc.DefaultClientMessages,
|
||||||
|
}
|
||||||
|
go vnc.Serve(context.Background(), ln, cfg)
|
||||||
|
|
||||||
|
// Process messages coming in on the ClientMessage channel.
|
||||||
|
for {
|
||||||
|
msg := <-chClient
|
||||||
|
switch msg.Type() {
|
||||||
|
default:
|
||||||
|
log.Printf("Received message type:%v msg:%v\n", msg.Type(), msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
handlers.go
Normal file
35
handlers.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package vnc
|
||||||
|
|
||||||
|
// ClientMessage is the interface
|
||||||
|
type ClientMessage interface {
|
||||||
|
Type() ClientMessageType
|
||||||
|
Read(Conn) error
|
||||||
|
Write(Conn) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerMessage is the interface
|
||||||
|
type ServerMessage interface {
|
||||||
|
Type() ServerMessageType
|
||||||
|
Read(Conn) error
|
||||||
|
Write(Conn) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServerVersionHandler(cfg *ServerConfig, c Conn) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServerSecurityHandler(cfg *ServerConfig, c Conn) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServerSecurityNoneHandler(cfg *ServerConfig, c Conn) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServerServerInitHandler(cfg *ServerConfig, c Conn) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ServerClientInitHandler(cfg *ServerConfig, c Conn) error {
|
||||||
|
return nil
|
||||||
|
}
|
156
image.go
Normal file
156
image.go
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
package vnc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
)
|
||||||
|
|
||||||
|
//var _ draw.Drawer = (*ServerConn)(nil)
|
||||||
|
//var _ draw.Image = (*ServerConn)(nil)
|
||||||
|
|
||||||
|
// Color represents a single color in a color map.
|
||||||
|
type Color struct {
|
||||||
|
pf *PixelFormat
|
||||||
|
cm *ColorMap
|
||||||
|
cmIndex uint32 // Only valid if pf.TrueColor is false.
|
||||||
|
R, G, B uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type ColorMap [256]Color
|
||||||
|
|
||||||
|
// NewColor returns a new Color object.
|
||||||
|
func NewColor(pf *PixelFormat, cm *ColorMap) *Color {
|
||||||
|
return &Color{
|
||||||
|
pf: pf,
|
||||||
|
cm: cm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rectangle represents a rectangle of pixel data.
|
||||||
|
type Rectangle struct {
|
||||||
|
X, Y uint16
|
||||||
|
Width, Height uint16
|
||||||
|
Enc Encoding
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal implements the Marshaler interface.
|
||||||
|
func (c *Color) Marshal() ([]byte, error) {
|
||||||
|
order := c.pf.order()
|
||||||
|
pixel := c.cmIndex
|
||||||
|
if c.pf.TrueColor == 1 {
|
||||||
|
pixel = uint32(c.R) << c.pf.RedShift
|
||||||
|
pixel |= uint32(c.G) << c.pf.GreenShift
|
||||||
|
pixel |= uint32(c.B) << c.pf.BlueShift
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes []byte
|
||||||
|
switch c.pf.BPP {
|
||||||
|
case 8:
|
||||||
|
bytes = make([]byte, 1)
|
||||||
|
bytes[0] = byte(pixel)
|
||||||
|
case 16:
|
||||||
|
bytes = make([]byte, 2)
|
||||||
|
order.PutUint16(bytes, uint16(pixel))
|
||||||
|
case 32:
|
||||||
|
bytes = make([]byte, 4)
|
||||||
|
order.PutUint32(bytes, pixel)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal implements the Unmarshaler interface.
|
||||||
|
func (c *Color) Unmarshal(data []byte) error {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
order := c.pf.order()
|
||||||
|
|
||||||
|
var pixel uint32
|
||||||
|
switch c.pf.BPP {
|
||||||
|
case 8:
|
||||||
|
pixel = uint32(data[0])
|
||||||
|
case 16:
|
||||||
|
pixel = uint32(order.Uint16(data))
|
||||||
|
case 32:
|
||||||
|
pixel = order.Uint32(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.pf.TrueColor == 1 {
|
||||||
|
c.R = uint16((pixel >> c.pf.RedShift) & uint32(c.pf.RedMax))
|
||||||
|
c.G = uint16((pixel >> c.pf.GreenShift) & uint32(c.pf.GreenMax))
|
||||||
|
c.B = uint16((pixel >> c.pf.BlueShift) & uint32(c.pf.BlueMax))
|
||||||
|
} else {
|
||||||
|
*c = c.cm[pixel]
|
||||||
|
c.cmIndex = pixel
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorsToImage(x, y, width, height uint16, colors []Color) *image.RGBA64 {
|
||||||
|
rect := image.Rect(int(x), int(y), int(x+width), int(y+height))
|
||||||
|
rgba := image.NewRGBA64(rect)
|
||||||
|
a := uint16(1)
|
||||||
|
for i, color := range colors {
|
||||||
|
rgba.Pix[4*i+0] = uint8(color.R >> 8)
|
||||||
|
rgba.Pix[4*i+1] = uint8(color.R)
|
||||||
|
rgba.Pix[4*i+2] = uint8(color.G >> 8)
|
||||||
|
rgba.Pix[4*i+3] = uint8(color.G)
|
||||||
|
rgba.Pix[4*i+4] = uint8(color.B >> 8)
|
||||||
|
rgba.Pix[4*i+5] = uint8(color.B)
|
||||||
|
rgba.Pix[4*i+6] = uint8(a >> 8)
|
||||||
|
rgba.Pix[4*i+7] = uint8(a)
|
||||||
|
}
|
||||||
|
return rgba
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal implements the Marshaler interface.
|
||||||
|
func (r *Rectangle) Marshal() ([]byte, error) {
|
||||||
|
/*
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
|
var msg Rectangle
|
||||||
|
msg.X, msg.Y, msg.W, msg.H = r.X, r.Y, r.Width, r.Height
|
||||||
|
msg.E = r.Enc.Type()
|
||||||
|
if err := binary.Write(buf, binary.BigEndian, msg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := r.Enc.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := binary.Write(buf, binary.BigEndian, bytes); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
*/
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal implements the Unmarshaler interface.
|
||||||
|
func (r *Rectangle) Unmarshal(data []byte) error {
|
||||||
|
/*
|
||||||
|
buf := bytes.NewBuffer(data)
|
||||||
|
|
||||||
|
var msg Rectangle
|
||||||
|
if err := binary.Read(buf, binary.BigEndian, &msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.X, r.Y, r.Width, r.Height = msg.X, msg.Y, msg.W, msg.H
|
||||||
|
|
||||||
|
switch msg.E {
|
||||||
|
case encodings.Raw:
|
||||||
|
r.Enc = &RawEncoding{}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unable to unmarshal encoding %v", msg.E)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
*/
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Area returns the total area in pixels of the Rectangle.
|
||||||
|
func (r *Rectangle) Area() int { return int(r.Width) * int(r.Height) }
|
197
key_string.go
Normal file
197
key_string.go
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
// Code generated by "stringer -type=Key"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package vnc
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const _Key_name = "SpaceExclaimQuoteDblNumberSignDollarPercentAmpersandApostropheParenLeftParenRightAsteriskPlusCommaMinusPeriodSlashDigit0Digit1Digit2Digit3Digit4Digit5Digit6Digit7Digit8Digit9ColonSemicolonLessEqualGreaterQuestionAtABCDEFGHIJKLMNOPQRSTUVWXYZBracketLeftBackslashBracketRightAsciiCircumUnderscoreGraveSmallASmallBSmallCSmallDSmallESmallFSmallGSmallHSmallISmallJSmallKSmallLSmallMSmallNSmallOSmallPSmallQSmallRSmallSSmallTSmallUSmallVSmallWSmallXSmallYSmallZBraceLeftBarBraceRightAsciiTildeBackSpaceTabLinefeedClearReturnPauseScrollLockSysReqEscapeHomeLeftUpRightDownPageUpPageDownEndBeginSelectModeSwitchNumLockKeypadSpaceKeypadTabKeypadEnterKeypadF1KeypadF2KeypadF3KeypadF4KeypadHomeKeypadLeftKeypadUpKeypadRightKeypadDownKeypadPriorKeypadPageUpKeypadNextKeypadPageDownKeypadEndKeypadBeginKeypadInsertKeypadDeleteKeypadMultiplyKeypadAddKeypadSeparatorKeypadSubtractKeypadDecimalKeypadDivideKeypad0Keypad1Keypad2Keypad3Keypad4Keypad5Keypad6Keypad7Keypad8Keypad9KeypadEqualF1F2F3F4F5F6F7F8F9F10F11F12ShiftLeftShiftRightControlLeftControlRightCapsLockShiftLockMetaLeftMetaRightAltLeftAltRightSuperLeftSuperRightHyperLeftHyperRightDelete"
|
||||||
|
|
||||||
|
var _Key_map = map[Key]string{
|
||||||
|
32: _Key_name[0:5],
|
||||||
|
33: _Key_name[5:12],
|
||||||
|
34: _Key_name[12:20],
|
||||||
|
35: _Key_name[20:30],
|
||||||
|
36: _Key_name[30:36],
|
||||||
|
37: _Key_name[36:43],
|
||||||
|
38: _Key_name[43:52],
|
||||||
|
39: _Key_name[52:62],
|
||||||
|
40: _Key_name[62:71],
|
||||||
|
41: _Key_name[71:81],
|
||||||
|
42: _Key_name[81:89],
|
||||||
|
43: _Key_name[89:93],
|
||||||
|
44: _Key_name[93:98],
|
||||||
|
45: _Key_name[98:103],
|
||||||
|
46: _Key_name[103:109],
|
||||||
|
47: _Key_name[109:114],
|
||||||
|
48: _Key_name[114:120],
|
||||||
|
49: _Key_name[120:126],
|
||||||
|
50: _Key_name[126:132],
|
||||||
|
51: _Key_name[132:138],
|
||||||
|
52: _Key_name[138:144],
|
||||||
|
53: _Key_name[144:150],
|
||||||
|
54: _Key_name[150:156],
|
||||||
|
55: _Key_name[156:162],
|
||||||
|
56: _Key_name[162:168],
|
||||||
|
57: _Key_name[168:174],
|
||||||
|
58: _Key_name[174:179],
|
||||||
|
59: _Key_name[179:188],
|
||||||
|
60: _Key_name[188:192],
|
||||||
|
61: _Key_name[192:197],
|
||||||
|
62: _Key_name[197:204],
|
||||||
|
63: _Key_name[204:212],
|
||||||
|
64: _Key_name[212:214],
|
||||||
|
65: _Key_name[214:215],
|
||||||
|
66: _Key_name[215:216],
|
||||||
|
67: _Key_name[216:217],
|
||||||
|
68: _Key_name[217:218],
|
||||||
|
69: _Key_name[218:219],
|
||||||
|
70: _Key_name[219:220],
|
||||||
|
71: _Key_name[220:221],
|
||||||
|
72: _Key_name[221:222],
|
||||||
|
73: _Key_name[222:223],
|
||||||
|
74: _Key_name[223:224],
|
||||||
|
75: _Key_name[224:225],
|
||||||
|
76: _Key_name[225:226],
|
||||||
|
77: _Key_name[226:227],
|
||||||
|
78: _Key_name[227:228],
|
||||||
|
79: _Key_name[228:229],
|
||||||
|
80: _Key_name[229:230],
|
||||||
|
81: _Key_name[230:231],
|
||||||
|
82: _Key_name[231:232],
|
||||||
|
83: _Key_name[232:233],
|
||||||
|
84: _Key_name[233:234],
|
||||||
|
85: _Key_name[234:235],
|
||||||
|
86: _Key_name[235:236],
|
||||||
|
87: _Key_name[236:237],
|
||||||
|
88: _Key_name[237:238],
|
||||||
|
89: _Key_name[238:239],
|
||||||
|
90: _Key_name[239:240],
|
||||||
|
91: _Key_name[240:251],
|
||||||
|
92: _Key_name[251:260],
|
||||||
|
93: _Key_name[260:272],
|
||||||
|
94: _Key_name[272:283],
|
||||||
|
95: _Key_name[283:293],
|
||||||
|
96: _Key_name[293:298],
|
||||||
|
97: _Key_name[298:304],
|
||||||
|
98: _Key_name[304:310],
|
||||||
|
99: _Key_name[310:316],
|
||||||
|
100: _Key_name[316:322],
|
||||||
|
101: _Key_name[322:328],
|
||||||
|
102: _Key_name[328:334],
|
||||||
|
103: _Key_name[334:340],
|
||||||
|
104: _Key_name[340:346],
|
||||||
|
105: _Key_name[346:352],
|
||||||
|
106: _Key_name[352:358],
|
||||||
|
107: _Key_name[358:364],
|
||||||
|
108: _Key_name[364:370],
|
||||||
|
109: _Key_name[370:376],
|
||||||
|
110: _Key_name[376:382],
|
||||||
|
111: _Key_name[382:388],
|
||||||
|
112: _Key_name[388:394],
|
||||||
|
113: _Key_name[394:400],
|
||||||
|
114: _Key_name[400:406],
|
||||||
|
115: _Key_name[406:412],
|
||||||
|
116: _Key_name[412:418],
|
||||||
|
117: _Key_name[418:424],
|
||||||
|
118: _Key_name[424:430],
|
||||||
|
119: _Key_name[430:436],
|
||||||
|
120: _Key_name[436:442],
|
||||||
|
121: _Key_name[442:448],
|
||||||
|
122: _Key_name[448:454],
|
||||||
|
123: _Key_name[454:463],
|
||||||
|
124: _Key_name[463:466],
|
||||||
|
125: _Key_name[466:476],
|
||||||
|
126: _Key_name[476:486],
|
||||||
|
65288: _Key_name[486:495],
|
||||||
|
65289: _Key_name[495:498],
|
||||||
|
65290: _Key_name[498:506],
|
||||||
|
65291: _Key_name[506:511],
|
||||||
|
65293: _Key_name[511:517],
|
||||||
|
65299: _Key_name[517:522],
|
||||||
|
65300: _Key_name[522:532],
|
||||||
|
65301: _Key_name[532:538],
|
||||||
|
65307: _Key_name[538:544],
|
||||||
|
65360: _Key_name[544:548],
|
||||||
|
65361: _Key_name[548:552],
|
||||||
|
65362: _Key_name[552:554],
|
||||||
|
65363: _Key_name[554:559],
|
||||||
|
65364: _Key_name[559:563],
|
||||||
|
65365: _Key_name[563:569],
|
||||||
|
65366: _Key_name[569:577],
|
||||||
|
65367: _Key_name[577:580],
|
||||||
|
65368: _Key_name[580:585],
|
||||||
|
65376: _Key_name[585:591],
|
||||||
|
65406: _Key_name[591:601],
|
||||||
|
65407: _Key_name[601:608],
|
||||||
|
65408: _Key_name[608:619],
|
||||||
|
65417: _Key_name[619:628],
|
||||||
|
65421: _Key_name[628:639],
|
||||||
|
65425: _Key_name[639:647],
|
||||||
|
65426: _Key_name[647:655],
|
||||||
|
65427: _Key_name[655:663],
|
||||||
|
65428: _Key_name[663:671],
|
||||||
|
65429: _Key_name[671:681],
|
||||||
|
65430: _Key_name[681:691],
|
||||||
|
65431: _Key_name[691:699],
|
||||||
|
65432: _Key_name[699:710],
|
||||||
|
65433: _Key_name[710:720],
|
||||||
|
65434: _Key_name[720:731],
|
||||||
|
65435: _Key_name[731:743],
|
||||||
|
65436: _Key_name[743:753],
|
||||||
|
65437: _Key_name[753:767],
|
||||||
|
65438: _Key_name[767:776],
|
||||||
|
65439: _Key_name[776:787],
|
||||||
|
65440: _Key_name[787:799],
|
||||||
|
65441: _Key_name[799:811],
|
||||||
|
65442: _Key_name[811:825],
|
||||||
|
65443: _Key_name[825:834],
|
||||||
|
65444: _Key_name[834:849],
|
||||||
|
65445: _Key_name[849:863],
|
||||||
|
65446: _Key_name[863:876],
|
||||||
|
65447: _Key_name[876:888],
|
||||||
|
65448: _Key_name[888:895],
|
||||||
|
65449: _Key_name[895:902],
|
||||||
|
65450: _Key_name[902:909],
|
||||||
|
65451: _Key_name[909:916],
|
||||||
|
65452: _Key_name[916:923],
|
||||||
|
65453: _Key_name[923:930],
|
||||||
|
65454: _Key_name[930:937],
|
||||||
|
65455: _Key_name[937:944],
|
||||||
|
65456: _Key_name[944:951],
|
||||||
|
65457: _Key_name[951:958],
|
||||||
|
65469: _Key_name[958:969],
|
||||||
|
65470: _Key_name[969:971],
|
||||||
|
65471: _Key_name[971:973],
|
||||||
|
65472: _Key_name[973:975],
|
||||||
|
65473: _Key_name[975:977],
|
||||||
|
65474: _Key_name[977:979],
|
||||||
|
65475: _Key_name[979:981],
|
||||||
|
65476: _Key_name[981:983],
|
||||||
|
65477: _Key_name[983:985],
|
||||||
|
65478: _Key_name[985:987],
|
||||||
|
65479: _Key_name[987:990],
|
||||||
|
65480: _Key_name[990:993],
|
||||||
|
65481: _Key_name[993:996],
|
||||||
|
65505: _Key_name[996:1005],
|
||||||
|
65506: _Key_name[1005:1015],
|
||||||
|
65507: _Key_name[1015:1026],
|
||||||
|
65508: _Key_name[1026:1038],
|
||||||
|
65509: _Key_name[1038:1046],
|
||||||
|
65510: _Key_name[1046:1055],
|
||||||
|
65511: _Key_name[1055:1063],
|
||||||
|
65512: _Key_name[1063:1072],
|
||||||
|
65513: _Key_name[1072:1079],
|
||||||
|
65514: _Key_name[1079:1087],
|
||||||
|
65515: _Key_name[1087:1096],
|
||||||
|
65516: _Key_name[1096:1106],
|
||||||
|
65517: _Key_name[1106:1115],
|
||||||
|
65518: _Key_name[1115:1125],
|
||||||
|
65535: _Key_name[1125:1131],
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i Key) String() string {
|
||||||
|
if str, ok := _Key_map[i]; ok {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Key(%d)", i)
|
||||||
|
}
|
254
keys.go
Normal file
254
keys.go
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
package vnc
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Key represents a VNC key press.
|
||||||
|
type Key uint32
|
||||||
|
|
||||||
|
//go:generate stringer -type=Key
|
||||||
|
|
||||||
|
// Keys is a slice of Key values.
|
||||||
|
type Keys []Key
|
||||||
|
|
||||||
|
var keymap = map[rune]Key{
|
||||||
|
'-': Minus,
|
||||||
|
'0': Digit0,
|
||||||
|
'1': Digit1,
|
||||||
|
'2': Digit2,
|
||||||
|
'3': Digit3,
|
||||||
|
'4': Digit4,
|
||||||
|
'5': Digit5,
|
||||||
|
'6': Digit6,
|
||||||
|
'7': Digit7,
|
||||||
|
'8': Digit8,
|
||||||
|
'9': Digit9,
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntToKeys returns Keys that represent the key presses required to type an int.
|
||||||
|
func IntToKeys(v int) Keys {
|
||||||
|
k := Keys{}
|
||||||
|
for _, c := range fmt.Sprintf("%d", v) {
|
||||||
|
k = append(k, keymap[c])
|
||||||
|
}
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
// Latin 1 (byte 3 = 0)
|
||||||
|
// ISO/IEC 8859-1 = Unicode U+0020..U+00FF
|
||||||
|
const (
|
||||||
|
Space Key = iota + 0x0020
|
||||||
|
Exclaim // exclamation mark
|
||||||
|
QuoteDbl
|
||||||
|
NumberSign
|
||||||
|
Dollar
|
||||||
|
Percent
|
||||||
|
Ampersand
|
||||||
|
Apostrophe
|
||||||
|
ParenLeft
|
||||||
|
ParenRight
|
||||||
|
Asterisk
|
||||||
|
Plus
|
||||||
|
Comma
|
||||||
|
Minus
|
||||||
|
Period
|
||||||
|
Slash
|
||||||
|
Digit0
|
||||||
|
Digit1
|
||||||
|
Digit2
|
||||||
|
Digit3
|
||||||
|
Digit4
|
||||||
|
Digit5
|
||||||
|
Digit6
|
||||||
|
Digit7
|
||||||
|
Digit8
|
||||||
|
Digit9
|
||||||
|
Colon
|
||||||
|
Semicolon
|
||||||
|
Less
|
||||||
|
Equal
|
||||||
|
Greater
|
||||||
|
Question
|
||||||
|
At
|
||||||
|
A
|
||||||
|
B
|
||||||
|
C
|
||||||
|
D
|
||||||
|
E
|
||||||
|
F
|
||||||
|
G
|
||||||
|
H
|
||||||
|
I
|
||||||
|
J
|
||||||
|
K
|
||||||
|
L
|
||||||
|
M
|
||||||
|
N
|
||||||
|
O
|
||||||
|
P
|
||||||
|
Q
|
||||||
|
R
|
||||||
|
S
|
||||||
|
T
|
||||||
|
U
|
||||||
|
V
|
||||||
|
W
|
||||||
|
X
|
||||||
|
Y
|
||||||
|
Z
|
||||||
|
BracketLeft
|
||||||
|
Backslash
|
||||||
|
BracketRight
|
||||||
|
AsciiCircum
|
||||||
|
Underscore
|
||||||
|
Grave
|
||||||
|
SmallA
|
||||||
|
SmallB
|
||||||
|
SmallC
|
||||||
|
SmallD
|
||||||
|
SmallE
|
||||||
|
SmallF
|
||||||
|
SmallG
|
||||||
|
SmallH
|
||||||
|
SmallI
|
||||||
|
SmallJ
|
||||||
|
SmallK
|
||||||
|
SmallL
|
||||||
|
SmallM
|
||||||
|
SmallN
|
||||||
|
SmallO
|
||||||
|
SmallP
|
||||||
|
SmallQ
|
||||||
|
SmallR
|
||||||
|
SmallS
|
||||||
|
SmallT
|
||||||
|
SmallU
|
||||||
|
SmallV
|
||||||
|
SmallW
|
||||||
|
SmallX
|
||||||
|
SmallY
|
||||||
|
SmallZ
|
||||||
|
BraceLeft
|
||||||
|
Bar
|
||||||
|
BraceRight
|
||||||
|
AsciiTilde
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BackSpace Key = iota + 0xff08
|
||||||
|
Tab
|
||||||
|
Linefeed
|
||||||
|
Clear
|
||||||
|
_
|
||||||
|
Return
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Pause Key = iota + 0xff13
|
||||||
|
ScrollLock
|
||||||
|
SysReq
|
||||||
|
Escape Key = 0xff1b
|
||||||
|
Delete Key = 0xffff
|
||||||
|
)
|
||||||
|
|
||||||
|
const ( // Cursor control & motion.
|
||||||
|
Home Key = iota + 0xff50
|
||||||
|
Left
|
||||||
|
Up
|
||||||
|
Right
|
||||||
|
Down
|
||||||
|
PageUp
|
||||||
|
PageDown
|
||||||
|
End
|
||||||
|
Begin
|
||||||
|
)
|
||||||
|
|
||||||
|
const ( // Misc functions.
|
||||||
|
Select Key = 0xff60
|
||||||
|
Print
|
||||||
|
Execute
|
||||||
|
Insert
|
||||||
|
Undo
|
||||||
|
Redo
|
||||||
|
Menu
|
||||||
|
Find
|
||||||
|
Cancel
|
||||||
|
Help
|
||||||
|
Break
|
||||||
|
ModeSwitch Key = 0xff7e
|
||||||
|
NumLock Key = 0xff7f
|
||||||
|
)
|
||||||
|
|
||||||
|
const ( // Keypad functions.
|
||||||
|
KeypadSpace Key = 0xff80
|
||||||
|
KeypadTab Key = 0xff89
|
||||||
|
KeypadEnter Key = 0xff8d
|
||||||
|
)
|
||||||
|
|
||||||
|
const ( // Keypad functions cont.
|
||||||
|
KeypadF1 Key = iota + 0xff91
|
||||||
|
KeypadF2
|
||||||
|
KeypadF3
|
||||||
|
KeypadF4
|
||||||
|
KeypadHome
|
||||||
|
KeypadLeft
|
||||||
|
KeypadUp
|
||||||
|
KeypadRight
|
||||||
|
KeypadDown
|
||||||
|
KeypadPrior
|
||||||
|
KeypadPageUp
|
||||||
|
KeypadNext
|
||||||
|
KeypadPageDown
|
||||||
|
KeypadEnd
|
||||||
|
KeypadBegin
|
||||||
|
KeypadInsert
|
||||||
|
KeypadDelete
|
||||||
|
KeypadMultiply
|
||||||
|
KeypadAdd
|
||||||
|
KeypadSeparator
|
||||||
|
KeypadSubtract
|
||||||
|
KeypadDecimal
|
||||||
|
KeypadDivide
|
||||||
|
Keypad0
|
||||||
|
Keypad1
|
||||||
|
Keypad2
|
||||||
|
Keypad3
|
||||||
|
Keypad4
|
||||||
|
Keypad5
|
||||||
|
Keypad6
|
||||||
|
Keypad7
|
||||||
|
Keypad8
|
||||||
|
Keypad9
|
||||||
|
KeypadEqual Key = 0xffbd
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
F1 Key = iota + 0xffbe
|
||||||
|
F2
|
||||||
|
F3
|
||||||
|
F4
|
||||||
|
F5
|
||||||
|
F6
|
||||||
|
F7
|
||||||
|
F8
|
||||||
|
F9
|
||||||
|
F10
|
||||||
|
F11
|
||||||
|
F12
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ShiftLeft Key = iota + 0xffe1
|
||||||
|
ShiftRight
|
||||||
|
ControlLeft
|
||||||
|
ControlRight
|
||||||
|
CapsLock
|
||||||
|
ShiftLock
|
||||||
|
MetaLeft
|
||||||
|
MetaRight
|
||||||
|
AltLeft
|
||||||
|
AltRight
|
||||||
|
SuperLeft
|
||||||
|
SuperRight
|
||||||
|
HyperLeft
|
||||||
|
HyperRight
|
||||||
|
)
|
112
pixel_format.go
Normal file
112
pixel_format.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// Implementation of RFC 6143 §7.4 Pixel Format Data Structure.
|
||||||
|
|
||||||
|
package vnc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
PixelFormat8bit *PixelFormat = NewPixelFormat(8)
|
||||||
|
PixelFormat16bit *PixelFormat = NewPixelFormat(16)
|
||||||
|
PixelFormat32bit *PixelFormat = NewPixelFormat(32)
|
||||||
|
)
|
||||||
|
|
||||||
|
// PixelFormat describes the way a pixel is formatted for a VNC connection.
|
||||||
|
type PixelFormat struct {
|
||||||
|
BPP uint8 // bits-per-pixel
|
||||||
|
Depth uint8 // depth
|
||||||
|
BigEndian uint8 // big-endian-flag
|
||||||
|
TrueColor uint8 // true-color-flag
|
||||||
|
RedMax, GreenMax, BlueMax uint16 // red-, green-, blue-max (2^BPP-1)
|
||||||
|
RedShift, GreenShift, BlueShift uint8 // red-, green-, blue-shift
|
||||||
|
_ [3]byte // padding
|
||||||
|
}
|
||||||
|
|
||||||
|
const pixelFormatLen = 16
|
||||||
|
|
||||||
|
// NewPixelFormat returns a populated PixelFormat structure.
|
||||||
|
func NewPixelFormat(bpp uint8) *PixelFormat {
|
||||||
|
bigEndian := uint8(1)
|
||||||
|
rgbMax := uint16(math.Exp2(float64(bpp))) - 1
|
||||||
|
var (
|
||||||
|
tc = uint8(1)
|
||||||
|
rs, gs, bs uint8
|
||||||
|
)
|
||||||
|
switch bpp {
|
||||||
|
case 8:
|
||||||
|
tc = 0
|
||||||
|
rs, gs, bs = 0, 0, 0
|
||||||
|
case 16:
|
||||||
|
rs, gs, bs = 0, 4, 8
|
||||||
|
case 32:
|
||||||
|
rs, gs, bs = 0, 8, 16
|
||||||
|
}
|
||||||
|
return &PixelFormat{bpp, bpp, bigEndian, tc, rgbMax, rgbMax, rgbMax, rs, gs, bs, [3]byte{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal implements the Marshaler interface.
|
||||||
|
func (pf *PixelFormat) Marshal() ([]byte, error) {
|
||||||
|
// Validation checks.
|
||||||
|
switch pf.BPP {
|
||||||
|
case 8, 16, 32:
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Invalid BPP value %v; must be 8, 16, or 32.", pf.BPP)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pf.Depth < pf.BPP {
|
||||||
|
return nil, fmt.Errorf("Invalid Depth value %v; cannot be < BPP", pf.Depth)
|
||||||
|
}
|
||||||
|
switch pf.Depth {
|
||||||
|
case 8, 16, 32:
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Invalid Depth value %v; must be 8, 16, or 32.", pf.Depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the slice of bytes
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
if err := binary.Write(buf, binary.BigEndian, &pf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from an io.Reader, and populates the PixelFormat.
|
||||||
|
func (pf *PixelFormat) Read(r io.Reader) error {
|
||||||
|
buf := make([]byte, pixelFormatLen)
|
||||||
|
if _, err := io.ReadAtLeast(r, buf, pixelFormatLen); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return pf.Unmarshal(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal implements the Unmarshaler interface.
|
||||||
|
func (pf *PixelFormat) Unmarshal(data []byte) error {
|
||||||
|
buf := bytes.NewBuffer(data)
|
||||||
|
|
||||||
|
var msg PixelFormat
|
||||||
|
if err := binary.Read(buf, binary.BigEndian, &msg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*pf = msg
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String implements the fmt.Stringer interface.
|
||||||
|
func (pf *PixelFormat) String() string {
|
||||||
|
return fmt.Sprintf("{ bpp: %d depth: %d big-endian: %s true-color: %s red-max: %d green-max: %d blue-max: %d red-shift: %d green-shift: %d blue-shift: %d }",
|
||||||
|
pf.BPP, pf.Depth, pf.BigEndian, pf.TrueColor, pf.RedMax, pf.GreenMax, pf.BlueMax, pf.RedShift, pf.GreenShift, pf.BlueShift)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pf *PixelFormat) order() binary.ByteOrder {
|
||||||
|
if pf.BigEndian == 1 {
|
||||||
|
return binary.BigEndian
|
||||||
|
}
|
||||||
|
return binary.LittleEndian
|
||||||
|
}
|
438
server.go
Normal file
438
server.go
Normal file
@ -0,0 +1,438 @@
|
|||||||
|
package vnc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DefaultClientMessages = []ClientMessage{
|
||||||
|
&SetPixelFormat{},
|
||||||
|
&SetEncodings{},
|
||||||
|
&FramebufferUpdateRequest{},
|
||||||
|
&KeyEvent{},
|
||||||
|
&PointerEvent{},
|
||||||
|
&ClientCutText{},
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Conn = (*ServerConn)(nil)
|
||||||
|
|
||||||
|
func (c *ServerConn) Flush() error {
|
||||||
|
return c.bw.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerConn) Close() error {
|
||||||
|
return c.c.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func (c *ServerConn) Input() chan *ServerMessage {
|
||||||
|
return c.cfg.ServerMessageCh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerConn) Output() chan *ClientMessage {
|
||||||
|
return c.cfg.ClientMessageCh
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
func (c *ServerConn) Read(buf []byte) (int, error) {
|
||||||
|
return c.br.Read(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerConn) Write(buf []byte) (int, error) {
|
||||||
|
return c.bw.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerConn) ColorMap() *ColorMap {
|
||||||
|
return c.colorMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerConn) SetColorMap(cm *ColorMap) {
|
||||||
|
c.colorMap = cm
|
||||||
|
}
|
||||||
|
func (c *ServerConn) DesktopName() string {
|
||||||
|
return c.desktopName
|
||||||
|
}
|
||||||
|
func (c *ServerConn) PixelFormat() *PixelFormat {
|
||||||
|
return c.pixelFormat
|
||||||
|
}
|
||||||
|
func (c *ServerConn) Encodings() []Encoding {
|
||||||
|
return c.encodings
|
||||||
|
}
|
||||||
|
func (c *ServerConn) Width() uint16 {
|
||||||
|
return c.fbWidth
|
||||||
|
}
|
||||||
|
func (c *ServerConn) Height() uint16 {
|
||||||
|
return c.fbHeight
|
||||||
|
}
|
||||||
|
func (c *ServerConn) Protocol() string {
|
||||||
|
return c.protocol
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO send desktopsize pseudo encoding
|
||||||
|
func (c *ServerConn) SetWidth(w uint16) {
|
||||||
|
c.fbWidth = w
|
||||||
|
}
|
||||||
|
func (c *ServerConn) SetHeight(h uint16) {
|
||||||
|
c.fbHeight = h
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerMessage represents a Client-to-Server RFB message type.
|
||||||
|
type ServerMessageType uint8
|
||||||
|
|
||||||
|
//go:generate stringer -type=ServerMessageType
|
||||||
|
|
||||||
|
// Client-to-Server message types.
|
||||||
|
const (
|
||||||
|
FramebufferUpdateMsgType ServerMessageType = iota
|
||||||
|
SetColorMapEntriesMsgType
|
||||||
|
BellMsgType
|
||||||
|
ServerCutTextMsgType
|
||||||
|
)
|
||||||
|
|
||||||
|
// FramebufferUpdate holds a FramebufferUpdate wire format message.
|
||||||
|
type FramebufferUpdate struct {
|
||||||
|
MsgType ServerMessageType
|
||||||
|
NumRect uint16 // number-of-rectangles
|
||||||
|
_ [1]byte // pad
|
||||||
|
Rects []Rectangle // rectangles
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *FramebufferUpdate) Type() ServerMessageType {
|
||||||
|
return msg.MsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *FramebufferUpdate) Read(c Conn) error {
|
||||||
|
if err := binary.Read(c, binary.BigEndian, msg.MsgType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Read(c, binary.BigEndian, msg.NumRect); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
// Extract rectangles.
|
||||||
|
rects := make([]Rectangle, msg.NumRect)
|
||||||
|
for i := uint16(0); i < msg.NumRect; i++ {
|
||||||
|
rect := NewRectangle(c)
|
||||||
|
if err := rect.Read(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.Rects = append(msg.Rects, *rect)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *FramebufferUpdate) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.MsgType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Write(c, binary.BigEndian, pad); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.NumRect); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
for _, rect := range msg.Rects {
|
||||||
|
if err := rect.Write(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return c.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConn struct {
|
||||||
|
c net.Conn
|
||||||
|
cfg *ServerConfig
|
||||||
|
br *bufio.Reader
|
||||||
|
bw *bufio.Writer
|
||||||
|
protocol string
|
||||||
|
|
||||||
|
// If the pixel format uses a color map, then this is the color
|
||||||
|
// map that is used. This should not be modified directly, since
|
||||||
|
// the data comes from the server.
|
||||||
|
// Definition in §5 - Representation of Pixel Data.
|
||||||
|
colorMap *ColorMap
|
||||||
|
|
||||||
|
// Name associated with the desktop, sent from the server.
|
||||||
|
desktopName string
|
||||||
|
|
||||||
|
// Encodings supported by the client. This should not be modified
|
||||||
|
// directly. Instead, SetEncodings() should be used.
|
||||||
|
encodings []Encoding
|
||||||
|
|
||||||
|
// Height of the frame buffer in pixels, sent to the client.
|
||||||
|
fbHeight uint16
|
||||||
|
|
||||||
|
// Width of the frame buffer in pixels, sent to the client.
|
||||||
|
fbWidth uint16
|
||||||
|
|
||||||
|
// The pixel format associated with the connection. This shouldn't
|
||||||
|
// be modified. If you wish to set a new pixel format, use the
|
||||||
|
// SetPixelFormat method.
|
||||||
|
pixelFormat *PixelFormat
|
||||||
|
|
||||||
|
quit chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerHandler func(*ServerConfig, Conn) error
|
||||||
|
|
||||||
|
type ServerConfig struct {
|
||||||
|
VersionHandler ServerHandler
|
||||||
|
SecurityHandler ServerHandler
|
||||||
|
SecurityHandlers []ServerHandler
|
||||||
|
ClientInitHandler ServerHandler
|
||||||
|
ServerInitHandler ServerHandler
|
||||||
|
Encodings []Encoding
|
||||||
|
PixelFormat *PixelFormat
|
||||||
|
ColorMap *ColorMap
|
||||||
|
ClientMessageCh chan ClientMessage
|
||||||
|
ServerMessageCh chan ServerMessage
|
||||||
|
ClientMessages []ClientMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerConn(c net.Conn, cfg *ServerConfig) (*ServerConn, error) {
|
||||||
|
if cfg.ClientMessageCh == nil {
|
||||||
|
return nil, fmt.Errorf("ClientMessageCh nil")
|
||||||
|
}
|
||||||
|
if len(cfg.ClientMessages) == 0 {
|
||||||
|
return nil, fmt.Errorf("ClientMessage 0")
|
||||||
|
}
|
||||||
|
return &ServerConn{
|
||||||
|
c: c,
|
||||||
|
br: bufio.NewReader(c),
|
||||||
|
bw: bufio.NewWriter(c),
|
||||||
|
cfg: cfg,
|
||||||
|
encodings: cfg.Encodings,
|
||||||
|
pixelFormat: cfg.PixelFormat,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Serve(ctx context.Context, ln net.Listener, cfg *ServerConfig) error {
|
||||||
|
for {
|
||||||
|
c, err := ln.Accept()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
conn, err := NewServerConn(c, cfg)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := cfg.VersionHandler(cfg, conn); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cfg.SecurityHandler(cfg, conn); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := cfg.ClientInitHandler(cfg, conn); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := cfg.ServerInitHandler(cfg, conn); err != nil {
|
||||||
|
conn.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go conn.Handle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerConn) Handle() error {
|
||||||
|
var err error
|
||||||
|
defer c.Close()
|
||||||
|
clientMessages := make(map[ClientMessageType]ClientMessage)
|
||||||
|
for _, m := range c.cfg.ClientMessages {
|
||||||
|
clientMessages[m.Type()] = m
|
||||||
|
}
|
||||||
|
|
||||||
|
serverLoop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case msg := <-c.cfg.ServerMessageCh:
|
||||||
|
if err = msg.Write(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Flush()
|
||||||
|
case <-c.quit:
|
||||||
|
break serverLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clientLoop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-c.quit:
|
||||||
|
break clientLoop
|
||||||
|
default:
|
||||||
|
var messageType ClientMessageType
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &messageType); err != nil {
|
||||||
|
break clientLoop
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, ok := clientMessages[messageType]
|
||||||
|
if !ok {
|
||||||
|
err = fmt.Errorf("unsupported message-type: %v", messageType)
|
||||||
|
break clientLoop
|
||||||
|
}
|
||||||
|
if err := msg.Read(c); err != nil {
|
||||||
|
break clientLoop
|
||||||
|
}
|
||||||
|
|
||||||
|
c.cfg.ClientMessageCh <- msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerCutText struct {
|
||||||
|
MsgType ServerMessageType
|
||||||
|
_ [1]byte
|
||||||
|
Length uint32
|
||||||
|
Text []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *ServerCutText) Type() ServerMessageType {
|
||||||
|
return msg.MsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *ServerCutText) Read(c Conn) error {
|
||||||
|
if err := binary.Read(c, binary.BigEndian, msg.MsgType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var length uint32
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &length); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
text := make([]byte, length)
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &text); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
msg.Text = text
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *ServerCutText) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.MsgType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Write(c, binary.BigEndian, pad); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Length); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.Text); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bell struct {
|
||||||
|
MsgType ServerMessageType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Bell) Type() ServerMessageType {
|
||||||
|
return msg.MsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Bell) Read(c Conn) error {
|
||||||
|
return binary.Read(c, binary.BigEndian, msg.MsgType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *Bell) Write(c Conn) error {
|
||||||
|
return binary.Write(c, binary.BigEndian, msg.MsgType)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetColorMapEntries struct {
|
||||||
|
MsgType ServerMessageType
|
||||||
|
_ [1]byte
|
||||||
|
FirstColor uint16
|
||||||
|
ColorsNum uint16
|
||||||
|
Colors []Color
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SetColorMapEntries) Type() ServerMessageType {
|
||||||
|
return msg.MsgType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SetColorMapEntries) Read(c Conn) error {
|
||||||
|
if err := binary.Read(c, binary.BigEndian, msg.MsgType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Read(c, binary.BigEndian, msg.FirstColor); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Read(c, binary.BigEndian, msg.ColorsNum); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Colors = make([]Color, msg.ColorsNum)
|
||||||
|
colorMap := c.ColorMap()
|
||||||
|
|
||||||
|
for i := uint16(0); i < msg.ColorsNum; i++ {
|
||||||
|
color := &msg.Colors[i]
|
||||||
|
if err := binary.Read(c, binary.BigEndian, &color); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
colorMap[msg.FirstColor+i] = *color
|
||||||
|
}
|
||||||
|
c.SetColorMap(colorMap)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *SetColorMapEntries) Write(c Conn) error {
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.MsgType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var pad [1]byte
|
||||||
|
if err := binary.Write(c, binary.BigEndian, &pad); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.FirstColor); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.ColorsNum < uint16(len(msg.Colors)) {
|
||||||
|
msg.ColorsNum = uint16(len(msg.Colors))
|
||||||
|
}
|
||||||
|
if err := binary.Write(c, binary.BigEndian, msg.ColorsNum); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(msg.Colors); i++ {
|
||||||
|
color := msg.Colors[i]
|
||||||
|
if err := binary.Write(c, binary.BigEndian, color); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
16
servermessagetype_string.go
Normal file
16
servermessagetype_string.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Code generated by "stringer -type=ServerMessageType"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package vnc
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const _ServerMessageType_name = "FramebufferUpdateMsgTypeSetColorMapEntriesMsgTypeBellMsgTypeServerCutTextMsgType"
|
||||||
|
|
||||||
|
var _ServerMessageType_index = [...]uint8{0, 24, 49, 60, 80}
|
||||||
|
|
||||||
|
func (i ServerMessageType) String() string {
|
||||||
|
if i >= ServerMessageType(len(_ServerMessageType_index)-1) {
|
||||||
|
return fmt.Sprintf("ServerMessageType(%d)", i)
|
||||||
|
}
|
||||||
|
return _ServerMessageType_name[_ServerMessageType_index[i]:_ServerMessageType_index[i+1]]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user