mirror of
https://github.com/XZB-1248/Spark
synced 2025-09-27 04:26:20 +08:00
optimize: desktop viewer
This commit is contained in:
@@ -43,12 +43,12 @@ func isPrivateIP(ip _net.IP) bool {
|
|||||||
func GetLocalIP() (string, error) {
|
func GetLocalIP() (string, error) {
|
||||||
ifaces, err := _net.Interfaces()
|
ifaces, err := _net.Interfaces()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return `Unknown`, err
|
return `<UNKNOWN>`, err
|
||||||
}
|
}
|
||||||
for _, i := range ifaces {
|
for _, i := range ifaces {
|
||||||
addrs, err := i.Addrs()
|
addrs, err := i.Addrs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return `Unknown`, err
|
return `<UNKNOWN>`, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, addr := range addrs {
|
for _, addr := range addrs {
|
||||||
@@ -68,7 +68,7 @@ func GetLocalIP() (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return `Unknown`, errors.New(`no IP address found`)
|
return `<UNKNOWN>`, errors.New(`no IP address found`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMacAddress() (string, error) {
|
func GetMacAddress() (string, error) {
|
||||||
@@ -179,16 +179,16 @@ func GetDevice() (*modules.Device, error) {
|
|||||||
}
|
}
|
||||||
localIP, err := GetLocalIP()
|
localIP, err := GetLocalIP()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
localIP = `<unknown>`
|
localIP = `<UNKNOWN>`
|
||||||
}
|
}
|
||||||
macAddr, err := GetMacAddress()
|
macAddr, err := GetMacAddress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
macAddr = `<unknown>`
|
macAddr = `<UNKNOWN>`
|
||||||
}
|
}
|
||||||
cpuInfo, err := GetCPUInfo()
|
cpuInfo, err := GetCPUInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cpuInfo = modules.CPU{
|
cpuInfo = modules.CPU{
|
||||||
Model: `<unknown>`,
|
Model: `<UNKNOWN>`,
|
||||||
Usage: 0,
|
Usage: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -221,11 +221,11 @@ func GetDevice() (*modules.Device, error) {
|
|||||||
}
|
}
|
||||||
hostname, err := os.Hostname()
|
hostname, err := os.Hostname()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hostname = `<unknown>`
|
hostname = `<UNKNOWN>`
|
||||||
}
|
}
|
||||||
username, err := user.Current()
|
username, err := user.Current()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
username = &user.User{Username: `<unknown>`}
|
username = &user.User{Username: `<UNKNOWN>`}
|
||||||
} else {
|
} else {
|
||||||
slashIndex := strings.Index(username.Username, `\`)
|
slashIndex := strings.Index(username.Username, `\`)
|
||||||
if slashIndex > -1 && slashIndex+1 < len(username.Username) {
|
if slashIndex > -1 && slashIndex+1 < len(username.Username) {
|
||||||
@@ -252,7 +252,7 @@ func GetPartialInfo() (*modules.Device, error) {
|
|||||||
cpuInfo, err := GetCPUInfo()
|
cpuInfo, err := GetCPUInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cpuInfo = modules.CPU{
|
cpuInfo = modules.CPU{
|
||||||
Model: `<unknown>`,
|
Model: `<UNKNOWN>`,
|
||||||
Usage: 0,
|
Usage: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -33,8 +33,7 @@ type message struct {
|
|||||||
frame *[]*[]byte
|
frame *[]*[]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// packet explanation:
|
// frame packet format:
|
||||||
|
|
||||||
// +---------+---------+----------+-------------+----------+---------+---------+---------+---------+-------+
|
// +---------+---------+----------+-------------+----------+---------+---------+---------+---------+-------+
|
||||||
// | magic | op code | event id | body length | img type | x | y | width | height | image |
|
// | magic | op code | event id | body length | img type | x | y | width | height | image |
|
||||||
// +---------+---------+----------+-------------+----------+---------+---------+---------+---------+-------+
|
// +---------+---------+----------+-------------+----------+---------+---------+---------+---------+-------+
|
||||||
@@ -54,9 +53,9 @@ type message struct {
|
|||||||
// 0: raw image
|
// 0: raw image
|
||||||
// 1: compressed image (jpeg)
|
// 1: compressed image (jpeg)
|
||||||
|
|
||||||
const fpsLimit = 10
|
const compress = 1
|
||||||
const compress = true
|
const fpsLimit = 24
|
||||||
const blockSize = 64
|
const blockSize = 96
|
||||||
const frameBuffer = 3
|
const frameBuffer = 3
|
||||||
const displayIndex = 0
|
const displayIndex = 0
|
||||||
const imageQuality = 70
|
const imageQuality = 70
|
||||||
@@ -66,7 +65,7 @@ var working = false
|
|||||||
var sessions = cmap.New()
|
var sessions = cmap.New()
|
||||||
var prevDesktop *image.RGBA
|
var prevDesktop *image.RGBA
|
||||||
var displayBounds image.Rectangle
|
var displayBounds image.Rectangle
|
||||||
var ErrNoImage = errors.New(`DESKTOP.NO_IMAGE_YET`)
|
var errNoImage = errors.New(`DESKTOP.NO_IMAGE_YET`)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
go healthCheck()
|
go healthCheck()
|
||||||
@@ -84,65 +83,68 @@ func worker() {
|
|||||||
lock.Unlock()
|
lock.Unlock()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
screen screen
|
numErrors int
|
||||||
img *image.RGBA
|
screen Screen
|
||||||
err error
|
img *image.RGBA
|
||||||
errors int
|
err error
|
||||||
)
|
)
|
||||||
screen.init(displayIndex)
|
screen.Init(displayIndex, displayBounds)
|
||||||
for working {
|
for working {
|
||||||
if sessions.Count() == 0 {
|
if sessions.Count() == 0 {
|
||||||
lock.Lock()
|
|
||||||
working = false
|
|
||||||
lock.Unlock()
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
img = image.NewRGBA(displayBounds)
|
img, err = screen.Capture()
|
||||||
err = screen.capture(img, displayBounds)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == ErrNoImage {
|
if err == errNoImage {
|
||||||
return
|
<-time.After(time.Second / fpsLimit)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
errors++
|
numErrors++
|
||||||
if errors > 10 {
|
if numErrors > 10 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
errors = 0
|
numErrors = 0
|
||||||
diff := imageCompare(img, prevDesktop, compress)
|
diff := imageCompare(img, prevDesktop, compress)
|
||||||
if diff != nil && len(diff) > 0 {
|
if diff != nil && len(diff) > 0 {
|
||||||
prevDesktop = img
|
prevDesktop = img
|
||||||
sessions.IterCb(func(uuid string, t any) bool {
|
sendImageDiff(diff)
|
||||||
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
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
<-time.After(time.Second / fpsLimit)
|
<-time.After(time.Second / fpsLimit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
img = nil
|
||||||
prevDesktop = nil
|
prevDesktop = nil
|
||||||
if errors > 10 {
|
if numErrors > 10 {
|
||||||
quitAll(err.Error())
|
quitAllDesktop(err.Error())
|
||||||
}
|
}
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
working = false
|
working = false
|
||||||
lock.Unlock()
|
lock.Unlock()
|
||||||
screen.release()
|
screen.Release()
|
||||||
runtime.UnlockOSThread()
|
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)
|
keys := make([]string, 0)
|
||||||
sessions.IterCb(func(uuid string, t any) bool {
|
sessions.IterCb(func(uuid string, t any) bool {
|
||||||
keys = append(keys, uuid)
|
keys = append(keys, uuid)
|
||||||
@@ -157,7 +159,7 @@ func quitAll(info string) {
|
|||||||
lock.Unlock()
|
lock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func imageCompare(img, prev *image.RGBA, compress bool) []*[]byte {
|
func imageCompare(img, prev *image.RGBA, compress int) []*[]byte {
|
||||||
result := make([]*[]byte, 0)
|
result := make([]*[]byte, 0)
|
||||||
if prev == nil {
|
if prev == nil {
|
||||||
return splitFullImage(img, compress)
|
return splitFullImage(img, compress)
|
||||||
@@ -168,24 +170,13 @@ func imageCompare(img, prev *image.RGBA, compress bool) []*[]byte {
|
|||||||
}
|
}
|
||||||
for _, rect := range diff {
|
for _, rect := range diff {
|
||||||
block := getImageBlock(img, rect, compress)
|
block := getImageBlock(img, rect, compress)
|
||||||
buf := make([]byte, 12)
|
block = makeImageBlock(block, rect, compress)
|
||||||
binary.BigEndian.PutUint16(buf[0:2], uint16(len(block)+10))
|
result = append(result, &block)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitFullImage(img *image.RGBA, compress bool) []*[]byte {
|
func splitFullImage(img *image.RGBA, compress int) []*[]byte {
|
||||||
if img == nil {
|
if img == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -197,26 +188,16 @@ func splitFullImage(img *image.RGBA, compress bool) []*[]byte {
|
|||||||
height := utils.If(y+blockSize > imgHeight, imgHeight-y, blockSize)
|
height := utils.If(y+blockSize > imgHeight, imgHeight-y, blockSize)
|
||||||
for x := rect.Min.X; x < rect.Max.X; x += blockSize {
|
for x := rect.Min.X; x < rect.Max.X; x += blockSize {
|
||||||
width := utils.If(x+blockSize > imgWidth, imgWidth-x, blockSize)
|
width := utils.If(x+blockSize > imgWidth, imgWidth-x, blockSize)
|
||||||
block := getImageBlock(img, image.Rect(x, y, x+width, y+height), compress)
|
blockRect := image.Rect(x, y, x+width, y+height)
|
||||||
buf := make([]byte, 12)
|
block := getImageBlock(img, blockRect, compress)
|
||||||
binary.BigEndian.PutUint16(buf[0:2], uint16(len(block)+10))
|
block = makeImageBlock(block, blockRect, compress)
|
||||||
if compress {
|
result = append(result, &block)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
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()
|
width := rect.Dx()
|
||||||
height := rect.Dy()
|
height := rect.Dy()
|
||||||
buf := make([]byte, width*height*4)
|
buf := make([]byte, width*height*4)
|
||||||
@@ -227,17 +208,32 @@ func getImageBlock(img *image.RGBA, rect image.Rectangle, compress bool) []byte
|
|||||||
bufPos += width * 4
|
bufPos += width * 4
|
||||||
imgPos += img.Stride
|
imgPos += img.Stride
|
||||||
}
|
}
|
||||||
if !compress {
|
switch compress {
|
||||||
|
case 0:
|
||||||
return buf
|
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{
|
return nil
|
||||||
Pix: buf,
|
}
|
||||||
Stride: width * 4,
|
|
||||||
Rect: image.Rect(0, 0, width, height),
|
func makeImageBlock(block []byte, rect image.Rectangle, compress int) []byte {
|
||||||
}
|
buf := make([]byte, 12)
|
||||||
writer := &bytes.Buffer{}
|
binary.BigEndian.PutUint16(buf[0:2], uint16(len(block)+10))
|
||||||
jpeg.Encode(writer, subImg, &jpeg.Options{Quality: imageQuality})
|
binary.BigEndian.PutUint16(buf[2:4], uint16(compress))
|
||||||
return writer.Bytes()
|
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 {
|
func getDiff(img, prev *image.RGBA) []image.Rectangle {
|
||||||
@@ -312,7 +308,7 @@ func InitDesktop(pack modules.Packet) error {
|
|||||||
rawEvent: rawEvent,
|
rawEvent: rawEvent,
|
||||||
lastPack: utils.Unix,
|
lastPack: utils.Unix,
|
||||||
escape: false,
|
escape: false,
|
||||||
channel: make(chan message, 3),
|
channel: make(chan message, 5),
|
||||||
lock: &sync.Mutex{},
|
lock: &sync.Mutex{},
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -350,9 +346,7 @@ func PingDesktop(pack modules.Packet) {
|
|||||||
} else {
|
} else {
|
||||||
uuid = val.(string)
|
uuid = val.(string)
|
||||||
}
|
}
|
||||||
if val, ok := sessions.Get(uuid); !ok {
|
if val, ok := sessions.Get(uuid); ok {
|
||||||
return
|
|
||||||
} else {
|
|
||||||
desktop = val.(*session)
|
desktop = val.(*session)
|
||||||
desktop.lastPack = utils.Unix
|
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()))
|
binary.BigEndian.PutUint16(data[4:6], uint16(displayBounds.Dy()))
|
||||||
buf = append(buf, data...)
|
buf = append(buf, data...)
|
||||||
common.WSConn.SendData(buf)
|
common.WSConn.SendData(buf)
|
||||||
break
|
continue
|
||||||
}
|
}
|
||||||
case <-time.After(time.Second * 5):
|
case <-time.After(7 * time.Second):
|
||||||
default:
|
continue
|
||||||
time.Sleep(50 * time.Millisecond)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
client/service/desktop/desktop_others.go
Normal file
23
client/service/desktop/desktop_others.go
Normal file
@@ -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() {}
|
198
client/service/desktop/desktop_windows.go
Normal file
198
client/service/desktop/desktop_windows.go
Normal file
@@ -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)
|
||||||
|
}
|
@@ -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() {
|
|
||||||
}
|
|
@@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
@@ -16,7 +16,7 @@ func ListProcesses() ([]Process, error) {
|
|||||||
for i := 0; i < len(processes); i++ {
|
for i := 0; i < len(processes); i++ {
|
||||||
name, err := processes[i].Name()
|
name, err := processes[i].Name()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
name = `<Unknown>`
|
name = `<UNKNOWN>`
|
||||||
}
|
}
|
||||||
result = append(result, Process{Name: name, Pid: processes[i].Pid})
|
result = append(result, Process{Name: name, Pid: processes[i].Pid})
|
||||||
}
|
}
|
||||||
|
13
go.mod
13
go.mod
@@ -11,14 +11,15 @@ require (
|
|||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/kataras/golog v0.1.7
|
github.com/kataras/golog v0.1.7
|
||||||
github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329
|
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/rakyll/statik v0.1.7
|
||||||
github.com/shirou/gopsutil/v3 v3.22.2
|
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 (
|
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/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
github.com/go-playground/locales v0.13.0 // 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/golang/protobuf v1.3.3 // indirect
|
||||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // 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/kataras/pio v0.0.10 // indirect
|
||||||
github.com/leodido/go-urn v1.2.0 // indirect
|
github.com/leodido/go-urn v1.2.0 // indirect
|
||||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // 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/mattn/go-isatty v0.0.12 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // 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/tklauser/numcpus v0.3.0 // indirect
|
||||||
github.com/ugorji/go/codec v1.1.7 // indirect
|
github.com/ugorji/go/codec v1.1.7 // indirect
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // 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/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
|
golang.org/x/text v0.3.7 // indirect
|
||||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||||
)
|
)
|
||||||
|
14
go.sum
14
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 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
|
||||||
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
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-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-20221026125803-c33c9e32b1c8 h1:u4/UVF0sNxlqDwCptjIUTUkZW4UoZDrcHzvd2kNnF/k=
|
||||||
github.com/gen2brain/shm v0.0.0-20210511105953-083dbc7d9d83/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
|
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 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
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=
|
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/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 h1:wFZ7B0dclCQyjClP5GwXRboUGIek5l0mCpodrGgT01c=
|
||||||
github.com/imroc/req/v3 v3.8.2/go.mod h1:3JIicOKEDHfCSYYNLb/ObZNpx64EV5y40VlHMwhUCzU=
|
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 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.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 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
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/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 h1:qq2nCpSrXrmvDGRxW0ruW9BVEV1CN2a9YDOExdt+U0o=
|
||||||
github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329/go.mod h1:2VPVQDR4wO7KXHwP+DAypEy67rXf+okUx2zjgpCxZw4=
|
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/go-d3d v1.0.0 h1:i1XycQ+8KCZqToyGq7o6I92EwcJDXgIICSVjLhmHuaM=
|
||||||
github.com/kirides/screencapture v0.0.0-20211101142135-282f3f7e0f33/go.mod h1:fMSGsolzmMhah/U24dXBHSf/6Ue/mXVSIb4wRU7U4Ts=
|
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 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
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=
|
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-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-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-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.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/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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
@@ -359,7 +359,7 @@ func checkAuth() gin.HandlerFunc {
|
|||||||
|
|
||||||
if ctx.IsAborted() {
|
if ctx.IsAborted() {
|
||||||
blocked.Set(addr, now+1)
|
blocked.Set(addr, now+1)
|
||||||
user = utils.If(len(user) == 0, `EMPTY`, user)
|
user = utils.If(len(user) == 0, `<EMPTY>`, user)
|
||||||
common.Warn(ctx, `LOGIN_ATTEMPT`, `fail`, ``, map[string]any{
|
common.Warn(ctx, `LOGIN_ATTEMPT`, `fail`, ``, map[string]any{
|
||||||
`user`: user,
|
`user`: user,
|
||||||
})
|
})
|
||||||
|
@@ -15,9 +15,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
JSON = jsoniter.Config{EscapeHTML: false, SortMapKeys: true, ValidateJsonRawMessage: true}.Froze()
|
||||||
|
|
||||||
ErrEntityInvalid = errors.New(`common.ENTITY_INVALID`)
|
ErrEntityInvalid = errors.New(`common.ENTITY_INVALID`)
|
||||||
ErrFailedVerification = errors.New(`common.ENTITY_CHECK_FAILED`)
|
ErrFailedVerification = errors.New(`common.ENTITY_CHECK_FAILED`)
|
||||||
JSON = jsoniter.ConfigCompatibleWithStandardLibrary
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func If[T any](b bool, t, f T) T {
|
func If[T any](b bool, t, f T) T {
|
||||||
|
@@ -41,6 +41,7 @@ function ScreenModal(props) {
|
|||||||
function initCanvas() {
|
function initCanvas() {
|
||||||
if (!canvas) return;
|
if (!canvas) return;
|
||||||
ctx = canvas.getContext('2d', {alpha: false});
|
ctx = canvas.getContext('2d', {alpha: false});
|
||||||
|
ctx.imageSmoothingEnabled = false;
|
||||||
}
|
}
|
||||||
function construct() {
|
function construct() {
|
||||||
if (ctx !== null) {
|
if (ctx !== null) {
|
||||||
@@ -87,24 +88,11 @@ function ScreenModal(props) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function fullScreen() {
|
function fullScreen() {
|
||||||
try {
|
canvas.requestFullscreen().catch(console.error);
|
||||||
canvas.requestFullscreen();
|
|
||||||
} catch {}
|
|
||||||
try {
|
|
||||||
canvas.webkitRequestFullscreen();
|
|
||||||
} catch {}
|
|
||||||
try {
|
|
||||||
canvas.mozRequestFullScreen();
|
|
||||||
} catch {}
|
|
||||||
try {
|
|
||||||
canvas.msRequestFullscreen();
|
|
||||||
} catch {}
|
|
||||||
}
|
}
|
||||||
function refresh() {
|
function refresh() {
|
||||||
if (canvas && props.open) {
|
if (canvas && props.open) {
|
||||||
if (!conn) {
|
if (!conn) {
|
||||||
canvas.width = 1920;
|
|
||||||
canvas.height = 1080;
|
|
||||||
initCanvas(canvas);
|
initCanvas(canvas);
|
||||||
construct(canvas);
|
construct(canvas);
|
||||||
} else {
|
} else {
|
||||||
@@ -150,13 +138,18 @@ function ScreenModal(props) {
|
|||||||
dv = null;
|
dv = null;
|
||||||
}
|
}
|
||||||
function updateImage(ab, it, dx, dy, bw, bh, canvasCtx) {
|
function updateImage(ab, it, dx, dy, bw, bh, canvasCtx) {
|
||||||
if (it === 0) {
|
switch (it) {
|
||||||
canvasCtx.putImageData(new ImageData(new Uint8ClampedArray(ab), bw, bh), dx, dy, 0, 0, bw, bh);
|
case 0:
|
||||||
} else {
|
canvasCtx.putImageData(new ImageData(new Uint8ClampedArray(ab), bw, bh), dx, dy, 0, 0, bw, bh);
|
||||||
createImageBitmap(new Blob([ab]), 0, 0, bw, bh)
|
break;
|
||||||
.then((ib) => {
|
case 1:
|
||||||
canvasCtx.drawImage(ib, 0, 0, bw, bh, dx, dy, bw, bh);
|
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) {
|
function handleJSON(ab) {
|
||||||
|
Reference in New Issue
Block a user