Files
natpass/code/client/rule/vnc/link.go
2021-11-18 23:17:44 +08:00

230 lines
5.1 KiB
Go

package vnc
import (
"bytes"
"image"
"image/jpeg"
"natpass/code/client/pool"
"natpass/code/client/rule/vnc/process"
"natpass/code/network"
"natpass/code/utils"
"time"
"github.com/lwch/logging"
)
const (
zoneWidth = 64
zoneHeight = 64
)
// Link vnc link
type Link struct {
parent *VNC
id string // link id
target string // target id
targetIdx uint32 // target idx
remote *pool.Conn
// vnc
ps *process.Process
quality uint32
showCursor bool
img *image.RGBA
// runtime
sendBytes uint64
recvBytes uint64
sendPacket uint64
recvPacket uint64
idx int
reDraw bool
}
// GetID get link id
func (link *Link) GetID() string {
return link.id
}
// GetBytes get send and recv bytes
func (link *Link) GetBytes() (uint64, uint64) {
return link.recvBytes, link.sendBytes
}
// GetPackets get send and recv packets
func (link *Link) GetPackets() (uint64, uint64) {
return link.recvPacket, link.sendPacket
}
// SetTargetIdx set link remote index
func (link *Link) SetTargetIdx(idx uint32) {
link.targetIdx = idx
}
// SetQuality transfer quality
func (link *Link) SetQuality(q uint32) {
link.quality = q
link.reDraw = true
}
// SetCursor set show cursor
func (link *Link) SetCursor(b bool) {
link.showCursor = b
link.reDraw = true
}
// Fork fork worker process
func (link *Link) Fork(confDir string) error {
p, err := process.CreateWorker(link.parent.Name, confDir, link.showCursor)
if err != nil {
return err
}
link.ps = p
return nil
}
// Forward forward data
func (link *Link) Forward() {
go link.remoteRead()
go link.localRead()
}
func (link *Link) remoteRead() {
defer link.close()
ch := link.remote.ChanRead(link.id)
for {
msg := <-ch
if msg == nil {
return
}
switch msg.GetXType() {
case network.Msg_vnc_ctrl:
ctrl := msg.GetVctrl()
link.SetQuality(ctrl.GetQuality())
link.SetCursor(ctrl.GetCursor())
link.ps.SetCursor(ctrl.GetCursor())
case network.Msg_vnc_mouse:
link.ps.MouseEvent(msg.GetVmouse())
case network.Msg_vnc_keyboard:
link.ps.KeyboardEvent(msg.GetVkbd())
case network.Msg_vnc_cad:
link.ps.CADEvent()
case network.Msg_vnc_scroll:
link.ps.ScrollEvent(msg.GetVscroll())
case network.Msg_disconnect:
logging.Info("link %s disconnected", link.id)
return
}
}
}
func (link *Link) localRead() {
// TODO: exit by context
defer utils.Recover("capture")
defer link.close()
img, err := link.ps.Capture(3 * time.Second)
if err != nil {
logging.Error("capture: %v", err)
return
}
link.sendAll(img)
link.img = img
size := img.Rect
sleep := time.Second / time.Duration(link.parent.cfg.Fps)
for {
time.Sleep(sleep)
img, err = link.ps.Capture(0)
if err != nil {
logging.Error("capture: %v", err)
continue
}
if img.Rect.Dx() != size.Dx() ||
img.Rect.Dy() != size.Dy() ||
link.reDraw ||
link.idx%10000 == 0 {
link.sendAll(img)
link.reDraw = false
} else {
link.sendDiff(img)
}
link.img = img
size = img.Rect
link.idx++
}
}
func (link *Link) close() {
if link.ps != nil {
link.ps.Close()
}
link.remote.SendDisconnect(link.target, link.targetIdx, link.id)
}
func cut(src *image.RGBA, rect image.Rectangle) *image.RGBA {
size := rect.Size()
ret := image.NewRGBA(image.Rect(0, 0, size.X, size.Y))
sx := src.Bounds().Size().X * 4
dx := rect.Min.X * 4
idx := rect.Min.Y*sx + dx
for y := 0; y < size.Y; y++ {
copy(ret.Pix[y*size.X*4:], src.Pix[idx:idx+size.X*4-1])
idx += sx
}
return ret
}
func (link *Link) sendAll(img *image.RGBA) {
size := img.Bounds()
screen := image.Rect(0, 0, img.Rect.Dx(), img.Rect.Dy())
var buf bytes.Buffer
for y := 0; y < size.Max.Y; y += zoneHeight {
for x := 0; x < size.Max.X; x += zoneWidth {
width := size.Max.X - x
height := size.Max.Y - y
if width > zoneWidth {
width = zoneWidth
}
if height > zoneHeight {
height = zoneHeight
}
rect := image.Rect(x, y, x+width, y+height)
next := cut(img, rect)
if link.quality == 100 {
link.remote.SendVNCImage(link.target, link.targetIdx, link.id,
screen, rect, network.VncImage_raw, next.Pix)
continue
}
buf.Reset()
err := jpeg.Encode(&buf, next, &jpeg.Options{Quality: int(link.quality)})
if err == nil {
link.remote.SendVNCImage(link.target, link.targetIdx, link.id,
screen, rect, network.VncImage_jpeg, buf.Bytes())
} else {
link.remote.SendVNCImage(link.target, link.targetIdx, link.id,
screen, rect, network.VncImage_raw, next.Pix)
}
}
}
}
func (link *Link) sendDiff(img *image.RGBA) {
blocks := calcDiff(link.img, img)
screen := image.Rect(0, 0, img.Rect.Dx(), img.Rect.Dy())
var buf bytes.Buffer
for _, block := range blocks {
next := cut(img, block)
if link.quality == 100 {
link.remote.SendVNCImage(link.target, link.targetIdx, link.id,
screen, block, network.VncImage_raw, next.Pix)
continue
}
buf.Reset()
err := jpeg.Encode(&buf, next, &jpeg.Options{Quality: int(link.quality)})
if err == nil {
link.remote.SendVNCImage(link.target, link.targetIdx, link.id,
screen, block, network.VncImage_jpeg, buf.Bytes())
} else {
link.remote.SendVNCImage(link.target, link.targetIdx, link.id,
screen, block, network.VncImage_raw, next.Pix)
}
}
}