Przeglądaj źródła

win-capture,UI: Look up display by id, not index

The index is not reliable. The id persists better, so use that.

Also remove matching against the first monitor if the expected monitor
is not available. That feature seems like a good way to get doxxed by
accident.
jpark37 3 lat temu
rodzic
commit
803fcc4c3e

+ 3 - 1
UI/context-bar-controls.cpp

@@ -311,10 +311,12 @@ void DisplayCaptureToolbar::Init()
 	const char *device_str =
 		get_os_text(mod, "Monitor", "DisplayCapture.Display", "Screen");
 	ui->deviceLabel->setText(device_str);
+#ifndef _WIN32
 	is_int = true;
+#endif
 
 #ifdef _WIN32
-	prop_name = "monitor";
+	prop_name = "monitor_id";
 #elif __APPLE__
 	prop_name = "display";
 #else

+ 200 - 69
plugins/win-capture/duplicator-monitor-capture.c

@@ -68,8 +68,8 @@ enum display_capture_method {
 struct duplicator_capture {
 	obs_source_t *source;
 	pthread_mutex_t update_mutex;
-	int monitor;
-	int dxgi_index;
+	char monitor_id[128];
+	char monitor_name[64];
 	enum display_capture_method method;
 	bool reset_wgc;
 	HMONITOR handle;
@@ -93,9 +93,8 @@ struct duplicator_capture {
 };
 
 struct wgc_monitor_info {
-	int cur_id;
-	int desired_id;
-	int id;
+	char device_id[128];
+	char name[64];
 	RECT rect;
 	HMONITOR handle;
 };
@@ -117,49 +116,128 @@ static const char *get_method_name(int method)
 	return method_name;
 }
 
+static bool GetMonitorTarget(LPCWSTR device,
+			     DISPLAYCONFIG_TARGET_DEVICE_NAME *target)
+{
+	bool found = false;
+
+	UINT32 numPath, numMode;
+	if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPath,
+					&numMode) == ERROR_SUCCESS) {
+		DISPLAYCONFIG_PATH_INFO *paths =
+			bmalloc(numPath * sizeof(DISPLAYCONFIG_PATH_INFO));
+		DISPLAYCONFIG_MODE_INFO *modes =
+			bmalloc(numMode * sizeof(DISPLAYCONFIG_MODE_INFO));
+		if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &numPath, paths,
+				       &numMode, modes,
+				       NULL) == ERROR_SUCCESS) {
+			for (size_t i = 0; i < numPath; ++i) {
+				const DISPLAYCONFIG_PATH_INFO *const path =
+					&paths[i];
+
+				DISPLAYCONFIG_SOURCE_DEVICE_NAME
+				source;
+				source.header.type =
+					DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
+				source.header.size = sizeof(source);
+				source.header.adapterId =
+					path->sourceInfo.adapterId;
+				source.header.id = path->sourceInfo.id;
+				if (DisplayConfigGetDeviceInfo(
+					    &source.header) == ERROR_SUCCESS &&
+				    wcscmp(device, source.viewGdiDeviceName) ==
+					    0) {
+					target->header.type =
+						DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
+					target->header.size = sizeof(*target);
+					target->header.adapterId =
+						path->sourceInfo.adapterId;
+					target->header.id = path->targetInfo.id;
+					found = DisplayConfigGetDeviceInfo(
+							&target->header) ==
+						ERROR_SUCCESS;
+					break;
+				}
+			}
+		}
+
+		bfree(modes);
+		bfree(paths);
+	}
+
+	return found;
+}
+
+static void GetMonitorName(HMONITOR handle, char *name, size_t count)
+{
+	MONITORINFOEXW mi;
+	DISPLAYCONFIG_TARGET_DEVICE_NAME target;
+
+	mi.cbSize = sizeof(mi);
+	if (GetMonitorInfoW(handle, (LPMONITORINFO)&mi) &&
+	    GetMonitorTarget(mi.szDevice, &target)) {
+		snprintf(name, count, "%ls", target.monitorFriendlyDeviceName);
+	} else {
+		strcpy_s(name, count, "[OBS: Unknown]");
+	}
+}
+
 static BOOL CALLBACK enum_monitor(HMONITOR handle, HDC hdc, LPRECT rect,
 				  LPARAM param)
 {
+	UNUSED_PARAMETER(hdc);
+
 	struct wgc_monitor_info *monitor = (struct wgc_monitor_info *)param;
 
-	if (monitor->cur_id == 0 || monitor->desired_id == monitor->cur_id) {
-		monitor->id = monitor->cur_id;
-		monitor->rect = *rect;
-		monitor->handle = handle;
+	bool match = false;
+
+	MONITORINFOEXA mi;
+	mi.cbSize = sizeof(mi);
+	if (GetMonitorInfoA(handle, (LPMONITORINFO)&mi)) {
+		DISPLAY_DEVICEA device;
+		device.cb = sizeof(device);
+		if (EnumDisplayDevicesA(mi.szDevice, 0, &device,
+					EDD_GET_DEVICE_INTERFACE_NAME)) {
+			const bool match = strcmp(monitor->device_id,
+						  device.DeviceID) == 0;
+			if (match) {
+				monitor->rect = *rect;
+				monitor->handle = handle;
+				GetMonitorName(handle, monitor->name,
+					       _countof(monitor->name));
+			}
+		}
 	}
 
-	UNUSED_PARAMETER(hdc);
-	return (monitor->desired_id > monitor->cur_id++);
+	return !match;
 }
 
-static void log_settings(struct duplicator_capture *capture, int monitor,
-			 LONG width, LONG height)
+static void log_settings(struct duplicator_capture *capture,
+			 const char *monitor, LONG width, LONG height)
 {
 	info("update settings:\n"
-	     "\tdisplay: %d (%ldx%ld)\n"
+	     "\tdisplay: %s (%ldx%ld)\n"
 	     "\tcursor: %s\n"
 	     "\tmethod: %s",
-	     monitor + 1, width, height,
-	     capture->capture_cursor ? "true" : "false",
+	     monitor, width, height, capture->capture_cursor ? "true" : "false",
 	     get_method_name(capture->method));
 }
 
 static enum display_capture_method
 choose_method(enum display_capture_method method, bool wgc_supported,
-	      HMONITOR monitor, int *dxgi_index)
+	      HMONITOR monitor)
 {
 	if (!wgc_supported)
 		method = METHOD_DXGI;
 
-	if (method != METHOD_WGC) {
+	if (method == METHOD_AUTO) {
+		method = METHOD_DXGI;
+
 		obs_enter_graphics();
-		*dxgi_index = gs_duplicator_get_monitor_index(monitor);
+		const int dxgi_index = gs_duplicator_get_monitor_index(monitor);
 		obs_leave_graphics();
-	}
 
-	if (method == METHOD_AUTO) {
-		method = METHOD_DXGI;
-		if (*dxgi_index == -1) {
+		if (dxgi_index == -1) {
 			method = METHOD_WGC;
 		} else {
 			SYSTEM_POWER_STATUS status;
@@ -185,14 +263,18 @@ static inline void update_settings(struct duplicator_capture *capture,
 	pthread_mutex_lock(&capture->update_mutex);
 
 	struct wgc_monitor_info monitor = {0};
-	monitor.desired_id = (int)obs_data_get_int(settings, "monitor");
+	strcpy_s(monitor.device_id, _countof(monitor.device_id),
+		 obs_data_get_string(settings, "monitor_id"));
 	EnumDisplayMonitors(NULL, NULL, enum_monitor, (LPARAM)&monitor);
 
-	capture->method = choose_method(
-		(int)obs_data_get_int(settings, "method"), wgc_supported,
-		monitor.handle, &capture->dxgi_index);
+	capture->method =
+		choose_method((int)obs_data_get_int(settings, "method"),
+			      wgc_supported, monitor.handle);
 
-	capture->monitor = monitor.id;
+	strcpy_s(capture->monitor_id, _countof(capture->monitor_id),
+		 monitor.device_id);
+	strcpy_s(capture->monitor_name, _countof(capture->monitor_name),
+		 monitor.name);
 	capture->handle = monitor.handle;
 
 	capture->capture_cursor = obs_data_get_bool(settings, "capture_cursor");
@@ -266,7 +348,7 @@ static void duplicator_capture_destroy(void *data)
 static void duplicator_capture_defaults(obs_data_t *settings)
 {
 	obs_data_set_default_int(settings, "method", METHOD_AUTO);
-	obs_data_set_default_int(settings, "monitor", 0);
+	obs_data_set_default_string(settings, "monitor_id", "DUMMY");
 	obs_data_set_default_int(settings, "monitor_wgc", 0);
 	obs_data_set_default_bool(settings, "capture_cursor", true);
 }
@@ -275,7 +357,7 @@ static void duplicator_capture_update(void *data, obs_data_t *settings)
 {
 	struct duplicator_capture *mc = data;
 	update_settings(mc, settings);
-	log_settings(mc, mc->monitor, mc->logged_width, mc->logged_height);
+	log_settings(mc, mc->monitor_name, mc->logged_width, mc->logged_height);
 
 	mc->reset_wgc = true;
 }
@@ -332,7 +414,7 @@ static void *duplicator_capture_create(obs_data_t *settings,
 	}
 
 	update_settings(capture, settings);
-	log_settings(capture, capture->monitor, capture->logged_width,
+	log_settings(capture, capture->monitor_name, capture->logged_width,
 		     capture->logged_height);
 
 	return capture;
@@ -343,7 +425,8 @@ static void reset_capture_data(struct duplicator_capture *capture)
 	struct gs_monitor_info monitor_info = {0};
 	gs_texture_t *texture = gs_duplicator_get_texture(capture->duplicator);
 
-	gs_get_duplicator_monitor_info(capture->dxgi_index, &monitor_info);
+	const int dxgi_index = gs_duplicator_get_monitor_index(capture->handle);
+	gs_get_duplicator_monitor_info(dxgi_index, &monitor_info);
 	if (texture) {
 		capture->width = gs_texture_get_width(texture);
 		capture->height = gs_texture_get_height(texture);
@@ -378,6 +461,15 @@ static void free_capture_data(struct duplicator_capture *capture)
 	capture->reset_timeout = 0.0f;
 }
 
+static void update_monitor_handle(struct duplicator_capture *capture)
+{
+	struct wgc_monitor_info monitor = {0};
+	strcpy_s(monitor.device_id, _countof(monitor.device_id),
+		 capture->monitor_id);
+	EnumDisplayMonitors(NULL, NULL, enum_monitor, (LPARAM)&monitor);
+	capture->handle = monitor.handle;
+}
+
 static void duplicator_capture_tick(void *data, float seconds)
 {
 	struct duplicator_capture *capture = data;
@@ -416,13 +508,27 @@ static void duplicator_capture_tick(void *data, float seconds)
 			capture->reset_timeout += seconds;
 
 			if (capture->reset_timeout >= RESET_INTERVAL_SEC) {
-				capture->capture_winrt =
-					capture->exports
-						.winrt_capture_init_monitor(
+				if (!capture->handle)
+					update_monitor_handle(capture);
+
+				if (capture->handle) {
+					capture->capture_winrt =
+						capture->exports.winrt_capture_init_monitor(
 							capture->capture_cursor,
 							capture->handle);
-
-				capture->reset_timeout = 0.0f;
+					if (!capture->capture_winrt) {
+						update_monitor_handle(capture);
+
+						if (capture->handle) {
+							capture->capture_winrt =
+								capture->exports.winrt_capture_init_monitor(
+									capture->capture_cursor,
+									capture->handle);
+						}
+					}
+
+					capture->reset_timeout = 0.0f;
+				}
 			}
 		}
 	} else {
@@ -436,10 +542,31 @@ static void duplicator_capture_tick(void *data, float seconds)
 			capture->reset_timeout += seconds;
 
 			if (capture->reset_timeout >= RESET_INTERVAL_SEC) {
-				capture->duplicator = gs_duplicator_create(
-					capture->dxgi_index);
+				if (!capture->handle)
+					update_monitor_handle(capture);
 
-				capture->reset_timeout = 0.0f;
+				if (capture->handle) {
+					int dxgi_index =
+						gs_duplicator_get_monitor_index(
+							capture->handle);
+
+					if (dxgi_index == -1) {
+						update_monitor_handle(capture);
+
+						if (capture->handle) {
+							dxgi_index = gs_duplicator_get_monitor_index(
+								capture->handle);
+						}
+					}
+
+					if (dxgi_index != -1) {
+						capture->duplicator =
+							gs_duplicator_create(
+								dxgi_index);
+
+						capture->reset_timeout = 0.0f;
+					}
+				}
 			}
 		}
 
@@ -600,38 +727,42 @@ static BOOL CALLBACK enum_monitor_props(HMONITOR handle, HDC hdc, LPRECT rect,
 	UNUSED_PARAMETER(hdc);
 	UNUSED_PARAMETER(rect);
 
-	obs_property_t *monitor_list = (obs_property_t *)param;
-	MONITORINFO mi;
-	size_t monitor_id = 0;
-	struct dstr monitor_desc = {0};
-	struct dstr resolution = {0};
-	struct dstr format_string = {0};
-
-	monitor_id = obs_property_list_item_count(monitor_list);
-
+	MONITORINFOEXA mi;
 	mi.cbSize = sizeof(mi);
-	GetMonitorInfo(handle, &mi);
+	if (GetMonitorInfoA(handle, (LPMONITORINFO)&mi)) {
+		DISPLAY_DEVICEA device;
+		device.cb = sizeof(device);
+		if (EnumDisplayDevicesA(mi.szDevice, 0, &device,
+					EDD_GET_DEVICE_INTERFACE_NAME)) {
+			obs_property_t *monitor_list = (obs_property_t *)param;
+			struct dstr monitor_desc = {0};
+			struct dstr resolution = {0};
+
+			dstr_catf(&resolution, "%dx%d @ %d,%d",
+				  mi.rcMonitor.right - mi.rcMonitor.left,
+				  mi.rcMonitor.bottom - mi.rcMonitor.top,
+				  mi.rcMonitor.left, mi.rcMonitor.top);
+
+			char monitor_name[64];
+			GetMonitorName(handle, monitor_name,
+				       sizeof(monitor_name));
+			dstr_catf(&monitor_desc, "%s: %s", monitor_name,
+				  resolution.array);
+
+			if (mi.dwFlags == MONITORINFOF_PRIMARY) {
+				dstr_catf(&monitor_desc, " (%s)",
+					  TEXT_PRIMARY_MONITOR);
+			}
 
-	dstr_catf(&resolution, "%dx%d @ %d,%d",
-		  mi.rcMonitor.right - mi.rcMonitor.left,
-		  mi.rcMonitor.bottom - mi.rcMonitor.top, mi.rcMonitor.left,
-		  mi.rcMonitor.top);
+			obs_property_list_add_string(monitor_list,
+						     monitor_desc.array,
+						     device.DeviceID);
 
-	dstr_copy(&format_string, "%s %d: %s");
-	if (mi.dwFlags == MONITORINFOF_PRIMARY) {
-		dstr_catf(&format_string, " (%s)", TEXT_PRIMARY_MONITOR);
+			dstr_free(&monitor_desc);
+			dstr_free(&resolution);
+		}
 	}
 
-	dstr_catf(&monitor_desc, format_string.array, TEXT_MONITOR,
-		  monitor_id + 1, resolution.array);
-
-	obs_property_list_add_int(monitor_list, monitor_desc.array,
-				  (int)monitor_id);
-
-	dstr_free(&monitor_desc);
-	dstr_free(&resolution);
-	dstr_free(&format_string);
-
 	return TRUE;
 }
 
@@ -689,8 +820,8 @@ static obs_properties_t *duplicator_capture_properties(void *data)
 	obs_property_set_modified_callback(p, display_capture_method_changed);
 
 	obs_property_t *monitors = obs_properties_add_list(
-		props, "monitor", TEXT_MONITOR, OBS_COMBO_TYPE_LIST,
-		OBS_COMBO_FORMAT_INT);
+		props, "monitor_id", TEXT_MONITOR, OBS_COMBO_TYPE_LIST,
+		OBS_COMBO_FORMAT_STRING);
 
 	obs_properties_add_bool(props, "capture_cursor", TEXT_CAPTURE_CURSOR);