xref: /reactos/dll/opengl/opengl32/icdload.c (revision 4561998a)
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