2017-06-12 14:11:23 +03:00
|
|
|
package vnc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"context"
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
2017-06-13 01:52:07 +03:00
|
|
|
"sync"
|
2017-06-12 14:11:23 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
var DefaultClientMessages = []ClientMessage{
|
|
|
|
&SetPixelFormat{},
|
|
|
|
&SetEncodings{},
|
|
|
|
&FramebufferUpdateRequest{},
|
|
|
|
&KeyEvent{},
|
|
|
|
&PointerEvent{},
|
|
|
|
&ClientCutText{},
|
|
|
|
}
|
|
|
|
|
2017-06-13 01:52:07 +03:00
|
|
|
type ServerInit struct {
|
|
|
|
FBWidth, FBHeight uint16
|
|
|
|
PixelFormat PixelFormat
|
|
|
|
NameLength uint32
|
|
|
|
NameText []byte
|
|
|
|
}
|
|
|
|
|
2017-06-12 14:11:23 +03:00
|
|
|
var _ Conn = (*ServerConn)(nil)
|
|
|
|
|
2017-06-13 01:52:07 +03:00
|
|
|
func (c *ServerConn) Conn() net.Conn {
|
|
|
|
return c.c
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ServerConn) SetEncodings(encs []EncodingType) error {
|
|
|
|
encodings := make(map[EncodingType]Encoding)
|
|
|
|
for _, enc := range c.cfg.Encodings {
|
|
|
|
encodings[enc.Type()] = enc
|
|
|
|
}
|
|
|
|
for _, encType := range encs {
|
|
|
|
if enc, ok := encodings[encType]; ok {
|
|
|
|
c.encodings = append(c.encodings, enc)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ServerConn) SetProtoVersion(pv string) {
|
|
|
|
c.protocol = pv
|
|
|
|
}
|
|
|
|
|
2017-06-12 14:11:23 +03:00
|
|
|
func (c *ServerConn) Flush() error {
|
|
|
|
return c.bw.Flush()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ServerConn) Close() error {
|
|
|
|
return c.c.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2017-06-13 01:52:07 +03:00
|
|
|
func (c *ServerConn) SetDesktopName(name string) {
|
|
|
|
c.desktopName = name
|
|
|
|
}
|
|
|
|
func (c *ServerConn) SetPixelFormat(pf *PixelFormat) error {
|
|
|
|
c.pixelFormat = pf
|
|
|
|
return nil
|
|
|
|
}
|
2017-06-12 14:11:23 +03:00
|
|
|
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 {
|
2017-06-13 16:20:35 +03:00
|
|
|
_ [1]byte // pad
|
|
|
|
NumRect uint16 // number-of-rectangles
|
|
|
|
Rects []*Rectangle // rectangles
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
2017-06-13 16:20:35 +03:00
|
|
|
func (*FramebufferUpdate) Type() ServerMessageType {
|
2017-06-13 01:52:07 +03:00
|
|
|
return FramebufferUpdateMsgType
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
2017-06-13 16:20:35 +03:00
|
|
|
func (*FramebufferUpdate) Read(c Conn) (ServerMessage, error) {
|
|
|
|
msg := FramebufferUpdate{}
|
2017-06-12 14:11:23 +03:00
|
|
|
var pad [1]byte
|
|
|
|
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
2017-06-13 16:20:35 +03:00
|
|
|
return nil, err
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
2017-06-13 16:20:35 +03:00
|
|
|
if err := binary.Read(c, binary.BigEndian, &msg.NumRect); err != nil {
|
|
|
|
return nil, err
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
2017-06-13 01:52:07 +03:00
|
|
|
for i := uint16(0); i < msg.NumRect; i++ {
|
2017-06-13 16:20:35 +03:00
|
|
|
rect := &Rectangle{}
|
2017-06-13 01:52:07 +03:00
|
|
|
if err := rect.Read(c); err != nil {
|
2017-06-13 16:20:35 +03:00
|
|
|
return nil, err
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
2017-06-13 16:20:35 +03:00
|
|
|
msg.Rects = append(msg.Rects, rect)
|
2017-06-13 01:52:07 +03:00
|
|
|
}
|
2017-06-13 16:20:35 +03:00
|
|
|
return &msg, nil
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (msg *FramebufferUpdate) Write(c Conn) error {
|
2017-06-13 16:20:35 +03:00
|
|
|
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
2017-06-12 14:11:23 +03:00
|
|
|
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
|
|
|
|
}
|
2017-06-13 01:52:07 +03:00
|
|
|
for _, rect := range msg.Rects {
|
|
|
|
if err := rect.Write(c); err != nil {
|
|
|
|
return err
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
2017-06-13 01:52:07 +03:00
|
|
|
}
|
2017-06-12 14:11:23 +03:00
|
|
|
return c.Flush()
|
|
|
|
}
|
|
|
|
|
|
|
|
type ServerConn struct {
|
|
|
|
c net.Conn
|
|
|
|
cfg *ServerConfig
|
|
|
|
br *bufio.Reader
|
|
|
|
bw *bufio.Writer
|
|
|
|
protocol string
|
2017-06-13 01:52:07 +03:00
|
|
|
m sync.Mutex
|
2017-06-12 14:11:23 +03:00
|
|
|
// 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
|
2017-06-13 01:52:07 +03:00
|
|
|
SecurityHandlers []SecurityHandler
|
2017-06-12 14:11:23 +03:00
|
|
|
ClientInitHandler ServerHandler
|
|
|
|
ServerInitHandler ServerHandler
|
|
|
|
Encodings []Encoding
|
|
|
|
PixelFormat *PixelFormat
|
|
|
|
ColorMap *ColorMap
|
|
|
|
ClientMessageCh chan ClientMessage
|
|
|
|
ServerMessageCh chan ServerMessage
|
2017-06-20 00:55:32 +03:00
|
|
|
ErrorCh chan error
|
2017-06-12 14:11:23 +03:00
|
|
|
ClientMessages []ClientMessage
|
2017-06-13 01:52:07 +03:00
|
|
|
DesktopName []byte
|
|
|
|
Height uint16
|
|
|
|
Width uint16
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewServerConn(c net.Conn, cfg *ServerConfig) (*ServerConn, error) {
|
|
|
|
if cfg.ClientMessageCh == nil {
|
|
|
|
return nil, fmt.Errorf("ClientMessageCh nil")
|
|
|
|
}
|
2017-06-13 01:52:07 +03:00
|
|
|
|
2017-06-12 14:11:23 +03:00
|
|
|
if len(cfg.ClientMessages) == 0 {
|
|
|
|
return nil, fmt.Errorf("ClientMessage 0")
|
|
|
|
}
|
2017-06-13 01:52:07 +03:00
|
|
|
|
2017-06-12 14:11:23 +03:00
|
|
|
return &ServerConn{
|
|
|
|
c: c,
|
|
|
|
br: bufio.NewReader(c),
|
|
|
|
bw: bufio.NewWriter(c),
|
|
|
|
cfg: cfg,
|
2017-06-13 01:52:07 +03:00
|
|
|
quit: make(chan struct{}),
|
2017-06-12 14:11:23 +03:00
|
|
|
encodings: cfg.Encodings,
|
|
|
|
pixelFormat: cfg.PixelFormat,
|
2017-06-13 01:52:07 +03:00
|
|
|
fbWidth: cfg.Width,
|
|
|
|
fbHeight: cfg.Height,
|
2017-06-12 14:11:23 +03:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func Serve(ctx context.Context, ln net.Listener, cfg *ServerConfig) error {
|
|
|
|
for {
|
2017-06-13 01:52:07 +03:00
|
|
|
|
2017-06-12 14:11:23 +03:00
|
|
|
c, err := ln.Accept()
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
2017-06-13 01:52:07 +03:00
|
|
|
|
2017-06-12 14:11:23 +03:00
|
|
|
conn, err := NewServerConn(c, cfg)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
2017-06-13 01:52:07 +03:00
|
|
|
|
2017-06-12 14:11:23 +03:00
|
|
|
if err := cfg.VersionHandler(cfg, conn); err != nil {
|
|
|
|
conn.Close()
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := cfg.SecurityHandler(cfg, conn); err != nil {
|
|
|
|
conn.Close()
|
|
|
|
continue
|
|
|
|
}
|
2017-06-13 01:52:07 +03:00
|
|
|
|
2017-06-12 14:11:23 +03:00
|
|
|
if err := cfg.ClientInitHandler(cfg, conn); err != nil {
|
|
|
|
conn.Close()
|
|
|
|
continue
|
|
|
|
}
|
2017-06-13 01:52:07 +03:00
|
|
|
|
2017-06-12 14:11:23 +03:00
|
|
|
if err := cfg.ServerInitHandler(cfg, conn); err != nil {
|
|
|
|
conn.Close()
|
|
|
|
continue
|
|
|
|
}
|
2017-06-13 01:52:07 +03:00
|
|
|
|
2017-06-12 14:11:23 +03:00
|
|
|
go conn.Handle()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ServerConn) Handle() error {
|
|
|
|
var err error
|
2017-06-13 01:52:07 +03:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
2017-06-12 14:11:23 +03:00
|
|
|
defer c.Close()
|
|
|
|
clientMessages := make(map[ClientMessageType]ClientMessage)
|
|
|
|
for _, m := range c.cfg.ClientMessages {
|
|
|
|
clientMessages[m.Type()] = m
|
|
|
|
}
|
2017-06-13 01:52:07 +03:00
|
|
|
wg.Add(2)
|
|
|
|
|
|
|
|
// server
|
|
|
|
go func() error {
|
|
|
|
defer wg.Done()
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case msg := <-c.cfg.ServerMessageCh:
|
|
|
|
if err = msg.Write(c); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case <-c.quit:
|
|
|
|
return nil
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
}
|
2017-06-13 01:52:07 +03:00
|
|
|
}()
|
|
|
|
|
|
|
|
// client
|
|
|
|
go func() error {
|
|
|
|
defer wg.Done()
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-c.quit:
|
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
var messageType ClientMessageType
|
|
|
|
if err := binary.Read(c, binary.BigEndian, &messageType); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
msg, ok := clientMessages[messageType]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("unsupported message-type: %v", messageType)
|
|
|
|
|
|
|
|
}
|
2017-06-13 16:20:35 +03:00
|
|
|
parsedMsg, err := msg.Read(c)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("srv err %s\n", err.Error())
|
2017-06-13 01:52:07 +03:00
|
|
|
return err
|
|
|
|
}
|
2017-06-13 16:20:35 +03:00
|
|
|
c.cfg.ClientMessageCh <- parsedMsg
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
}
|
2017-06-13 01:52:07 +03:00
|
|
|
}()
|
|
|
|
|
|
|
|
wg.Wait()
|
2017-06-12 14:11:23 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type ServerCutText struct {
|
2017-06-13 16:20:35 +03:00
|
|
|
_ [1]byte
|
|
|
|
Length uint32
|
|
|
|
Text []byte
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
2017-06-13 16:20:35 +03:00
|
|
|
func (*ServerCutText) Type() ServerMessageType {
|
2017-06-13 01:52:07 +03:00
|
|
|
return ServerCutTextMsgType
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
2017-06-13 16:20:35 +03:00
|
|
|
func (*ServerCutText) Read(c Conn) (ServerMessage, error) {
|
|
|
|
msg := ServerCutText{}
|
|
|
|
|
2017-06-12 14:11:23 +03:00
|
|
|
var pad [1]byte
|
|
|
|
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
2017-06-13 16:20:35 +03:00
|
|
|
return nil, err
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
2017-06-13 16:20:35 +03:00
|
|
|
if err := binary.Read(c, binary.BigEndian, &msg.Length); err != nil {
|
|
|
|
return nil, err
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
2017-06-13 16:20:35 +03:00
|
|
|
msg.Text = make([]byte, msg.Length)
|
|
|
|
if err := binary.Read(c, binary.BigEndian, &msg.Text); err != nil {
|
|
|
|
return nil, err
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
2017-06-13 16:20:35 +03:00
|
|
|
return &msg, nil
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (msg *ServerCutText) Write(c Conn) error {
|
2017-06-13 16:20:35 +03:00
|
|
|
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
2017-06-12 14:11:23 +03:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
var pad [1]byte
|
|
|
|
if err := binary.Write(c, binary.BigEndian, pad); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-06-13 16:20:35 +03:00
|
|
|
if msg.Length < uint32(len(msg.Text)) {
|
|
|
|
msg.Length = uint32(len(msg.Text))
|
|
|
|
}
|
2017-06-12 14:11:23 +03:00
|
|
|
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
|
|
|
|
}
|
2017-06-20 00:55:32 +03:00
|
|
|
return c.Flush()
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
2017-06-13 16:20:35 +03:00
|
|
|
type Bell struct{}
|
2017-06-12 14:11:23 +03:00
|
|
|
|
2017-06-13 16:20:35 +03:00
|
|
|
func (*Bell) Type() ServerMessageType {
|
2017-06-13 01:52:07 +03:00
|
|
|
return BellMsgType
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
2017-06-13 16:20:35 +03:00
|
|
|
func (*Bell) Read(c Conn) (ServerMessage, error) {
|
|
|
|
return &Bell{}, nil
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (msg *Bell) Write(c Conn) error {
|
2017-06-20 00:55:32 +03:00
|
|
|
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return c.Flush()
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type SetColorMapEntries struct {
|
|
|
|
_ [1]byte
|
|
|
|
FirstColor uint16
|
|
|
|
ColorsNum uint16
|
|
|
|
Colors []Color
|
|
|
|
}
|
|
|
|
|
2017-06-13 16:20:35 +03:00
|
|
|
func (*SetColorMapEntries) Type() ServerMessageType {
|
2017-06-13 01:52:07 +03:00
|
|
|
return SetColorMapEntriesMsgType
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
2017-06-13 16:20:35 +03:00
|
|
|
func (*SetColorMapEntries) Read(c Conn) (ServerMessage, error) {
|
|
|
|
msg := SetColorMapEntries{}
|
2017-06-12 14:11:23 +03:00
|
|
|
var pad [1]byte
|
|
|
|
if err := binary.Read(c, binary.BigEndian, &pad); err != nil {
|
2017-06-13 16:20:35 +03:00
|
|
|
return nil, err
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
2017-06-13 16:20:35 +03:00
|
|
|
if err := binary.Read(c, binary.BigEndian, &msg.FirstColor); err != nil {
|
|
|
|
return nil, err
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
2017-06-13 16:20:35 +03:00
|
|
|
if err := binary.Read(c, binary.BigEndian, &msg.ColorsNum); err != nil {
|
|
|
|
return nil, err
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2017-06-13 16:20:35 +03:00
|
|
|
return nil, err
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
colorMap[msg.FirstColor+i] = *color
|
|
|
|
}
|
|
|
|
c.SetColorMap(colorMap)
|
2017-06-13 16:20:35 +03:00
|
|
|
return &msg, nil
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (msg *SetColorMapEntries) Write(c Conn) error {
|
2017-06-13 16:20:35 +03:00
|
|
|
if err := binary.Write(c, binary.BigEndian, msg.Type()); err != nil {
|
2017-06-12 14:11:23 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-20 00:55:32 +03:00
|
|
|
return c.Flush()
|
2017-06-12 14:11:23 +03:00
|
|
|
}
|