2 * Wine X11drv Xrandr interface
4 * Copyright 2003 Alexander James Pasadyn
5 * Copyright 2012 Henri Verbeet for CodeWeavers
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.
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.
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
23 #include "wine/port.h"
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(xrandr);
27 WINE_DECLARE_DEBUG_CHANNEL(winediag);
29 #ifdef SONAME_LIBXRANDR
32 #include <X11/extensions/Xrandr.h>
35 #include "wine/library.h"
37 static void *xrandr_handle;
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)
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;
66 static struct x11drv_mode_info *dd_modes;
67 static SizeID *xrandr10_modes;
68 static unsigned int xrandr_mode_count;
70 static int load_xrandr(void)
74 if (wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
75 (xrandr_handle = wine_dlopen(SONAME_LIBXRANDR, RTLD_NOW, NULL, 0)))
78 #define LOAD_FUNCPTR(f) \
79 if((p##f = wine_dlsym(xrandr_handle, #f, NULL, 0)) == NULL) \
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)
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)
107 if (!r) TRACE("Unable to load function ptrs from XRandR library\n");
112 static int XRandRErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
117 static int xrandr10_get_current_mode(void)
121 XRRScreenConfiguration *sc;
126 sc = pXRRGetScreenInfo (gdi_display, DefaultRootWindow( gdi_display ));
127 size = pXRRConfigCurrentConfiguration (sc, &rot);
128 rate = pXRRConfigCurrentRate (sc);
129 pXRRFreeScreenConfigInfo(sc);
131 for (i = 0; i < xrandr_mode_count; ++i)
133 if (xrandr10_modes[i] == size && dd_modes[i].refresh_rate == rate)
141 ERR("In unknown mode, returning default\n");
147 static LONG xrandr10_set_current_mode( int mode )
152 XRRScreenConfiguration *sc;
156 root = DefaultRootWindow( gdi_display );
157 sc = pXRRGetScreenInfo (gdi_display, root);
158 size = pXRRConfigCurrentConfiguration (sc, &rot);
159 mode = mode % xrandr_mode_count;
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);
166 size = xrandr10_modes[mode];
167 rate = dd_modes[mode].refresh_rate;
170 stat = pXRRSetScreenConfigAndRate( gdi_display, sc, root, size, rot, rate, CurrentTime );
172 stat = pXRRSetScreenConfig( gdi_display, sc, root, size, rot, CurrentTime );
174 pXRRFreeScreenConfigInfo(sc);
176 if (stat == RRSetConfigSuccess)
178 X11DRV_resize_desktop( dd_modes[mode].width, dd_modes[mode].height );
179 return DISP_CHANGE_SUCCESSFUL;
182 ERR("Resolution change not successful -- perhaps display has changed?\n");
183 return DISP_CHANGE_FAILED;
186 static void xrandr10_init_modes(void)
188 XRRScreenSize *sizes;
190 int i, j, nmodes = 0;
192 sizes = pXRRSizes( gdi_display, DefaultScreen(gdi_display), &sizes_count );
193 if (sizes_count <= 0) return;
195 TRACE("XRandR: found %d sizes.\n", sizes_count);
196 for (i = 0; i < sizes_count; ++i)
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);
205 nmodes += rates_count;
206 for (j = 0; j < rates_count; ++j)
210 TRACE(" %d", rates[j]);
221 TRACE("XRandR modes: count=%d\n", nmodes);
223 if (!(xrandr10_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr10_modes) * nmodes )))
225 ERR("Failed to allocate xrandr mode info array.\n");
229 dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.0",
230 xrandr10_get_current_mode,
231 xrandr10_set_current_mode,
234 xrandr_mode_count = 0;
235 for (i = 0; i < sizes_count; ++i)
240 rates = pXRRRates( gdi_display, DefaultScreen(gdi_display), i, &rates_count );
244 for (j = 0; j < rates_count; ++j)
246 X11DRV_Settings_AddOneMode( sizes[i].width, sizes[i].height, 0, rates[j] );
247 xrandr10_modes[xrandr_mode_count++] = i;
252 X11DRV_Settings_AddOneMode( sizes[i].width, sizes[i].height, 0, 0 );
253 xrandr10_modes[xrandr_mode_count++] = i;
257 X11DRV_Settings_AddDepthModes();
258 nmodes = X11DRV_Settings_GetModeCount();
260 TRACE("Available DD modes: count=%d\n", nmodes);
261 TRACE("Enabling XRandR\n");
264 #ifdef HAVE_XRRGETSCREENRESOURCES
266 static int xrandr12_get_current_mode(void)
268 XRRScreenResources *resources;
269 XRRCrtcInfo *crtc_info;
272 if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
274 ERR("Failed to get screen resources.\n");
278 if (resources->ncrtc <= primary_crtc ||
279 !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[primary_crtc] )))
281 pXRRFreeScreenResources( resources );
282 ERR("Failed to get CRTC info.\n");
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);
289 for (i = 0; i < xrandr_mode_count; ++i)
291 if (xrandr12_modes[i] == crtc_info->mode)
298 pXRRFreeCrtcInfo( crtc_info );
299 pXRRFreeScreenResources( resources );
303 ERR("Unknown mode, returning default.\n");
310 static LONG xrandr12_set_current_mode( int mode )
312 Status status = RRSetConfigFailed;
313 XRRScreenResources *resources;
314 XRRCrtcInfo *crtc_info;
316 mode = mode % xrandr_mode_count;
318 if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
320 ERR("Failed to get screen resources.\n");
321 return DISP_CHANGE_FAILED;
324 if (resources->ncrtc <= primary_crtc ||
325 !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[primary_crtc] )))
327 pXRRFreeScreenResources( resources );
328 ERR("Failed to get CRTC info.\n");
329 return DISP_CHANGE_FAILED;
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);
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 );
339 pXRRFreeCrtcInfo( crtc_info );
340 pXRRFreeScreenResources( resources );
342 if (status != RRSetConfigSuccess)
344 ERR("Resolution change not successful -- perhaps display has changed?\n");
345 return DISP_CHANGE_FAILED;
348 X11DRV_resize_desktop( dd_modes[mode].width, dd_modes[mode].height );
349 return DISP_CHANGE_SUCCESSFUL;
352 static XRRCrtcInfo *xrandr12_get_primary_crtc_info( XRRScreenResources *resources, int *crtc_idx )
354 XRRCrtcInfo *crtc_info;
357 for (i = 0; i < resources->ncrtc; ++i)
359 crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[i] );
360 if (!crtc_info || crtc_info->mode == None)
362 pXRRFreeCrtcInfo( crtc_info );
373 static int xrandr12_init_modes(void)
375 XRRScreenResources *resources;
376 XRROutputInfo *output_info;
377 XRRCrtcInfo *crtc_info;
381 if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
383 ERR("Failed to get screen resources.\n");
387 if (!resources->ncrtc)
389 pXRRFreeScreenResources( resources );
390 if (!(resources = pXRRGetScreenResources( gdi_display, root_window )))
392 ERR("Failed to get screen resources.\n");
397 if (!(crtc_info = xrandr12_get_primary_crtc_info( resources, &primary_crtc )))
399 pXRRFreeScreenResources( resources );
400 ERR("Failed to get primary CRTC info.\n");
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);
407 if (!crtc_info->noutput || !(output_info = pXRRGetOutputInfo( gdi_display, resources, crtc_info->outputs[0] )))
409 pXRRFreeCrtcInfo( crtc_info );
410 pXRRFreeScreenResources( resources );
411 ERR("Failed to get output info.\n");
415 TRACE("OUTPUT 0: name %s.\n", debugstr_a(output_info->name));
417 if (!output_info->nmode)
419 ERR("Output has no modes.\n");
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
430 if (output_info->nmode == 1 && XQueryExtension( gdi_display, "NV-CONTROL", &i, &j, &ret ))
432 ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. "
433 "Please consider using the Nouveau driver instead.\n");
438 if (!(xrandr12_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr12_modes) * output_info->nmode )))
440 ERR("Failed to allocate xrandr mode info array.\n");
444 dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.2",
445 xrandr12_get_current_mode,
446 xrandr12_set_current_mode,
447 output_info->nmode, 1 );
449 xrandr_mode_count = 0;
450 for (i = 0; i < output_info->nmode; ++i)
452 for (j = 0; j < resources->nmode; ++j)
454 XRRModeInfo *mode = &resources->modes[j];
456 if (mode->id == output_info->modes[i])
458 unsigned int dots = mode->hTotal * mode->vTotal;
459 unsigned int refresh = dots ? (mode->dotClock + dots / 2) / dots : 0;
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;
469 X11DRV_Settings_AddDepthModes();
473 pXRRFreeOutputInfo( output_info );
474 pXRRFreeCrtcInfo( crtc_info );
475 pXRRFreeScreenResources( resources );
479 #endif /* HAVE_XRRGETSCREENRESOURCES */
481 void X11DRV_XRandR_Init(void)
483 int event_base, error_base, minor, ret;
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 */
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;
498 TRACE("Found XRandR %d.%d.\n", major, minor);
500 #ifdef HAVE_XRRGETSCREENRESOURCES
501 if (ret >= 2 && (major > 1 || (major == 1 && minor >= 2)))
503 if (major > 1 || (major == 1 && minor >= 3))
504 pXRRGetScreenResourcesCurrent = wine_dlsym( xrandr_handle, "XRRGetScreenResourcesCurrent", NULL, 0 );
505 if (!pXRRGetScreenResourcesCurrent)
506 pXRRGetScreenResourcesCurrent = pXRRGetScreenResources;
509 if (!pXRRGetScreenResourcesCurrent || xrandr12_init_modes() < 0)
511 xrandr10_init_modes();
514 #else /* SONAME_LIBXRANDR */
516 void X11DRV_XRandR_Init(void)
518 TRACE("XRandR support not compiled in.\n");
521 #endif /* SONAME_LIBXRANDR */