Implement SHRegGetBoolUSValue{A|W}.
[wine] / dlls / x11drv / xvidmode.c
1 /*
2  * DirectDraw XVidMode interface
3  *
4  * Copyright 2001 TransGaming Technologies, Inc.
5  */
6
7 /* FIXME: ChangeDisplaySettings ought to be able to use this */
8
9 #include "config.h"
10 #include <string.h>
11
12 #include "ts_xlib.h"
13 #include "ts_xf86vmode.h"
14 #include "x11drv.h"
15 #include "x11ddraw.h"
16 #include "xvidmode.h"
17
18 #include "windef.h"
19 #include "wingdi.h"
20 #include "ddrawi.h"
21 #include "debugtools.h"
22
23 DEFAULT_DEBUG_CHANNEL(x11drv);
24
25 #ifdef HAVE_LIBXXF86VM
26
27 extern int usexvidmode;
28
29 static int xf86vm_event, xf86vm_error, xf86vm_major, xf86vm_minor;
30
31 #ifdef X_XF86VidModeSetGammaRamp
32 static int xf86vm_gammaramp_size;
33 static BOOL xf86vm_use_gammaramp;
34 #endif
35
36 LPDDHALMODEINFO xf86vm_modes;
37 unsigned xf86vm_mode_count;
38 XF86VidModeModeInfo** modes;
39
40 static void convert_modeinfo( const XF86VidModeModeInfo *mode, LPDDHALMODEINFO info )
41 {
42   info->dwWidth      = mode->hdisplay;
43   info->dwHeight     = mode->vdisplay;
44   if (mode->htotal!=0 && mode->vtotal!=0)
45       info->wRefreshRate = mode->dotclock * 1000 / (mode->htotal * mode->vtotal);
46   else
47       info->wRefreshRate = 0;
48   TRACE(" width=%ld, height=%ld, refresh=%d\n",
49         info->dwWidth, info->dwHeight, info->wRefreshRate);
50   /* XVidMode cannot change display depths... */
51   /* let's not bother with filling out these then... */
52   info->lPitch         = 0;
53   info->dwBPP          = 0;
54   info->wFlags         = 0;
55   info->dwRBitMask     = 0;
56   info->dwGBitMask     = 0;
57   info->dwBBitMask     = 0;
58   info->dwAlphaBitMask = 0;
59 }
60
61 static void convert_modeline(int dotclock, const XF86VidModeModeLine *mode, LPDDHALMODEINFO info)
62 {
63   info->dwWidth      = mode->hdisplay;
64   info->dwHeight     = mode->vdisplay;
65   if (mode->htotal!=0 && mode->vtotal!=0)
66       info->wRefreshRate = dotclock * 1000 / (mode->htotal * mode->vtotal);
67   else
68       info->wRefreshRate = 0;
69   TRACE(" width=%ld, height=%ld, refresh=%d\n",
70         info->dwWidth, info->dwHeight, info->wRefreshRate);
71   /* XVidMode cannot change display depths... */
72   /* let's not bother with filling out these then... */
73   info->lPitch         = 0;
74   info->dwBPP          = 0;
75   info->wFlags         = 0;
76   info->dwRBitMask     = 0;
77   info->dwGBitMask     = 0;
78   info->dwBBitMask     = 0;
79   info->dwAlphaBitMask = 0;
80 }
81
82 void X11DRV_XF86VM_Init(void)
83 {
84   int nmodes, i;
85
86   if (xf86vm_major) return; /* already initialized? */
87
88   /* if in desktop mode, don't use XVidMode */
89   if (root_window != DefaultRootWindow(gdi_display)) return;
90
91   if (!usexvidmode) return;
92
93   /* see if XVidMode is available */
94   if (!TSXF86VidModeQueryExtension(gdi_display, &xf86vm_event, &xf86vm_error)) return;
95   if (!TSXF86VidModeQueryVersion(gdi_display, &xf86vm_major, &xf86vm_minor)) return;
96
97 #ifdef X_XF86VidModeSetGammaRamp
98   if (xf86vm_major > 2 || (xf86vm_major == 2 && xf86vm_minor >= 1))
99   {
100       wine_tsx11_lock();
101       XF86VidModeGetGammaRampSize(gdi_display, DefaultScreen(gdi_display),
102                                   &xf86vm_gammaramp_size);
103       wine_tsx11_unlock();
104
105       if (xf86vm_gammaramp_size == 256)
106           xf86vm_use_gammaramp = TRUE;
107   }
108 #endif
109
110   /* retrieve modes */
111   if (!TSXF86VidModeGetAllModeLines(gdi_display, DefaultScreen(gdi_display), &nmodes,
112                                     &modes))
113     return;
114
115   TRACE("XVidMode modes: count=%d\n", nmodes);
116
117   xf86vm_mode_count = nmodes;
118   xf86vm_modes = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DDHALMODEINFO) * nmodes);
119
120   /* convert modes to DDHALMODEINFO format */
121   for (i=0; i<nmodes; i++)
122     convert_modeinfo(modes[i], &xf86vm_modes[i]);
123
124   TRACE("Enabling XVidMode\n");
125 }
126
127 void X11DRV_XF86VM_Cleanup(void)
128 {
129   if (modes) TSXFree(modes);
130 }
131
132 int X11DRV_XF86VM_GetCurrentMode(void)
133 {
134   XF86VidModeModeLine line;
135   int dotclock, i;
136   DDHALMODEINFO cmode;
137
138   if (!xf86vm_modes) return 0; /* no XVidMode */
139
140   TRACE("Querying XVidMode current mode\n");
141   TSXF86VidModeGetModeLine(gdi_display, DefaultScreen(gdi_display), &dotclock, &line);
142   convert_modeline(dotclock, &line, &cmode);
143   for (i=0; i<xf86vm_mode_count; i++)
144     if (memcmp(&xf86vm_modes[i], &cmode, sizeof(cmode)) == 0) {
145       TRACE("mode=%d\n", i);
146       return i;
147     }
148   ERR("unknown mode, shouldn't happen\n");
149   return 0; /* return first mode */
150 }
151
152 void X11DRV_XF86VM_SetCurrentMode(int mode)
153 {
154   if (!xf86vm_modes) return; /* no XVidMode */
155
156   TSXF86VidModeSwitchToMode(gdi_display, DefaultScreen(gdi_display), modes[mode]);
157 #if 0 /* it is said that SetViewPort causes problems with some X servers */
158   TSXF86VidModeSetViewPort(gdi_display, DefaultScreen(gdi_display), 0, 0);
159 #else
160   TSXWarpPointer(gdi_display, None, DefaultRootWindow(gdi_display), 0, 0, 0, 0, 0, 0);
161 #endif
162   TSXSync(gdi_display, False);
163 }
164
165 void X11DRV_XF86VM_SetExclusiveMode(int lock)
166 {
167   if (!xf86vm_modes) return; /* no XVidMode */
168
169   TSXF86VidModeLockModeSwitch(gdi_display, DefaultScreen(gdi_display), lock);
170 }
171
172 /* actual DirectDraw HAL stuff */
173
174 static DWORD PASCAL X11DRV_XF86VM_SetMode(LPDDHAL_SETMODEDATA data)
175 {
176   X11DRV_XF86VM_SetCurrentMode(data->dwModeIndex);
177   X11DRV_DDHAL_SwitchMode(data->dwModeIndex, NULL, NULL);
178   data->ddRVal = DD_OK;
179   return DDHAL_DRIVER_HANDLED;
180 }
181
182 int X11DRV_XF86VM_CreateDriver(LPDDHALINFO info)
183 {
184   if (!xf86vm_mode_count) return 0; /* no XVidMode */
185
186   info->dwNumModes = xf86vm_mode_count;
187   info->lpModeInfo = xf86vm_modes;
188   X11DRV_DDHAL_SwitchMode(X11DRV_XF86VM_GetCurrentMode(), NULL, NULL);
189   info->lpDDCallbacks->SetMode = X11DRV_XF86VM_SetMode;
190   return TRUE;
191 }
192
193
194 /***** GAMMA CONTROL *****/
195 /* (only available in XF86VidMode 2.x) */
196
197 #ifdef X_XF86VidModeSetGamma
198
199 static void GenerateRampFromGamma(WORD ramp[256], float gamma)
200 {
201   float r_gamma = 1/gamma;
202   unsigned i;
203   TRACE("gamma is %f\n", r_gamma);
204   for (i=0; i<256; i++)
205     ramp[i] = pow(i/255.0, r_gamma) * 65535.0;
206 }
207
208 static BOOL ComputeGammaFromRamp(WORD ramp[256], float *gamma)
209 {
210   float r_x, r_y, r_lx, r_ly, r_d, r_v, r_e, g_avg, g_min, g_max;
211   unsigned i, f, l, g_n, c;
212   f = ramp[0];
213   l = ramp[255];
214   if (f >= l) {
215     ERR("inverted or flat gamma ramp (%d->%d), rejected\n", f, l);
216     return FALSE;
217   }
218   r_d = l - f;
219   g_min = g_max = g_avg = 0.0;
220   /* check gamma ramp entries to estimate the gamma */
221   TRACE("analyzing gamma ramp (%d->%d)\n", f, l);
222   for (i=1, g_n=0; i<255; i++) {
223     if (ramp[i] < f || ramp[i] > l) {
224       ERR("strange gamma ramp ([%d]=%d for %d->%d), rejected\n", i, ramp[i], f, l);
225       return FALSE;
226     }
227     c = ramp[i] - f;
228     if (!c) continue; /* avoid log(0) */
229
230     /* normalize entry values into 0..1 range */
231     r_x = i/255.0; r_y = c / r_d;
232     /* compute logarithms of values */
233     r_lx = log(r_x); r_ly = log(r_y);
234     /* compute gamma for this entry */
235     r_v = r_ly / r_lx;
236     /* compute differential (error estimate) for this entry */
237     /* some games use table-based logarithms that magnifies the error by 128 */
238     r_e = -r_lx * 128 / (c * r_lx * r_lx);
239
240     /* compute min & max while compensating for estimated error */
241     if (!g_n || g_min > (r_v + r_e)) g_min = r_v + r_e;
242     if (!g_n || g_max < (r_v - r_e)) g_max = r_v - r_e;
243
244     /* add to average */
245     g_avg += r_v;
246     g_n++;
247     /* TRACE("[%d]=%d, gamma=%f, error=%f\n", i, ramp[i], r_v, r_e); */
248   }
249   if (!g_n) {
250     ERR("no gamma data, shouldn't happen\n");
251     return FALSE;
252   }
253   g_avg /= g_n;
254   TRACE("low bias is %d, high is %d, gamma is %5.3f\n", f, 65535-l, g_avg);
255   /* the bias could be because the app wanted something like a "red shift"
256    * like when you're hit in Quake, but XVidMode doesn't support it,
257    * so we have to reject a significant bias */
258   if (f && f > (pow(1/255.0, g_avg) * 65536.0)) {
259     ERR("low-biased gamma ramp (%d), rejected\n", f);
260     return FALSE;
261   }
262   /* check that the gamma is reasonably uniform across the ramp */
263   if (g_max - g_min > 0.1) {
264     ERR("ramp not uniform (max=%f, min=%f, avg=%f), rejected\n", g_max, g_min, g_avg);
265     return FALSE;
266   }
267   /* ok, now we're pretty sure we can set the desired gamma ramp,
268    * so go for it */
269   *gamma = 1/g_avg;
270   return TRUE;
271 }
272
273 #endif /* X_XF86VidModeSetGamma */
274
275 /* Hmm... should gamma control be available in desktop mode or not?
276  * I'll assume that it should */
277
278 BOOL X11DRV_XF86VM_GetGammaRamp(LPDDGAMMARAMP ramp)
279 {
280 #ifdef X_XF86VidModeSetGamma
281   XF86VidModeGamma gamma;
282   Bool ret;
283
284   if (xf86vm_major < 2) return FALSE; /* no gamma control */
285 #ifdef X_XF86VidModeSetGammaRamp
286   else if (xf86vm_use_gammaramp)
287   {
288       Bool ret;
289       wine_tsx11_lock();
290       ret = XF86VidModeGetGammaRamp(gdi_display, DefaultScreen(gdi_display), 256,
291                                     ramp->red, ramp->green, ramp->blue);
292       wine_tsx11_unlock();
293       return ret;
294   }
295 #endif
296   else
297   {
298       wine_tsx11_lock();
299       ret = XF86VidModeGetGamma(gdi_display, DefaultScreen(gdi_display), &gamma);
300       wine_tsx11_unlock();
301       if (ret) {
302           GenerateRampFromGamma(ramp->red,   gamma.red);
303           GenerateRampFromGamma(ramp->green, gamma.green);
304           GenerateRampFromGamma(ramp->blue,  gamma.blue);
305           return TRUE;
306       }
307   }
308 #endif /* X_XF86VidModeSetGamma */
309   return FALSE;
310 }
311
312 BOOL X11DRV_XF86VM_SetGammaRamp(LPDDGAMMARAMP ramp)
313 {
314 #ifdef X_XF86VidModeSetGamma
315   XF86VidModeGamma gamma;
316
317   if (xf86vm_major < 2) return FALSE; /* no gamma control */
318 #ifdef X_XF86VidModeSetGammaRamp
319   else if (xf86vm_use_gammaramp)
320   {
321       Bool ret;
322       wine_tsx11_lock();
323       ret = XF86VidModeSetGammaRamp(gdi_display, DefaultScreen(gdi_display), 256,
324                                     ramp->red, ramp->green, ramp->blue);
325       wine_tsx11_unlock();
326       return ret;
327   }
328 #endif
329   else
330   {
331       if (ComputeGammaFromRamp(ramp->red,   &gamma.red) &&
332           ComputeGammaFromRamp(ramp->green, &gamma.green) &&
333           ComputeGammaFromRamp(ramp->blue,  &gamma.blue)) {
334           Bool ret;
335           wine_tsx11_lock();
336           ret = XF86VidModeSetGamma(gdi_display, DefaultScreen(gdi_display), &gamma);
337           wine_tsx11_unlock();
338           return ret;
339       }
340   }
341 #endif /* X_XF86VidModeSetGamma */
342   return FALSE;
343 }
344
345 #endif /* HAVE_LIBXXF86VM */
346
347 /***********************************************************************
348  *              GetDeviceGammaRamp (X11DRV.@)
349  *
350  * FIXME: should move to somewhere appropriate, but probably not before
351  * the stuff in graphics/x11drv/ has been moved to dlls/x11drv, so that
352  * they can include xvidmode.h directly
353  */
354 BOOL X11DRV_GetDeviceGammaRamp(DC *dc, LPVOID ramp)
355 {
356 #ifdef HAVE_LIBXXF86VM
357   return X11DRV_XF86VM_GetGammaRamp(ramp);
358 #else
359   return FALSE;
360 #endif
361 }
362
363 /***********************************************************************
364  *              SetDeviceGammaRamp (X11DRV.@)
365  *
366  * FIXME: should move to somewhere appropriate, but probably not before
367  * the stuff in graphics/x11drv/ has been moved to dlls/x11drv, so that
368  * they can include xvidmode.h directly
369  */
370 BOOL X11DRV_SetDeviceGammaRamp(DC *dc, LPVOID ramp)
371 {
372 #ifdef HAVE_LIBXXF86VM
373   return X11DRV_XF86VM_SetGammaRamp(ramp);
374 #else
375   return FALSE;
376 #endif
377 }