From 91f5da07810983963665bd7c6503e7dabb225dd7 Mon Sep 17 00:00:00 2001 From: pigeatgarlic Date: Sat, 4 Apr 2026 10:11:21 +0700 Subject: [PATCH] init go wgc --- go/go.mod | 3 ++ go/main.go | 105 +++++++++++++++++++++++++++++++++++++++++++ go/wgc.cpp | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++ go/wgc.h | 23 ++++++++++ 4 files changed, 260 insertions(+) create mode 100644 go/go.mod create mode 100644 go/main.go create mode 100644 go/wgc.cpp create mode 100644 go/wgc.h diff --git a/go/go.mod b/go/go.mod new file mode 100644 index 00000000..6dff4e29 --- /dev/null +++ b/go/go.mod @@ -0,0 +1,3 @@ +module sunshine-wgc-go + +go 1.25.0 diff --git a/go/main.go b/go/main.go new file mode 100644 index 00000000..9448b995 --- /dev/null +++ b/go/main.go @@ -0,0 +1,105 @@ +package main + +/* +#cgo CXXFLAGS: -std=c++20 -IC:/scoop/apps/msys2/current/mingw64/include +#cgo LDFLAGS: -lwindowsapp -lwtsapi32 -ld3d11 -ldxgi +#include "wgc.h" +extern void goFrameCallback(int width, int height, void* frame_data, void* user_data); +*/ +import "C" + +import ( + "fmt" + "os" + "os/signal" + "runtime" + "runtime/cgo" + "syscall" + "unsafe" +) + +func init() { + runtime.LockOSThread() +} + +type Frame struct { + Width int + Height int +} + +type Capturer struct { + OnFrame <-chan Frame + frameChan chan Frame + handle cgo.Handle +} + +func NewCapturer() *Capturer { + c := &Capturer{ + frameChan: make(chan Frame, 10), + } + c.OnFrame = c.frameChan + return c +} + +func (c *Capturer) Start() error { + c.handle = cgo.NewHandle(c) + ret := C.start_capture((C.frame_callback_t)(unsafe.Pointer(C.goFrameCallback)), unsafe.Pointer(c.handle)) + if ret != 0 { + c.handle.Delete() + return fmt.Errorf("failed to start capture: %d", int(ret)) + } + return nil +} + +func (c *Capturer) Stop() { + C.stop_capture() + if c.handle != 0 { + c.handle.Delete() + c.handle = 0 + } +} + +func (c *Capturer) handleFrame(width, height int, data unsafe.Pointer) { + select { + case c.frameChan <- Frame{Width: width, Height: height}: + default: + // Drop frame if channel is full + } +} + +//export goFrameCallback +func goFrameCallback(width C.int, height C.int, frame_data unsafe.Pointer, user_data unsafe.Pointer) { + handle := cgo.Handle(user_data) + capturer := handle.Value().(*Capturer) + capturer.handleFrame(int(width), int(height), frame_data) +} + +func main() { + fmt.Println("Starting WGC Screen Capture in Go...") + + capturer := NewCapturer() + if err := capturer.Start(); err != nil { + fmt.Printf("Error: %v\n", err) + return + } + defer capturer.Stop() + + fmt.Println("Capture started. Press Ctrl+C to stop.") + + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) + + count := 0 + for { + select { + case frame := <-capturer.OnFrame: + count++ + if count%60 == 0 { + fmt.Printf("Received 60 frames... Latest Resolution: %dx%d\n", frame.Width, frame.Height) + } + case <-sigChan: + fmt.Println("\nStopping capture...") + return + } + } +} diff --git a/go/wgc.cpp b/go/wgc.cpp new file mode 100644 index 00000000..b27a726e --- /dev/null +++ b/go/wgc.cpp @@ -0,0 +1,129 @@ +#define ____FIReference_1_boolean_INTERFACE_DEFINED__ +#include "wgc.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace winrt::Windows::Graphics::Capture; +using namespace winrt::Windows::Graphics::DirectX::Direct3D11; + +namespace winrt { + struct +#if WINRT_IMPL_HAS_DECLSPEC_UUID + __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")) +#endif + IDirect3DDxgiInterfaceAccess: ::IUnknown { + virtual HRESULT __stdcall GetInterface(REFIID id, void **object) = 0; + }; +} + +#if !WINRT_IMPL_HAS_DECLSPEC_UUID +static constexpr GUID GUID__IDirect3DDxgiInterfaceAccess = { + 0xA9B3D012, 0x3DF2, 0x4EE3, { 0xB8, 0xD1, 0x86, 0x95, 0xF4, 0x57, 0xD3, 0xC1 } +}; +template <> +constexpr auto +__mingw_uuidof() -> GUID const & { + return GUID__IDirect3DDxgiInterfaceAccess; +} +#endif + +typedef HRESULT (WINAPI *pCreateDirect3D11DeviceFromDXGIDevice)(IDXGIDevice*, IInspectable**); + +static frame_callback_t g_callback = nullptr; +static void* g_user_data = nullptr; +static GraphicsCaptureSession g_session{ nullptr }; +static Direct3D11CaptureFramePool g_frame_pool{ nullptr }; +static Direct3D11CaptureFramePool::FrameArrived_revoker g_frame_arrived_revoker; + +extern "C" int start_capture(frame_callback_t callback, void* user_data) { + try { + winrt::init_apartment(); + + if (!GraphicsCaptureSession::IsSupported()) { + return -1; + } + + g_callback = callback; + g_user_data = user_data; + + // 1. Create D3D11 Device + winrt::com_ptr d3d_device; + HRESULT hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, nullptr, 0, D3D11_SDK_VERSION, d3d_device.put(), nullptr, nullptr); + if (FAILED(hr)) return -2; + + // 2. Create WinRT Device + winrt::com_ptr dxgi_device = d3d_device.as(); + winrt::com_ptr<::IInspectable> inspectable; + + HMODULE d3d11_dll = GetModuleHandleW(L"d3d11.dll"); + if (!d3d11_dll) d3d11_dll = LoadLibraryW(L"d3d11.dll"); + + if (d3d11_dll) { + auto func = (pCreateDirect3D11DeviceFromDXGIDevice)GetProcAddress(d3d11_dll, "CreateDirect3D11DeviceFromDXGIDevice"); + if (func) { + hr = func(dxgi_device.get(), inspectable.put()); + } else { return -3; } + } else { return -3; } + + if (FAILED(hr)) return -4; + IDirect3DDevice uwp_device = inspectable.as(); + + // 3. Select Primary Monitor + HMONITOR hMonitor = MonitorFromWindow(GetDesktopWindow(), MONITOR_DEFAULTTOPRIMARY); + + // 4. Create Capture Item + auto monitor_factory = winrt::get_activation_factory(); + GraphicsCaptureItem item{ nullptr }; + hr = monitor_factory->CreateForMonitor(hMonitor, winrt::guid_of(), winrt::put_abi(item)); + if (FAILED(hr)) return -5; + + auto size = item.Size(); + + // 5. Create Frame Pool + g_frame_pool = Direct3D11CaptureFramePool::CreateFreeThreaded(uwp_device, winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized, 2, size); + + // 6. Setup Event Handler + g_frame_arrived_revoker = g_frame_pool.FrameArrived(winrt::auto_revoke, [](Direct3D11CaptureFramePool const& sender, winrt::Windows::Foundation::IInspectable const&) { + auto frame = sender.TryGetNextFrame(); + if (frame && g_callback) { + auto size = frame.ContentSize(); + g_callback(size.Width, size.Height, winrt::get_abi(frame), g_user_data); + } + }); + + // 7. Start Session + g_session = g_frame_pool.CreateCaptureSession(item); + + if (winrt::Windows::Foundation::Metadata::ApiInformation::IsPropertyPresent(L"Windows.Graphics.Capture.GraphicsCaptureSession", L"IsBorderRequired")) { + g_session.IsBorderRequired(false); + } + + g_session.StartCapture(); + return 0; + + } catch (...) { + return -99; + } +} + + +extern "C" void stop_capture() { + if (g_session) { + g_session.Close(); + g_session = nullptr; + } + if (g_frame_pool) { + g_frame_arrived_revoker.revoke(); + g_frame_pool.Close(); + g_frame_pool = nullptr; + } +} + diff --git a/go/wgc.h b/go/wgc.h new file mode 100644 index 00000000..94880a20 --- /dev/null +++ b/go/wgc.h @@ -0,0 +1,23 @@ +#ifndef WGC_H +#define WGC_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*frame_callback_t)(int width, int height, void* frame_data, void* user_data); + +typedef struct { + int width; + int height; + int status; +} capture_status_t; + +int start_capture(frame_callback_t callback, void* user_data); +void stop_capture(); + +#ifdef __cplusplus +} +#endif + +#endif