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
jpark37 2020-03-07 09:27:58 -08:00
parent 87f5bd6e9f
commit cb4954c279
3 changed files with 128 additions and 22 deletions

View File

@ -12,12 +12,24 @@ struct __declspec(uuid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1"))
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::
IsApiContractPresent(L"Windows.Foundation.UniversalApiContract",
8);
IsPropertyPresent(
L"Windows.Graphics.Capture.GraphicsCaptureSession",
L"IsCursorCaptureEnabled");
#else
return false;
#endif
}
template<typename T>
@ -78,10 +90,12 @@ static bool get_client_box(HWND window, uint32_t width, uint32_t height,
}
struct winrt_capture {
bool capture_cursor;
HWND window;
bool client_area;
bool capture_cursor;
bool cursor_visible;
gs_texture_t *texture;
bool texture_written;
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item{nullptr};
@ -104,6 +118,58 @@ struct winrt_capture {
bool thread_changed;
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::
Direct3D11CaptureFramePool const &sender,
winrt::Windows::Foundation::IInspectable const &)
@ -146,9 +212,8 @@ struct winrt_capture {
}
if (!texture) {
texture = gs_texture_create(texture_width,
texture_height, GS_BGRA, 1,
nullptr, 0);
texture = gs_texture_create_gdi(texture_width,
texture_height);
}
if (client_box_available) {
@ -163,6 +228,10 @@ struct winrt_capture {
frame_surface.get());
}
if (capture_cursor && cursor_visible) {
draw_cursor();
}
texture_written = true;
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 =
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;
d3d_device->GetImmediateContext(&capture->context);
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;
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();
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 =
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)
initialized_tls = true;
struct winrt_capture *capture = new winrt_capture{};
capture->capture_cursor = cursor;
capture->window = window;
capture->client_area = client_area;
capture->capture_cursor = cursor && cursor_toggle_supported;
capture->item = item;
capture->device = device;
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);
}
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,
gs_effect_t *effect)
{

View File

@ -9,11 +9,14 @@
extern "C" {
#endif
EXPORT bool winrt_capture_supported();
EXPORT struct winrt_capture *winrt_capture_init(bool cursor, HWND window,
bool client_area);
EXPORT BOOL winrt_capture_supported();
EXPORT BOOL winrt_capture_cursor_toggle_supported();
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_show_cursor(struct winrt_capture *capture,
BOOL visible);
EXPORT void winrt_capture_render(struct winrt_capture *capture,
gs_effect_t *effect);
EXPORT uint32_t winrt_capture_width(const struct winrt_capture *capture);

View File

@ -26,10 +26,13 @@
#define WC_CHECK_TIMER 1.0f
struct winrt_exports {
bool *(*winrt_capture_supported)();
struct winrt_capture *(*winrt_capture_init)(bool cursor, HWND window,
bool client_area);
BOOL *(*winrt_capture_supported)();
BOOL *(*winrt_capture_cursor_toggle_supported)();
struct winrt_capture *(*winrt_capture_init)(BOOL cursor, HWND window,
BOOL client_area);
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,
gs_effect_t *effect);
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;
WINRT_IMPORT(winrt_capture_supported);
WINRT_IMPORT(winrt_capture_cursor_toggle_supported);
WINRT_IMPORT(winrt_capture_init);
WINRT_IMPORT(winrt_capture_free);
WINRT_IMPORT(winrt_capture_show_cursor);
WINRT_IMPORT(winrt_capture_render);
WINRT_IMPORT(winrt_capture_width);
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,
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 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_set_visible(p, bitblt_options);
obs_property_set_visible(p, bitblt_options || wgc_cursor_toggle);
p = obs_properties_get(props, "compatibility");
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);
update_settings(wc, settings);
update_settings_visibility(props, wc->method);
update_settings_visibility(props, wc);
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);
update_settings(wc, settings);
update_settings_visibility(props, wc->method);
update_settings_visibility(props, wc);
check_window_property_setting(props, p, settings, "window", 0);
return true;
@ -417,8 +427,12 @@ static void wc_tick(void *data, float seconds)
if (!GetWindowThreadProcessId(wc->window, &target_pid))
target_pid = 0;
wc->capture.cursor_hidden = foreground_pid && target_pid &&
foreground_pid != target_pid;
const bool cursor_hidden = 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;
}