1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS kernel 4 * FILE: lib/opengl32/icdload.c 5 * PURPOSE: OpenGL32 lib, ICD dll loader 6 */ 7 8 #include "opengl32.h" 9 10 #include <winreg.h> 11 12 WINE_DEFAULT_DEBUG_CHANNEL(opengl32); 13 14 typedef struct 15 { 16 DWORD Version; /*!< Driver interface version */ 17 DWORD DriverVersion; /*!< Driver version */ 18 WCHAR DriverName[256]; /*!< Driver name */ 19 } Drv_Opengl_Info, *pDrv_Opengl_Info; 20 21 typedef enum 22 { 23 OGL_CD_NOT_QUERIED, 24 OGL_CD_NONE, 25 OGL_CD_ROSSWI, 26 OGL_CD_CUSTOM_ICD 27 } CUSTOM_DRIVER_STATE; 28 29 static CRITICAL_SECTION icdload_cs = {NULL, -1, 0, 0, 0, 0}; 30 static struct ICD_Data* ICD_Data_List = NULL; 31 static const WCHAR OpenGLDrivers_Key[] = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers"; 32 static const WCHAR CustomDrivers_Key[] = L"SOFTWARE\\ReactOS\\OpenGL"; 33 static Drv_Opengl_Info CustomDrvInfo; 34 static CUSTOM_DRIVER_STATE CustomDriverState = OGL_CD_NOT_QUERIED; 35 36 static void APIENTRY wglSetCurrentValue(PVOID value) 37 { 38 IntSetCurrentICDPrivate(value); 39 } 40 41 static PVOID APIENTRY wglGetCurrentValue() 42 { 43 return IntGetCurrentICDPrivate(); 44 } 45 46 static DHGLRC APIENTRY wglGetDHGLRC(struct wgl_context* context) 47 { 48 return context->dhglrc; 49 } 50 51 /* GDI entry points (win32k) */ 52 extern INT APIENTRY GdiDescribePixelFormat(HDC hdc, INT ipfd, UINT cjpfd, PPIXELFORMATDESCRIPTOR ppfd); 53 extern BOOL APIENTRY GdiSetPixelFormat(HDC hdc, INT ipfd); 54 extern BOOL APIENTRY GdiSwapBuffers(HDC hdc); 55 56 /* Retrieves the ICD data (driver version + relevant DLL entry points) for a device context */ 57 struct ICD_Data* IntGetIcdData(HDC hdc) 58 { 59 int ret; 60 DWORD dwInput, dwValueType, Version, DriverVersion, Flags; 61 Drv_Opengl_Info DrvInfo; 62 pDrv_Opengl_Info pDrvInfo; 63 struct ICD_Data* data; 64 HKEY OglKey = NULL; 65 HKEY DrvKey, CustomKey; 66 WCHAR DllName[MAX_PATH]; 67 BOOL (WINAPI *DrvValidateVersion)(DWORD); 68 void (WINAPI *DrvSetCallbackProcs)(int nProcs, PROC* pProcs); 69 70 /* The following code is ReactOS specific and allows us to easily load an arbitrary ICD: 71 * It checks HKCU\Software\ReactOS\OpenGL for a custom ICD and will always load it 72 * no matter what driver the DC is associated with. It can also force using the 73 * built-in Software Implementation*/ 74 if(CustomDriverState == OGL_CD_NOT_QUERIED) 75 { 76 /* Only do this once so there's not any significant performance penalty */ 77 CustomDriverState = OGL_CD_NONE; 78 memset(&CustomDrvInfo, 0, sizeof(Drv_Opengl_Info)); 79 80 ret = RegOpenKeyExW(HKEY_CURRENT_USER, CustomDrivers_Key, 0, KEY_READ, &CustomKey); 81 if(ret != ERROR_SUCCESS) 82 goto custom_end; 83 84 dwInput = sizeof(CustomDrvInfo.DriverName); 85 ret = RegQueryValueExW(CustomKey, L"", 0, &dwValueType, (LPBYTE)CustomDrvInfo.DriverName, &dwInput); 86 RegCloseKey(CustomKey); 87 88 if((ret != ERROR_SUCCESS) || (dwValueType != REG_SZ) || !wcslen(CustomDrvInfo.DriverName)) 89 goto custom_end; 90 91 if(!_wcsicmp(CustomDrvInfo.DriverName, L"ReactOS Software Implementation")) 92 { 93 /* Always announce the fact that we're forcing ROSSWI */ 94 ERR("Forcing ReactOS Software Implementation\n"); 95 CustomDriverState = OGL_CD_ROSSWI; 96 return NULL; 97 } 98 99 ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, OpenGLDrivers_Key, 0, KEY_READ, &OglKey); 100 if(ret != ERROR_SUCCESS) 101 goto custom_end; 102 103 ret = RegOpenKeyExW(OglKey, CustomDrvInfo.DriverName, 0, KEY_READ, &OglKey); 104 if(ret != ERROR_SUCCESS) 105 goto custom_end; 106 107 dwInput = sizeof(CustomDrvInfo.Version); 108 ret = RegQueryValueExW(OglKey, L"Version", 0, &dwValueType, (LPBYTE)&CustomDrvInfo.Version, &dwInput); 109 if((ret != ERROR_SUCCESS) || (dwValueType != REG_DWORD)) 110 goto custom_end; 111 112 dwInput = sizeof(DriverVersion); 113 ret = RegQueryValueExW(OglKey, L"DriverVersion", 0, &dwValueType, (LPBYTE)&CustomDrvInfo.DriverVersion, &dwInput); 114 CustomDriverState = OGL_CD_CUSTOM_ICD; 115 116 /* Always announce the fact that we're overriding the default driver */ 117 ERR("Overriding the default OGL ICD with %S\n", CustomDrvInfo.DriverName); 118 119 custom_end: 120 if(OglKey) 121 RegCloseKey(OglKey); 122 RegCloseKey(CustomKey); 123 } 124 125 /* If there's a custom ICD or ROSSWI was requested use it, otherwise proceed as usual */ 126 if(CustomDriverState == OGL_CD_CUSTOM_ICD) 127 { 128 pDrvInfo = &CustomDrvInfo; 129 } 130 else if(CustomDriverState == OGL_CD_ROSSWI) 131 { 132 return NULL; 133 } 134 else 135 { 136 /* First, see if the driver supports this */ 137 dwInput = OPENGL_GETINFO; 138 ret = ExtEscape(hdc, QUERYESCSUPPORT, sizeof(DWORD), (LPCSTR)&dwInput, 0, NULL); 139 140 /* Driver doesn't support opengl */ 141 if(ret <= 0) 142 return NULL; 143 144 /* Query for the ICD DLL name and version */ 145 dwInput = 0; 146 ret = ExtEscape(hdc, OPENGL_GETINFO, sizeof(DWORD), (LPCSTR)&dwInput, sizeof(DrvInfo), (LPSTR)&DrvInfo); 147 148 if(ret <= 0) 149 { 150 ERR("Driver claims to support OPENGL_GETINFO escape code, but doesn't.\n"); 151 return NULL; 152 } 153 154 pDrvInfo = &DrvInfo; 155 } 156 157 /* Protect the list while we are loading*/ 158 EnterCriticalSection(&icdload_cs); 159 160 /* Search for it in the list of already loaded modules */ 161 data = ICD_Data_List; 162 while(data) 163 { 164 if(!_wcsicmp(data->DriverName, pDrvInfo->DriverName)) 165 { 166 /* Found it */ 167 TRACE("Found already loaded %p.\n", data); 168 LeaveCriticalSection(&icdload_cs); 169 return data; 170 } 171 data = data->next; 172 } 173 174 /* It was still not loaded, look for it in the registry */ 175 ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, OpenGLDrivers_Key, 0, KEY_READ, &OglKey); 176 if(ret != ERROR_SUCCESS) 177 { 178 ERR("Failed to open the OpenGLDrivers key.\n"); 179 goto end; 180 } 181 ret = RegOpenKeyExW(OglKey, pDrvInfo->DriverName, 0, KEY_READ, &DrvKey); 182 if(ret != ERROR_SUCCESS) 183 { 184 /* Some driver installer just provide the DLL name, like the Matrox G400 */ 185 TRACE("No driver subkey for %S, trying to get DLL name directly.\n", pDrvInfo->DriverName); 186 dwInput = sizeof(DllName); 187 ret = RegQueryValueExW(OglKey, pDrvInfo->DriverName, 0, &dwValueType, (LPBYTE)DllName, &dwInput); 188 if((ret != ERROR_SUCCESS) || (dwValueType != REG_SZ)) 189 { 190 ERR("Unable to get ICD DLL name!\n"); 191 RegCloseKey(OglKey); 192 goto end; 193 } 194 Version = DriverVersion = Flags = 0; 195 TRACE("DLL name is %S.\n", DllName); 196 } 197 else 198 { 199 /* The driver have a subkey for the ICD */ 200 TRACE("Querying details from registry for %S.\n", pDrvInfo->DriverName); 201 dwInput = sizeof(DllName); 202 ret = RegQueryValueExW(DrvKey, L"Dll", 0, &dwValueType, (LPBYTE)DllName, &dwInput); 203 if((ret != ERROR_SUCCESS) || (dwValueType != REG_SZ)) 204 { 205 ERR("Unable to get ICD DLL name!.\n"); 206 RegCloseKey(DrvKey); 207 RegCloseKey(OglKey); 208 goto end; 209 } 210 211 dwInput = sizeof(Version); 212 ret = RegQueryValueExW(DrvKey, L"Version", 0, &dwValueType, (LPBYTE)&Version, &dwInput); 213 if((ret != ERROR_SUCCESS) || (dwValueType != REG_DWORD)) 214 { 215 WARN("No version in driver subkey\n"); 216 } 217 else if(Version != pDrvInfo->Version) 218 { 219 ERR("Version mismatch between registry (%lu) and display driver (%lu).\n", Version, pDrvInfo->Version); 220 RegCloseKey(DrvKey); 221 RegCloseKey(OglKey); 222 goto end; 223 } 224 225 dwInput = sizeof(DriverVersion); 226 ret = RegQueryValueExW(DrvKey, L"DriverVersion", 0, &dwValueType, (LPBYTE)&DriverVersion, &dwInput); 227 if((ret != ERROR_SUCCESS) || (dwValueType != REG_DWORD)) 228 { 229 WARN("No driver version in driver subkey\n"); 230 } 231 else if(DriverVersion != pDrvInfo->DriverVersion) 232 { 233 ERR("Driver version mismatch between registry (%lu) and display driver (%lu).\n", DriverVersion, pDrvInfo->DriverVersion); 234 RegCloseKey(DrvKey); 235 RegCloseKey(OglKey); 236 goto end; 237 } 238 239 dwInput = sizeof(Flags); 240 ret = RegQueryValueExW(DrvKey, L"Flags", 0, &dwValueType, (LPBYTE)&Flags, &dwInput); 241 if((ret != ERROR_SUCCESS) || (dwValueType != REG_DWORD)) 242 { 243 WARN("No driver version in driver subkey\n"); 244 Flags = 0; 245 } 246 247 /* We're done */ 248 RegCloseKey(DrvKey); 249 TRACE("DLL name is %S, Version %lx, DriverVersion %lx, Flags %lx.\n", DllName, Version, DriverVersion, Flags); 250 } 251 /* No need for this anymore */ 252 RegCloseKey(OglKey); 253 254 /* So far so good, allocate data */ 255 data = HeapAlloc(GetProcessHeap(), 0, sizeof(*data)); 256 if(!data) 257 { 258 ERR("Unable to allocate ICD data!\n"); 259 goto end; 260 } 261 262 /* Load the library */ 263 data->hModule = LoadLibraryW(DllName); 264 if(!data->hModule) 265 { 266 ERR("Could not load the ICD DLL: %S.\n", DllName); 267 HeapFree(GetProcessHeap(), 0, data); 268 data = NULL; 269 goto end; 270 } 271 272 /* 273 * Validate version, if needed. 274 * Some drivers (at least VBOX), initialize stuff upon this call. 275 */ 276 DrvValidateVersion = (void*)GetProcAddress(data->hModule, "DrvValidateVersion"); 277 if(DrvValidateVersion) 278 { 279 if(!DrvValidateVersion(pDrvInfo->DriverVersion)) 280 { 281 ERR("DrvValidateVersion failed!.\n"); 282 goto fail; 283 } 284 } 285 286 /* Pass the callbacks */ 287 DrvSetCallbackProcs = (void*)GetProcAddress(data->hModule, "DrvSetCallbackProcs"); 288 if(DrvSetCallbackProcs) 289 { 290 PROC callbacks[] = {(PROC)wglGetCurrentValue, 291 (PROC)wglSetCurrentValue, 292 (PROC)wglGetDHGLRC}; 293 DrvSetCallbackProcs(3, callbacks); 294 } 295 296 /* Get the DLL exports */ 297 #define DRV_LOAD(x) do \ 298 { \ 299 data->x = (void*)GetProcAddress(data->hModule, #x); \ 300 if(!data->x) { \ 301 ERR("%S lacks " #x "!\n", DllName); \ 302 goto fail; \ 303 } \ 304 } while(0) 305 DRV_LOAD(DrvCopyContext); 306 DRV_LOAD(DrvCreateContext); 307 DRV_LOAD(DrvCreateLayerContext); 308 DRV_LOAD(DrvDeleteContext); 309 DRV_LOAD(DrvDescribeLayerPlane); 310 DRV_LOAD(DrvDescribePixelFormat); 311 DRV_LOAD(DrvGetLayerPaletteEntries); 312 DRV_LOAD(DrvGetProcAddress); 313 DRV_LOAD(DrvReleaseContext); 314 DRV_LOAD(DrvRealizeLayerPalette); 315 DRV_LOAD(DrvSetContext); 316 DRV_LOAD(DrvSetLayerPaletteEntries); 317 DRV_LOAD(DrvSetPixelFormat); 318 DRV_LOAD(DrvShareLists); 319 DRV_LOAD(DrvSwapBuffers); 320 DRV_LOAD(DrvSwapLayerBuffers); 321 #undef DRV_LOAD 322 323 /* Let's see if GDI should handle this instead of the ICD DLL */ 324 // FIXME: maybe there is a better way 325 if (GdiDescribePixelFormat(hdc, 0, 0, NULL) != 0) 326 { 327 /* GDI knows what to do with that. Override */ 328 TRACE("Forwarding WGL calls to win32k!\n"); 329 data->DrvDescribePixelFormat = GdiDescribePixelFormat; 330 data->DrvSetPixelFormat = GdiSetPixelFormat; 331 data->DrvSwapBuffers = GdiSwapBuffers; 332 } 333 334 /* Copy the DriverName */ 335 wcscpy(data->DriverName, pDrvInfo->DriverName); 336 337 /* Push the list */ 338 data->next = ICD_Data_List; 339 ICD_Data_List = data; 340 341 TRACE("Returning %p.\n", data); 342 TRACE("ICD driver %S (%S) successfully loaded.\n", pDrvInfo->DriverName, DllName); 343 344 end: 345 /* Unlock and return */ 346 LeaveCriticalSection(&icdload_cs); 347 return data; 348 349 fail: 350 LeaveCriticalSection(&icdload_cs); 351 FreeLibrary(data->hModule); 352 HeapFree(GetProcessHeap(), 0, data); 353 return NULL; 354 } 355 356 void IntDeleteAllICDs(void) 357 { 358 struct ICD_Data* data; 359 360 EnterCriticalSection(&icdload_cs); 361 362 while (ICD_Data_List != NULL) 363 { 364 data = ICD_Data_List; 365 ICD_Data_List = data->next; 366 367 FreeLibrary(data->hModule); 368 HeapFree(GetProcessHeap(), 0, data); 369 } 370 } 371