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);
28 #ifdef SONAME_LIBXRANDR
31 #include <X11/extensions/Xrandr.h>
34 #include "wine/library.h"
36 static void *xrandr_handle;
38 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
39 MAKE_FUNCPTR(XRRConfigCurrentConfiguration)
40 MAKE_FUNCPTR(XRRConfigCurrentRate)
41 MAKE_FUNCPTR(XRRFreeScreenConfigInfo)
42 MAKE_FUNCPTR(XRRGetScreenInfo)
43 MAKE_FUNCPTR(XRRQueryExtension)
44 MAKE_FUNCPTR(XRRQueryVersion)
45 MAKE_FUNCPTR(XRRRates)
46 MAKE_FUNCPTR(XRRSetScreenConfig)
47 MAKE_FUNCPTR(XRRSetScreenConfigAndRate)
48 MAKE_FUNCPTR(XRRSizes)
50 #ifdef HAVE_XRRGETSCREENRESOURCES
51 MAKE_FUNCPTR(XRRFreeCrtcInfo)
52 MAKE_FUNCPTR(XRRFreeOutputInfo)
53 MAKE_FUNCPTR(XRRFreeScreenResources)
54 MAKE_FUNCPTR(XRRGetCrtcInfo)
55 MAKE_FUNCPTR(XRRGetOutputInfo)
56 MAKE_FUNCPTR(XRRGetScreenResources)
57 MAKE_FUNCPTR(XRRSetCrtcConfig)
58 static typeof(XRRGetScreenResources) *pXRRGetScreenResourcesCurrent;
59 static RRMode *xrandr12_modes;
64 static struct x11drv_mode_info *dd_modes;
65 static SizeID *xrandr10_modes;
66 static unsigned int xrandr_mode_count;
68 static int load_xrandr(void)
72 if (wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
73 (xrandr_handle = wine_dlopen(SONAME_LIBXRANDR, RTLD_NOW, NULL, 0)))
76 #define LOAD_FUNCPTR(f) \
77 if((p##f = wine_dlsym(xrandr_handle, #f, NULL, 0)) == NULL) \
80 LOAD_FUNCPTR(XRRConfigCurrentConfiguration)
81 LOAD_FUNCPTR(XRRConfigCurrentRate)
82 LOAD_FUNCPTR(XRRFreeScreenConfigInfo)
83 LOAD_FUNCPTR(XRRGetScreenInfo)
84 LOAD_FUNCPTR(XRRQueryExtension)
85 LOAD_FUNCPTR(XRRQueryVersion)
86 LOAD_FUNCPTR(XRRRates)
87 LOAD_FUNCPTR(XRRSetScreenConfig)
88 LOAD_FUNCPTR(XRRSetScreenConfigAndRate)
89 LOAD_FUNCPTR(XRRSizes)
92 #ifdef HAVE_XRRGETSCREENRESOURCES
93 LOAD_FUNCPTR(XRRFreeCrtcInfo)
94 LOAD_FUNCPTR(XRRFreeOutputInfo)
95 LOAD_FUNCPTR(XRRFreeScreenResources)
96 LOAD_FUNCPTR(XRRGetCrtcInfo)
97 LOAD_FUNCPTR(XRRGetOutputInfo)
98 LOAD_FUNCPTR(XRRGetScreenResources)
99 LOAD_FUNCPTR(XRRSetCrtcConfig)
105 if (!r) TRACE("Unable to load function ptrs from XRandR library\n");
110 static int XRandRErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
115 static int xrandr10_get_current_mode(void)
119 XRRScreenConfiguration *sc;
124 sc = pXRRGetScreenInfo (gdi_display, DefaultRootWindow( gdi_display ));
125 size = pXRRConfigCurrentConfiguration (sc, &rot);
126 rate = pXRRConfigCurrentRate (sc);
127 pXRRFreeScreenConfigInfo(sc);
129 for (i = 0; i < xrandr_mode_count; ++i)
131 if (xrandr10_modes[i] == size && dd_modes[i].refresh_rate == rate)
139 ERR("In unknown mode, returning default\n");
145 static LONG xrandr10_set_current_mode( int mode )
150 XRRScreenConfiguration *sc;
154 root = DefaultRootWindow( gdi_display );
155 sc = pXRRGetScreenInfo (gdi_display, root);
156 size = pXRRConfigCurrentConfiguration (sc, &rot);
157 mode = mode % xrandr_mode_count;
159 TRACE("Changing Resolution to %dx%d @%d Hz\n",
160 dd_modes[mode].width,
161 dd_modes[mode].height,
162 dd_modes[mode].refresh_rate);
164 size = xrandr10_modes[mode];
165 rate = dd_modes[mode].refresh_rate;
168 stat = pXRRSetScreenConfigAndRate( gdi_display, sc, root, size, rot, rate, CurrentTime );
170 stat = pXRRSetScreenConfig( gdi_display, sc, root, size, rot, CurrentTime );
172 pXRRFreeScreenConfigInfo(sc);
174 if (stat == RRSetConfigSuccess)
176 X11DRV_resize_desktop( dd_modes[mode].width, dd_modes[mode].height );
177 return DISP_CHANGE_SUCCESSFUL;
180 ERR("Resolution change not successful -- perhaps display has changed?\n");
181 return DISP_CHANGE_FAILED;
184 static void xrandr10_init_modes(void)
186 XRRScreenSize *sizes;
188 int i, j, nmodes = 0;
190 sizes = pXRRSizes( gdi_display, DefaultScreen(gdi_display), &sizes_count );
191 if (sizes_count <= 0) return;
193 TRACE("XRandR: found %d sizes.\n", sizes_count);
194 for (i = 0; i < sizes_count; ++i)
199 rates = pXRRRates( gdi_display, DefaultScreen(gdi_display), i, &rates_count );
200 TRACE("- at %d: %dx%d (%d rates):", i, sizes[i].width, sizes[i].height, rates_count);
203 nmodes += rates_count;
204 for (j = 0; j < rates_count; ++j)
208 TRACE(" %d", rates[j]);
219 TRACE("XRandR modes: count=%d\n", nmodes);
221 if (!(xrandr10_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr10_modes) * nmodes )))
223 ERR("Failed to allocate xrandr mode info array.\n");
227 dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.0",
228 xrandr10_get_current_mode,
229 xrandr10_set_current_mode,
232 xrandr_mode_count = 0;
233 for (i = 0; i < sizes_count; ++i)
238 rates = pXRRRates( gdi_display, DefaultScreen(gdi_display), i, &rates_count );
242 for (j = 0; j < rates_count; ++j)
244 X11DRV_Settings_AddOneMode( sizes[i].width, sizes[i].height, 0, rates[j] );
245 xrandr10_modes[xrandr_mode_count++] = i;
250 X11DRV_Settings_AddOneMode( sizes[i].width, sizes[i].height, 0, 0 );
251 xrandr10_modes[xrandr_mode_count++] = i;
255 X11DRV_Settings_AddDepthModes();
256 nmodes = X11DRV_Settings_GetModeCount();
258 TRACE("Available DD modes: count=%d\n", nmodes);
259 TRACE("Enabling XRandR\n");
262 #ifdef HAVE_XRRGETSCREENRESOURCES
264 static int xrandr12_get_current_mode(void)
266 XRRScreenResources *resources;
267 XRRCrtcInfo *crtc_info;
270 if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
272 ERR("Failed to get screen resources.\n");
276 if (!resources->ncrtc || !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[0] )))
278 pXRRFreeScreenResources( resources );
279 ERR("Failed to get CRTC info.\n");
283 TRACE("CRTC 0: mode %#lx, %ux%u+%d+%d.\n", crtc_info->mode,
284 crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
286 for (i = 0; i < xrandr_mode_count; ++i)
288 if (xrandr12_modes[i] == crtc_info->mode)
295 pXRRFreeCrtcInfo( crtc_info );
296 pXRRFreeScreenResources( resources );
300 ERR("Unknown mode, returning default.\n");
307 static LONG xrandr12_set_current_mode( int mode )
309 Status status = RRSetConfigFailed;
310 XRRScreenResources *resources;
311 XRRCrtcInfo *crtc_info;
313 mode = mode % xrandr_mode_count;
315 if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
317 ERR("Failed to get screen resources.\n");
318 return DISP_CHANGE_FAILED;
321 if (!resources->ncrtc || !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[0] )))
323 pXRRFreeScreenResources( resources );
324 ERR("Failed to get CRTC info.\n");
325 return DISP_CHANGE_FAILED;
328 TRACE("CRTC 0: mode %#lx, %ux%u+%d+%d.\n", crtc_info->mode,
329 crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
331 status = pXRRSetCrtcConfig( gdi_display, resources, resources->crtcs[0], CurrentTime, crtc_info->x,
332 crtc_info->y, xrandr12_modes[mode], crtc_info->rotation, crtc_info->outputs, crtc_info->noutput );
334 pXRRFreeCrtcInfo( crtc_info );
335 pXRRFreeScreenResources( resources );
337 if (status != RRSetConfigSuccess)
339 ERR("Resolution change not successful -- perhaps display has changed?\n");
340 return DISP_CHANGE_FAILED;
343 X11DRV_resize_desktop( dd_modes[mode].width, dd_modes[mode].height );
344 return DISP_CHANGE_SUCCESSFUL;
347 static int xrandr12_init_modes(void)
349 XRRScreenResources *resources;
350 XRROutputInfo *output_info;
351 XRRCrtcInfo *crtc_info;
355 if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
357 ERR("Failed to get screen resources.\n");
361 if (!resources->ncrtc)
363 pXRRFreeScreenResources( resources );
364 if (!(resources = pXRRGetScreenResources( gdi_display, root_window )))
366 ERR("Failed to get screen resources.\n");
371 if (!resources->ncrtc || !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[0] )))
373 pXRRFreeScreenResources( resources );
374 ERR("Failed to get CRTC info.\n");
378 TRACE("CRTC 0: mode %#lx, %ux%u+%d+%d.\n", crtc_info->mode,
379 crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
381 if (!crtc_info->noutput || !(output_info = pXRRGetOutputInfo( gdi_display, resources, crtc_info->outputs[0] )))
383 pXRRFreeCrtcInfo( crtc_info );
384 pXRRFreeScreenResources( resources );
385 ERR("Failed to get output info.\n");
389 TRACE("OUTPUT 0: name %s.\n", debugstr_a(output_info->name));
391 if (!output_info->nmode)
393 ERR("Output has no modes.\n");
397 if (!(xrandr12_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr12_modes) * output_info->nmode )))
399 ERR("Failed to allocate xrandr mode info array.\n");
403 dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.2",
404 xrandr12_get_current_mode,
405 xrandr12_set_current_mode,
406 output_info->nmode, 1 );
408 xrandr_mode_count = 0;
409 for (i = 0; i < output_info->nmode; ++i)
411 for (j = 0; j < resources->nmode; ++j)
413 XRRModeInfo *mode = &resources->modes[j];
415 if (mode->id == output_info->modes[i])
417 unsigned int dots = mode->hTotal * mode->vTotal;
418 unsigned int refresh = dots ? (mode->dotClock + dots / 2) / dots : 0;
420 TRACE("Adding mode %#lx: %ux%u@%u.\n", mode->id, mode->width, mode->height, refresh);
421 X11DRV_Settings_AddOneMode( mode->width, mode->height, 0, refresh );
422 xrandr12_modes[xrandr_mode_count++] = mode->id;
428 X11DRV_Settings_AddDepthModes();
432 pXRRFreeOutputInfo( output_info );
433 pXRRFreeCrtcInfo( crtc_info );
434 pXRRFreeScreenResources( resources );
438 #endif /* HAVE_XRRGETSCREENRESOURCES */
440 void X11DRV_XRandR_Init(void)
442 int event_base, error_base, minor, ret;
446 if (major) return; /* already initialized? */
447 if (!usexrandr) return; /* disabled in config */
448 if (root_window != DefaultRootWindow( gdi_display )) return;
449 if (!(ret = load_xrandr())) return; /* can't load the Xrandr library */
451 /* see if Xrandr is available */
452 if (!pXRRQueryExtension( gdi_display, &event_base, &error_base )) return;
453 X11DRV_expect_error( gdi_display, XRandRErrorHandler, NULL );
454 ok = pXRRQueryVersion( gdi_display, &major, &minor );
455 if (X11DRV_check_error() || !ok) return;
457 TRACE("Found XRandR %d.%d.\n", major, minor);
459 #ifdef HAVE_XRRGETSCREENRESOURCES
460 if (ret >= 2 && (major > 1 || (major == 1 && minor >= 2)))
462 if (major > 1 || (major == 1 && minor >= 3))
463 pXRRGetScreenResourcesCurrent = wine_dlsym( xrandr_handle, "XRRGetScreenResourcesCurrent", NULL, 0 );
464 if (!pXRRGetScreenResourcesCurrent)
465 pXRRGetScreenResourcesCurrent = pXRRGetScreenResources;
468 if (!pXRRGetScreenResourcesCurrent || xrandr12_init_modes() < 0)
470 xrandr10_init_modes();
473 #else /* SONAME_LIBXRANDR */
475 void X11DRV_XRandR_Init(void)
477 TRACE("XRandR support not compiled in.\n");
480 #endif /* SONAME_LIBXRANDR */