winex11: Avoid passing a window data structure to functions that can send messages.
[wine] / dlls / winex11.drv / xrandr.c
1 /*
2  * Wine X11drv Xrandr interface
3  *
4  * Copyright 2003 Alexander James Pasadyn
5  * Copyright 2012 Henri Verbeet for CodeWeavers
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24 #include "wine/debug.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(xrandr);
27
28 #ifdef SONAME_LIBXRANDR
29
30 #include <X11/Xlib.h>
31 #include <X11/extensions/Xrandr.h>
32 #include "x11drv.h"
33
34 #include "wine/library.h"
35
36 static void *xrandr_handle;
37
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)
49
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;
60 #endif
61
62 #undef MAKE_FUNCPTR
63
64 static struct x11drv_mode_info *dd_modes;
65 static SizeID *xrandr10_modes;
66 static unsigned int xrandr_mode_count;
67
68 static int load_xrandr(void)
69 {
70     int r = 0;
71
72     if (wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
73         (xrandr_handle = wine_dlopen(SONAME_LIBXRANDR, RTLD_NOW, NULL, 0)))
74     {
75
76 #define LOAD_FUNCPTR(f) \
77         if((p##f = wine_dlsym(xrandr_handle, #f, NULL, 0)) == NULL) \
78             goto sym_not_found;
79
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)
90         r = 1;
91
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)
100         r = 2;
101 #endif
102 #undef LOAD_FUNCPTR
103
104 sym_not_found:
105         if (!r)  TRACE("Unable to load function ptrs from XRandR library\n");
106     }
107     return r;
108 }
109
110 static int XRandRErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
111 {
112     return 1;
113 }
114
115 static int xrandr10_get_current_mode(void)
116 {
117     SizeID size;
118     Rotation rot;
119     XRRScreenConfiguration *sc;
120     short rate;
121     unsigned int i;
122     int res = -1;
123
124     sc = pXRRGetScreenInfo (gdi_display, DefaultRootWindow( gdi_display ));
125     size = pXRRConfigCurrentConfiguration (sc, &rot);
126     rate = pXRRConfigCurrentRate (sc);
127     pXRRFreeScreenConfigInfo(sc);
128
129     for (i = 0; i < xrandr_mode_count; ++i)
130     {
131         if (xrandr10_modes[i] == size && dd_modes[i].refresh_rate == rate)
132         {
133             res = i;
134             break;
135         }
136     }
137     if (res == -1)
138     {
139         ERR("In unknown mode, returning default\n");
140         res = 0;
141     }
142     return res;
143 }
144
145 static LONG xrandr10_set_current_mode( int mode )
146 {
147     SizeID size;
148     Rotation rot;
149     Window root;
150     XRRScreenConfiguration *sc;
151     Status stat;
152     short rate;
153
154     root = DefaultRootWindow( gdi_display );
155     sc = pXRRGetScreenInfo (gdi_display, root);
156     size = pXRRConfigCurrentConfiguration (sc, &rot);
157     mode = mode % xrandr_mode_count;
158
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);
163
164     size = xrandr10_modes[mode];
165     rate = dd_modes[mode].refresh_rate;
166
167     if (rate)
168         stat = pXRRSetScreenConfigAndRate( gdi_display, sc, root, size, rot, rate, CurrentTime );
169     else
170         stat = pXRRSetScreenConfig( gdi_display, sc, root, size, rot, CurrentTime );
171
172     pXRRFreeScreenConfigInfo(sc);
173
174     if (stat == RRSetConfigSuccess)
175     {
176         X11DRV_resize_desktop( dd_modes[mode].width, dd_modes[mode].height );
177         return DISP_CHANGE_SUCCESSFUL;
178     }
179
180     ERR("Resolution change not successful -- perhaps display has changed?\n");
181     return DISP_CHANGE_FAILED;
182 }
183
184 static void xrandr10_init_modes(void)
185 {
186     XRRScreenSize *sizes;
187     int sizes_count;
188     int i, j, nmodes = 0;
189
190     sizes = pXRRSizes( gdi_display, DefaultScreen(gdi_display), &sizes_count );
191     if (sizes_count <= 0) return;
192
193     TRACE("XRandR: found %d sizes.\n", sizes_count);
194     for (i = 0; i < sizes_count; ++i)
195     {
196         int rates_count;
197         short *rates;
198
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);
201         if (rates_count)
202         {
203             nmodes += rates_count;
204             for (j = 0; j < rates_count; ++j)
205             {
206                 if (j > 0)
207                     TRACE(",");
208                 TRACE("  %d", rates[j]);
209             }
210         }
211         else
212         {
213             ++nmodes;
214             TRACE(" <default>");
215         }
216         TRACE(" Hz\n");
217     }
218
219     TRACE("XRandR modes: count=%d\n", nmodes);
220
221     if (!(xrandr10_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr10_modes) * nmodes )))
222     {
223         ERR("Failed to allocate xrandr mode info array.\n");
224         return;
225     }
226
227     dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.0",
228                                             xrandr10_get_current_mode,
229                                             xrandr10_set_current_mode,
230                                             nmodes, 1 );
231
232     xrandr_mode_count = 0;
233     for (i = 0; i < sizes_count; ++i)
234     {
235         int rates_count;
236         short *rates;
237
238         rates = pXRRRates( gdi_display, DefaultScreen(gdi_display), i, &rates_count );
239
240         if (rates_count)
241         {
242             for (j = 0; j < rates_count; ++j)
243             {
244                 X11DRV_Settings_AddOneMode( sizes[i].width, sizes[i].height, 0, rates[j] );
245                 xrandr10_modes[xrandr_mode_count++] = i;
246             }
247         }
248         else
249         {
250             X11DRV_Settings_AddOneMode( sizes[i].width, sizes[i].height, 0, 0 );
251             xrandr10_modes[xrandr_mode_count++] = i;
252         }
253     }
254
255     X11DRV_Settings_AddDepthModes();
256     nmodes = X11DRV_Settings_GetModeCount();
257
258     TRACE("Available DD modes: count=%d\n", nmodes);
259     TRACE("Enabling XRandR\n");
260 }
261
262 #ifdef HAVE_XRRGETSCREENRESOURCES
263
264 static int xrandr12_get_current_mode(void)
265 {
266     XRRScreenResources *resources;
267     XRRCrtcInfo *crtc_info;
268     int i, ret = -1;
269
270     if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
271     {
272         ERR("Failed to get screen resources.\n");
273         return 0;
274     }
275
276     if (!resources->ncrtc || !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[0] )))
277     {
278         pXRRFreeScreenResources( resources );
279         ERR("Failed to get CRTC info.\n");
280         return 0;
281     }
282
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);
285
286     for (i = 0; i < xrandr_mode_count; ++i)
287     {
288         if (xrandr12_modes[i] == crtc_info->mode)
289         {
290             ret = i;
291             break;
292         }
293     }
294
295     pXRRFreeCrtcInfo( crtc_info );
296     pXRRFreeScreenResources( resources );
297
298     if (ret == -1)
299     {
300         ERR("Unknown mode, returning default.\n");
301         ret = 0;
302     }
303
304     return ret;
305 }
306
307 static LONG xrandr12_set_current_mode( int mode )
308 {
309     Status status = RRSetConfigFailed;
310     XRRScreenResources *resources;
311     XRRCrtcInfo *crtc_info;
312
313     mode = mode % xrandr_mode_count;
314
315     if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
316     {
317         ERR("Failed to get screen resources.\n");
318         return DISP_CHANGE_FAILED;
319     }
320
321     if (!resources->ncrtc || !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[0] )))
322     {
323         pXRRFreeScreenResources( resources );
324         ERR("Failed to get CRTC info.\n");
325         return DISP_CHANGE_FAILED;
326     }
327
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);
330
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 );
333
334     pXRRFreeCrtcInfo( crtc_info );
335     pXRRFreeScreenResources( resources );
336
337     if (status != RRSetConfigSuccess)
338     {
339         ERR("Resolution change not successful -- perhaps display has changed?\n");
340         return DISP_CHANGE_FAILED;
341     }
342
343     X11DRV_resize_desktop( dd_modes[mode].width, dd_modes[mode].height );
344     return DISP_CHANGE_SUCCESSFUL;
345 }
346
347 static int xrandr12_init_modes(void)
348 {
349     XRRScreenResources *resources;
350     XRROutputInfo *output_info;
351     XRRCrtcInfo *crtc_info;
352     int ret = -1;
353     int i, j;
354
355     if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
356     {
357         ERR("Failed to get screen resources.\n");
358         return ret;
359     }
360
361     if (!resources->ncrtc)
362     {
363         pXRRFreeScreenResources( resources );
364         if (!(resources = pXRRGetScreenResources( gdi_display, root_window )))
365         {
366             ERR("Failed to get screen resources.\n");
367             return ret;
368         }
369     }
370
371     if (!resources->ncrtc || !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[0] )))
372     {
373         pXRRFreeScreenResources( resources );
374         ERR("Failed to get CRTC info.\n");
375         return ret;
376     }
377
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);
380
381     if (!crtc_info->noutput || !(output_info = pXRRGetOutputInfo( gdi_display, resources, crtc_info->outputs[0] )))
382     {
383         pXRRFreeCrtcInfo( crtc_info );
384         pXRRFreeScreenResources( resources );
385         ERR("Failed to get output info.\n");
386         return ret;
387     }
388
389     TRACE("OUTPUT 0: name %s.\n", debugstr_a(output_info->name));
390
391     if (!output_info->nmode)
392     {
393         ERR("Output has no modes.\n");
394         goto done;
395     }
396
397     if (!(xrandr12_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr12_modes) * output_info->nmode )))
398     {
399         ERR("Failed to allocate xrandr mode info array.\n");
400         goto done;
401     }
402
403     dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.2",
404                                             xrandr12_get_current_mode,
405                                             xrandr12_set_current_mode,
406                                             output_info->nmode, 1 );
407
408     xrandr_mode_count = 0;
409     for (i = 0; i < output_info->nmode; ++i)
410     {
411         for (j = 0; j < resources->nmode; ++j)
412         {
413             XRRModeInfo *mode = &resources->modes[j];
414
415             if (mode->id == output_info->modes[i])
416             {
417                 unsigned int dots = mode->hTotal * mode->vTotal;
418                 unsigned int refresh = dots ? (mode->dotClock + dots / 2) / dots : 0;
419
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;
423                 break;
424             }
425         }
426     }
427
428     X11DRV_Settings_AddDepthModes();
429     ret = 0;
430
431 done:
432     pXRRFreeOutputInfo( output_info );
433     pXRRFreeCrtcInfo( crtc_info );
434     pXRRFreeScreenResources( resources );
435     return ret;
436 }
437
438 #endif /* HAVE_XRRGETSCREENRESOURCES */
439
440 void X11DRV_XRandR_Init(void)
441 {
442     int event_base, error_base, minor, ret;
443     static int major;
444     Bool ok;
445
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 */
450
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;
456
457     TRACE("Found XRandR %d.%d.\n", major, minor);
458
459 #ifdef HAVE_XRRGETSCREENRESOURCES
460     if (ret >= 2 && (major > 1 || (major == 1 && minor >= 2)))
461     {
462         if (major > 1 || (major == 1 && minor >= 3))
463             pXRRGetScreenResourcesCurrent = wine_dlsym( xrandr_handle, "XRRGetScreenResourcesCurrent", NULL, 0 );
464         if (!pXRRGetScreenResourcesCurrent)
465             pXRRGetScreenResourcesCurrent = pXRRGetScreenResources;
466     }
467
468     if (!pXRRGetScreenResourcesCurrent || xrandr12_init_modes() < 0)
469 #endif
470         xrandr10_init_modes();
471 }
472
473 #else /* SONAME_LIBXRANDR */
474
475 void X11DRV_XRandR_Init(void)
476 {
477     TRACE("XRandR support not compiled in.\n");
478 }
479
480 #endif /* SONAME_LIBXRANDR */