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