gdiplus: Don't bother to free memory at process exit.
[wine/wine-gecko.git] / dlls / winex11.drv / xrandr.c
blobfc09020d8b933acb761263a644ad7b8a6ee0a75f
1 /*
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
22 #include "config.h"
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);
29 #endif
31 #ifdef SONAME_LIBXRANDR
33 #include <X11/Xlib.h>
34 #include <X11/extensions/Xrandr.h>
35 #include "x11drv.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;
64 #endif
66 #undef MAKE_FUNCPTR
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)
75 int r = 0;
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) \
83 goto sym_not_found;
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)
95 r = 1;
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)
105 r = 2;
106 #endif
107 #undef LOAD_FUNCPTR
109 sym_not_found:
110 if (!r) TRACE("Unable to load function ptrs from XRandR library\n");
112 return r;
115 static int XRandRErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
117 return 1;
120 static int xrandr10_get_current_mode(void)
122 SizeID size;
123 Rotation rot;
124 XRRScreenConfiguration *sc;
125 short rate;
126 unsigned int i;
127 int res = -1;
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)
141 res = i;
142 break;
145 if (res == -1)
147 ERR("In unknown mode, returning default\n");
148 return 0;
151 xrandr_current_mode = res;
152 return res;
155 static LONG xrandr10_set_current_mode( int mode )
157 SizeID size;
158 Rotation rot;
159 Window root;
160 XRRScreenConfiguration *sc;
161 Status stat;
162 short rate;
164 root = DefaultRootWindow( gdi_display );
165 sc = pXRRGetScreenInfo (gdi_display, root);
166 size = 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;
177 if (rate)
178 stat = pXRRSetScreenConfigAndRate( gdi_display, sc, root, size, rot, rate, CurrentTime );
179 else
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;
198 int sizes_count;
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)
207 int rates_count;
208 short *rates;
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);
212 if (rates_count)
214 nmodes += rates_count;
215 for (j = 0; j < rates_count; ++j)
217 if (j > 0)
218 TRACE(",");
219 TRACE(" %d", rates[j]);
222 else
224 ++nmodes;
225 TRACE(" <default>");
227 TRACE(" Hz\n");
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");
235 return;
238 dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.0",
239 xrandr10_get_current_mode,
240 xrandr10_set_current_mode,
241 nmodes, 1 );
243 xrandr_mode_count = 0;
244 for (i = 0; i < sizes_count; ++i)
246 int rates_count;
247 short *rates;
249 rates = pXRRRates( gdi_display, DefaultScreen(gdi_display), i, &rates_count );
251 if (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;
259 else
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;
279 int i, ret = -1;
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");
287 return 0;
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");
295 return 0;
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)
305 ret = i;
306 break;
310 pXRRFreeCrtcInfo( crtc_info );
311 pXRRFreeScreenResources( resources );
313 if (ret == -1)
315 ERR("Unknown mode, returning default.\n");
316 return 0;
319 xrandr_current_mode = ret;
320 return 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;
369 int i;
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 );
377 continue;
380 *crtc_idx = i;
381 return crtc_info;
384 return NULL;
387 static int xrandr12_init_modes(void)
389 XRRScreenResources *resources;
390 XRROutputInfo *output_info;
391 XRRCrtcInfo *crtc_info;
392 int ret = -1;
393 int i, j;
395 if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
397 ERR("Failed to get screen resources.\n");
398 return ret;
401 if (!resources->ncrtc)
403 pXRRFreeScreenResources( resources );
404 if (!(resources = pXRRGetScreenResources( gdi_display, root_window )))
406 ERR("Failed to get screen resources.\n");
407 return ret;
411 if (!(crtc_info = xrandr12_get_primary_crtc_info( resources, &primary_crtc )))
413 pXRRFreeScreenResources( resources );
414 ERR("Failed to get primary CRTC info.\n");
415 return ret;
418 TRACE("CRTC %d: mode %#lx, %ux%u+%d+%d.\n", primary_crtc, crtc_info->mode,
419 crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
421 if (!crtc_info->noutput || !(output_info = pXRRGetOutputInfo( gdi_display, resources, crtc_info->outputs[0] )))
423 pXRRFreeCrtcInfo( crtc_info );
424 pXRRFreeScreenResources( resources );
425 ERR("Failed to get output info.\n");
426 return ret;
429 TRACE("OUTPUT 0: name %s.\n", debugstr_a(output_info->name));
431 if (!output_info->nmode)
433 ERR("Output has no modes.\n");
434 goto done;
437 /* Recent (304.64, possibly earlier) versions of the nvidia driver only
438 * report a DFP's native mode through RandR 1.2 / 1.3. Standard DMT modes
439 * are only listed through RandR 1.0 / 1.1. This is completely useless,
440 * but NVIDIA considers this a feature, so it's unlikely to change. The
441 * best we can do is to fall back to RandR 1.0 and encourage users to
442 * consider more cooperative driver vendors when we detect such a
443 * configuration. */
444 if (output_info->nmode == 1 && XQueryExtension( gdi_display, "NV-CONTROL", &i, &j, &ret ))
446 ERR_(winediag)("Broken NVIDIA RandR detected, falling back to RandR 1.0. "
447 "Please consider using the Nouveau driver instead.\n");
448 ret = -1;
449 goto done;
452 if (!(xrandr12_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr12_modes) * output_info->nmode )))
454 ERR("Failed to allocate xrandr mode info array.\n");
455 goto done;
458 dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.2",
459 xrandr12_get_current_mode,
460 xrandr12_set_current_mode,
461 output_info->nmode, 1 );
463 xrandr_mode_count = 0;
464 for (i = 0; i < output_info->nmode; ++i)
466 for (j = 0; j < resources->nmode; ++j)
468 XRRModeInfo *mode = &resources->modes[j];
470 if (mode->id == output_info->modes[i])
472 unsigned int dots = mode->hTotal * mode->vTotal;
473 unsigned int refresh = dots ? (mode->dotClock + dots / 2) / dots : 0;
475 TRACE("Adding mode %#lx: %ux%u@%u.\n", mode->id, mode->width, mode->height, refresh);
476 X11DRV_Settings_AddOneMode( mode->width, mode->height, 0, refresh );
477 xrandr12_modes[xrandr_mode_count++] = mode->id;
478 break;
483 X11DRV_Settings_AddDepthModes();
484 ret = 0;
486 done:
487 pXRRFreeOutputInfo( output_info );
488 pXRRFreeCrtcInfo( crtc_info );
489 pXRRFreeScreenResources( resources );
490 return ret;
493 #endif /* HAVE_XRRGETSCREENRESOURCES */
495 void X11DRV_XRandR_Init(void)
497 int event_base, error_base, minor, ret;
498 static int major;
499 Bool ok;
501 if (major) return; /* already initialized? */
502 if (!usexrandr) return; /* disabled in config */
503 if (root_window != DefaultRootWindow( gdi_display )) return;
504 if (!(ret = load_xrandr())) return; /* can't load the Xrandr library */
506 /* see if Xrandr is available */
507 if (!pXRRQueryExtension( gdi_display, &event_base, &error_base )) return;
508 X11DRV_expect_error( gdi_display, XRandRErrorHandler, NULL );
509 ok = pXRRQueryVersion( gdi_display, &major, &minor );
510 if (X11DRV_check_error() || !ok) return;
512 TRACE("Found XRandR %d.%d.\n", major, minor);
514 #ifdef HAVE_XRRGETSCREENRESOURCES
515 if (ret >= 2 && (major > 1 || (major == 1 && minor >= 2)))
517 if (major > 1 || (major == 1 && minor >= 3))
518 pXRRGetScreenResourcesCurrent = wine_dlsym( xrandr_handle, "XRRGetScreenResourcesCurrent", NULL, 0 );
519 if (!pXRRGetScreenResourcesCurrent)
520 pXRRGetScreenResourcesCurrent = pXRRGetScreenResources;
523 if (!pXRRGetScreenResourcesCurrent || xrandr12_init_modes() < 0)
524 #endif
525 xrandr10_init_modes();
528 #else /* SONAME_LIBXRANDR */
530 void X11DRV_XRandR_Init(void)
532 TRACE("XRandR support not compiled in.\n");
535 #endif /* SONAME_LIBXRANDR */