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