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 #ifdef HAVE_XRRGETSCREENRESOURCES
28 WINE_DECLARE_DEBUG_CHANNEL(winediag
);
31 #ifdef SONAME_LIBXRANDR
34 #include <X11/extensions/Xrandr.h>
37 #include "wine/library.h"
39 static void *xrandr_handle
;
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
)
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
;
68 static struct x11drv_mode_info
*dd_modes
;
69 static SizeID
*xrandr10_modes
;
70 static unsigned int xrandr_mode_count
;
71 static int xrandr_current_mode
= -1;
73 static int load_xrandr(void)
77 if (wine_dlopen(SONAME_LIBXRENDER
, RTLD_NOW
|RTLD_GLOBAL
, NULL
, 0) &&
78 (xrandr_handle
= wine_dlopen(SONAME_LIBXRANDR
, RTLD_NOW
, NULL
, 0)))
81 #define LOAD_FUNCPTR(f) \
82 if((p##f = wine_dlsym(xrandr_handle, #f, NULL, 0)) == NULL) \
85 LOAD_FUNCPTR(XRRConfigCurrentConfiguration
)
86 LOAD_FUNCPTR(XRRConfigCurrentRate
)
87 LOAD_FUNCPTR(XRRFreeScreenConfigInfo
)
88 LOAD_FUNCPTR(XRRGetScreenInfo
)
89 LOAD_FUNCPTR(XRRQueryExtension
)
90 LOAD_FUNCPTR(XRRQueryVersion
)
91 LOAD_FUNCPTR(XRRRates
)
92 LOAD_FUNCPTR(XRRSetScreenConfig
)
93 LOAD_FUNCPTR(XRRSetScreenConfigAndRate
)
94 LOAD_FUNCPTR(XRRSizes
)
97 #ifdef HAVE_XRRGETSCREENRESOURCES
98 LOAD_FUNCPTR(XRRFreeCrtcInfo
)
99 LOAD_FUNCPTR(XRRFreeOutputInfo
)
100 LOAD_FUNCPTR(XRRFreeScreenResources
)
101 LOAD_FUNCPTR(XRRGetCrtcInfo
)
102 LOAD_FUNCPTR(XRRGetOutputInfo
)
103 LOAD_FUNCPTR(XRRGetScreenResources
)
104 LOAD_FUNCPTR(XRRSetCrtcConfig
)
110 if (!r
) TRACE("Unable to load function ptrs from XRandR library\n");
115 static int XRandRErrorHandler(Display
*dpy
, XErrorEvent
*event
, void *arg
)
120 static int xrandr10_get_current_mode(void)
124 XRRScreenConfiguration
*sc
;
129 if (xrandr_current_mode
!= -1)
130 return xrandr_current_mode
;
132 sc
= pXRRGetScreenInfo (gdi_display
, DefaultRootWindow( gdi_display
));
133 size
= pXRRConfigCurrentConfiguration (sc
, &rot
);
134 rate
= pXRRConfigCurrentRate (sc
);
135 pXRRFreeScreenConfigInfo(sc
);
137 for (i
= 0; i
< xrandr_mode_count
; ++i
)
139 if (xrandr10_modes
[i
] == size
&& dd_modes
[i
].refresh_rate
== rate
)
147 ERR("In unknown mode, returning default\n");
151 xrandr_current_mode
= res
;
155 static LONG
xrandr10_set_current_mode( int mode
)
160 XRRScreenConfiguration
*sc
;
164 root
= DefaultRootWindow( gdi_display
);
165 sc
= pXRRGetScreenInfo (gdi_display
, root
);
166 pXRRConfigCurrentConfiguration (sc
, &rot
);
167 mode
= mode
% xrandr_mode_count
;
169 TRACE("Changing Resolution to %dx%d @%d Hz\n",
170 dd_modes
[mode
].width
,
171 dd_modes
[mode
].height
,
172 dd_modes
[mode
].refresh_rate
);
174 size
= xrandr10_modes
[mode
];
175 rate
= dd_modes
[mode
].refresh_rate
;
178 stat
= pXRRSetScreenConfigAndRate( gdi_display
, sc
, root
, size
, rot
, rate
, CurrentTime
);
180 stat
= pXRRSetScreenConfig( gdi_display
, sc
, root
, size
, rot
, CurrentTime
);
182 pXRRFreeScreenConfigInfo(sc
);
184 if (stat
== RRSetConfigSuccess
)
186 xrandr_current_mode
= mode
;
187 X11DRV_resize_desktop( dd_modes
[mode
].width
, dd_modes
[mode
].height
);
188 return DISP_CHANGE_SUCCESSFUL
;
191 ERR("Resolution change not successful -- perhaps display has changed?\n");
192 return DISP_CHANGE_FAILED
;
195 static void xrandr10_init_modes(void)
197 XRRScreenSize
*sizes
;
199 int i
, j
, nmodes
= 0;
201 sizes
= pXRRSizes( gdi_display
, DefaultScreen(gdi_display
), &sizes_count
);
202 if (sizes_count
<= 0) return;
204 TRACE("XRandR: found %d sizes.\n", sizes_count
);
205 for (i
= 0; i
< sizes_count
; ++i
)
210 rates
= pXRRRates( gdi_display
, DefaultScreen(gdi_display
), i
, &rates_count
);
211 TRACE("- at %d: %dx%d (%d rates):", i
, sizes
[i
].width
, sizes
[i
].height
, rates_count
);
214 nmodes
+= rates_count
;
215 for (j
= 0; j
< rates_count
; ++j
)
219 TRACE(" %d", rates
[j
]);
230 TRACE("XRandR modes: count=%d\n", nmodes
);
232 if (!(xrandr10_modes
= HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr10_modes
) * nmodes
)))
234 ERR("Failed to allocate xrandr mode info array.\n");
238 dd_modes
= X11DRV_Settings_SetHandlers( "XRandR 1.0",
239 xrandr10_get_current_mode
,
240 xrandr10_set_current_mode
,
243 xrandr_mode_count
= 0;
244 for (i
= 0; i
< sizes_count
; ++i
)
249 rates
= pXRRRates( gdi_display
, DefaultScreen(gdi_display
), i
, &rates_count
);
253 for (j
= 0; j
< rates_count
; ++j
)
255 X11DRV_Settings_AddOneMode( sizes
[i
].width
, sizes
[i
].height
, 0, rates
[j
] );
256 xrandr10_modes
[xrandr_mode_count
++] = i
;
261 X11DRV_Settings_AddOneMode( sizes
[i
].width
, sizes
[i
].height
, 0, 0 );
262 xrandr10_modes
[xrandr_mode_count
++] = i
;
266 X11DRV_Settings_AddDepthModes();
267 nmodes
= X11DRV_Settings_GetModeCount();
269 TRACE("Available DD modes: count=%d\n", nmodes
);
270 TRACE("Enabling XRandR\n");
273 #ifdef HAVE_XRRGETSCREENRESOURCES
275 static int xrandr12_get_current_mode(void)
277 XRRScreenResources
*resources
;
278 XRRCrtcInfo
*crtc_info
;
281 if (xrandr_current_mode
!= -1)
282 return xrandr_current_mode
;
284 if (!(resources
= pXRRGetScreenResourcesCurrent( gdi_display
, root_window
)))
286 ERR("Failed to get screen resources.\n");
290 if (resources
->ncrtc
<= primary_crtc
||
291 !(crtc_info
= pXRRGetCrtcInfo( gdi_display
, resources
, resources
->crtcs
[primary_crtc
] )))
293 pXRRFreeScreenResources( resources
);
294 ERR("Failed to get CRTC info.\n");
298 TRACE("CRTC %d: mode %#lx, %ux%u+%d+%d.\n", primary_crtc
, crtc_info
->mode
,
299 crtc_info
->width
, crtc_info
->height
, crtc_info
->x
, crtc_info
->y
);
301 for (i
= 0; i
< xrandr_mode_count
; ++i
)
303 if (xrandr12_modes
[i
] == crtc_info
->mode
)
310 pXRRFreeCrtcInfo( crtc_info
);
311 pXRRFreeScreenResources( resources
);
315 ERR("Unknown mode, returning default.\n");
319 xrandr_current_mode
= ret
;
323 static LONG
xrandr12_set_current_mode( int mode
)
325 Status status
= RRSetConfigFailed
;
326 XRRScreenResources
*resources
;
327 XRRCrtcInfo
*crtc_info
;
329 mode
= mode
% xrandr_mode_count
;
331 if (!(resources
= pXRRGetScreenResourcesCurrent( gdi_display
, root_window
)))
333 ERR("Failed to get screen resources.\n");
334 return DISP_CHANGE_FAILED
;
337 if (resources
->ncrtc
<= primary_crtc
||
338 !(crtc_info
= pXRRGetCrtcInfo( gdi_display
, resources
, resources
->crtcs
[primary_crtc
] )))
340 pXRRFreeScreenResources( resources
);
341 ERR("Failed to get CRTC info.\n");
342 return DISP_CHANGE_FAILED
;
345 TRACE("CRTC %d: mode %#lx, %ux%u+%d+%d.\n", primary_crtc
, crtc_info
->mode
,
346 crtc_info
->width
, crtc_info
->height
, crtc_info
->x
, crtc_info
->y
);
348 status
= pXRRSetCrtcConfig( gdi_display
, resources
, resources
->crtcs
[primary_crtc
],
349 CurrentTime
, crtc_info
->x
, crtc_info
->y
, xrandr12_modes
[mode
],
350 crtc_info
->rotation
, crtc_info
->outputs
, crtc_info
->noutput
);
352 pXRRFreeCrtcInfo( crtc_info
);
353 pXRRFreeScreenResources( resources
);
355 if (status
!= RRSetConfigSuccess
)
357 ERR("Resolution change not successful -- perhaps display has changed?\n");
358 return DISP_CHANGE_FAILED
;
361 xrandr_current_mode
= mode
;
362 X11DRV_resize_desktop( dd_modes
[mode
].width
, dd_modes
[mode
].height
);
363 return DISP_CHANGE_SUCCESSFUL
;
366 static XRRCrtcInfo
*xrandr12_get_primary_crtc_info( XRRScreenResources
*resources
, int *crtc_idx
)
368 XRRCrtcInfo
*crtc_info
;
371 for (i
= 0; i
< resources
->ncrtc
; ++i
)
373 crtc_info
= pXRRGetCrtcInfo( gdi_display
, resources
, resources
->crtcs
[i
] );
374 if (!crtc_info
|| crtc_info
->mode
== None
)
376 pXRRFreeCrtcInfo( crtc_info
);
387 static int xrandr12_init_modes(void)
389 unsigned int only_one_resolution
= 1, mode_count
;
390 XRRScreenResources
*resources
;
391 XRROutputInfo
*output_info
;
392 XRRCrtcInfo
*crtc_info
;
396 if (!(resources
= pXRRGetScreenResourcesCurrent( gdi_display
, root_window
)))
398 ERR("Failed to get screen resources.\n");
402 if (!resources
->ncrtc
)
404 pXRRFreeScreenResources( resources
);
405 if (!(resources
= pXRRGetScreenResources( gdi_display
, root_window
)))
407 ERR("Failed to get screen resources.\n");
412 if (!(crtc_info
= xrandr12_get_primary_crtc_info( resources
, &primary_crtc
)))
414 pXRRFreeScreenResources( resources
);
415 ERR("Failed to get primary CRTC info.\n");
419 TRACE("CRTC %d: mode %#lx, %ux%u+%d+%d.\n", primary_crtc
, crtc_info
->mode
,
420 crtc_info
->width
, crtc_info
->height
, crtc_info
->x
, crtc_info
->y
);
422 if (!crtc_info
->noutput
|| !(output_info
= pXRRGetOutputInfo( gdi_display
, resources
, crtc_info
->outputs
[0] )))
424 pXRRFreeCrtcInfo( crtc_info
);
425 pXRRFreeScreenResources( resources
);
426 ERR("Failed to get output info.\n");
430 TRACE("OUTPUT 0: name %s.\n", debugstr_a(output_info
->name
));
432 if (!output_info
->nmode
)
434 WARN("Output has no modes.\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 mode_count
= X11DRV_Settings_GetModeCount();
470 for (i
= 1; i
< mode_count
; ++i
)
472 if (dd_modes
[i
].width
!= dd_modes
[0].width
|| dd_modes
[i
].height
!= dd_modes
[0].height
)
474 only_one_resolution
= 0;
479 /* Recent (304.64, possibly earlier) versions of the nvidia driver only
480 * report a DFP's native mode through RandR 1.2 / 1.3. Standard DMT modes
481 * are only listed through RandR 1.0 / 1.1. This is completely useless,
482 * but NVIDIA considers this a feature, so it's unlikely to change. The
483 * best we can do is to fall back to RandR 1.0 and encourage users to
484 * consider more cooperative driver vendors when we detect such a
486 if (only_one_resolution
&& XQueryExtension( gdi_display
, "NV-CONTROL", &i
, &j
, &ret
))
488 ERR_(winediag
)("Broken NVIDIA RandR detected, falling back to RandR 1.0. "
489 "Please consider using the Nouveau driver instead.\n");
491 HeapFree( GetProcessHeap(), 0, xrandr12_modes
);
495 X11DRV_Settings_AddDepthModes();
499 pXRRFreeOutputInfo( output_info
);
500 pXRRFreeCrtcInfo( crtc_info
);
501 pXRRFreeScreenResources( resources
);
505 #endif /* HAVE_XRRGETSCREENRESOURCES */
507 void X11DRV_XRandR_Init(void)
509 int event_base
, error_base
, minor
, ret
;
513 if (major
) return; /* already initialized? */
514 if (!usexrandr
) return; /* disabled in config */
515 if (root_window
!= DefaultRootWindow( gdi_display
)) return;
516 if (!(ret
= load_xrandr())) return; /* can't load the Xrandr library */
518 /* see if Xrandr is available */
519 if (!pXRRQueryExtension( gdi_display
, &event_base
, &error_base
)) return;
520 X11DRV_expect_error( gdi_display
, XRandRErrorHandler
, NULL
);
521 ok
= pXRRQueryVersion( gdi_display
, &major
, &minor
);
522 if (X11DRV_check_error() || !ok
) return;
524 TRACE("Found XRandR %d.%d.\n", major
, minor
);
526 #ifdef HAVE_XRRGETSCREENRESOURCES
527 if (ret
>= 2 && (major
> 1 || (major
== 1 && minor
>= 2)))
529 if (major
> 1 || (major
== 1 && minor
>= 3))
530 pXRRGetScreenResourcesCurrent
= wine_dlsym( xrandr_handle
, "XRRGetScreenResourcesCurrent", NULL
, 0 );
531 if (!pXRRGetScreenResourcesCurrent
)
532 pXRRGetScreenResourcesCurrent
= pXRRGetScreenResources
;
535 if (!pXRRGetScreenResourcesCurrent
|| xrandr12_init_modes() < 0)
537 xrandr10_init_modes();
540 #else /* SONAME_LIBXRANDR */
542 void X11DRV_XRandR_Init(void)
544 TRACE("XRandR support not compiled in.\n");
547 #endif /* SONAME_LIBXRANDR */