[OPENGL]
[reactos.git] / 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 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 }