Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/winemac.drv/Makefile.in | 2 +- dlls/winemac.drv/display.c | 233 +++++++++++++++++++++++++++++++++++++++++ dlls/winemac.drv/macdrv.h | 1 + dlls/winemac.drv/macdrv_main.c | 3 + 4 files changed, 238 insertions(+), 1 deletion(-)
diff --git a/dlls/winemac.drv/Makefile.in b/dlls/winemac.drv/Makefile.in index 3ffb7d666c..a5bd9a426f 100644 --- a/dlls/winemac.drv/Makefile.in +++ b/dlls/winemac.drv/Makefile.in @@ -1,5 +1,5 @@ MODULE = winemac.drv -IMPORTS = uuid user32 gdi32 advapi32 +IMPORTS = uuid user32 gdi32 advapi32 setupapi ntoskrnl DELAYIMPORTS = ole32 shell32 imm32 EXTRALIBS = -framework AppKit -framework Carbon -framework Security -framework OpenGL -framework IOKit -framework CoreVideo $(METAL_LIBS)
diff --git a/dlls/winemac.drv/display.c b/dlls/winemac.drv/display.c index 00ad7738bb..b756510875 100644 --- a/dlls/winemac.drv/display.c +++ b/dlls/winemac.drv/display.c @@ -25,6 +25,13 @@ #include "winuser.h" #include "winreg.h" #include "ddrawi.h" +#include "initguid.h" +#include "devguid.h" +#include "devpkey.h" +#include "setupapi.h" +#define WIN32_NO_STATUS +#include "winternl.h" +#include "ddk/ntddk.h" #include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(display); @@ -47,6 +54,41 @@ BOOL CDECL macdrv_EnumDisplaySettingsEx(LPCWSTR devname, DWORD mode, LPDEVMODEW
static const char initial_mode_key[] = "Initial Display Mode"; static const WCHAR pixelencodingW[] = {'P','i','x','e','l','E','n','c','o','d','i','n','g',0}; +static const WCHAR driver_descW[] = {'D','r','i','v','e','r','D','e','s','c',0}; +static const WCHAR graphics_driverW[] = {'G','r','a','p','h','i','c','s','D','r','i','v','e','r',0}; +static const WCHAR video_idW[] = {'V','i','d','e','o','I','D',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}; +static const WCHAR gpu_instance_fmtW[] = { + 'P','C','I','\', + 'V','E','N','_','%','0','4','X','&', + 'D','E','V','_','%','0','4','X','&', + 'S','U','B','S','Y','S','_','%','0','8','X','&', + 'R','E','V','_','%','0','2','X','\', + '%','0','8','X',0}; +static const WCHAR gpu_hardware_id_fmtW[] = { + 'P','C','I','\', + 'V','E','N','_','%','0','4','X','&', + 'D','E','V','_','%','0','4','X','&', + 'S','U','B','S','Y','S','_','0','0','0','0','0','0','0','0','&', + 'R','E','V','_','0','0',0}; +static const WCHAR winemac_drvW[] = { + 'C',':','\', + 'W','i','n','d','o','w','s','\', + 'S','y','s','t','e','m','3','2','\', + 'w','i','n','e','m','a','c','.','d','r','v',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 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 CFArrayRef modes; @@ -1397,3 +1439,194 @@ void macdrv_displays_changed(const macdrv_event *event) MAKELPARAM(width, height)); } } + +/*********************************************************************** + * macdrv_init_gpu + * + * 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, WCHAR *guid_string, + WCHAR *driver) +{ + static const BOOL present = TRUE; + SP_DEVINFO_DATA device_data = {sizeof(device_data)}; + WCHAR instanceW[MAX_PATH]; + WCHAR nameW[MAX_PATH]; + WCHAR bufferW[1024]; + HKEY hkey = NULL; + GUID guid; + INT written; + DWORD size; + BOOL ret = FALSE; + + sprintfW(instanceW, gpu_instance_fmtW, gpu->vendor_id, gpu->device_id, gpu->subsys_id, gpu->revision_id, gpu_index); + MultiByteToWideChar(CP_UTF8, 0, gpu->name, -1, nameW, ARRAY_SIZE(nameW)); + if (!SetupDiOpenDeviceInfoW(devinfo, instanceW, NULL, 0, &device_data)) + { + SetupDiCreateDeviceInfoW(devinfo, instanceW, &GUID_DEVCLASS_DISPLAY, nameW, NULL, 0, &device_data); + if (!SetupDiRegisterDeviceInfo(devinfo, &device_data, 0, NULL, NULL, NULL)) + goto fail; + } + + /* Write HaredwareID registry property */ + written = sprintfW(bufferW, gpu_hardware_id_fmtW, gpu->vendor_id, gpu->device_id); + bufferW[written + 1] = 0; + if (!SetupDiSetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_HARDWAREID, (const BYTE *)bufferW, + (written + 2) * sizeof(WCHAR))) + goto fail; + + /* Write DEVPKEY_Device_IsPresent property */ + if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &DEVPKEY_Device_IsPresent, DEVPROP_TYPE_BOOLEAN, + (const BYTE *)&present, sizeof(present), 0)) + goto fail; + + /* Open driver key. + * This is where HKLM\System\CurrentControlSet\Control\Video{GPU GUID}{Adapter Index} links to */ + hkey = SetupDiOpenDevRegKey(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_ALL_ACCESS); + if (hkey == INVALID_HANDLE_VALUE) + hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL); + + /* Write GraphicsDriver value */ + if (RegSetValueExW(hkey, graphics_driverW, 0, REG_SZ, (const BYTE *)winemac_drvW, sizeof(winemac_drvW))) + goto fail; + + /* Write DriverDesc value */ + if (RegSetValueExW(hkey, driver_descW, 0, REG_SZ, (const BYTE *)nameW, (strlenW(nameW) + 1) * sizeof(WCHAR))) + goto fail; + RegCloseKey(hkey); + hkey = NULL; + + /* Retrieve driver value for adapters */ + if (!SetupDiGetDeviceRegistryPropertyW(devinfo, &device_data, SPDRP_DRIVER, NULL, (BYTE *)bufferW, sizeof(bufferW), + NULL)) + goto fail; + strcpyW(driver, nt_classW); + strcatW(driver, bufferW); + + /* Write GUID in VideoID in .../instance/Device Parameters, reuse the GUID if it's existent */ + hkey = SetupDiOpenDevRegKey(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_ALL_ACCESS); + if (hkey == INVALID_HANDLE_VALUE) + hkey = SetupDiCreateDevRegKeyW(devinfo, &device_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, NULL, NULL); + size = sizeof(bufferW); + bufferW[0] = 0; + if (RegQueryValueExW(hkey, video_idW, 0, NULL, (BYTE *)bufferW, &size)) + { + ExUuidCreate(&guid); + sprintfW(bufferW, guid_fmtW, guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], + guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); + if (RegSetValueExW(hkey, video_idW, 0, REG_SZ, (const BYTE *)bufferW, (strlenW(bufferW) + 1) * sizeof(WCHAR))) + goto fail; + } + strcpyW(guid_string, bufferW); + + ret = TRUE; +fail: + RegCloseKey(hkey); + if (!ret) + ERR("Failed to initialize GPU\n"); + return ret; +} + +static void prepare_devices(void) +{ + static const BOOL not_present = FALSE; + SP_DEVINFO_DATA device_data = {sizeof(device_data)}; + HDEVINFO devinfo; + DWORD i = 0; + + /* Set all GPUs as not present. We can't simply delete them because we need to keep the GUID consistent with + * each initialization run. We clean up non present GPUs at the end of initialization */ + devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, NULL, NULL, 0); + while (SetupDiEnumDeviceInfo(devinfo, i++, &device_data)) + { + if (!SetupDiSetDevicePropertyW(devinfo, &device_data, &DEVPKEY_Device_IsPresent, DEVPROP_TYPE_BOOLEAN, + (const BYTE *)¬_present, sizeof(not_present), 0)) + ERR("Failed to set GPU present property\n"); + } + SetupDiDestroyDeviceInfoList(devinfo); +} + +static void cleanup_devices(void) +{ + SP_DEVINFO_DATA device_data = {sizeof(device_data)}; + HDEVINFO devinfo; + DWORD type; + DWORD i = 0; + BOOL present; + + devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, NULL, NULL, 0); + while (SetupDiEnumDeviceInfo(devinfo, i++, &device_data)) + { + present = FALSE; + SetupDiGetDevicePropertyW(devinfo, &device_data, &DEVPKEY_Device_IsPresent, &type, (BYTE *)&present, + sizeof(present), NULL, 0); + if (!present && !SetupDiRemoveDevice(devinfo, &device_data)) + ERR("Failed to remove GPU\n"); + } + SetupDiDestroyDeviceInfoList(devinfo); +} + +/*********************************************************************** + * macdrv_init_display_devices + * + * Initialize display device registry data. + * + * Return FALSE on failure and TRUE on success. + */ +BOOL macdrv_init_display_devices(void) +{ + struct macdrv_gpu *gpus = NULL; + INT gpu_count; + INT gpu; + HDEVINFO gpu_devinfo = NULL; + HKEY video_hkey = NULL; + DWORD disposition = 0; + WCHAR guidW[40]; + WCHAR driverW[1024]; + BOOL ret = FALSE; + + TRACE("\n"); + + if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, video_keyW, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &video_hkey, + &disposition)) + goto fail; + + /* Ensure only one thread is initializing the registry and avoid unnecessary reinit */ + if (disposition != REG_CREATED_NEW_KEY) + { + ret = TRUE; + goto fail; + } + + /* FIXME: + * Currently we have no idea how to implement SetupDiGetClassDevsW with DIGCF_PRESENT properly. So we need to clean + * up not present devices in case applications use SetupDiGetClassDevsW to enumerate devices. Wrong devices could + * exist in registry as a result of prefix copying or having devices unplugged. But then we couldn't simply delete + * GPUs because we need to retain the same GUID for the same GPU. */ + prepare_devices(); + + gpu_devinfo = SetupDiCreateDeviceInfoList(&GUID_DEVCLASS_DISPLAY, NULL); + + /* Initialize GPUs */ + if (macdrv_get_gpus(&gpus, &gpu_count)) + goto fail; + + for (gpu = 0; gpu < gpu_count; gpu++) + { + if (!macdrv_init_gpu(gpu_devinfo, &gpus[gpu], gpu, guidW, driverW)) + goto fail; + } + + ret = TRUE; +fail: + cleanup_devices(); + SetupDiDestroyDeviceInfoList(gpu_devinfo); + RegCloseKey(video_hkey); + if (gpus) + macdrv_free_gpus(gpus); + if (!ret) + ERR("Failed to initialize display devices\n"); + return ret; +} \ No newline at end of file diff --git a/dlls/winemac.drv/macdrv.h b/dlls/winemac.drv/macdrv.h index f948da42c7..dca89b04f0 100644 --- a/dlls/winemac.drv/macdrv.h +++ b/dlls/winemac.drv/macdrv.h @@ -223,6 +223,7 @@ extern CGImageRef create_cgimage_from_icon_bitmaps(HDC hdc, HANDLE icon, HBITMAP extern void macdrv_status_item_mouse_move(const macdrv_event *event) DECLSPEC_HIDDEN;
extern void check_retina_status(void) DECLSPEC_HIDDEN; +extern BOOL macdrv_init_display_devices(void) DECLSPEC_HIDDEN;
/************************************************************************** * Mac IME driver diff --git a/dlls/winemac.drv/macdrv_main.c b/dlls/winemac.drv/macdrv_main.c index 544d448f9f..0779952e9a 100644 --- a/dlls/winemac.drv/macdrv_main.c +++ b/dlls/winemac.drv/macdrv_main.c @@ -299,6 +299,9 @@ static BOOL process_attach(void) return FALSE; }
+ if (!macdrv_init_display_devices()) + return FALSE; + return TRUE; }