- modules/Screen_Record/wgc_source.jai: the screen through Windows.Graphics.Capture, with hand-written bindings for its WinRT interfaces (capture item interop, item, free-threaded frame pool, frame, session 1-3, IDirect3DDxgiInterfaceAccess, IClosable). A small COM handler on FrameArrived sets an event per frame; when Windows refuses it the pool is polled each millisecond. The cursor is drawn by Windows; the yellow border is turned off where the system allows (Windows 11). A screen that changes size makes the pool again. WinRT's own calls are loaded from combase.dll at run time: runtimeobject.lib names no DLL for #library. - capture_gpu.jai: what both sources share (the device on the screen's adapter, the region copy, the staging ring), so each is only "the next image's region into a texture". - Record_Options.capture: AUTO (Desktop Duplication, else Windows.Graphics.Capture when it says fall_back), DXGI or WGC. Sources open on the capture thread, as WinRT objects belong to their apartment; record_start waits for it to say how that went. Record_Options.testing simulates the two-GPU failure, forces polling, or keeps the border, for record-probe. - record-probe: -capture auto|dxgi|wgc, -simulate-unsupported, -poll, -border-test with -keep-border as its control, -cursor-test; DPI awareness set already is no longer reported as a failure. - On the RTX 5080 laptop: auto falls back on the simulated failure; WGC records 4K at 60 fps with nothing dropped; its cursor and the one cursor.jai draws for duplication are the same; the border is off for an unpackaged app (0 yellow edge pixels, 14,214 with it kept); WDA_EXCLUDEFROMCAPTURE is respected; polling holds 30 fps. The plan records the results. - README: Screen_Record's line, which held a carriage return in place of "\r" since 0.1.3.
407 lines
18 KiB
Plaintext
407 lines
18 KiB
Plaintext
// Windows.Graphics.Capture: the screen as Windows' own capture API hands it over, for when Desktop
|
|
// Duplication cannot run (dxgi_source.jai). It works across GPUs, hands over frames the right way up
|
|
// on a rotated screen, and draws the cursor in itself.
|
|
//
|
|
// It is WinRT, bound here by hand: the few interfaces it needs, as vtables after IInspectable's six
|
|
// methods. Everything is made and used on the capture thread, which is in the multithreaded apartment
|
|
// (RoInitialize), with a free-threaded frame pool, so no dispatcher queue or message loop is needed.
|
|
//
|
|
// A frame's arrival sets an event, through a small handler object (Frame_Handler); if Windows will not
|
|
// take the handler, the pool is polled every millisecond instead. Windows 11 lets the yellow border it
|
|
// draws around a screen being captured be turned off (IsBorderRequired); Windows 10 always draws it.
|
|
|
|
Wgc_Source :: struct {
|
|
#as using gpu: Capture_Gpu;
|
|
screen_size: Size_Int32; // the frames' size, as the pool was made for
|
|
item: *Capture_Item;
|
|
winrt_device: *IInspectable; // IDirect3DDevice, over gpu.device
|
|
pool: *Frame_Pool;
|
|
session: *Capture_Session;
|
|
handler: Frame_Handler;
|
|
handler_token: s64;
|
|
polling: bool; // Windows would not take the handler
|
|
border: string; // what came of turning the border off, for the log
|
|
}
|
|
|
|
// Starts capturing the screen. region is in the screen's pixels and lies on it.
|
|
wgc_open :: (s: *Wgc_Source, screen: Record_Screen, region: RECT, cursor: bool, testing: Record_Testing = 0) -> ok: bool, error: string {
|
|
ok, error := gpu_open(s, screen, region);
|
|
if !ok return false, error;
|
|
|
|
session_statics: *Session_Statics;
|
|
hr := activation_factory("Windows.Graphics.Capture.GraphicsCaptureSession", IID_SESSION_STATICS, xx *session_statics);
|
|
if FAILED(hr) return false, hresult_text("Windows.Graphics.Capture is not on this system", hr);
|
|
supported: u8;
|
|
session_statics.vtable.IsSupported(session_statics, *supported);
|
|
com_release(*session_statics);
|
|
if !supported return false, "Windows.Graphics.Capture is not supported here";
|
|
|
|
interop: *Capture_Item_Interop;
|
|
hr = activation_factory("Windows.Graphics.Capture.GraphicsCaptureItem", IID_ITEM_INTEROP, xx *interop);
|
|
if FAILED(hr) return false, hresult_text("IGraphicsCaptureItemInterop", hr);
|
|
hr = interop.vtable.CreateForMonitor(interop, screen.monitor, *IID_ITEM, xx *s.item);
|
|
com_release(*interop);
|
|
if FAILED(hr) return false, hresult_text("a capture item for the screen", hr);
|
|
s.item.vtable.get_Size(s.item, *s.screen_size);
|
|
desktop_width := screen.desktop.right - screen.desktop.left;
|
|
desktop_height := screen.desktop.bottom - screen.desktop.top;
|
|
if s.screen_size.width != desktop_width || s.screen_size.height != desktop_height {
|
|
return false, tprint("the screen is % x %, but this process sees it as % x %: it must be aware of each monitor's DPI",
|
|
s.screen_size.width, s.screen_size.height, desktop_width, desktop_height);
|
|
}
|
|
|
|
dxgi_device: *IDXGIDevice;
|
|
hr = IUnknown_QueryInterface(s.device, *uid(IDXGIDevice_UUID), xx *dxgi_device);
|
|
if FAILED(hr) return false, hresult_text("the device's IDXGIDevice", hr);
|
|
inspectable: *IInspectable;
|
|
hr = CreateDirect3D11DeviceFromDXGIDevice(dxgi_device, *inspectable);
|
|
com_release(*dxgi_device);
|
|
if FAILED(hr) return false, hresult_text("CreateDirect3D11DeviceFromDXGIDevice", hr);
|
|
hr = inspectable.vtable.QueryInterface(inspectable, *IID_DIRECT3D_DEVICE, xx *s.winrt_device);
|
|
com_release(*inspectable);
|
|
if FAILED(hr) return false, hresult_text("the device as IDirect3DDevice", hr);
|
|
|
|
pool_statics: *Frame_Pool_Statics2;
|
|
hr = activation_factory("Windows.Graphics.Capture.Direct3D11CaptureFramePool", IID_POOL_STATICS2, xx *pool_statics);
|
|
if FAILED(hr) return false, hresult_text("Direct3D11CaptureFramePool's statics", hr);
|
|
hr = pool_statics.vtable.CreateFreeThreaded(pool_statics, s.winrt_device, PIXEL_FORMAT_B8G8R8A8, POOL_BUFFERS, size_packed(s.screen_size), *s.pool);
|
|
com_release(*pool_statics);
|
|
if FAILED(hr) return false, hresult_text("a free-threaded frame pool", hr);
|
|
hr = s.pool.vtable.CreateCaptureSession(s.pool, s.item, *s.session);
|
|
if FAILED(hr) return false, hresult_text("a capture session", hr);
|
|
|
|
// The cursor (Windows 10 2004 on) and the border (Windows 11): optional, by what the system has.
|
|
session2: *Capture_Session2;
|
|
if SUCCEEDED(s.session.vtable.QueryInterface(s.session, *IID_SESSION2, xx *session2)) {
|
|
session2.vtable.put_IsCursorCaptureEnabled(session2, xx cursor);
|
|
com_release(*session2);
|
|
}
|
|
session3: *Capture_Session3;
|
|
if testing & .KEEP_BORDER {
|
|
s.border = "border left on";
|
|
} else if SUCCEEDED(s.session.vtable.QueryInterface(s.session, *IID_SESSION3, xx *session3)) {
|
|
hr = session3.vtable.put_IsBorderRequired(session3, 0);
|
|
s.border = ifx SUCCEEDED(hr) then "border off" else hresult_text("the border could not be turned off", hr);
|
|
com_release(*session3);
|
|
} else {
|
|
s.border = "border always drawn (no IGraphicsCaptureSession3: Windows 10)";
|
|
}
|
|
|
|
s.handler.vtable = *frame_handler_vtable;
|
|
s.handler.event = CreateEventW(null, 0, 0, null);
|
|
hr = ifx testing & .WGC_POLL then cast,no_check(HRESULT) 0x80004002 else s.pool.vtable.add_FrameArrived(s.pool, *s.handler, *s.handler_token);
|
|
if FAILED(hr) {
|
|
s.polling = true;
|
|
timeBeginPeriod(1); // so polling every millisecond is every millisecond
|
|
log("[record] FrameArrived would not take the handler (%); polling the frame pool.", hresult_text("add_FrameArrived", hr));
|
|
}
|
|
|
|
hr = s.session.vtable.StartCapture(s.session);
|
|
if FAILED(hr) return false, hresult_text("StartCapture", hr);
|
|
return true, "";
|
|
}
|
|
|
|
// Waits up to timeout_ms for a new frame; the newest's region goes into s.latest.
|
|
wgc_next :: (s: *Wgc_Source, timeout_ms: u32) -> Capture_Next, error: string {
|
|
frame := wgc_take_newest(s);
|
|
if !frame {
|
|
if s.polling {
|
|
start := ticks_now();
|
|
frequency: LARGE_INTEGER;
|
|
QueryPerformanceFrequency(*frequency);
|
|
while !frame && (ticks_now() - start) * 1000 / frequency.QuadPart < timeout_ms {
|
|
sleep_milliseconds(1);
|
|
frame = wgc_take_newest(s);
|
|
}
|
|
} else {
|
|
WaitForSingleObject(s.handler.event, timeout_ms);
|
|
frame = wgc_take_newest(s);
|
|
}
|
|
}
|
|
if !frame return .NONE, "";
|
|
defer close_and_release(frame);
|
|
|
|
// The screen changed size (a new resolution): the pool is made again; the region must still fit.
|
|
size: Size_Int32;
|
|
frame.vtable.get_ContentSize(frame, *size);
|
|
if size.width != s.screen_size.width || size.height != s.screen_size.height {
|
|
s.screen_size = size;
|
|
hr := s.pool.vtable.Recreate(s.pool, s.winrt_device, PIXEL_FORMAT_B8G8R8A8, POOL_BUFFERS, size_packed(size));
|
|
if FAILED(hr) return .FAILED, hresult_text("the frame pool could not be made again", hr);
|
|
if cast(s32) s.box.right > size.width || cast(s32) s.box.bottom > size.height
|
|
return .FAILED, tprint("the screen is now % x %, and the region is not on it", size.width, size.height);
|
|
return .NONE, "";
|
|
}
|
|
|
|
surface: *IInspectable;
|
|
hr := frame.vtable.get_Surface(frame, *surface);
|
|
if FAILED(hr) return .FAILED, hresult_text("the frame's surface", hr);
|
|
defer com_release(*surface);
|
|
access: *Dxgi_Interface_Access;
|
|
hr = surface.vtable.QueryInterface(surface, *IID_DXGI_INTERFACE_ACCESS, xx *access);
|
|
if FAILED(hr) return .FAILED, hresult_text("the surface's IDirect3DDxgiInterfaceAccess", hr);
|
|
defer com_release(*access);
|
|
texture: *ID3D11Texture2D;
|
|
hr = access.vtable.GetInterface(access, *uid(ID3D11Texture2D_UUID), xx *texture);
|
|
if FAILED(hr) return .FAILED, hresult_text("the surface's texture", hr);
|
|
gpu_copy_region(s, texture);
|
|
IUnknown_Release(texture);
|
|
return .IMAGE, "";
|
|
}
|
|
|
|
wgc_close :: (s: *Wgc_Source) {
|
|
if s.pool && s.handler_token s.pool.vtable.remove_FrameArrived(s.pool, s.handler_token);
|
|
if s.session close_and_release(s.session);
|
|
if s.pool close_and_release(s.pool);
|
|
s.session, s.pool = null, null;
|
|
com_release(*s.item);
|
|
com_release(*s.winrt_device);
|
|
if s.handler.event CloseHandle(s.handler.event);
|
|
if s.polling timeEndPeriod(1);
|
|
gpu_close(s);
|
|
s.* = .{};
|
|
}
|
|
|
|
// The thread's apartment: WinRT on this thread from here on.
|
|
wgc_thread_start :: () -> ok: bool {
|
|
if !winrt_load() return false;
|
|
return SUCCEEDED(winrt.RoInitialize(RO_INIT_MULTITHREADED));
|
|
}
|
|
|
|
wgc_thread_end :: () {
|
|
if winrt.RoUninitialize winrt.RoUninitialize();
|
|
}
|
|
|
|
#scope_file
|
|
|
|
POOL_BUFFERS :: 2;
|
|
PIXEL_FORMAT_B8G8R8A8 :: 87; // DirectXPixelFormat.B8G8R8A8UIntNormalized
|
|
RO_INIT_MULTITHREADED :: 1;
|
|
|
|
// The pool's newest frame, the older ones let go; null when there is none.
|
|
wgc_take_newest :: (s: *Wgc_Source) -> *Capture_Frame {
|
|
newest: *Capture_Frame;
|
|
while true {
|
|
frame: *Capture_Frame;
|
|
if FAILED(s.pool.vtable.TryGetNextFrame(s.pool, *frame)) || !frame break;
|
|
if newest close_and_release(newest);
|
|
newest = frame;
|
|
}
|
|
return newest;
|
|
}
|
|
|
|
// IClosable.Close, which hands a frame back to the pool at once, then Release.
|
|
close_and_release :: (object: *$T) {
|
|
closable: *Closable;
|
|
if SUCCEEDED(object.vtable.QueryInterface(object, *IID_CLOSABLE, xx *closable)) {
|
|
closable.vtable.Close(closable);
|
|
closable.vtable.Release(closable);
|
|
}
|
|
object.vtable.Release(object);
|
|
}
|
|
|
|
activation_factory :: (class_name: string, iid: GUID, factory: **void) -> HRESULT {
|
|
wide := utf8_to_wide(class_name,, temp);
|
|
length: u32;
|
|
while wide[length] length += 1;
|
|
name: HSTRING;
|
|
if !winrt.loaded return cast,no_check(HRESULT) 0x80004001; // E_NOTIMPL: no WinRT on this system
|
|
hr := winrt.WindowsCreateString(wide, length, *name);
|
|
if FAILED(hr) return hr;
|
|
defer winrt.WindowsDeleteString(name);
|
|
iid_copy := iid;
|
|
return winrt.RoGetActivationFactory(name, *iid_copy, factory);
|
|
}
|
|
|
|
size_packed :: (size: Size_Int32) -> u64 { // SizeInt32 by value: 8 bytes, in one register
|
|
width := cast(u64) cast,no_check(u32) size.width;
|
|
height := cast(u64) cast,no_check(u32) size.height;
|
|
return width | (height << 32);
|
|
}
|
|
|
|
// ---------------------------------------------------------------- the FrameArrived handler
|
|
|
|
// TypedEventHandler<Direct3D11CaptureFramePool, Object>: a COM object of our own whose Invoke sets an
|
|
// event. It lives in the Wgc_Source and is never freed by its count; it is unregistered first.
|
|
Frame_Handler :: struct {
|
|
vtable: *Frame_Handler_VTable;
|
|
event: HANDLE;
|
|
}
|
|
|
|
Frame_Handler_VTable :: struct {
|
|
QueryInterface: (this: *Frame_Handler, iid: *GUID, out: **void) -> HRESULT #c_call;
|
|
AddRef: (this: *Frame_Handler) -> u32 #c_call;
|
|
Release: (this: *Frame_Handler) -> u32 #c_call;
|
|
Invoke: (this: *Frame_Handler, sender: *void, args: *void) -> HRESULT #c_call;
|
|
}
|
|
|
|
frame_handler_vtable := Frame_Handler_VTable.{handler_query, handler_add_ref, handler_release, handler_invoke};
|
|
|
|
handler_query :: (this: *Frame_Handler, iid: *GUID, out: **void) -> HRESULT #c_call {
|
|
if guid_equal(iid, *IID_UNKNOWN) || guid_equal(iid, *IID_AGILE_OBJECT) || guid_equal(iid, *IID_FRAME_ARRIVED_HANDLER) {
|
|
out.* = this;
|
|
return 0;
|
|
}
|
|
out.* = null;
|
|
return cast,no_check(HRESULT) 0x80004002; // E_NOINTERFACE
|
|
}
|
|
|
|
handler_add_ref :: (this: *Frame_Handler) -> u32 #c_call { return 1; }
|
|
handler_release :: (this: *Frame_Handler) -> u32 #c_call { return 1; }
|
|
|
|
handler_invoke :: (this: *Frame_Handler, sender: *void, args: *void) -> HRESULT #c_call {
|
|
SetEvent(this.event);
|
|
return 0;
|
|
}
|
|
|
|
guid_equal :: (a: *GUID, b: *GUID) -> bool #c_call {
|
|
if a.Data1 != b.Data1 || a.Data2 != b.Data2 || a.Data3 != b.Data3 return false;
|
|
for 0..7 if a.Data4[it] != b.Data4[it] return false;
|
|
return true;
|
|
}
|
|
|
|
// ---------------------------------------------------------------- bindings
|
|
|
|
d3d11_dll :: #library,system "d3d11";
|
|
kernel32_dll :: #library,system "kernel32";
|
|
|
|
HSTRING :: *void;
|
|
|
|
// WinRT's own calls, from combase.dll at run time: its import library is runtimeobject.lib, which
|
|
// names no DLL of its own for #library to find.
|
|
winrt: struct {
|
|
loaded: bool;
|
|
RoInitialize: (init_type: s32) -> HRESULT #c_call;
|
|
RoUninitialize: () #c_call;
|
|
RoGetActivationFactory: (class_id: HSTRING, iid: *GUID, factory: **void) -> HRESULT #c_call;
|
|
WindowsCreateString: (source: *u16, length: u32, out: *HSTRING) -> HRESULT #c_call;
|
|
WindowsDeleteString: (text: HSTRING) -> HRESULT #c_call;
|
|
}
|
|
|
|
winrt_load :: () -> bool {
|
|
if winrt.loaded return true;
|
|
library := LoadLibraryW(utf8_to_wide("combase.dll",, temp));
|
|
if !library return false;
|
|
winrt.RoInitialize = xx GetProcAddress(library, "RoInitialize");
|
|
winrt.RoUninitialize = xx GetProcAddress(library, "RoUninitialize");
|
|
winrt.RoGetActivationFactory = xx GetProcAddress(library, "RoGetActivationFactory");
|
|
winrt.WindowsCreateString = xx GetProcAddress(library, "WindowsCreateString");
|
|
winrt.WindowsDeleteString = xx GetProcAddress(library, "WindowsDeleteString");
|
|
winrt.loaded = winrt.RoInitialize && winrt.RoUninitialize && winrt.RoGetActivationFactory && winrt.WindowsCreateString && winrt.WindowsDeleteString;
|
|
return winrt.loaded;
|
|
}
|
|
CreateDirect3D11DeviceFromDXGIDevice :: (device: *IDXGIDevice, out: **IInspectable) -> HRESULT #foreign d3d11_dll;
|
|
SetEvent :: (event: HANDLE) -> s32 #foreign kernel32_dll;
|
|
|
|
Size_Int32 :: struct { width, height: s32; }
|
|
|
|
IID_UNKNOWN := #run guid("00000000-0000-0000-C000-000000000046");
|
|
IID_AGILE_OBJECT := #run guid("94ea2b94-e9cc-49e0-c0ff-ee64ca8f5b90");
|
|
IID_FRAME_ARRIVED_HANDLER := #run guid("51a947f7-79cf-5a3e-a3a5-1289cfa6dfe8"); // TypedEventHandler<Direct3D11CaptureFramePool, Object>
|
|
IID_ITEM_INTEROP := #run guid("3628E81B-3CAC-4C60-B7F4-23CE0E0C3356");
|
|
IID_ITEM := #run guid("79C3F95B-31F7-4EC2-A464-632EF5D30760");
|
|
IID_SESSION_STATICS := #run guid("2224A540-5974-49AA-B232-0882536F4CB5");
|
|
IID_SESSION2 := #run guid("2C39AE40-7D2E-5044-804E-8B6799D4CF9E");
|
|
IID_SESSION3 := #run guid("F2CDD966-22AE-5EA1-9596-3A289344C3BE");
|
|
IID_POOL_STATICS2 := #run guid("589B103F-6BBC-5DF5-A991-02E28B3B66D5");
|
|
IID_DIRECT3D_DEVICE := #run guid("A37624AB-8D5F-4650-9D3E-9EAE3D9BC670");
|
|
IID_DXGI_INTERFACE_ACCESS := #run guid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1");
|
|
IID_CLOSABLE := #run guid("30D5A829-7FA4-4026-83BB-D75BAE4EA99E");
|
|
|
|
guid :: (text: string) -> GUID {
|
|
hex :: (s: string) -> u64 {
|
|
value: u64;
|
|
for 0..s.count - 1 {
|
|
c := s[it];
|
|
digit: u64 = ifx c >= #char "a" then cast(u64) (c - #char "a" + 10) else ifx c >= #char "A" then cast(u64) (c - #char "A" + 10) else cast(u64) (c - #char "0");
|
|
value = value * 16 + digit;
|
|
}
|
|
return value;
|
|
}
|
|
g: GUID;
|
|
g.Data1 = cast(u32) hex(slice(text, 0, 8));
|
|
g.Data2 = cast(u16) hex(slice(text, 9, 4));
|
|
g.Data3 = cast(u16) hex(slice(text, 14, 4));
|
|
for 0..1 g.Data4[it] = cast(u8) hex(slice(text, 19 + it * 2, 2));
|
|
for 0..5 g.Data4[it + 2] = cast(u8) hex(slice(text, 24 + it * 2, 2));
|
|
return g;
|
|
}
|
|
|
|
// IUnknown, then IInspectable's three: every WinRT interface starts so.
|
|
Inspectable_Methods :: struct {
|
|
QueryInterface: (this: *void, iid: *GUID, out: **void) -> HRESULT #c_call;
|
|
AddRef: (this: *void) -> u32 #c_call;
|
|
Release: (this: *void) -> u32 #c_call;
|
|
GetIids: *void;
|
|
GetRuntimeClassName: *void;
|
|
GetTrustLevel: *void;
|
|
}
|
|
|
|
IInspectable :: struct { vtable: *Inspectable_Methods; }
|
|
|
|
Closable :: struct { vtable: *struct { using base: Inspectable_Methods; Close: (this: *Closable) -> HRESULT #c_call; }; }
|
|
|
|
Session_Statics :: struct { vtable: *struct { using base: Inspectable_Methods; IsSupported: (this: *Session_Statics, out: *u8) -> HRESULT #c_call; }; }
|
|
|
|
Capture_Item_Interop :: struct { vtable: *struct {
|
|
QueryInterface: (this: *void, iid: *GUID, out: **void) -> HRESULT #c_call;
|
|
AddRef: (this: *void) -> u32 #c_call;
|
|
Release: (this: *void) -> u32 #c_call;
|
|
CreateForWindow: (this: *Capture_Item_Interop, window: HWND, iid: *GUID, out: **void) -> HRESULT #c_call;
|
|
CreateForMonitor: (this: *Capture_Item_Interop, monitor: HMONITOR, iid: *GUID, out: **void) -> HRESULT #c_call;
|
|
}; }
|
|
|
|
Capture_Item :: struct { vtable: *struct {
|
|
using base: Inspectable_Methods;
|
|
get_DisplayName: *void;
|
|
get_Size: (this: *Capture_Item, out: *Size_Int32) -> HRESULT #c_call;
|
|
add_Closed: *void;
|
|
remove_Closed: *void;
|
|
}; }
|
|
|
|
Frame_Pool_Statics2 :: struct { vtable: *struct {
|
|
using base: Inspectable_Methods;
|
|
CreateFreeThreaded: (this: *Frame_Pool_Statics2, device: *IInspectable, format: s32, buffers: s32, size: u64, out: **Frame_Pool) -> HRESULT #c_call;
|
|
}; }
|
|
|
|
Frame_Pool :: struct { vtable: *struct {
|
|
using base: Inspectable_Methods;
|
|
Recreate: (this: *Frame_Pool, device: *IInspectable, format: s32, buffers: s32, size: u64) -> HRESULT #c_call;
|
|
TryGetNextFrame: (this: *Frame_Pool, out: **Capture_Frame) -> HRESULT #c_call;
|
|
add_FrameArrived: (this: *Frame_Pool, handler: *Frame_Handler, token: *s64) -> HRESULT #c_call;
|
|
remove_FrameArrived: (this: *Frame_Pool, token: s64) -> HRESULT #c_call;
|
|
CreateCaptureSession: (this: *Frame_Pool, item: *Capture_Item, out: **Capture_Session) -> HRESULT #c_call;
|
|
get_DispatcherQueue: *void;
|
|
}; }
|
|
|
|
Capture_Frame :: struct { vtable: *struct {
|
|
using base: Inspectable_Methods;
|
|
get_Surface: (this: *Capture_Frame, out: **IInspectable) -> HRESULT #c_call;
|
|
get_SystemRelativeTime: (this: *Capture_Frame, out: *s64) -> HRESULT #c_call;
|
|
get_ContentSize: (this: *Capture_Frame, out: *Size_Int32) -> HRESULT #c_call;
|
|
}; }
|
|
|
|
Capture_Session :: struct { vtable: *struct {
|
|
using base: Inspectable_Methods;
|
|
StartCapture: (this: *Capture_Session) -> HRESULT #c_call;
|
|
}; }
|
|
|
|
Capture_Session2 :: struct { vtable: *struct {
|
|
using base: Inspectable_Methods;
|
|
get_IsCursorCaptureEnabled: (this: *Capture_Session2, out: *u8) -> HRESULT #c_call;
|
|
put_IsCursorCaptureEnabled: (this: *Capture_Session2, value: u8) -> HRESULT #c_call;
|
|
}; }
|
|
|
|
Capture_Session3 :: struct { vtable: *struct {
|
|
using base: Inspectable_Methods;
|
|
get_IsBorderRequired: (this: *Capture_Session3, out: *u8) -> HRESULT #c_call;
|
|
put_IsBorderRequired: (this: *Capture_Session3, value: u8) -> HRESULT #c_call;
|
|
}; }
|
|
|
|
Dxgi_Interface_Access :: struct { vtable: *struct {
|
|
QueryInterface: (this: *void, iid: *GUID, out: **void) -> HRESULT #c_call;
|
|
AddRef: (this: *void) -> u32 #c_call;
|
|
Release: (this: *void) -> u32 #c_call;
|
|
GetInterface: (this: *Dxgi_Interface_Access, iid: *GUID, out: **void) -> HRESULT #c_call;
|
|
}; }
|