From 73c1b26b7e9744a6eee34ba4dd85cb7a2ac98291 Mon Sep 17 00:00:00 2001 From: XZB-1248 Date: Thu, 2 Feb 2023 16:44:27 +0800 Subject: [PATCH] optimize: desktop viewer --- client/core/device.go | 18 +- client/service/desktop/desktop.go | 167 ++++++++-------- client/service/desktop/desktop_others.go | 23 +++ client/service/desktop/desktop_windows.go | 198 +++++++++++++++++++ client/service/desktop/screenshot_others.go | 28 --- client/service/desktop/screenshot_windows.go | 66 ------- client/service/process/process.go | 2 +- go.mod | 13 +- go.sum | 14 +- server/main.go | 2 +- utils/utils.go | 3 +- web/src/components/desktop/desktop.jsx | 35 ++-- 12 files changed, 342 insertions(+), 227 deletions(-) create mode 100644 client/service/desktop/desktop_others.go create mode 100644 client/service/desktop/desktop_windows.go delete mode 100644 client/service/desktop/screenshot_others.go delete mode 100644 client/service/desktop/screenshot_windows.go diff --git a/client/core/device.go b/client/core/device.go index be30766..4604006 100644 --- a/client/core/device.go +++ b/client/core/device.go @@ -43,12 +43,12 @@ func isPrivateIP(ip _net.IP) bool { func GetLocalIP() (string, error) { ifaces, err := _net.Interfaces() if err != nil { - return `Unknown`, err + return ``, err } for _, i := range ifaces { addrs, err := i.Addrs() if err != nil { - return `Unknown`, err + return ``, err } for _, addr := range addrs { @@ -68,7 +68,7 @@ func GetLocalIP() (string, error) { } } } - return `Unknown`, errors.New(`no IP address found`) + return ``, errors.New(`no IP address found`) } func GetMacAddress() (string, error) { @@ -179,16 +179,16 @@ func GetDevice() (*modules.Device, error) { } localIP, err := GetLocalIP() if err != nil { - localIP = `` + localIP = `` } macAddr, err := GetMacAddress() if err != nil { - macAddr = `` + macAddr = `` } cpuInfo, err := GetCPUInfo() if err != nil { cpuInfo = modules.CPU{ - Model: ``, + Model: ``, Usage: 0, } } @@ -221,11 +221,11 @@ func GetDevice() (*modules.Device, error) { } hostname, err := os.Hostname() if err != nil { - hostname = `` + hostname = `` } username, err := user.Current() if err != nil { - username = &user.User{Username: ``} + username = &user.User{Username: ``} } else { slashIndex := strings.Index(username.Username, `\`) if slashIndex > -1 && slashIndex+1 < len(username.Username) { @@ -252,7 +252,7 @@ func GetPartialInfo() (*modules.Device, error) { cpuInfo, err := GetCPUInfo() if err != nil { cpuInfo = modules.CPU{ - Model: ``, + Model: ``, Usage: 0, } } diff --git a/client/service/desktop/desktop.go b/client/service/desktop/desktop.go index 7ea8536..64bcd51 100644 --- a/client/service/desktop/desktop.go +++ b/client/service/desktop/desktop.go @@ -33,8 +33,7 @@ type message struct { frame *[]*[]byte } -// packet explanation: - +// frame packet format: // +---------+---------+----------+-------------+----------+---------+---------+---------+---------+-------+ // | magic | op code | event id | body length | img type | x | y | width | height | image | // +---------+---------+----------+-------------+----------+---------+---------+---------+---------+-------+ @@ -54,9 +53,9 @@ type message struct { // 0: raw image // 1: compressed image (jpeg) -const fpsLimit = 10 -const compress = true -const blockSize = 64 +const compress = 1 +const fpsLimit = 24 +const blockSize = 96 const frameBuffer = 3 const displayIndex = 0 const imageQuality = 70 @@ -66,7 +65,7 @@ var working = false var sessions = cmap.New() var prevDesktop *image.RGBA var displayBounds image.Rectangle -var ErrNoImage = errors.New(`DESKTOP.NO_IMAGE_YET`) +var errNoImage = errors.New(`DESKTOP.NO_IMAGE_YET`) func init() { go healthCheck() @@ -84,65 +83,68 @@ func worker() { lock.Unlock() var ( - screen screen - img *image.RGBA - err error - errors int + numErrors int + screen Screen + img *image.RGBA + err error ) - screen.init(displayIndex) + screen.Init(displayIndex, displayBounds) for working { if sessions.Count() == 0 { - lock.Lock() - working = false - lock.Unlock() break } - img = image.NewRGBA(displayBounds) - err = screen.capture(img, displayBounds) + img, err = screen.Capture() if err != nil { - if err == ErrNoImage { - return + if err == errNoImage { + <-time.After(time.Second / fpsLimit) + continue } - errors++ - if errors > 10 { + numErrors++ + if numErrors > 10 { break } } else { - errors = 0 + numErrors = 0 diff := imageCompare(img, prevDesktop, compress) if diff != nil && len(diff) > 0 { prevDesktop = img - sessions.IterCb(func(uuid string, t any) bool { - desktop := t.(*session) - desktop.lock.Lock() - if !desktop.escape { - if len(desktop.channel) >= frameBuffer { - select { - case <-desktop.channel: - default: - } - } - desktop.channel <- message{t: 0, frame: &diff} - } - desktop.lock.Unlock() - return true - }) + sendImageDiff(diff) } <-time.After(time.Second / fpsLimit) } } + img = nil prevDesktop = nil - if errors > 10 { - quitAll(err.Error()) + if numErrors > 10 { + quitAllDesktop(err.Error()) } lock.Lock() working = false lock.Unlock() - screen.release() + screen.Release() runtime.UnlockOSThread() + go runtime.GC() } -func quitAll(info string) { +func sendImageDiff(diff []*[]byte) { + sessions.IterCb(func(uuid string, t any) bool { + desktop := t.(*session) + desktop.lock.Lock() + if !desktop.escape { + if len(desktop.channel) >= frameBuffer { + select { + case <-desktop.channel: + default: + } + } + desktop.channel <- message{t: 0, frame: &diff} + } + desktop.lock.Unlock() + return true + }) +} + +func quitAllDesktop(info string) { keys := make([]string, 0) sessions.IterCb(func(uuid string, t any) bool { keys = append(keys, uuid) @@ -157,7 +159,7 @@ func quitAll(info string) { lock.Unlock() } -func imageCompare(img, prev *image.RGBA, compress bool) []*[]byte { +func imageCompare(img, prev *image.RGBA, compress int) []*[]byte { result := make([]*[]byte, 0) if prev == nil { return splitFullImage(img, compress) @@ -168,24 +170,13 @@ func imageCompare(img, prev *image.RGBA, compress bool) []*[]byte { } for _, rect := range diff { block := getImageBlock(img, rect, compress) - buf := make([]byte, 12) - binary.BigEndian.PutUint16(buf[0:2], uint16(len(block)+10)) - if compress { - binary.BigEndian.PutUint16(buf[2:4], uint16(1)) - } else { - binary.BigEndian.PutUint16(buf[2:4], uint16(0)) - } - binary.BigEndian.PutUint16(buf[4:6], uint16(rect.Min.X)) - binary.BigEndian.PutUint16(buf[6:8], uint16(rect.Min.Y)) - binary.BigEndian.PutUint16(buf[8:10], uint16(rect.Size().X)) - binary.BigEndian.PutUint16(buf[10:12], uint16(rect.Size().Y)) - buf = append(buf, block...) - result = append(result, &buf) + block = makeImageBlock(block, rect, compress) + result = append(result, &block) } return result } -func splitFullImage(img *image.RGBA, compress bool) []*[]byte { +func splitFullImage(img *image.RGBA, compress int) []*[]byte { if img == nil { return nil } @@ -197,26 +188,16 @@ func splitFullImage(img *image.RGBA, compress bool) []*[]byte { height := utils.If(y+blockSize > imgHeight, imgHeight-y, blockSize) for x := rect.Min.X; x < rect.Max.X; x += blockSize { width := utils.If(x+blockSize > imgWidth, imgWidth-x, blockSize) - block := getImageBlock(img, image.Rect(x, y, x+width, y+height), compress) - buf := make([]byte, 12) - binary.BigEndian.PutUint16(buf[0:2], uint16(len(block)+10)) - if compress { - binary.BigEndian.PutUint16(buf[2:4], uint16(1)) - } else { - binary.BigEndian.PutUint16(buf[2:4], uint16(0)) - } - binary.BigEndian.PutUint16(buf[4:6], uint16(x)) - binary.BigEndian.PutUint16(buf[6:8], uint16(y)) - binary.BigEndian.PutUint16(buf[8:10], uint16(width)) - binary.BigEndian.PutUint16(buf[10:12], uint16(height)) - buf = append(buf, block...) - result = append(result, &buf) + blockRect := image.Rect(x, y, x+width, y+height) + block := getImageBlock(img, blockRect, compress) + block = makeImageBlock(block, blockRect, compress) + result = append(result, &block) } } return result } -func getImageBlock(img *image.RGBA, rect image.Rectangle, compress bool) []byte { +func getImageBlock(img *image.RGBA, rect image.Rectangle, compress int) []byte { width := rect.Dx() height := rect.Dy() buf := make([]byte, width*height*4) @@ -227,17 +208,32 @@ func getImageBlock(img *image.RGBA, rect image.Rectangle, compress bool) []byte bufPos += width * 4 imgPos += img.Stride } - if !compress { + switch compress { + case 0: return buf + case 1: + subImg := &image.RGBA{ + Pix: buf, + Stride: width * 4, + Rect: image.Rect(0, 0, width, height), + } + writer := &bytes.Buffer{} + jpeg.Encode(writer, subImg, &jpeg.Options{Quality: imageQuality}) + return writer.Bytes() } - subImg := &image.RGBA{ - Pix: buf, - Stride: width * 4, - Rect: image.Rect(0, 0, width, height), - } - writer := &bytes.Buffer{} - jpeg.Encode(writer, subImg, &jpeg.Options{Quality: imageQuality}) - return writer.Bytes() + return nil +} + +func makeImageBlock(block []byte, rect image.Rectangle, compress int) []byte { + buf := make([]byte, 12) + binary.BigEndian.PutUint16(buf[0:2], uint16(len(block)+10)) + binary.BigEndian.PutUint16(buf[2:4], uint16(compress)) + binary.BigEndian.PutUint16(buf[4:6], uint16(rect.Min.X)) + binary.BigEndian.PutUint16(buf[6:8], uint16(rect.Min.Y)) + binary.BigEndian.PutUint16(buf[8:10], uint16(rect.Size().X)) + binary.BigEndian.PutUint16(buf[10:12], uint16(rect.Size().Y)) + buf = append(buf, block...) + return buf } func getDiff(img, prev *image.RGBA) []image.Rectangle { @@ -312,7 +308,7 @@ func InitDesktop(pack modules.Packet) error { rawEvent: rawEvent, lastPack: utils.Unix, escape: false, - channel: make(chan message, 3), + channel: make(chan message, 5), lock: &sync.Mutex{}, } { @@ -350,9 +346,7 @@ func PingDesktop(pack modules.Packet) { } else { uuid = val.(string) } - if val, ok := sessions.Get(uuid); !ok { - return - } else { + if val, ok := sessions.Get(uuid); ok { desktop = val.(*session) desktop.lastPack = utils.Unix } @@ -442,11 +436,10 @@ func handleDesktop(pack modules.Packet, uuid string, desktop *session) { binary.BigEndian.PutUint16(data[4:6], uint16(displayBounds.Dy())) buf = append(buf, data...) common.WSConn.SendData(buf) - break + continue } - case <-time.After(time.Second * 5): - default: - time.Sleep(50 * time.Millisecond) + case <-time.After(7 * time.Second): + continue } } } diff --git a/client/service/desktop/desktop_others.go b/client/service/desktop/desktop_others.go new file mode 100644 index 0000000..2cc54f8 --- /dev/null +++ b/client/service/desktop/desktop_others.go @@ -0,0 +1,23 @@ +//go:build !windows +// +build !windows + +package desktop + +import ( + "github.com/kbinani/screenshot" + "image" +) + +type Screen struct { + rect image.Rectangle +} + +func (s *Screen) Init(_ uint, rect image.Rectangle) { + s.rect = rect +} + +func (s *Screen) Capture() (*image.RGBA, error) { + return screenshot.CaptureRect(s.rect) +} + +func (s *Screen) Release() {} diff --git a/client/service/desktop/desktop_windows.go b/client/service/desktop/desktop_windows.go new file mode 100644 index 0000000..cd7211a --- /dev/null +++ b/client/service/desktop/desktop_windows.go @@ -0,0 +1,198 @@ +package desktop + +import ( + "errors" + "github.com/kirides/go-d3d/d3d11" + "github.com/kirides/go-d3d/outputduplication" + "github.com/kirides/go-d3d/outputduplication/swizzle" + winDXGI "github.com/kirides/go-d3d/win" + winGDI "github.com/lxn/win" + "image" + "syscall" + "unsafe" +) + +var ( + libUser32, _ = syscall.LoadLibrary("user32.dll") + funcGetDesktopWindow, _ = syscall.GetProcAddress(syscall.Handle(libUser32), "GetDesktopWindow") + funcEnumDisplayMonitors, _ = syscall.GetProcAddress(syscall.Handle(libUser32), "EnumDisplayMonitors") + funcGetMonitorInfo, _ = syscall.GetProcAddress(syscall.Handle(libUser32), "GetMonitorInfoW") + funcEnumDisplaySettings, _ = syscall.GetProcAddress(syscall.Handle(libUser32), "EnumDisplaySettingsW") +) + +type Screen struct { + screen ScreenCapture +} +type ScreenCapture interface { + Init(uint, image.Rectangle) error + Capture() (*image.RGBA, error) + Release() +} +type ScreenDXGI struct { + rect image.Rectangle + device *d3d11.ID3D11Device + deviceCtx *d3d11.ID3D11DeviceContext + ddup *outputduplication.OutputDuplicator +} +type ScreenGDI struct { + rect image.Rectangle + width int + height int + hwnd winGDI.HWND + hdc winGDI.HDC + memoryDevice winGDI.HDC + bitmap winGDI.HBITMAP + bitmapInfo winGDI.BITMAPINFOHEADER + bitmapDataSize uintptr + hmem winGDI.HGLOBAL + memptr unsafe.Pointer +} + +func (s *Screen) Init(displayIndex uint, rect image.Rectangle) { + dxgi := ScreenDXGI{} + if dxgi.Init(displayIndex, rect) == nil { + s.screen = &dxgi + } else { + gdi := ScreenGDI{} + gdi.Init(displayIndex, rect) + s.screen = &gdi + } +} +func (s *Screen) Capture() (*image.RGBA, error) { + return s.screen.Capture() +} +func (s *Screen) Release() { + s.screen.Release() +} + +func (s *ScreenDXGI) Init(displayIndex uint, rect image.Rectangle) error { + s.rect = rect + var err error + if !winDXGI.IsValidDpiAwarenessContext(winDXGI.DpiAwarenessContextPerMonitorAwareV2) { + return errors.New("no valid DPI awareness context") + } + _, err = winDXGI.SetThreadDpiAwarenessContext(winDXGI.DpiAwarenessContextPerMonitorAwareV2) + if err != nil { + return err + } + + s.device, s.deviceCtx, err = d3d11.NewD3D11Device() + s.ddup, err = outputduplication.NewIDXGIOutputDuplication(s.device, s.deviceCtx, displayIndex) + if err != nil { + s.device.Release() + s.deviceCtx.Release() + return err + } + return nil +} +func (s *ScreenDXGI) Capture() (*image.RGBA, error) { + img := image.NewRGBA(image.Rect(0, 0, s.rect.Dx(), s.rect.Dy())) + err := s.ddup.GetImage(img, 100) + if err == outputduplication.ErrNoImageYet { + return nil, errNoImage + } + return img, err +} +func (s *ScreenDXGI) Release() { + if s.ddup != nil { + s.ddup.Release() + s.ddup = nil + } + if s.device != nil { + s.device.Release() + s.device = nil + } + if s.deviceCtx != nil { + s.deviceCtx.Release() + s.deviceCtx = nil + } +} + +func (s *ScreenGDI) Init(_ uint, rect image.Rectangle) error { + s.rect = rect + s.width = rect.Dx() + s.height = rect.Dy() + + s.hwnd = getDesktopWindow() + s.hdc = winGDI.GetDC(s.hwnd) + if s.hdc == 0 { + s.Release() + return errors.New("GetDC failed") + } + s.memoryDevice = winGDI.CreateCompatibleDC(s.hdc) + if s.memoryDevice == 0 { + s.Release() + return errors.New("CreateCompatibleDC failed") + } + s.bitmap = winGDI.CreateCompatibleBitmap(s.hdc, int32(s.width), int32(s.height)) + if s.bitmap == 0 { + s.Release() + return errors.New("CreateCompatibleBitmap failed") + } + + s.bitmapInfo = winGDI.BITMAPINFOHEADER{} + s.bitmapInfo.BiSize = uint32(unsafe.Sizeof(s.bitmapInfo)) + s.bitmapInfo.BiPlanes = 1 + s.bitmapInfo.BiBitCount = 32 + s.bitmapInfo.BiWidth = int32(s.width) + s.bitmapInfo.BiHeight = -int32(s.height) + s.bitmapInfo.BiCompression = winGDI.BI_RGB + s.bitmapInfo.BiSizeImage = uint32(s.width * s.height * 4) + + s.bitmapDataSize = uintptr(((int64(s.width)*int64(s.bitmapInfo.BiBitCount) + 31) / 32) * 4 * int64(s.height)) + s.hmem = winGDI.GlobalAlloc(winGDI.GMEM_MOVEABLE, s.bitmapDataSize) + if s.hmem == 0 { + s.Release() + return errors.New("GlobalAlloc failed") + } + s.memptr = winGDI.GlobalLock(s.hmem) + if s.memptr == nil { + s.Release() + return errors.New("GlobalLock failed") + } + return nil +} +func (s *ScreenGDI) Capture() (*image.RGBA, error) { + old := winGDI.SelectObject(s.memoryDevice, winGDI.HGDIOBJ(s.bitmap)) + if old == 0 { + return nil, errors.New("SelectObject failed") + } + + if !winGDI.BitBlt(s.memoryDevice, 0, 0, int32(s.width), int32(s.height), s.hdc, int32(s.rect.Min.X), int32(s.rect.Min.Y), winGDI.SRCCOPY) { + return nil, errors.New("BitBlt failed") + } + + if winGDI.GetDIBits(s.hdc, s.bitmap, 0, uint32(s.height), (*uint8)(s.memptr), (*winGDI.BITMAPINFO)(unsafe.Pointer(&s.bitmapInfo)), winGDI.DIB_RGB_COLORS) == 0 { + return nil, errors.New("GetDIBits failed") + } + + img := image.NewRGBA(image.Rect(0, 0, s.width, s.height)) + imageBytes := ((*[1 << 30]byte)(unsafe.Pointer(s.memptr)))[:s.bitmapDataSize:s.bitmapDataSize] + copy(img.Pix[:s.bitmapDataSize], imageBytes) + swizzle.BGRA(img.Pix) + + return img, nil +} +func (s *ScreenGDI) Release() { + if s.hdc != 0 { + winGDI.ReleaseDC(s.hwnd, s.hdc) + s.hdc = 0 + } + if s.memoryDevice != 0 { + winGDI.DeleteDC(s.memoryDevice) + s.memoryDevice = 0 + } + if s.bitmap != 0 { + winGDI.DeleteObject(winGDI.HGDIOBJ(s.bitmap)) + s.bitmap = 0 + } + if s.hmem != 0 { + winGDI.GlobalUnlock(s.hmem) + winGDI.GlobalFree(s.hmem) + s.hmem = 0 + } +} +func getDesktopWindow() winGDI.HWND { + ret, _, _ := syscall.SyscallN(funcGetDesktopWindow) + return winGDI.HWND(ret) +} diff --git a/client/service/desktop/screenshot_others.go b/client/service/desktop/screenshot_others.go deleted file mode 100644 index d9a86bd..0000000 --- a/client/service/desktop/screenshot_others.go +++ /dev/null @@ -1,28 +0,0 @@ -//go:build !windows -// +build !windows - -package desktop - -import ( - "github.com/kbinani/screenshot" - "image" -) - -type screen struct { - displayIndex int -} - -func (s *screen) init(displayIndex int) { - s.displayIndex = displayIndex -} - -func (s *screen) capture(img *image.RGBA, _ image.Rectangle) error { - image, err := screenshot.CaptureDisplay(displayIndex) - if err == nil { - *img = *image - } - return err -} - -func (s *screen) release() { -} diff --git a/client/service/desktop/screenshot_windows.go b/client/service/desktop/screenshot_windows.go deleted file mode 100644 index 1ccabc0..0000000 --- a/client/service/desktop/screenshot_windows.go +++ /dev/null @@ -1,66 +0,0 @@ -//go:build windows -// +build windows - -package desktop - -import ( - "github.com/kirides/screencapture/d3d" - "github.com/kirides/screencapture/screenshot" - "github.com/kirides/screencapture/win" - "image" -) - -type screen struct { - dxgi bool - ddup *d3d.OutputDuplicator - device *d3d.ID3D11Device - deviceCtx *d3d.ID3D11DeviceContext - displayIndex int -} - -func (s *screen) init(displayIndex int) { - var err error - s.displayIndex = displayIndex - s.dxgi = false - return - if win.IsValidDpiAwarenessContext(win.DpiAwarenessContextPerMonitorAwareV2) { - _, err = win.SetThreadDpiAwarenessContext(win.DpiAwarenessContextPerMonitorAwareV2) - s.dxgi = err == nil - } - if s.dxgi { - s.device, s.deviceCtx, err = d3d.NewD3D11Device() - s.ddup, err = d3d.NewIDXGIOutputDuplication(s.device, s.deviceCtx, uint(displayIndex)) - if err != nil { - s.dxgi = false - s.device.Release() - s.deviceCtx.Release() - } - } -} - -func (s *screen) capture(img *image.RGBA, bounds image.Rectangle) error { - var err error - if s.dxgi { - err = s.ddup.GetImage(img, 100) - if err != nil { - if err == d3d.ErrNoImageYet { - return ErrNoImage - } - return err - } - return nil - } - return screenshot.CaptureImg(img, 0, 0, bounds.Dx(), bounds.Dy()) -} - -func (s *screen) release() { - if s.ddup != nil { - s.ddup.Release() - } - if s.device != nil { - s.device.Release() - } - if s.deviceCtx != nil { - s.deviceCtx.Release() - } -} diff --git a/client/service/process/process.go b/client/service/process/process.go index 7e858be..ab3671d 100644 --- a/client/service/process/process.go +++ b/client/service/process/process.go @@ -16,7 +16,7 @@ func ListProcesses() ([]Process, error) { for i := 0; i < len(processes); i++ { name, err := processes[i].Name() if err != nil { - name = `` + name = `` } result = append(result, Process{Name: name, Pid: processes[i].Pid}) } diff --git a/go.mod b/go.mod index dfde537..e2f4b7c 100644 --- a/go.mod +++ b/go.mod @@ -11,14 +11,15 @@ require ( github.com/json-iterator/go v1.1.12 github.com/kataras/golog v0.1.7 github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329 + github.com/kirides/go-d3d v1.0.0 + github.com/lxn/win v0.0.0-20210218163916-a377121e959e github.com/rakyll/statik v0.1.7 github.com/shirou/gopsutil/v3 v3.22.2 + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 ) -require github.com/kirides/screencapture v0.0.0-20211101142135-282f3f7e0f33 - require ( - github.com/gen2brain/shm v0.0.0-20210511105953-083dbc7d9d83 // indirect + github.com/gen2brain/shm v0.0.0-20221026125803-c33c9e32b1c8 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-playground/locales v0.13.0 // indirect @@ -27,11 +28,10 @@ require ( github.com/golang/protobuf v1.3.3 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 // indirect + github.com/jezek/xgb v1.1.0 // indirect github.com/kataras/pio v0.0.10 // indirect github.com/leodido/go-urn v1.2.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect - github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect github.com/mattn/go-isatty v0.0.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -40,9 +40,8 @@ require ( github.com/tklauser/numcpus v0.3.0 // indirect github.com/ugorji/go/codec v1.1.7 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect golang.org/x/net v0.0.0-20220111093109-d55c255bac03 // indirect - golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect + golang.org/x/sys v0.3.0 // indirect golang.org/x/text v0.3.7 // indirect gopkg.in/yaml.v2 v2.2.8 // indirect ) diff --git a/go.sum b/go.sum index 47d15fa..ce689d4 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ= github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI= github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo= -github.com/gen2brain/shm v0.0.0-20210511105953-083dbc7d9d83 h1:fRNwUddc/xxdx5kQ38X4+q/Grnqlp9zfV/ssKzSzVk0= -github.com/gen2brain/shm v0.0.0-20210511105953-083dbc7d9d83/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo= +github.com/gen2brain/shm v0.0.0-20221026125803-c33c9e32b1c8 h1:u4/UVF0sNxlqDwCptjIUTUkZW4UoZDrcHzvd2kNnF/k= +github.com/gen2brain/shm v0.0.0-20221026125803-c33c9e32b1c8/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= @@ -36,8 +36,9 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/imroc/req/v3 v3.8.2 h1:wFZ7B0dclCQyjClP5GwXRboUGIek5l0mCpodrGgT01c= github.com/imroc/req/v3 v3.8.2/go.mod h1:3JIicOKEDHfCSYYNLb/ObZNpx64EV5y40VlHMwhUCzU= -github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 h1:dy+DS31tGEGCsZzB45HmJJNHjur8GDgtRNX9U7HnSX4= github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240/go.mod h1:3P4UH/k22rXyHIJD2w4h2XMqPX4Of/eySEZq9L6wqc4= +github.com/jezek/xgb v1.1.0 h1:wnpxJzP1+rkbGclEkmwpVFQWpuE2PUGNUzP8SbfFobk= +github.com/jezek/xgb v1.1.0/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -47,8 +48,8 @@ github.com/kataras/pio v0.0.10 h1:b0qtPUqOpM2O+bqa5wr2O6dN4cQNwSmFd6HQqgVae0g= github.com/kataras/pio v0.0.10/go.mod h1:gS3ui9xSD+lAUpbYnjOGiQyY7sUMJO+EHpiRzhtZ5no= github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329 h1:qq2nCpSrXrmvDGRxW0ruW9BVEV1CN2a9YDOExdt+U0o= github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329/go.mod h1:2VPVQDR4wO7KXHwP+DAypEy67rXf+okUx2zjgpCxZw4= -github.com/kirides/screencapture v0.0.0-20211101142135-282f3f7e0f33 h1:DXPbp2f7LBZzkWnpPGUG+H1+82++20vNIpalTamWv6E= -github.com/kirides/screencapture v0.0.0-20211101142135-282f3f7e0f33/go.mod h1:fMSGsolzmMhah/U24dXBHSf/6Ue/mXVSIb4wRU7U4Ts= +github.com/kirides/go-d3d v1.0.0 h1:i1XycQ+8KCZqToyGq7o6I92EwcJDXgIICSVjLhmHuaM= +github.com/kirides/go-d3d v1.0.0/go.mod h1:KUNIIJoB+psyPedDBv8TY4gKXmPJT3K1+F2nL2pKDPQ= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= @@ -100,8 +101,9 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY= golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= diff --git a/server/main.go b/server/main.go index 4335d28..e03af4c 100644 --- a/server/main.go +++ b/server/main.go @@ -359,7 +359,7 @@ func checkAuth() gin.HandlerFunc { if ctx.IsAborted() { blocked.Set(addr, now+1) - user = utils.If(len(user) == 0, `EMPTY`, user) + user = utils.If(len(user) == 0, ``, user) common.Warn(ctx, `LOGIN_ATTEMPT`, `fail`, ``, map[string]any{ `user`: user, }) diff --git a/utils/utils.go b/utils/utils.go index c49fb3b..ac14592 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -15,9 +15,10 @@ import ( ) var ( + JSON = jsoniter.Config{EscapeHTML: false, SortMapKeys: true, ValidateJsonRawMessage: true}.Froze() + ErrEntityInvalid = errors.New(`common.ENTITY_INVALID`) ErrFailedVerification = errors.New(`common.ENTITY_CHECK_FAILED`) - JSON = jsoniter.ConfigCompatibleWithStandardLibrary ) func If[T any](b bool, t, f T) T { diff --git a/web/src/components/desktop/desktop.jsx b/web/src/components/desktop/desktop.jsx index 4cff46e..18f6a2d 100644 --- a/web/src/components/desktop/desktop.jsx +++ b/web/src/components/desktop/desktop.jsx @@ -41,6 +41,7 @@ function ScreenModal(props) { function initCanvas() { if (!canvas) return; ctx = canvas.getContext('2d', {alpha: false}); + ctx.imageSmoothingEnabled = false; } function construct() { if (ctx !== null) { @@ -87,24 +88,11 @@ function ScreenModal(props) { } } function fullScreen() { - try { - canvas.requestFullscreen(); - } catch {} - try { - canvas.webkitRequestFullscreen(); - } catch {} - try { - canvas.mozRequestFullScreen(); - } catch {} - try { - canvas.msRequestFullscreen(); - } catch {} + canvas.requestFullscreen().catch(console.error); } function refresh() { if (canvas && props.open) { if (!conn) { - canvas.width = 1920; - canvas.height = 1080; initCanvas(canvas); construct(canvas); } else { @@ -150,13 +138,18 @@ function ScreenModal(props) { dv = null; } function updateImage(ab, it, dx, dy, bw, bh, canvasCtx) { - if (it === 0) { - canvasCtx.putImageData(new ImageData(new Uint8ClampedArray(ab), bw, bh), dx, dy, 0, 0, bw, bh); - } else { - createImageBitmap(new Blob([ab]), 0, 0, bw, bh) - .then((ib) => { - canvasCtx.drawImage(ib, 0, 0, bw, bh, dx, dy, bw, bh); - }); + switch (it) { + case 0: + canvasCtx.putImageData(new ImageData(new Uint8ClampedArray(ab), bw, bh), dx, dy, 0, 0, bw, bh); + break; + case 1: + createImageBitmap(new Blob([ab]), 0, 0, bw, bh, { + premultiplyAlpha: 'none', + colorSpaceConversion: 'none' + }).then((ib) => { + canvasCtx.drawImage(ib, 0, 0, bw, bh, dx, dy, bw, bh); + }); + break; } } function handleJSON(ab) {