uxtheme: Remove unused variable.
[wine] / dlls / winex11.drv / xrandr.c
1 /*
2  * Wine X11drv Xrandr interface
3  *
4  * Copyright 2003 Alexander James Pasadyn
5  * Copyright 2012 Henri Verbeet for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24 #include "wine/debug.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(xrandr);
27 WINE_DECLARE_DEBUG_CHANNEL(winediag);
28
29 #ifdef SONAME_LIBXRANDR
30
31 #include <X11/Xlib.h>
32 #include <X11/extensions/Xrandr.h>
33 #include "x11drv.h"
34
35 #include "wine/library.h"
36
37 static void *xrandr_handle;
38
39 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
40 MAKE_FUNCPTR(XRRConfigCurrentConfiguration)
41 MAKE_FUNCPTR(XRRConfigCurrentRate)
42 MAKE_FUNCPTR(XRRFreeScreenConfigInfo)
43 MAKE_FUNCPTR(XRRGetScreenInfo)
44 MAKE_FUNCPTR(XRRQueryExtension)
45 MAKE_FUNCPTR(XRRQueryVersion)
46 MAKE_FUNCPTR(XRRRates)
47 MAKE_FUNCPTR(XRRSetScreenConfig)
48 MAKE_FUNCPTR(XRRSetScreenConfigAndRate)
49 MAKE_FUNCPTR(XRRSizes)
50
51 #ifdef HAVE_XRRGETSCREENRESOURCES
52 MAKE_FUNCPTR(XRRFreeCrtcInfo)
53 MAKE_FUNCPTR(XRRFreeOutputInfo)
54 MAKE_FUNCPTR(XRRFreeScreenResources)
55 MAKE_FUNCPTR(XRRGetCrtcInfo)
56 MAKE_FUNCPTR(XRRGetOutputInfo)
57 MAKE_FUNCPTR(XRRGetScreenResources)
58 MAKE_FUNCPTR(XRRSetCrtcConfig)
59 static typeof(XRRGetScreenResources) *pXRRGetScreenResourcesCurrent;
60 static RRMode *xrandr12_modes;
61 static int primary_crtc;
62 #endif
63
64 #undef MAKE_FUNCPTR
65
66 static struct x11drv_mode_info *dd_modes;
67 static SizeID *xrandr10_modes;
68 static unsigned int xrandr_mode_count;
69
70 static int load_xrandr(void)
71 {
72     int r = 0;
73
74     if (wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
75         (xrandr_handle = wine_dlopen(SONAME_LIBXRANDR, RTLD_NOW, NULL, 0)))
76     {
77
78 #define LOAD_FUNCPTR(f) \
79         if((p##f = wine_dlsym(xrandr_handle, #f, NULL, 0)) == NULL) \
80             goto sym_not_found;
81
82         LOAD_FUNCPTR(XRRConfigCurrentConfiguration)
83         LOAD_FUNCPTR(XRRConfigCurrentRate)
84         LOAD_FUNCPTR(XRRFreeScreenConfigInfo)
85         LOAD_FUNCPTR(XRRGetScreenInfo)
86         LOAD_FUNCPTR(XRRQueryExtension)
87         LOAD_FUNCPTR(XRRQueryVersion)
88         LOAD_FUNCPTR(XRRRates)
89         LOAD_FUNCPTR(XRRSetScreenConfig)
90         LOAD_FUNCPTR(XRRSetScreenConfigAndRate)
91         LOAD_FUNCPTR(XRRSizes)
92         r = 1;
93
94 #ifdef HAVE_XRRGETSCREENRESOURCES
95         LOAD_FUNCPTR(XRRFreeCrtcInfo)
96         LOAD_FUNCPTR(XRRFreeOutputInfo)
97         LOAD_FUNCPTR(XRRFreeScreenResources)
98         LOAD_FUNCPTR(XRRGetCrtcInfo)
99         LOAD_FUNCPTR(XRRGetOutputInfo)
100         LOAD_FUNCPTR(XRRGetScreenResources)
101         LOAD_FUNCPTR(XRRSetCrtcConfig)
102         r = 2;
103 #endif
104 #undef LOAD_FUNCPTR
105
106 sym_not_found:
107         if (!r)  TRACE("Unable to load function ptrs from XRandR library\n");
108     }
109     return r;
110 }
111
112 static int XRandRErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
113 {
114     return 1;
115 }
116
117 static int xrandr10_get_current_mode(void)
118 {
119     SizeID size;
120     Rotation rot;
121     XRRScreenConfiguration *sc;
122     short rate;
123     unsigned int i;
124     int res = -1;
125
126     sc = pXRRGetScreenInfo (gdi_display, DefaultRootWindow( gdi_display ));
127     size = pXRRConfigCurrentConfiguration (sc, &rot);
128     rate = pXRRConfigCurrentRate (sc);
129     pXRRFreeScreenConfigInfo(sc);
130
131     for (i = 0; i < xrandr_mode_count; ++i)
132     {
133         if (xrandr10_modes[i] == size && dd_modes[i].refresh_rate == rate)
134         {
135             res = i;
136             break;
137         }
138     }
139     if (res == -1)
140     {
141         ERR("In unknown mode, returning default\n");
142         res = 0;
143     }
144     return res;
145 }
146
147 static LONG xrandr10_set_current_mode( int mode )
148 {
149     SizeID size;
150     Rotation rot;
151     Window root;
152     XRRScreenConfiguration *sc;
153     Status stat;
154     short rate;
155
156     root = DefaultRootWindow( gdi_display );
157     sc = pXRRGetScreenInfo (gdi_display, root);
158     size = pXRRConfigCurrentConfiguration (sc, &rot);
159     mode = mode % xrandr_mode_count;
160
161     TRACE("Changing Resolution to %dx%d @%d Hz\n",
162           dd_modes[mode].width,
163           dd_modes[mode].height,
164           dd_modes[mode].refresh_rate);
165
166     size = xrandr10_modes[mode];
167     rate = dd_modes[mode].refresh_rate;
168
169     if (rate)
170         stat = pXRRSetScreenConfigAndRate( gdi_display, sc, root, size, rot, rate, CurrentTime );
171     else
172         stat = pXRRSetScreenConfig( gdi_display, sc, root, size, rot, CurrentTime );
173
174     pXRRFreeScreenConfigInfo(sc);
175
176     if (stat == RRSetConfigSuccess)
177     {
178         X11DRV_resize_desktop( dd_modes[mode].width, dd_modes[mode].height );
179         return DISP_CHANGE_SUCCESSFUL;
180     }
181
182     ERR("Resolution change not successful -- perhaps display has changed?\n");
183     return DISP_CHANGE_FAILED;
184 }
185
186 static void xrandr10_init_modes(void)
187 {
188     XRRScreenSize *sizes;
189     int sizes_count;
190     int i, j, nmodes = 0;
191
192     sizes = pXRRSizes( gdi_display, DefaultScreen(gdi_display), &sizes_count );
193     if (sizes_count <= 0) return;
194
195     TRACE("XRandR: found %d sizes.\n", sizes_count);
196     for (i = 0; i < sizes_count; ++i)
197     {
198         int rates_count;
199         short *rates;
200
201         rates = pXRRRates( gdi_display, DefaultScreen(gdi_display), i, &rates_count );
202         TRACE("- at %d: %dx%d (%d rates):", i, sizes[i].width, sizes[i].height, rates_count);
203         if (rates_count)
204         {
205             nmodes += rates_count;
206             for (j = 0; j < rates_count; ++j)
207             {
208                 if (j > 0)
209                     TRACE(",");
210                 TRACE("  %d", rates[j]);
211             }
212         }
213         else
214         {
215             ++nmodes;
216             TRACE(" <default>");
217         }
218         TRACE(" Hz\n");
219     }
220
221     TRACE("XRandR modes: count=%d\n", nmodes);
222
223     if (!(xrandr10_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr10_modes) * nmodes )))
224     {
225         ERR("Failed to allocate xrandr mode info array.\n");
226         return;
227     }
228
229     dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.0",
230                                             xrandr10_get_current_mode,
231                                             xrandr10_set_current_mode,
232                                             nmodes, 1 );
233
234     xrandr_mode_count = 0;
235     for (i = 0; i < sizes_count; ++i)
236     {
237         int rates_count;
238         short *rates;
239
240         rates = pXRRRates( gdi_display, DefaultScreen(gdi_display), i, &rates_count );
241
242         if (rates_count)
243         {
244             for (j = 0; j < rates_count; ++j)
245             {
246                 X11DRV_Settings_AddOneMode( sizes[i].width, sizes[i].height, 0, rates[j] );
247                 xrandr10_modes[xrandr_mode_count++] = i;
248             }
249         }
250         else
251         {
252             X11DRV_Settings_AddOneMode( sizes[i].width, sizes[i].height, 0, 0 );
253             xrandr10_modes[xrandr_mode_count++] = i;
254         }
255     }
256
257     X11DRV_Settings_AddDepthModes();
258     nmodes = X11DRV_Settings_GetModeCount();
259
260     TRACE("Available DD modes: count=%d\n", nmodes);
261     TRACE("Enabling XRandR\n");
262 }
263
264 #ifdef HAVE_XRRGETSCREENRESOURCES
265
266 static int xrandr12_get_current_mode(void)
267 {
268     XRRScreenResources *resources;
269     XRRCrtcInfo *crtc_info;
270     int i, ret = -1;
271
272     if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
273     {
274         ERR("Failed to get screen resources.\n");
275         return 0;
276     }
277
278     if (resources->ncrtc <= primary_crtc ||
279         !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[primary_crtc] )))
280     {
281         pXRRFreeScreenResources( resources );
282         ERR("Failed to get CRTC info.\n");
283         return 0;
284     }
285
286     TRACE("CRTC %d: mode %#lx, %ux%u+%d+%d.\n", primary_crtc, crtc_info->mode,
287           crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
288
289     for (i = 0; i < xrandr_mode_count; ++i)
290     {
291         if (xrandr12_modes[i] == crtc_info->mode)
292         {
293             ret = i;
294             break;
295         }
296     }
297
298     pXRRFreeCrtcInfo( crtc_info );
299     pXRRFreeScreenResources( resources );
300
301     if (ret == -1)
302     {
303         ERR("Unknown mode, returning default.\n");
304         ret = 0;
305     }
306
307     return ret;
308 }
309
310 static LONG xrandr12_set_current_mode( int mode )
311 {
312     Status status = RRSetConfigFailed;
313     XRRScreenResources *resources;
314     XRRCrtcInfo *crtc_info;
315
316     mode = mode % xrandr_mode_count;
317
318     if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
319     {
320         ERR("Failed to get screen resources.\n");
321         return DISP_CHANGE_FAILED;
322     }
323
324     if (resources->ncrtc <= primary_crtc ||
325         !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[primary_crtc] )))
326     {
327         pXRRFreeScreenResources( resources );
328         ERR("Failed to get CRTC info.\n");
329         return DISP_CHANGE_FAILED;
330     }
331
332     TRACE("CRTC %d: mode %#lx, %ux%u+%d+%d.\n", primary_crtc, crtc_info->mode,
333           crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
334
335     status = pXRRSetCrtcConfig( gdi_display, resources, resources->crtcs[primary_crtc],
336                                 CurrentTime, crtc_info->x, crtc_info->y, xrandr12_modes[mode],
337                                 crtc_info->rotation, crtc_info->outputs, crtc_info->noutput );
338
339     pXRRFreeCrtcInfo( crtc_info );
340     pXRRFreeScreenResources( resources );
341
342     if (status != RRSetConfigSuccess)
343     {
344         ERR("Resolution change not successful -- perhaps display has changed?\n");
345         return DISP_CHANGE_FAILED;
346     }
347
348     X11DRV_resize_desktop( dd_modes[mode].width, dd_modes[mode].height );
349     return DISP_CHANGE_SUCCESSFUL;
350 }
351
352 static XRRCrtcInfo *xrandr12_get_primary_crtc_info( XRRScreenResources *resources, int *crtc_idx )
353 {
354     XRRCrtcInfo *crtc_info;
355     int i;
356
357     for (i = 0; i < resources->ncrtc; ++i)
358     {
359         crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[i] );
360         if (!crtc_info || crtc_info->mode == None)
361         {
362             pXRRFreeCrtcInfo( crtc_info );
363             continue;
364         }
365
366         *crtc_idx = i;
367         return crtc_info;
368     }
369
370     return NULL;
371 }
372
373 static int xrandr12_init_modes(void)
374 {
375     XRRScreenResources *resources;
376     XRROutputInfo *output_info;
377     XRRCrtcInfo *crtc_info;
378     int ret = -1;
379     int i, j;
380
381     if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
382     {
383         ERR("Failed to get screen resources.\n");
384         return ret;
385     }
386
387     if (!resources->ncrtc)
388     {
389         pXRRFreeScreenResources( resources );
390         if (!(resources = pXRRGetScreenResources( gdi_display, root_window )))
391         {
392             ERR("Failed to get screen resources.\n");
393             return ret;
394         }
395     }
396
397     if (!(crtc_info = xrandr12_get_primary_crtc_info( resources, &primary_crtc )))
398     {
399         pXRRFreeScreenResources( resources );
400         ERR("Failed to get primary CRTC info.\n");
401         return ret;
402     }
403
404     TRACE("CRTC %d: mode %#lx, %ux%u+%d+%d.\n", primary_crtc, crtc_info->mode,
405           crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
406
407     if (!crtc_info->noutput || !(output_info = pXRRGetOutputInfo( gdi_display, resources, crtc_info->outputs[0] )))
408     {
409         pXRRFreeCrtcInfo( crtc_info );
410         pXRRFreeScreenResources( resources );
411         ERR("Failed to get output info.\n");
412         return ret;
413     }
414
415     TRACE("OUTPUT 0: name %s.\n", debugstr_a(output_info->name));
416
417     if (!output_info->nmode)
418     {
419         ERR("Output has no modes.\n");
420         goto done;
421     }
422
423     /* Recent (304.64, possibly earlier) versions of the nvidia driver only
424      * report a DFP's native mode through RandR 1.2 / 1.3. Standard DMT modes
425      * are only listed through RandR 1.0 / 1.1. This is completely useless,
426      * but NVIDIA considers this a feature, so it's unlikely to change. The
427      * best we can do is to fall back to RandR 1.0 and encourage users to
428      * consider more cooperative driver vendors when we detect such a
429      * configuration. */
430     if (output_info->nmode == 1 && XQueryExtension( gdi_display, "NV-CONTROL", &i, &j, &ret ))
431     {
432         ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. "
433                        "Please consider using the Nouveau driver instead.\n");
434         ret = -1;
435         goto done;
436     }
437
438     if (!(xrandr12_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr12_modes) * output_info->nmode )))
439     {
440         ERR("Failed to allocate xrandr mode info array.\n");
441         goto done;
442     }
443
444     dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.2",
445                                             xrandr12_get_current_mode,
446                                             xrandr12_set_current_mode,
447                                             output_info->nmode, 1 );
448
449     xrandr_mode_count = 0;
450     for (i = 0; i < output_info->nmode; ++i)
451     {
452         for (j = 0; j < resources->nmode; ++j)
453         {
454             XRRModeInfo *mode = &resources->modes[j];
455
456             if (mode->id == output_info->modes[i])
457             {
458                 unsigned int dots = mode->hTotal * mode->vTotal;
459                 unsigned int refresh = dots ? (mode->dotClock + dots / 2) / dots : 0;
460
461                 TRACE("Adding mode %#lx: %ux%u@%u.\n", mode->id, mode->width, mode->height, refresh);
462                 X11DRV_Settings_AddOneMode( mode->width, mode->height, 0, refresh );
463                 xrandr12_modes[xrandr_mode_count++] = mode->id;
464                 break;
465             }
466         }
467     }
468
469     X11DRV_Settings_AddDepthModes();
470     ret = 0;
471
472 done:
473     pXRRFreeOutputInfo( output_info );
474     pXRRFreeCrtcInfo( crtc_info );
475     pXRRFreeScreenResources( resources );
476     return ret;
477 }
478
479 #endif /* HAVE_XRRGETSCREENRESOURCES */
480
481 void X11DRV_XRandR_Init(void)
482 {
483     int event_base, error_base, minor, ret;
484     static int major;
485     Bool ok;
486
487     if (major) return; /* already initialized? */
488     if (!usexrandr) return; /* disabled in config */
489     if (root_window != DefaultRootWindow( gdi_display )) return;
490     if (!(ret = load_xrandr())) return;  /* can't load the Xrandr library */
491
492     /* see if Xrandr is available */
493     if (!pXRRQueryExtension( gdi_display, &event_base, &error_base )) return;
494     X11DRV_expect_error( gdi_display, XRandRErrorHandler, NULL );
495     ok = pXRRQueryVersion( gdi_display, &major, &minor );
496     if (X11DRV_check_error() || !ok) return;
497
498     TRACE("Found XRandR %d.%d.\n", major, minor);
499
500 #ifdef HAVE_XRRGETSCREENRESOURCES
501     if (ret >= 2 && (major > 1 || (major == 1 && minor >= 2)))
502     {
503         if (major > 1 || (major == 1 && minor >= 3))
504             pXRRGetScreenResourcesCurrent = wine_dlsym( xrandr_handle, "XRRGetScreenResourcesCurrent", NULL, 0 );
505         if (!pXRRGetScreenResourcesCurrent)
506             pXRRGetScreenResourcesCurrent = pXRRGetScreenResources;
507     }
508
509     if (!pXRRGetScreenResourcesCurrent || xrandr12_init_modes() < 0)
510 #endif
511         xrandr10_init_modes();
512 }
513
514 #else /* SONAME_LIBXRANDR */
515
516 void X11DRV_XRandR_Init(void)
517 {
518     TRACE("XRandR support not compiled in.\n");
519 }
520
521 #endif /* SONAME_LIBXRANDR */