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 MAKE_FUNCPTR(XRRSetScreenSize
)
62 static typeof(XRRGetScreenResources
) *pXRRGetScreenResourcesCurrent
;
63 static RRMode
*xrandr12_modes
;
64 static int primary_crtc
;
69 static struct x11drv_mode_info
*dd_modes
;
70 static SizeID
*xrandr10_modes
;
71 static unsigned int xrandr_mode_count
;
72 static int xrandr_current_mode
= -1;
74 static int load_xrandr(void)
78 if (wine_dlopen(SONAME_LIBXRENDER
, RTLD_NOW
|RTLD_GLOBAL
, NULL
, 0) &&
79 (xrandr_handle
= wine_dlopen(SONAME_LIBXRANDR
, RTLD_NOW
, NULL
, 0)))
82 #define LOAD_FUNCPTR(f) \
83 if((p##f = wine_dlsym(xrandr_handle, #f, NULL, 0)) == NULL) \
86 LOAD_FUNCPTR(XRRConfigCurrentConfiguration
)
87 LOAD_FUNCPTR(XRRConfigCurrentRate
)
88 LOAD_FUNCPTR(XRRFreeScreenConfigInfo
)
89 LOAD_FUNCPTR(XRRGetScreenInfo
)
90 LOAD_FUNCPTR(XRRQueryExtension
)
91 LOAD_FUNCPTR(XRRQueryVersion
)
92 LOAD_FUNCPTR(XRRRates
)
93 LOAD_FUNCPTR(XRRSetScreenConfig
)
94 LOAD_FUNCPTR(XRRSetScreenConfigAndRate
)
95 LOAD_FUNCPTR(XRRSizes
)
98 #ifdef HAVE_XRRGETSCREENRESOURCES
99 LOAD_FUNCPTR(XRRFreeCrtcInfo
)
100 LOAD_FUNCPTR(XRRFreeOutputInfo
)
101 LOAD_FUNCPTR(XRRFreeScreenResources
)
102 LOAD_FUNCPTR(XRRGetCrtcInfo
)
103 LOAD_FUNCPTR(XRRGetOutputInfo
)
104 LOAD_FUNCPTR(XRRGetScreenResources
)
105 LOAD_FUNCPTR(XRRSetCrtcConfig
)
106 LOAD_FUNCPTR(XRRSetScreenSize
)
112 if (!r
) TRACE("Unable to load function ptrs from XRandR library\n");
117 static int XRandRErrorHandler(Display
*dpy
, XErrorEvent
*event
, void *arg
)
122 static int xrandr10_get_current_mode(void)
126 XRRScreenConfiguration
*sc
;
131 if (xrandr_current_mode
!= -1)
132 return xrandr_current_mode
;
134 sc
= pXRRGetScreenInfo (gdi_display
, DefaultRootWindow( gdi_display
));
135 size
= pXRRConfigCurrentConfiguration (sc
, &rot
);
136 rate
= pXRRConfigCurrentRate (sc
);
137 pXRRFreeScreenConfigInfo(sc
);
139 for (i
= 0; i
< xrandr_mode_count
; ++i
)
141 if (xrandr10_modes
[i
] == size
&& dd_modes
[i
].refresh_rate
== rate
)
149 ERR("In unknown mode, returning default\n");
153 xrandr_current_mode
= res
;
157 static LONG
xrandr10_set_current_mode( int mode
)
162 XRRScreenConfiguration
*sc
;
166 root
= DefaultRootWindow( gdi_display
);
167 sc
= pXRRGetScreenInfo (gdi_display
, root
);
168 pXRRConfigCurrentConfiguration (sc
, &rot
);
169 mode
= mode
% xrandr_mode_count
;
171 TRACE("Changing Resolution to %dx%d @%d Hz\n",
172 dd_modes
[mode
].width
,
173 dd_modes
[mode
].height
,
174 dd_modes
[mode
].refresh_rate
);
176 size
= xrandr10_modes
[mode
];
177 rate
= dd_modes
[mode
].refresh_rate
;
180 stat
= pXRRSetScreenConfigAndRate( gdi_display
, sc
, root
, size
, rot
, rate
, CurrentTime
);
182 stat
= pXRRSetScreenConfig( gdi_display
, sc
, root
, size
, rot
, CurrentTime
);
184 pXRRFreeScreenConfigInfo(sc
);
186 if (stat
== RRSetConfigSuccess
)
188 xrandr_current_mode
= mode
;
189 X11DRV_resize_desktop( dd_modes
[mode
].width
, dd_modes
[mode
].height
);
190 return DISP_CHANGE_SUCCESSFUL
;
193 ERR("Resolution change not successful -- perhaps display has changed?\n");
194 return DISP_CHANGE_FAILED
;
197 static void xrandr10_init_modes(void)
199 XRRScreenSize
*sizes
;
201 int i
, j
, nmodes
= 0;
203 sizes
= pXRRSizes( gdi_display
, DefaultScreen(gdi_display
), &sizes_count
);
204 if (sizes_count
<= 0) return;
206 TRACE("XRandR: found %d sizes.\n", sizes_count
);
207 for (i
= 0; i
< sizes_count
; ++i
)
212 rates
= pXRRRates( gdi_display
, DefaultScreen(gdi_display
), i
, &rates_count
);
213 TRACE("- at %d: %dx%d (%d rates):", i
, sizes
[i
].width
, sizes
[i
].height
, rates_count
);
216 nmodes
+= rates_count
;
217 for (j
= 0; j
< rates_count
; ++j
)
221 TRACE(" %d", rates
[j
]);
232 TRACE("XRandR modes: count=%d\n", nmodes
);
234 if (!(xrandr10_modes
= HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr10_modes
) * nmodes
)))
236 ERR("Failed to allocate xrandr mode info array.\n");
240 dd_modes
= X11DRV_Settings_SetHandlers( "XRandR 1.0",
241 xrandr10_get_current_mode
,
242 xrandr10_set_current_mode
,
245 xrandr_mode_count
= 0;
246 for (i
= 0; i
< sizes_count
; ++i
)
251 rates
= pXRRRates( gdi_display
, DefaultScreen(gdi_display
), i
, &rates_count
);
255 for (j
= 0; j
< rates_count
; ++j
)
257 X11DRV_Settings_AddOneMode( sizes
[i
].width
, sizes
[i
].height
, 0, rates
[j
] );
258 xrandr10_modes
[xrandr_mode_count
++] = i
;
263 X11DRV_Settings_AddOneMode( sizes
[i
].width
, sizes
[i
].height
, 0, 0 );
264 xrandr10_modes
[xrandr_mode_count
++] = i
;
268 X11DRV_Settings_AddDepthModes();
269 nmodes
= X11DRV_Settings_GetModeCount();
271 TRACE("Available DD modes: count=%d\n", nmodes
);
272 TRACE("Enabling XRandR\n");
275 #ifdef HAVE_XRRGETSCREENRESOURCES
277 static int xrandr12_get_current_mode(void)
279 XRRScreenResources
*resources
;
280 XRRCrtcInfo
*crtc_info
;
283 if (xrandr_current_mode
!= -1)
284 return xrandr_current_mode
;
286 if (!(resources
= pXRRGetScreenResourcesCurrent( gdi_display
, root_window
)))
288 ERR("Failed to get screen resources.\n");
292 if (resources
->ncrtc
<= primary_crtc
||
293 !(crtc_info
= pXRRGetCrtcInfo( gdi_display
, resources
, resources
->crtcs
[primary_crtc
] )))
295 pXRRFreeScreenResources( resources
);
296 ERR("Failed to get CRTC info.\n");
300 TRACE("CRTC %d: mode %#lx, %ux%u+%d+%d.\n", primary_crtc
, crtc_info
->mode
,
301 crtc_info
->width
, crtc_info
->height
, crtc_info
->x
, crtc_info
->y
);
303 for (i
= 0; i
< xrandr_mode_count
; ++i
)
305 if (xrandr12_modes
[i
] == crtc_info
->mode
)
312 pXRRFreeCrtcInfo( crtc_info
);
313 pXRRFreeScreenResources( resources
);
317 ERR("Unknown mode, returning default.\n");
321 xrandr_current_mode
= ret
;
325 static void get_screen_size( XRRScreenResources
*resources
, unsigned int *width
, unsigned int *height
)
327 XRRCrtcInfo
*crtc_info
;
329 *width
= *height
= 0;
331 for (i
= 0; i
< resources
->ncrtc
; ++i
)
333 if (!(crtc_info
= pXRRGetCrtcInfo( gdi_display
, resources
, resources
->crtcs
[i
] )))
336 if (crtc_info
->mode
!= None
)
338 *width
= max(*width
, crtc_info
->x
+ crtc_info
->width
);
339 *height
= max(*height
, crtc_info
->y
+ crtc_info
->height
);
342 pXRRFreeCrtcInfo( crtc_info
);
346 static LONG
xrandr12_set_current_mode( int mode
)
348 unsigned int screen_width
, screen_height
;
349 Status status
= RRSetConfigFailed
;
350 XRRScreenResources
*resources
;
351 XRRCrtcInfo
*crtc_info
;
353 mode
= mode
% xrandr_mode_count
;
355 if (!(resources
= pXRRGetScreenResourcesCurrent( gdi_display
, root_window
)))
357 ERR("Failed to get screen resources.\n");
358 return DISP_CHANGE_FAILED
;
361 if (resources
->ncrtc
<= primary_crtc
||
362 !(crtc_info
= pXRRGetCrtcInfo( gdi_display
, resources
, resources
->crtcs
[primary_crtc
] )))
364 pXRRFreeScreenResources( resources
);
365 ERR("Failed to get CRTC info.\n");
366 return DISP_CHANGE_FAILED
;
369 TRACE("CRTC %d: mode %#lx, %ux%u+%d+%d.\n", primary_crtc
, crtc_info
->mode
,
370 crtc_info
->width
, crtc_info
->height
, crtc_info
->x
, crtc_info
->y
);
372 /* According to the RandR spec, the entire CRTC must fit inside the screen.
373 * Since we use the union of all enabled CRTCs to determine the necessary
374 * screen size, this might involve shrinking the screen, so we must disable
375 * the CRTC in question first. */
377 XGrabServer( gdi_display
);
379 status
= pXRRSetCrtcConfig( gdi_display
, resources
, resources
->crtcs
[primary_crtc
],
380 CurrentTime
, crtc_info
->x
, crtc_info
->y
, None
,
381 crtc_info
->rotation
, NULL
, 0 );
382 if (status
!= RRSetConfigSuccess
)
384 XUngrabServer( gdi_display
);
385 ERR("Failed to disable CRTC.\n");
386 pXRRFreeCrtcInfo( crtc_info
);
387 pXRRFreeScreenResources( resources
);
388 return DISP_CHANGE_FAILED
;
391 get_screen_size( resources
, &screen_width
, &screen_height
);
392 screen_width
= max( screen_width
, crtc_info
->x
+ dd_modes
[mode
].width
);
393 screen_height
= max( screen_height
, crtc_info
->y
+ dd_modes
[mode
].height
);
395 pXRRSetScreenSize( gdi_display
, root_window
, screen_width
, screen_height
,
396 screen_width
* DisplayWidthMM( gdi_display
, default_visual
.screen
)
397 / DisplayWidth( gdi_display
, default_visual
.screen
),
398 screen_height
* DisplayHeightMM( gdi_display
, default_visual
.screen
)
399 / DisplayHeight( gdi_display
, default_visual
.screen
));
401 status
= pXRRSetCrtcConfig( gdi_display
, resources
, resources
->crtcs
[primary_crtc
],
402 CurrentTime
, crtc_info
->x
, crtc_info
->y
, xrandr12_modes
[mode
],
403 crtc_info
->rotation
, crtc_info
->outputs
, crtc_info
->noutput
);
405 XUngrabServer( gdi_display
);
407 pXRRFreeCrtcInfo( crtc_info
);
408 pXRRFreeScreenResources( resources
);
410 if (status
!= RRSetConfigSuccess
)
412 ERR("Resolution change not successful -- perhaps display has changed?\n");
413 return DISP_CHANGE_FAILED
;
416 xrandr_current_mode
= mode
;
417 X11DRV_resize_desktop( dd_modes
[mode
].width
, dd_modes
[mode
].height
);
418 return DISP_CHANGE_SUCCESSFUL
;
421 static XRRCrtcInfo
*xrandr12_get_primary_crtc_info( XRRScreenResources
*resources
, int *crtc_idx
)
423 XRRCrtcInfo
*crtc_info
;
426 for (i
= 0; i
< resources
->ncrtc
; ++i
)
428 crtc_info
= pXRRGetCrtcInfo( gdi_display
, resources
, resources
->crtcs
[i
] );
429 if (!crtc_info
|| crtc_info
->mode
== None
)
431 pXRRFreeCrtcInfo( crtc_info
);
442 static int xrandr12_init_modes(void)
444 unsigned int only_one_resolution
= 1, mode_count
;
445 XRRScreenResources
*resources
;
446 XRROutputInfo
*output_info
;
447 XRRCrtcInfo
*crtc_info
;
451 if (!(resources
= pXRRGetScreenResourcesCurrent( gdi_display
, root_window
)))
453 ERR("Failed to get screen resources.\n");
457 if (!resources
->ncrtc
)
459 pXRRFreeScreenResources( resources
);
460 if (!(resources
= pXRRGetScreenResources( gdi_display
, root_window
)))
462 ERR("Failed to get screen resources.\n");
467 if (!(crtc_info
= xrandr12_get_primary_crtc_info( resources
, &primary_crtc
)))
469 pXRRFreeScreenResources( resources
);
470 ERR("Failed to get primary CRTC info.\n");
474 TRACE("CRTC %d: mode %#lx, %ux%u+%d+%d.\n", primary_crtc
, crtc_info
->mode
,
475 crtc_info
->width
, crtc_info
->height
, crtc_info
->x
, crtc_info
->y
);
477 if (!crtc_info
->noutput
|| !(output_info
= pXRRGetOutputInfo( gdi_display
, resources
, crtc_info
->outputs
[0] )))
479 pXRRFreeCrtcInfo( crtc_info
);
480 pXRRFreeScreenResources( resources
);
481 ERR("Failed to get output info.\n");
485 TRACE("OUTPUT 0: name %s.\n", debugstr_a(output_info
->name
));
487 if (!output_info
->nmode
)
489 WARN("Output has no modes.\n");
493 if (!(xrandr12_modes
= HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr12_modes
) * output_info
->nmode
)))
495 ERR("Failed to allocate xrandr mode info array.\n");
499 dd_modes
= X11DRV_Settings_SetHandlers( "XRandR 1.2",
500 xrandr12_get_current_mode
,
501 xrandr12_set_current_mode
,
502 output_info
->nmode
, 1 );
504 xrandr_mode_count
= 0;
505 for (i
= 0; i
< output_info
->nmode
; ++i
)
507 for (j
= 0; j
< resources
->nmode
; ++j
)
509 XRRModeInfo
*mode
= &resources
->modes
[j
];
511 if (mode
->id
== output_info
->modes
[i
])
513 unsigned int dots
= mode
->hTotal
* mode
->vTotal
;
514 unsigned int refresh
= dots
? (mode
->dotClock
+ dots
/ 2) / dots
: 0;
516 TRACE("Adding mode %#lx: %ux%u@%u.\n", mode
->id
, mode
->width
, mode
->height
, refresh
);
517 X11DRV_Settings_AddOneMode( mode
->width
, mode
->height
, 0, refresh
);
518 xrandr12_modes
[xrandr_mode_count
++] = mode
->id
;
524 mode_count
= X11DRV_Settings_GetModeCount();
525 for (i
= 1; i
< mode_count
; ++i
)
527 if (dd_modes
[i
].width
!= dd_modes
[0].width
|| dd_modes
[i
].height
!= dd_modes
[0].height
)
529 only_one_resolution
= 0;
534 /* Recent (304.64, possibly earlier) versions of the nvidia driver only
535 * report a DFP's native mode through RandR 1.2 / 1.3. Standard DMT modes
536 * are only listed through RandR 1.0 / 1.1. This is completely useless,
537 * but NVIDIA considers this a feature, so it's unlikely to change. The
538 * best we can do is to fall back to RandR 1.0 and encourage users to
539 * consider more cooperative driver vendors when we detect such a
541 if (only_one_resolution
&& XQueryExtension( gdi_display
, "NV-CONTROL", &i
, &j
, &ret
))
543 ERR_(winediag
)("Broken NVIDIA RandR detected, falling back to RandR 1.0. "
544 "Please consider using the Nouveau driver instead.\n");
546 HeapFree( GetProcessHeap(), 0, xrandr12_modes
);
550 X11DRV_Settings_AddDepthModes();
554 pXRRFreeOutputInfo( output_info
);
555 pXRRFreeCrtcInfo( crtc_info
);
556 pXRRFreeScreenResources( resources
);
560 #endif /* HAVE_XRRGETSCREENRESOURCES */
562 void X11DRV_XRandR_Init(void)
564 int event_base
, error_base
, minor
, ret
;
568 if (major
) return; /* already initialized? */
569 if (!usexrandr
) return; /* disabled in config */
570 if (root_window
!= DefaultRootWindow( gdi_display
)) return;
571 if (!(ret
= load_xrandr())) return; /* can't load the Xrandr library */
573 /* see if Xrandr is available */
574 if (!pXRRQueryExtension( gdi_display
, &event_base
, &error_base
)) return;
575 X11DRV_expect_error( gdi_display
, XRandRErrorHandler
, NULL
);
576 ok
= pXRRQueryVersion( gdi_display
, &major
, &minor
);
577 if (X11DRV_check_error() || !ok
) return;
579 TRACE("Found XRandR %d.%d.\n", major
, minor
);
581 #ifdef HAVE_XRRGETSCREENRESOURCES
582 if (ret
>= 2 && (major
> 1 || (major
== 1 && minor
>= 2)))
584 if (major
> 1 || (major
== 1 && minor
>= 3))
585 pXRRGetScreenResourcesCurrent
= wine_dlsym( xrandr_handle
, "XRRGetScreenResourcesCurrent", NULL
, 0 );
586 if (!pXRRGetScreenResourcesCurrent
)
587 pXRRGetScreenResourcesCurrent
= pXRRGetScreenResources
;
590 if (!pXRRGetScreenResourcesCurrent
|| xrandr12_init_modes() < 0)
592 xrandr10_init_modes();
595 #else /* SONAME_LIBXRANDR */
597 void X11DRV_XRandR_Init(void)
599 TRACE("XRandR support not compiled in.\n");
602 #endif /* SONAME_LIBXRANDR */