Signed-off-by: Zhiyi Zhang <zzhang(a)codeweavers.com>
---
dlls/winemac.drv/cocoa_display.m | 101 +++++++++++++++++++++++++++++
dlls/winemac.drv/display.c | 134 +++++++++++++++++++++++++++++++++++++--
dlls/winemac.drv/macdrv_cocoa.h | 15 +++++
3 files changed, 243 insertions(+), 7 deletions(-)
diff --git a/dlls/winemac.drv/cocoa_display.m b/dlls/winemac.drv/cocoa_display.m
index 82c9e3fcc3..53b1fb0a68 100644
--- a/dlls/winemac.drv/cocoa_display.m
+++ b/dlls/winemac.drv/cocoa_display.m
@@ -26,6 +26,8 @@
#endif
#include "macdrv_cocoa.h"
+static uint64_t dedicated_gpu_id;
+static uint64_t integrated_gpu_id;
/***********************************************************************
* convert_display_rect
@@ -286,6 +288,7 @@ static int macdrv_get_gpus_from_metal_devices(struct macdrv_gpu** new_gpus, int*
/* Hide the integrated GPU if the system default device is a dedicated GPU */
if (!primary_device.isLowPower)
{
+ dedicated_gpu_id = primary_gpu.id;
hide_integrated = TRUE;
}
@@ -296,6 +299,7 @@ static int macdrv_get_gpus_from_metal_devices(struct macdrv_gpu** new_gpus, int*
if (hide_integrated && devices[i].isLowPower)
{
+ integrated_gpu_id = gpus[gpu_count].id;
continue;
}
@@ -415,6 +419,7 @@ static int macdrv_get_gpus_from_displays(struct macdrv_gpu** new_gpus, int* coun
* Assuming integrated GPU vendor is Intel for now */
if (gpus[gpu_count].vendor_id == 0x8086)
{
+ integrated_gpu_id = gpus[gpu_count].id;
integrated_index = gpu_count;
}
@@ -433,7 +438,10 @@ static int macdrv_get_gpus_from_displays(struct macdrv_gpu** new_gpus, int* coun
* Choose the first dedicated GPU as primary */
if (primary_index == integrated_index)
primary_index = 0;
+ else if (primary_index == gpu_count - 1)
+ primary_index = integrated_index;
+ dedicated_gpu_id = gpus[primary_index].id;
gpu_count--;
}
@@ -465,6 +473,9 @@ static int macdrv_get_gpus_from_displays(struct macdrv_gpu** new_gpus, int* coun
*/
int macdrv_get_gpus(struct macdrv_gpu** new_gpus, int* count)
{
+ integrated_gpu_id = 0;
+ dedicated_gpu_id = 0;
+
if (is_metal_available())
return macdrv_get_gpus_from_metal_devices(new_gpus, count);
else
@@ -481,3 +492,93 @@ void macdrv_free_gpus(struct macdrv_gpu* gpus)
if (gpus)
free(gpus);
}
+
+/***********************************************************************
+ * macdrv_get_adapters
+ *
+ * Get a list of adapters under gpu_id. The first adapter is primary if GPU is primary.
+ * Call macdrv_free_adapters() when you are done using the data.
+ *
+ * Returns non-zero value on failure with parameters unchanged and zero on success.
+ */
+int macdrv_get_adapters(uint64_t gpu_id, struct macdrv_adapter** new_adapters, int* count)
+{
+ CGDirectDisplayID display_ids[16];
+ uint32_t display_id_count;
+ struct macdrv_adapter* adapters;
+ struct macdrv_gpu gpu;
+ int primary_index = 0;
+ int adapter_count = 0;
+ int ret = -1;
+ uint32_t i;
+
+ if (CGGetOnlineDisplayList(sizeof(display_ids) / sizeof(display_ids[0]), display_ids, &display_id_count)
+ != kCGErrorSuccess)
+ return -1;
+
+ if (!display_id_count)
+ {
+ *new_adapters = NULL;
+ *count = 0;
+ return 0;
+ }
+
+ /* Actual adapter count may be less */
+ adapters = calloc(display_id_count, sizeof(*adapters));
+ if (!adapters)
+ return -1;
+
+ for (i = 0; i < display_id_count; i++)
+ {
+ /* Mirrored displays are under the same adapter with primary display, so they doesn't increase adapter count */
+ if (CGDisplayMirrorsDisplay(display_ids[i]) != kCGNullDirectDisplay)
+ continue;
+
+ if (macdrv_get_gpu_info_from_display_id(&gpu, display_ids[i]))
+ goto done;
+
+ if (gpu.id == gpu_id || (gpu_id == dedicated_gpu_id && gpu.id == integrated_gpu_id))
+ {
+ adapters[adapter_count].id = display_ids[i];
+
+ if (CGDisplayIsMain(display_ids[i]))
+ {
+ adapters[adapter_count].state_flags |= DISPLAY_DEVICE_PRIMARY_DEVICE;
+ primary_index = adapter_count;
+ }
+
+ if (CGDisplayIsActive(display_ids[i]))
+ adapters[adapter_count].state_flags |= DISPLAY_DEVICE_ATTACHED_TO_DESKTOP;
+
+ adapter_count++;
+ }
+ }
+
+ /* Make sure the first adapter is primary if the GPU is primary */
+ if (primary_index)
+ {
+ struct macdrv_adapter tmp;
+ tmp = adapters[0];
+ adapters[0] = adapters[primary_index];
+ adapters[primary_index] = tmp;
+ }
+
+ *new_adapters = adapters;
+ *count = adapter_count;
+ ret = 0;
+done:
+ if (ret)
+ macdrv_free_adapters(adapters);
+ return ret;
+}
+
+/***********************************************************************
+ * macdrv_free_adapters
+ *
+ * Frees an adapter list allocated from macdrv_get_adapters()
+ */
+void macdrv_free_adapters(struct macdrv_adapter* adapters)
+{
+ if (adapters)
+ free(adapters);
+}
diff --git a/dlls/winemac.drv/display.c b/dlls/winemac.drv/display.c
index 3e7335f5aa..6b2eea9ecc 100644
--- a/dlls/winemac.drv/display.c
+++ b/dlls/winemac.drv/display.c
@@ -57,6 +57,9 @@ static const WCHAR pixelencodingW[] = {'P','i','x','e','l','E','n','c','o','d','
static const WCHAR driver_descW[] = {'D','r','i','v','e','r','D','e','s','c',0};
static const WCHAR pciW[] = {'P','C','I',0};
static const WCHAR video_idW[] = {'V','i','d','e','o','I','D',0};
+static const WCHAR symbolic_link_valueW[]= {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0};
+static const WCHAR gpu_idW[] = {'G','P','U','I','D',0};
+static const WCHAR state_flagsW[] = {'S','t','a','t','e','F','l','a','g','s',0};
static const WCHAR guid_fmtW[] = {
'{','%','0','8','x','-','%','0','4','x','-','%','0','4','x','-','%','0','2','x','%','0','2','x','-',
'%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','}',0};
@@ -77,6 +80,26 @@ static const WCHAR video_keyW[] = {
'H','A','R','D','W','A','R','E','\\',
'D','E','V','I','C','E','M','A','P','\\',
'V','I','D','E','O',0};
+static const WCHAR adapter_key_fmtW[] = {
+ 'S','y','s','t','e','m','\\',
+ 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
+ 'C','o','n','t','r','o','l','\\',
+ 'V','i','d','e','o','\\',
+ '%','s','\\',
+ '%','0','4','x',0};
+static const WCHAR device_video_fmtW[] = {
+ '\\','D','e','v','i','c','e','\\',
+ 'V','i','d','e','o','%','d',0};
+static const WCHAR machine_prefixW[] = {
+ '\\','R','e','g','i','s','t','r','y','\\',
+ 'M','a','c','h','i','n','e','\\',0};
+static const WCHAR nt_classW[] = {
+ '\\','R','e','g','i','s','t','r','y','\\',
+ 'M','a','c','h','i','n','e','\\',
+ 'S','y','s','t','e','m','\\',
+ 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
+ 'C','o','n','t','r','o','l','\\',
+ 'C','l','a','s','s','\\',0};
static CFArrayRef modes;
@@ -1431,11 +1454,12 @@ void macdrv_displays_changed(const macdrv_event *event)
/***********************************************************************
* macdrv_init_gpu
*
- * Initialize a GPU instance.
+ * Initialize a GPU instance and return its GUID string in guid_string and driver value in driver parameter.
*
* Return FALSE on failure and TRUE on success.
*/
-static BOOL macdrv_init_gpu(HDEVINFO devinfo, const struct macdrv_gpu *gpu, int gpu_index)
+static BOOL macdrv_init_gpu(HDEVINFO devinfo, const struct macdrv_gpu *gpu, int gpu_index, WCHAR *guid_string,
+ WCHAR *driver)
{
static const BOOL present = TRUE;
SP_DEVINFO_DATA device_data = {sizeof(device_data)};
@@ -1478,6 +1502,13 @@ static BOOL macdrv_init_gpu(HDEVINFO devinfo, const struct macdrv_gpu *gpu, int
goto done;
RegCloseKey(hkey);
+ /* Retrieve driver value for adapters */
+ if (!SetupDiGetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_DRIVER, NULL, (BYTE *)bufferW, sizeof(bufferW),
+ NULL))
+ goto done;
+ lstrcpyW(driver, nt_classW);
+ lstrcatW(driver, bufferW);
+
/* Write GUID in VideoID in .../instance/Device Parameters, reuse the GUID if it's existent */
hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, NULL, NULL);
@@ -1490,6 +1521,7 @@ static BOOL macdrv_init_gpu(HDEVINFO devinfo, const struct macdrv_gpu *gpu, int
if (RegSetValueExW(hkey, video_idW, 0, REG_SZ, (const BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR)))
goto done;
}
+ lstrcpyW(guid_string, bufferW);
ret = TRUE;
done:
@@ -1499,13 +1531,80 @@ done:
return ret;
}
-static void prepare_devices(void)
+/***********************************************************************
+ * macdrv_init_adapter
+ *
+ * Initialize an adapter.
+ *
+ * Return FALSE on failure and TRUE on success.
+ */
+static BOOL macdrv_init_adapter(HKEY video_hkey, int video_index, int gpu_index, int adapter_index,
+ const struct macdrv_gpu *gpu, const WCHAR *guid_string, const WCHAR *gpu_driver,
+ const struct macdrv_adapter *adapter)
+{
+ WCHAR adapter_keyW[MAX_PATH];
+ WCHAR key_nameW[MAX_PATH];
+ WCHAR bufferW[1024];
+ HKEY hkey = NULL;
+ BOOL ret = FALSE;
+ LSTATUS ls;
+
+ sprintfW(key_nameW, device_video_fmtW, video_index);
+ lstrcpyW(bufferW, machine_prefixW);
+ sprintfW(adapter_keyW, adapter_key_fmtW, guid_string, adapter_index);
+ lstrcatW(bufferW, adapter_keyW);
+
+ /* Write value of \Device\Video? (adapter key) in HKLM\HARDWARE\DEVICEMAP\VIDEO\ */
+ if (RegSetValueExW(video_hkey, key_nameW, 0, REG_SZ, (const BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR)))
+ goto done;
+
+ /* Create HKLM\System\CurrentControlSet\Control\Video\{GPU GUID}\{Adapter Index} link to GPU driver */
+ ls = RegCreateKeyExW(HKEY_LOCAL_MACHINE, adapter_keyW, 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK,
+ KEY_ALL_ACCESS, NULL, &hkey, NULL);
+ if (ls == ERROR_ALREADY_EXISTS)
+ RegCreateKeyExW(HKEY_LOCAL_MACHINE, adapter_keyW, 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_OPEN_LINK,
+ KEY_ALL_ACCESS, NULL, &hkey, NULL);
+ if (RegSetValueExW(hkey, symbolic_link_valueW, 0, REG_LINK, (const BYTE *)gpu_driver,
+ lstrlenW(gpu_driver) * sizeof(WCHAR)))
+ goto done;
+ RegCloseKey(hkey);
+ hkey = NULL;
+
+ /* FIXME:
+ * Following information is Wine specific, it doesn't really exist on Windows. It is used so that we can
+ * implement EnumDisplayDevices etc by querying registry only. This information is most likely reported by the
+ * device driver on Windows */
+ RegCreateKeyExW(HKEY_CURRENT_CONFIG, adapter_keyW, 0, NULL, REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hkey, NULL);
+
+ /* Write GPU instance path so that we can find the GPU instance via adapters quickly. Another way is trying to match
+ * them via the GUID in Device Paramters/VideoID, but it would required enumrating all GPU instances */
+ sprintfW(bufferW, gpu_instance_fmtW, gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, gpu_index);
+ if (RegSetValueExW(hkey, gpu_idW, 0, REG_SZ, (const BYTE *)bufferW, (lstrlenW(bufferW) + 1) * sizeof(WCHAR)))
+ goto done;
+
+ /* Write StateFlags */
+ if (RegSetValueExW(hkey, state_flagsW, 0, REG_DWORD, (const BYTE *)&adapter->state_flags,
+ sizeof(adapter->state_flags)))
+ goto done;
+
+ ret = TRUE;
+done:
+ RegCloseKey(hkey);
+ if (!ret)
+ ERR("Failed to initialize adapter\n");
+ return ret;
+}
+
+static void prepare_devices(HKEY video_hkey)
{
static const BOOL not_present = FALSE;
SP_DEVINFO_DATA device_data = {sizeof(device_data)};
HDEVINFO devinfo;
DWORD i = 0;
+ /* Clean up old adapter keys for reinitialization */
+ RegDeleteTreeW(video_hkey, NULL);
+
/* FIXME:
* Currently SetupDiGetClassDevsW with DIGCF_PRESENT is unsupported, So we need to clean up not present devices in
* case application uses SetupDiGetClassDevsW to enumerate devices. Wrong devices could exist in registry as a result
@@ -1551,11 +1650,15 @@ void macdrv_init_display_devices(void)
static const WCHAR init_mutexW[] = {'d','i','s','p','l','a','y','_','d','e','v','i','c','e','_','i','n','i','t',0};
HANDLE mutex;
struct macdrv_gpu *gpus = NULL;
- INT gpu_count;
- INT gpu;
+ struct macdrv_adapter *adapters = NULL;
+ INT gpu_count, adapter_count;
+ INT gpu, adapter;
HDEVINFO gpu_devinfo = NULL;
HKEY video_hkey = NULL;
+ INT video_index = 0;
DWORD disposition = 0;
+ WCHAR guidW[40];
+ WCHAR driverW[1024];
mutex = CreateMutexW(NULL, FALSE, init_mutexW);
WaitForSingleObject(mutex, INFINITE);
@@ -1573,7 +1676,7 @@ void macdrv_init_display_devices(void)
TRACE("\n");
- prepare_devices();
+ prepare_devices(video_hkey);
gpu_devinfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_DISPLAY, NULL);
@@ -1583,8 +1686,24 @@ void macdrv_init_display_devices(void)
for (gpu = 0; gpu < gpu_count; gpu++)
{
- if (!macdrv_init_gpu(gpu_devinfo, &gpus[gpu], gpu))
+ if (!macdrv_init_gpu(gpu_devinfo, &gpus[gpu], gpu, guidW, driverW))
goto done;
+
+ /* Initialize adapters */
+ if (macdrv_get_adapters(gpus[gpu].id, &adapters, &adapter_count))
+ goto done;
+
+ for (adapter = 0; adapter < adapter_count; adapter++)
+ {
+ if (!macdrv_init_adapter(video_hkey, video_index, gpu, adapter, &gpus[gpu], guidW, driverW,
+ &adapters[adapter]))
+ goto done;
+
+ video_index++;
+ }
+
+ macdrv_free_adapters(adapters);
+ adapters = NULL;
}
done:
@@ -1596,4 +1715,5 @@ done:
CloseHandle(mutex);
macdrv_free_gpus(gpus);
+ macdrv_free_adapters(adapters);
}
diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h
index 513746fa0e..b960e902e3 100644
--- a/dlls/winemac.drv/macdrv_cocoa.h
+++ b/dlls/winemac.drv/macdrv_cocoa.h
@@ -258,6 +258,10 @@ static inline CGPoint cgpoint_win_from_mac(CGPoint point)
/* display */
+/* Used DISPLAY_DEVICE.StateFlags for adapters */
+#define DISPLAY_DEVICE_ATTACHED_TO_DESKTOP 0x00000001
+#define DISPLAY_DEVICE_PRIMARY_DEVICE 0x00000004
+
/* Represent a physical GPU in the PCI slots */
struct macdrv_gpu
{
@@ -272,12 +276,23 @@ static inline CGPoint cgpoint_win_from_mac(CGPoint point)
uint32_t revision_id;
};
+/* Represent an adapter in EnumDisplayDevices context */
+struct macdrv_adapter
+{
+ /* ID to uniquely identify an adapter. Currently it's a CGDirectDisplayID */
+ uint32_t id;
+ /* as StateFlags in DISPLAY_DEVICE struct */
+ uint32_t state_flags;
+};
+
extern int macdrv_get_displays(struct macdrv_display** displays, int* count) DECLSPEC_HIDDEN;
extern void macdrv_free_displays(struct macdrv_display* displays) DECLSPEC_HIDDEN;
extern int macdrv_set_display_mode(const struct macdrv_display* display,
CGDisplayModeRef display_mode) DECLSPEC_HIDDEN;
extern int macdrv_get_gpus(struct macdrv_gpu** gpus, int* count) DECLSPEC_HIDDEN;
extern void macdrv_free_gpus(struct macdrv_gpu* gpus) DECLSPEC_HIDDEN;
+extern int macdrv_get_adapters(uint64_t gpu_id, struct macdrv_adapter** adapters, int* count) DECLSPEC_HIDDEN;
+extern void macdrv_free_adapters(struct macdrv_adapter* adapters) DECLSPEC_HIDDEN;
/* event */
--
2.15.2 (Apple Git-101.1)