d3d10core: Implement d3d10_device_SOSetTargets().
[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 static int primary_crtc;
61 #endif
62
63 #undef MAKE_FUNCPTR
64
65 static struct x11drv_mode_info *dd_modes;
66 static SizeID *xrandr10_modes;
67 static unsigned int xrandr_mode_count;
68
69 static int load_xrandr(void)
70 {
71     int r = 0;
72
73     if (wine_dlopen(SONAME_LIBXRENDER, RTLD_NOW|RTLD_GLOBAL, NULL, 0) &&
74         (xrandr_handle = wine_dlopen(SONAME_LIBXRANDR, RTLD_NOW, NULL, 0)))
75     {
76
77 #define LOAD_FUNCPTR(f) \
78         if((p##f = wine_dlsym(xrandr_handle, #f, NULL, 0)) == NULL) \
79             goto sym_not_found;
80
81         LOAD_FUNCPTR(XRRConfigCurrentConfiguration)
82         LOAD_FUNCPTR(XRRConfigCurrentRate)
83         LOAD_FUNCPTR(XRRFreeScreenConfigInfo)
84         LOAD_FUNCPTR(XRRGetScreenInfo)
85         LOAD_FUNCPTR(XRRQueryExtension)
86         LOAD_FUNCPTR(XRRQueryVersion)
87         LOAD_FUNCPTR(XRRRates)
88         LOAD_FUNCPTR(XRRSetScreenConfig)
89         LOAD_FUNCPTR(XRRSetScreenConfigAndRate)
90         LOAD_FUNCPTR(XRRSizes)
91         r = 1;
92
93 #ifdef HAVE_XRRGETSCREENRESOURCES
94         LOAD_FUNCPTR(XRRFreeCrtcInfo)
95         LOAD_FUNCPTR(XRRFreeOutputInfo)
96         LOAD_FUNCPTR(XRRFreeScreenResources)
97         LOAD_FUNCPTR(XRRGetCrtcInfo)
98         LOAD_FUNCPTR(XRRGetOutputInfo)
99         LOAD_FUNCPTR(XRRGetScreenResources)
100         LOAD_FUNCPTR(XRRSetCrtcConfig)
101         r = 2;
102 #endif
103 #undef LOAD_FUNCPTR
104
105 sym_not_found:
106         if (!r)  TRACE("Unable to load function ptrs from XRandR library\n");
107     }
108     return r;
109 }
110
111 static int XRandRErrorHandler(Display *dpy, XErrorEvent *event, void *arg)
112 {
113     return 1;
114 }
115
116 static int xrandr10_get_current_mode(void)
117 {
118     SizeID size;
119     Rotation rot;
120     XRRScreenConfiguration *sc;
121     short rate;
122     unsigned int i;
123     int res = -1;
124
125     sc = pXRRGetScreenInfo (gdi_display, DefaultRootWindow( gdi_display ));
126     size = pXRRConfigCurrentConfiguration (sc, &rot);
127     rate = pXRRConfigCurrentRate (sc);
128     pXRRFreeScreenConfigInfo(sc);
129
130     for (i = 0; i < xrandr_mode_count; ++i)
131     {
132         if (xrandr10_modes[i] == size && dd_modes[i].refresh_rate == rate)
133         {
134             res = i;
135             break;
136         }
137     }
138     if (res == -1)
139     {
140         ERR("In unknown mode, returning default\n");
141         res = 0;
142     }
143     return res;
144 }
145
146 static LONG xrandr10_set_current_mode( int mode )
147 {
148     SizeID size;
149     Rotation rot;
150     Window root;
151     XRRScreenConfiguration *sc;
152     Status stat;
153     short rate;
154
155     root = DefaultRootWindow( gdi_display );
156     sc = pXRRGetScreenInfo (gdi_display, root);
157     size = pXRRConfigCurrentConfiguration (sc, &rot);
158     mode = mode % xrandr_mode_count;
159
160     TRACE("Changing Resolution to %dx%d @%d Hz\n",
161           dd_modes[mode].width,
162           dd_modes[mode].height,
163           dd_modes[mode].refresh_rate);
164
165     size = xrandr10_modes[mode];
166     rate = dd_modes[mode].refresh_rate;
167
168     if (rate)
169         stat = pXRRSetScreenConfigAndRate( gdi_display, sc, root, size, rot, rate, CurrentTime );
170     else
171         stat = pXRRSetScreenConfig( gdi_display, sc, root, size, rot, CurrentTime );
172
173     pXRRFreeScreenConfigInfo(sc);
174
175     if (stat == RRSetConfigSuccess)
176     {
177         X11DRV_resize_desktop( dd_modes[mode].width, dd_modes[mode].height );
178         return DISP_CHANGE_SUCCESSFUL;
179     }
180
181     ERR("Resolution change not successful -- perhaps display has changed?\n");
182     return DISP_CHANGE_FAILED;
183 }
184
185 static void xrandr10_init_modes(void)
186 {
187     XRRScreenSize *sizes;
188     int sizes_count;
189     int i, j, nmodes = 0;
190
191     sizes = pXRRSizes( gdi_display, DefaultScreen(gdi_display), &sizes_count );
192     if (sizes_count <= 0) return;
193
194     TRACE("XRandR: found %d sizes.\n", sizes_count);
195     for (i = 0; i < sizes_count; ++i)
196     {
197         int rates_count;
198         short *rates;
199
200         rates = pXRRRates( gdi_display, DefaultScreen(gdi_display), i, &rates_count );
201         TRACE("- at %d: %dx%d (%d rates):", i, sizes[i].width, sizes[i].height, rates_count);
202         if (rates_count)
203         {
204             nmodes += rates_count;
205             for (j = 0; j < rates_count; ++j)
206             {
207                 if (j > 0)
208                     TRACE(",");
209                 TRACE("  %d", rates[j]);
210             }
211         }
212         else
213         {
214             ++nmodes;
215             TRACE(" <default>");
216         }
217         TRACE(" Hz\n");
218     }
219
220     TRACE("XRandR modes: count=%d\n", nmodes);
221
222     if (!(xrandr10_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr10_modes) * nmodes )))
223     {
224         ERR("Failed to allocate xrandr mode info array.\n");
225         return;
226     }
227
228     dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.0",
229                                             xrandr10_get_current_mode,
230                                             xrandr10_set_current_mode,
231                                             nmodes, 1 );
232
233     xrandr_mode_count = 0;
234     for (i = 0; i < sizes_count; ++i)
235     {
236         int rates_count;
237         short *rates;
238
239         rates = pXRRRates( gdi_display, DefaultScreen(gdi_display), i, &rates_count );
240
241         if (rates_count)
242         {
243             for (j = 0; j < rates_count; ++j)
244             {
245                 X11DRV_Settings_AddOneMode( sizes[i].width, sizes[i].height, 0, rates[j] );
246                 xrandr10_modes[xrandr_mode_count++] = i;
247             }
248         }
249         else
250         {
251             X11DRV_Settings_AddOneMode( sizes[i].width, sizes[i].height, 0, 0 );
252             xrandr10_modes[xrandr_mode_count++] = i;
253         }
254     }
255
256     X11DRV_Settings_AddDepthModes();
257     nmodes = X11DRV_Settings_GetModeCount();
258
259     TRACE("Available DD modes: count=%d\n", nmodes);
260     TRACE("Enabling XRandR\n");
261 }
262
263 #ifdef HAVE_XRRGETSCREENRESOURCES
264
265 static int xrandr12_get_current_mode(void)
266 {
267     XRRScreenResources *resources;
268     XRRCrtcInfo *crtc_info;
269     int i, ret = -1;
270
271     if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
272     {
273         ERR("Failed to get screen resources.\n");
274         return 0;
275     }
276
277     if (resources->ncrtc <= primary_crtc ||
278         !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[primary_crtc] )))
279     {
280         pXRRFreeScreenResources( resources );
281         ERR("Failed to get CRTC info.\n");
282         return 0;
283     }
284
285     TRACE("CRTC %d: mode %#lx, %ux%u+%d+%d.\n", primary_crtc, crtc_info->mode,
286           crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
287
288     for (i = 0; i < xrandr_mode_count; ++i)
289     {
290         if (xrandr12_modes[i] == crtc_info->mode)
291         {
292             ret = i;
293             break;
294         }
295     }
296
297     pXRRFreeCrtcInfo( crtc_info );
298     pXRRFreeScreenResources( resources );
299
300     if (ret == -1)
301     {
302         ERR("Unknown mode, returning default.\n");
303         ret = 0;
304     }
305
306     return ret;
307 }
308
309 static LONG xrandr12_set_current_mode( int mode )
310 {
311     Status status = RRSetConfigFailed;
312     XRRScreenResources *resources;
313     XRRCrtcInfo *crtc_info;
314
315     mode = mode % xrandr_mode_count;
316
317     if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
318     {
319         ERR("Failed to get screen resources.\n");
320         return DISP_CHANGE_FAILED;
321     }
322
323     if (resources->ncrtc <= primary_crtc ||
324         !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[primary_crtc] )))
325     {
326         pXRRFreeScreenResources( resources );
327         ERR("Failed to get CRTC info.\n");
328         return DISP_CHANGE_FAILED;
329     }
330
331     TRACE("CRTC %d: mode %#lx, %ux%u+%d+%d.\n", primary_crtc, crtc_info->mode,
332           crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
333
334     status = pXRRSetCrtcConfig( gdi_display, resources, resources->crtcs[primary_crtc],
335                                 CurrentTime, crtc_info->x, crtc_info->y, xrandr12_modes[mode],
336                                 crtc_info->rotation, crtc_info->outputs, crtc_info->noutput );
337
338     pXRRFreeCrtcInfo( crtc_info );
339     pXRRFreeScreenResources( resources );
340
341     if (status != RRSetConfigSuccess)
342     {
343         ERR("Resolution change not successful -- perhaps display has changed?\n");
344         return DISP_CHANGE_FAILED;
345     }
346
347     X11DRV_resize_desktop( dd_modes[mode].width, dd_modes[mode].height );
348     return DISP_CHANGE_SUCCESSFUL;
349 }
350
351 static XRRCrtcInfo *xrandr12_get_primary_crtc_info( XRRScreenResources *resources, int *crtc_idx )
352 {
353     XRRCrtcInfo *crtc_info;
354     int i;
355
356     for (i = 0; i < resources->ncrtc; ++i)
357     {
358         crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[i] );
359         if (!crtc_info || crtc_info->mode == None)
360         {
361             pXRRFreeCrtcInfo( crtc_info );
362             continue;
363         }
364
365         *crtc_idx = i;
366         return crtc_info;
367     }
368
369     return NULL;
370 }
371
372 static int xrandr12_init_modes(void)
373 {
374     XRRScreenResources *resources;
375     XRROutputInfo *output_info;
376     XRRCrtcInfo *crtc_info;
377     int ret = -1;
378     int i, j;
379
380     if (!(resources = pXRRGetScreenResourcesCurrent( gdi_display, root_window )))
381     {
382         ERR("Failed to get screen resources.\n");
383         return ret;
384     }
385
386     if (!resources->ncrtc)
387     {
388         pXRRFreeScreenResources( resources );
389         if (!(resources = pXRRGetScreenResources( gdi_display, root_window )))
390         {
391             ERR("Failed to get screen resources.\n");
392             return ret;
393         }
394     }
395
396     if (!(crtc_info = xrandr12_get_primary_crtc_info( resources, &primary_crtc )))
397     {
398         pXRRFreeScreenResources( resources );
399         ERR("Failed to get primary CRTC info.\n");
400         return ret;
401     }
402
403     TRACE("CRTC %d: mode %#lx, %ux%u+%d+%d.\n", primary_crtc, crtc_info->mode,
404           crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
405
406     if (!crtc_info->noutput || !(output_info = pXRRGetOutputInfo( gdi_display, resources, crtc_info->outputs[0] )))
407     {
408         pXRRFreeCrtcInfo( crtc_info );
409         pXRRFreeScreenResources( resources );
410         ERR("Failed to get output info.\n");
411         return ret;
412     }
413
414     TRACE("OUTPUT 0: name %s.\n", debugstr_a(output_info->name));
415
416     if (!output_info->nmode)
417     {
418         ERR("Output has no modes.\n");
419         goto done;
420     }
421
422     if (!(xrandr12_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr12_modes) * output_info->nmode )))
423     {
424         ERR("Failed to allocate xrandr mode info array.\n");
425         goto done;
426     }
427
428     dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.2",
429                                             xrandr12_get_current_mode,
430                                             xrandr12_set_current_mode,
431                                             output_info->nmode, 1 );
432
433     xrandr_mode_count = 0;
434     for (i = 0; i < output_info->nmode; ++i)
435     {
436         for (j = 0; j < resources->nmode; ++j)
437         {
438             XRRModeInfo *mode = &resources->modes[j];
439
440             if (mode->id == output_info->modes[i])
441             {
442                 unsigned int dots = mode->hTotal * mode->vTotal;
443                 unsigned int refresh = dots ? (mode->dotClock + dots / 2) / dots : 0;
444
445                 TRACE("Adding mode %#lx: %ux%u@%u.\n", mode->id, mode->width, mode->height, refresh);
446                 X11DRV_Settings_AddOneMode( mode->width, mode->height, 0, refresh );
447                 xrandr12_modes[xrandr_mode_count++] = mode->id;
448                 break;
449             }
450         }
451     }
452
453     X11DRV_Settings_AddDepthModes();
454     ret = 0;
455
456 done:
457     pXRRFreeOutputInfo( output_info );
458     pXRRFreeCrtcInfo( crtc_info );
459     pXRRFreeScreenResources( resources );
460     return ret;
461 }
462
463 #endif /* HAVE_XRRGETSCREENRESOURCES */
464
465 void X11DRV_XRandR_Init(void)
466 {
467     int event_base, error_base, minor, ret;
468     static int major;
469     Bool ok;
470
471     if (major) return; /* already initialized? */
472     if (!usexrandr) return; /* disabled in config */
473     if (root_window != DefaultRootWindow( gdi_display )) return;
474     if (!(ret = load_xrandr())) return;  /* can't load the Xrandr library */
475
476     /* see if Xrandr is available */
477     if (!pXRRQueryExtension( gdi_display, &event_base, &error_base )) return;
478     X11DRV_expect_error( gdi_display, XRandRErrorHandler, NULL );
479     ok = pXRRQueryVersion( gdi_display, &major, &minor );
480     if (X11DRV_check_error() || !ok) return;
481
482     TRACE("Found XRandR %d.%d.\n", major, minor);
483
484 #ifdef HAVE_XRRGETSCREENRESOURCES
485     if (ret >= 2 && (major > 1 || (major == 1 && minor >= 2)))
486     {
487         if (major > 1 || (major == 1 && minor >= 3))
488             pXRRGetScreenResourcesCurrent = wine_dlsym( xrandr_handle, "XRRGetScreenResourcesCurrent", NULL, 0 );
489         if (!pXRRGetScreenResourcesCurrent)
490             pXRRGetScreenResourcesCurrent = pXRRGetScreenResources;
491     }
492
493     if (!pXRRGetScreenResourcesCurrent || xrandr12_init_modes() < 0)
494 #endif
495         xrandr10_init_modes();
496 }
497
498 #else /* SONAME_LIBXRANDR */
499
500 void X11DRV_XRandR_Init(void)
501 {
502     TRACE("XRandR support not compiled in.\n");
503 }
504
505 #endif /* SONAME_LIBXRANDR */