libobs-winrt: win-capture: HDC cursor capture for WGC
Starting with Windows 10 2004, we can disable WGC cursor capture, and provide a user toggle. We swap out WGC support for our own though because ours does not break hardware cursor support.master
parent
87f5bd6e9f
commit
cb4954c279
|
@ -12,12 +12,24 @@ struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1"))
|
||||||
void **object) = 0;
|
void **object) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern "C" EXPORT bool winrt_capture_supported()
|
extern "C" EXPORT BOOL winrt_capture_supported()
|
||||||
{
|
{
|
||||||
/* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */
|
return winrt::Windows::Foundation::Metadata::ApiInformation::IsTypePresent(
|
||||||
|
L"Windows.Graphics.Capture.GraphicsCaptureSession") &&
|
||||||
|
winrt::Windows::Graphics::Capture::GraphicsCaptureSession::
|
||||||
|
IsSupported();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" EXPORT BOOL winrt_capture_cursor_toggle_supported()
|
||||||
|
{
|
||||||
|
#ifdef NTDDI_WIN10_VB
|
||||||
return winrt::Windows::Foundation::Metadata::ApiInformation::
|
return winrt::Windows::Foundation::Metadata::ApiInformation::
|
||||||
IsApiContractPresent(L"Windows.Foundation.UniversalApiContract",
|
IsPropertyPresent(
|
||||||
8);
|
L"Windows.Graphics.Capture.GraphicsCaptureSession",
|
||||||
|
L"IsCursorCaptureEnabled");
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -78,10 +90,12 @@ static bool get_client_box(HWND window, uint32_t width, uint32_t height,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct winrt_capture {
|
struct winrt_capture {
|
||||||
bool capture_cursor;
|
|
||||||
HWND window;
|
HWND window;
|
||||||
bool client_area;
|
bool client_area;
|
||||||
|
|
||||||
|
bool capture_cursor;
|
||||||
|
bool cursor_visible;
|
||||||
|
|
||||||
gs_texture_t *texture;
|
gs_texture_t *texture;
|
||||||
bool texture_written;
|
bool texture_written;
|
||||||
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item{nullptr};
|
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item{nullptr};
|
||||||
|
@ -104,6 +118,58 @@ struct winrt_capture {
|
||||||
bool thread_changed;
|
bool thread_changed;
|
||||||
struct winrt_capture *next;
|
struct winrt_capture *next;
|
||||||
|
|
||||||
|
void draw_cursor()
|
||||||
|
{
|
||||||
|
CURSORINFO ci{};
|
||||||
|
ci.cbSize = sizeof(CURSORINFO);
|
||||||
|
if (!GetCursorInfo(&ci))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!ci.flags & CURSOR_SHOWING)
|
||||||
|
return;
|
||||||
|
|
||||||
|
HICON icon = CopyIcon(ci.hCursor);
|
||||||
|
if (!icon)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ICONINFO ii;
|
||||||
|
if (GetIconInfo(icon, &ii)) {
|
||||||
|
POINT win_pos{};
|
||||||
|
if (window) {
|
||||||
|
if (client_area) {
|
||||||
|
ClientToScreen(window, &win_pos);
|
||||||
|
} else {
|
||||||
|
RECT window_rect;
|
||||||
|
if (DwmGetWindowAttribute(
|
||||||
|
window,
|
||||||
|
DWMWA_EXTENDED_FRAME_BOUNDS,
|
||||||
|
&window_rect,
|
||||||
|
sizeof(window_rect)) ==
|
||||||
|
S_OK) {
|
||||||
|
win_pos.x = window_rect.left;
|
||||||
|
win_pos.y = window_rect.top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
POINT pos;
|
||||||
|
pos.x = ci.ptScreenPos.x - (int)ii.xHotspot - win_pos.x;
|
||||||
|
pos.y = ci.ptScreenPos.y - (int)ii.yHotspot - win_pos.y;
|
||||||
|
|
||||||
|
HDC hdc = (HDC)gs_texture_get_dc(texture);
|
||||||
|
|
||||||
|
DrawIconEx(hdc, pos.x, pos.y, icon, 0, 0, 0, NULL,
|
||||||
|
DI_NORMAL);
|
||||||
|
|
||||||
|
gs_texture_release_dc(texture);
|
||||||
|
|
||||||
|
DeleteObject(ii.hbmColor);
|
||||||
|
DeleteObject(ii.hbmMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
DestroyIcon(icon);
|
||||||
|
}
|
||||||
|
|
||||||
void on_frame_arrived(winrt::Windows::Graphics::Capture::
|
void on_frame_arrived(winrt::Windows::Graphics::Capture::
|
||||||
Direct3D11CaptureFramePool const &sender,
|
Direct3D11CaptureFramePool const &sender,
|
||||||
winrt::Windows::Foundation::IInspectable const &)
|
winrt::Windows::Foundation::IInspectable const &)
|
||||||
|
@ -146,9 +212,8 @@ struct winrt_capture {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!texture) {
|
if (!texture) {
|
||||||
texture = gs_texture_create(texture_width,
|
texture = gs_texture_create_gdi(texture_width,
|
||||||
texture_height, GS_BGRA, 1,
|
texture_height);
|
||||||
nullptr, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client_box_available) {
|
if (client_box_available) {
|
||||||
|
@ -163,6 +228,10 @@ struct winrt_capture {
|
||||||
frame_surface.get());
|
frame_surface.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (capture_cursor && cursor_visible) {
|
||||||
|
draw_cursor();
|
||||||
|
}
|
||||||
|
|
||||||
texture_written = true;
|
texture_written = true;
|
||||||
|
|
||||||
if (frame_content_size.Width != last_size.Width ||
|
if (frame_content_size.Width != last_size.Width ||
|
||||||
|
@ -222,6 +291,12 @@ static void winrt_capture_device_loss_rebuild(void *device_void, void *data)
|
||||||
const winrt::Windows::Graphics::Capture::GraphicsCaptureSession session =
|
const winrt::Windows::Graphics::Capture::GraphicsCaptureSession session =
|
||||||
frame_pool.CreateCaptureSession(capture->item);
|
frame_pool.CreateCaptureSession(capture->item);
|
||||||
|
|
||||||
|
/* disable cursor capture if possible since ours performs better */
|
||||||
|
#ifdef NTDDI_WIN10_VB
|
||||||
|
if (winrt_capture_cursor_toggle_supported())
|
||||||
|
session.IsCursorCaptureEnabled(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
capture->device = device;
|
capture->device = device;
|
||||||
d3d_device->GetImmediateContext(&capture->context);
|
d3d_device->GetImmediateContext(&capture->context);
|
||||||
capture->frame_pool = frame_pool;
|
capture->frame_pool = frame_pool;
|
||||||
|
@ -236,7 +311,7 @@ static void winrt_capture_device_loss_rebuild(void *device_void, void *data)
|
||||||
thread_local bool initialized_tls;
|
thread_local bool initialized_tls;
|
||||||
|
|
||||||
extern "C" EXPORT struct winrt_capture *
|
extern "C" EXPORT struct winrt_capture *
|
||||||
winrt_capture_init(bool cursor, HWND window, bool client_area)
|
winrt_capture_init(BOOL cursor, HWND window, BOOL client_area)
|
||||||
{
|
{
|
||||||
ID3D11Device *const d3d_device = (ID3D11Device *)gs_get_device_obj();
|
ID3D11Device *const d3d_device = (ID3D11Device *)gs_get_device_obj();
|
||||||
ComPtr<IDXGIDevice> dxgi_device;
|
ComPtr<IDXGIDevice> dxgi_device;
|
||||||
|
@ -287,13 +362,21 @@ winrt_capture_init(bool cursor, HWND window, bool client_area)
|
||||||
const winrt::Windows::Graphics::Capture::GraphicsCaptureSession session =
|
const winrt::Windows::Graphics::Capture::GraphicsCaptureSession session =
|
||||||
frame_pool.CreateCaptureSession(item);
|
frame_pool.CreateCaptureSession(item);
|
||||||
|
|
||||||
|
/* disable cursor capture if possible since ours performs better */
|
||||||
|
const BOOL cursor_toggle_supported =
|
||||||
|
winrt_capture_cursor_toggle_supported();
|
||||||
|
#ifdef NTDDI_WIN10_VB
|
||||||
|
if (cursor_toggle_supported)
|
||||||
|
session.IsCursorCaptureEnabled(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (capture_list == nullptr)
|
if (capture_list == nullptr)
|
||||||
initialized_tls = true;
|
initialized_tls = true;
|
||||||
|
|
||||||
struct winrt_capture *capture = new winrt_capture{};
|
struct winrt_capture *capture = new winrt_capture{};
|
||||||
capture->capture_cursor = cursor;
|
|
||||||
capture->window = window;
|
capture->window = window;
|
||||||
capture->client_area = client_area;
|
capture->client_area = client_area;
|
||||||
|
capture->capture_cursor = cursor && cursor_toggle_supported;
|
||||||
capture->item = item;
|
capture->item = item;
|
||||||
capture->device = device;
|
capture->device = device;
|
||||||
d3d_device->GetImmediateContext(&capture->context);
|
d3d_device->GetImmediateContext(&capture->context);
|
||||||
|
@ -366,6 +449,12 @@ static void draw_texture(struct winrt_capture *capture, gs_effect_t *effect)
|
||||||
gs_technique_end(tech);
|
gs_technique_end(tech);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" EXPORT void winrt_capture_show_cursor(struct winrt_capture *capture,
|
||||||
|
BOOL visible)
|
||||||
|
{
|
||||||
|
capture->cursor_visible = visible;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" EXPORT void winrt_capture_render(struct winrt_capture *capture,
|
extern "C" EXPORT void winrt_capture_render(struct winrt_capture *capture,
|
||||||
gs_effect_t *effect)
|
gs_effect_t *effect)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,11 +9,14 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
EXPORT bool winrt_capture_supported();
|
EXPORT BOOL winrt_capture_supported();
|
||||||
EXPORT struct winrt_capture *winrt_capture_init(bool cursor, HWND window,
|
EXPORT BOOL winrt_capture_cursor_toggle_supported();
|
||||||
bool client_area);
|
EXPORT struct winrt_capture *winrt_capture_init(BOOL cursor, HWND window,
|
||||||
|
BOOL client_area);
|
||||||
EXPORT void winrt_capture_free(struct winrt_capture *capture);
|
EXPORT void winrt_capture_free(struct winrt_capture *capture);
|
||||||
|
|
||||||
|
EXPORT void winrt_capture_show_cursor(struct winrt_capture *capture,
|
||||||
|
BOOL visible);
|
||||||
EXPORT void winrt_capture_render(struct winrt_capture *capture,
|
EXPORT void winrt_capture_render(struct winrt_capture *capture,
|
||||||
gs_effect_t *effect);
|
gs_effect_t *effect);
|
||||||
EXPORT uint32_t winrt_capture_width(const struct winrt_capture *capture);
|
EXPORT uint32_t winrt_capture_width(const struct winrt_capture *capture);
|
||||||
|
|
|
@ -26,10 +26,13 @@
|
||||||
#define WC_CHECK_TIMER 1.0f
|
#define WC_CHECK_TIMER 1.0f
|
||||||
|
|
||||||
struct winrt_exports {
|
struct winrt_exports {
|
||||||
bool *(*winrt_capture_supported)();
|
BOOL *(*winrt_capture_supported)();
|
||||||
struct winrt_capture *(*winrt_capture_init)(bool cursor, HWND window,
|
BOOL *(*winrt_capture_cursor_toggle_supported)();
|
||||||
bool client_area);
|
struct winrt_capture *(*winrt_capture_init)(BOOL cursor, HWND window,
|
||||||
|
BOOL client_area);
|
||||||
void (*winrt_capture_free)(struct winrt_capture *capture);
|
void (*winrt_capture_free)(struct winrt_capture *capture);
|
||||||
|
void (*winrt_capture_show_cursor)(struct winrt_capture *capture,
|
||||||
|
BOOL visible);
|
||||||
void (*winrt_capture_render)(struct winrt_capture *capture,
|
void (*winrt_capture_render)(struct winrt_capture *capture,
|
||||||
gs_effect_t *effect);
|
gs_effect_t *effect);
|
||||||
uint32_t (*winrt_capture_width)(const struct winrt_capture *capture);
|
uint32_t (*winrt_capture_width)(const struct winrt_capture *capture);
|
||||||
|
@ -171,8 +174,10 @@ static bool load_winrt_imports(struct winrt_exports *exports, void *module,
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
|
||||||
WINRT_IMPORT(winrt_capture_supported);
|
WINRT_IMPORT(winrt_capture_supported);
|
||||||
|
WINRT_IMPORT(winrt_capture_cursor_toggle_supported);
|
||||||
WINRT_IMPORT(winrt_capture_init);
|
WINRT_IMPORT(winrt_capture_init);
|
||||||
WINRT_IMPORT(winrt_capture_free);
|
WINRT_IMPORT(winrt_capture_free);
|
||||||
|
WINRT_IMPORT(winrt_capture_show_cursor);
|
||||||
WINRT_IMPORT(winrt_capture_render);
|
WINRT_IMPORT(winrt_capture_render);
|
||||||
WINRT_IMPORT(winrt_capture_width);
|
WINRT_IMPORT(winrt_capture_width);
|
||||||
WINRT_IMPORT(winrt_capture_height);
|
WINRT_IMPORT(winrt_capture_height);
|
||||||
|
@ -260,13 +265,18 @@ static void wc_defaults(obs_data_t *defaults)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_settings_visibility(obs_properties_t *props,
|
static void update_settings_visibility(obs_properties_t *props,
|
||||||
enum window_capture_method method)
|
struct window_capture *wc)
|
||||||
{
|
{
|
||||||
|
const enum window_capture_method method = wc->method;
|
||||||
const bool bitblt_options = method == METHOD_BITBLT;
|
const bool bitblt_options = method == METHOD_BITBLT;
|
||||||
const bool wgc_options = method == METHOD_WGC;
|
const bool wgc_options = method == METHOD_WGC;
|
||||||
|
|
||||||
|
const bool wgc_cursor_toggle =
|
||||||
|
wgc_options &&
|
||||||
|
wc->exports.winrt_capture_cursor_toggle_supported();
|
||||||
|
|
||||||
obs_property_t *p = obs_properties_get(props, "cursor");
|
obs_property_t *p = obs_properties_get(props, "cursor");
|
||||||
obs_property_set_visible(p, bitblt_options);
|
obs_property_set_visible(p, bitblt_options || wgc_cursor_toggle);
|
||||||
|
|
||||||
p = obs_properties_get(props, "compatibility");
|
p = obs_properties_get(props, "compatibility");
|
||||||
obs_property_set_visible(p, bitblt_options);
|
obs_property_set_visible(p, bitblt_options);
|
||||||
|
@ -281,7 +291,7 @@ static bool wc_capture_method_changed(obs_properties_t *props,
|
||||||
struct window_capture *wc = obs_properties_get_param(props);
|
struct window_capture *wc = obs_properties_get_param(props);
|
||||||
update_settings(wc, settings);
|
update_settings(wc, settings);
|
||||||
|
|
||||||
update_settings_visibility(props, wc->method);
|
update_settings_visibility(props, wc);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -297,7 +307,7 @@ static bool wc_window_changed(obs_properties_t *props, obs_property_t *p,
|
||||||
struct window_capture *wc = obs_properties_get_param(props);
|
struct window_capture *wc = obs_properties_get_param(props);
|
||||||
update_settings(wc, settings);
|
update_settings(wc, settings);
|
||||||
|
|
||||||
update_settings_visibility(props, wc->method);
|
update_settings_visibility(props, wc);
|
||||||
|
|
||||||
check_window_property_setting(props, p, settings, "window", 0);
|
check_window_property_setting(props, p, settings, "window", 0);
|
||||||
return true;
|
return true;
|
||||||
|
@ -417,8 +427,12 @@ static void wc_tick(void *data, float seconds)
|
||||||
if (!GetWindowThreadProcessId(wc->window, &target_pid))
|
if (!GetWindowThreadProcessId(wc->window, &target_pid))
|
||||||
target_pid = 0;
|
target_pid = 0;
|
||||||
|
|
||||||
wc->capture.cursor_hidden = foreground_pid && target_pid &&
|
const bool cursor_hidden = foreground_pid && target_pid &&
|
||||||
foreground_pid != target_pid;
|
foreground_pid != target_pid;
|
||||||
|
wc->capture.cursor_hidden = cursor_hidden;
|
||||||
|
if (wc->capture_winrt)
|
||||||
|
wc->exports.winrt_capture_show_cursor(wc->capture_winrt,
|
||||||
|
!cursor_hidden);
|
||||||
|
|
||||||
wc->cursor_check_time = 0.0f;
|
wc->cursor_check_time = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue