mshtml: Default to previous script type if not given.
[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(XRRSetCrtcConfig)
57 static typeof(XRRGetScreenResources) *xrandr_get_screen_resources;
58 static RRMode *xrandr12_modes;
59 #endif
60
61 #undef MAKE_FUNCPTR
62
63 extern int usexrandr;
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(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     Window root;
120     XRRScreenConfiguration *sc;
121     short rate;
122     unsigned int i;
123     int res = -1;
124     
125     wine_tsx11_lock();
126     root = RootWindow (gdi_display, DefaultScreen(gdi_display));
127     sc = pXRRGetScreenInfo (gdi_display, root);
128     size = pXRRConfigCurrentConfiguration (sc, &rot);
129     rate = pXRRConfigCurrentRate (sc);
130     pXRRFreeScreenConfigInfo(sc);
131     wine_tsx11_unlock();
132     for (i = 0; i < xrandr_mode_count; ++i)
133     {
134         if (xrandr10_modes[i] == size && dd_modes[i].refresh_rate == rate)
135         {
136             res = i;
137             break;
138         }
139     }
140     if (res == -1)
141     {
142         ERR("In unknown mode, returning default\n");
143         res = 0;
144     }
145     return res;
146 }
147
148 static LONG xrandr10_set_current_mode( int mode )
149 {
150     SizeID size;
151     Rotation rot;
152     Window root;
153     XRRScreenConfiguration *sc;
154     Status stat;
155     short rate;
156
157     wine_tsx11_lock();
158     root = RootWindow (gdi_display, DefaultScreen(gdi_display));
159     sc = pXRRGetScreenInfo (gdi_display, root);
160     size = pXRRConfigCurrentConfiguration (sc, &rot);
161     mode = mode % xrandr_mode_count;
162
163     TRACE("Changing Resolution to %dx%d @%d Hz\n",
164           dd_modes[mode].width,
165           dd_modes[mode].height,
166           dd_modes[mode].refresh_rate);
167
168     size = xrandr10_modes[mode];
169     rate = dd_modes[mode].refresh_rate;
170
171     if (rate)
172         stat = pXRRSetScreenConfigAndRate( gdi_display, sc, root, size, rot, rate, CurrentTime );
173     else
174         stat = pXRRSetScreenConfig( gdi_display, sc, root, size, rot, CurrentTime );
175
176     pXRRFreeScreenConfigInfo(sc);
177     wine_tsx11_unlock();
178     if (stat == RRSetConfigSuccess)
179     {
180         X11DRV_resize_desktop( dd_modes[mode].width, dd_modes[mode].height );
181         return DISP_CHANGE_SUCCESSFUL;
182     }
183
184     ERR("Resolution change not successful -- perhaps display has changed?\n");
185     return DISP_CHANGE_FAILED;
186 }
187
188 static void xrandr10_init_modes(void)
189 {
190     XRRScreenSize *sizes;
191     int sizes_count;
192     int i, j, nmodes = 0;
193
194     sizes = pXRRSizes( gdi_display, DefaultScreen(gdi_display), &sizes_count );
195     if (sizes_count <= 0) return;
196
197     TRACE("XRandR: found %d sizes.\n", sizes_count);
198     for (i = 0; i < sizes_count; ++i)
199     {
200         int rates_count;
201         short *rates;
202
203         rates = pXRRRates( gdi_display, DefaultScreen(gdi_display), i, &rates_count );
204         TRACE("- at %d: %dx%d (%d rates):", i, sizes[i].width, sizes[i].height, rates_count);
205         if (rates_count)
206         {
207             nmodes += rates_count;
208             for (j = 0; j < rates_count; ++j)
209             {
210                 if (j > 0)
211                     TRACE(",");
212                 TRACE("  %d", rates[j]);
213             }
214         }
215         else
216         {
217             ++nmodes;
218             TRACE(" <default>");
219         }
220         TRACE(" Hz\n");
221     }
222
223     TRACE("XRandR modes: count=%d\n", nmodes);
224
225     if (!(xrandr10_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr10_modes) * nmodes )))
226     {
227         ERR("Failed to allocate xrandr mode info array.\n");
228         return;
229     }
230
231     dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.0",
232                                             xrandr10_get_current_mode,
233                                             xrandr10_set_current_mode,
234                                             nmodes, 1 );
235
236     xrandr_mode_count = 0;
237     for (i = 0; i < sizes_count; ++i)
238     {
239         int rates_count;
240         short *rates;
241
242         rates = pXRRRates( gdi_display, DefaultScreen(gdi_display), i, &rates_count );
243
244         if (rates_count)
245         {
246             for (j = 0; j < rates_count; ++j)
247             {
248                 X11DRV_Settings_AddOneMode( sizes[i].width, sizes[i].height, 0, rates[j] );
249                 xrandr10_modes[xrandr_mode_count++] = i;
250             }
251         }
252         else
253         {
254             X11DRV_Settings_AddOneMode( sizes[i].width, sizes[i].height, 0, 0 );
255             xrandr10_modes[xrandr_mode_count++] = i;
256         }
257     }
258
259     X11DRV_Settings_AddDepthModes();
260     nmodes = X11DRV_Settings_GetModeCount();
261
262     TRACE("Available DD modes: count=%d\n", nmodes);
263     TRACE("Enabling XRandR\n");
264 }
265
266 #ifdef HAVE_XRRGETSCREENRESOURCES
267
268 static int xrandr12_get_current_mode(void)
269 {
270     XRRScreenResources *resources;
271     XRRCrtcInfo *crtc_info;
272     int i, ret = -1;
273
274     wine_tsx11_lock();
275     if (!(resources = xrandr_get_screen_resources( gdi_display, root_window )))
276     {
277         wine_tsx11_unlock();
278         ERR("Failed to get screen resources.\n");
279         return 0;
280     }
281
282     if (!resources->ncrtc || !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[0] )))
283     {
284         pXRRFreeScreenResources( resources );
285         wine_tsx11_unlock();
286         ERR("Failed to get CRTC info.\n");
287         return 0;
288     }
289
290     TRACE("CRTC 0: mode %#lx, %ux%u+%d+%d.\n", crtc_info->mode,
291           crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
292
293     for (i = 0; i < xrandr_mode_count; ++i)
294     {
295         if (xrandr12_modes[i] == crtc_info->mode)
296         {
297             ret = i;
298             break;
299         }
300     }
301
302     pXRRFreeCrtcInfo( crtc_info );
303     pXRRFreeScreenResources( resources );
304     wine_tsx11_unlock();
305
306     if (ret == -1)
307     {
308         ERR("Unknown mode, returning default.\n");
309         ret = 0;
310     }
311
312     return ret;
313 }
314
315 static LONG xrandr12_set_current_mode( int mode )
316 {
317     Status status = RRSetConfigFailed;
318     XRRScreenResources *resources;
319     XRRCrtcInfo *crtc_info;
320
321     mode = mode % xrandr_mode_count;
322
323     wine_tsx11_lock();
324     if (!(resources = xrandr_get_screen_resources( gdi_display, root_window )))
325     {
326         wine_tsx11_unlock();
327         ERR("Failed to get screen resources.\n");
328         return DISP_CHANGE_FAILED;
329     }
330
331     if (!resources->ncrtc || !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[0] )))
332     {
333         pXRRFreeScreenResources( resources );
334         wine_tsx11_unlock();
335         ERR("Failed to get CRTC info.\n");
336         return DISP_CHANGE_FAILED;
337     }
338
339     TRACE("CRTC 0: mode %#lx, %ux%u+%d+%d.\n", crtc_info->mode,
340           crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
341
342     status = pXRRSetCrtcConfig( gdi_display, resources, resources->crtcs[0], CurrentTime, crtc_info->x,
343             crtc_info->y, xrandr12_modes[mode], crtc_info->rotation, crtc_info->outputs, crtc_info->noutput );
344
345     pXRRFreeCrtcInfo( crtc_info );
346     pXRRFreeScreenResources( resources );
347     wine_tsx11_unlock();
348
349     if (status != RRSetConfigSuccess)
350     {
351         ERR("Resolution change not successful -- perhaps display has changed?\n");
352         return DISP_CHANGE_FAILED;
353     }
354
355     X11DRV_resize_desktop( dd_modes[mode].width, dd_modes[mode].height );
356     return DISP_CHANGE_SUCCESSFUL;
357 }
358
359 static void xrandr12_init_modes(void)
360 {
361     XRRScreenResources *resources;
362     XRROutputInfo *output_info;
363     XRRCrtcInfo *crtc_info;
364     int i, j;
365
366     if (!(resources = xrandr_get_screen_resources( gdi_display, root_window )))
367     {
368         ERR("Failed to get screen resources.\n");
369         return;
370     }
371
372     if (!resources->ncrtc || !(crtc_info = pXRRGetCrtcInfo( gdi_display, resources, resources->crtcs[0] )))
373     {
374         pXRRFreeScreenResources( resources );
375         ERR("Failed to get CRTC info.\n");
376         return;
377     }
378
379     TRACE("CRTC 0: mode %#lx, %ux%u+%d+%d.\n", crtc_info->mode,
380           crtc_info->width, crtc_info->height, crtc_info->x, crtc_info->y);
381
382     if (!crtc_info->noutput || !(output_info = pXRRGetOutputInfo( gdi_display, resources, crtc_info->outputs[0] )))
383     {
384         pXRRFreeCrtcInfo( crtc_info );
385         pXRRFreeScreenResources( resources );
386         ERR("Failed to get output info.\n");
387         return;
388     }
389
390     TRACE("OUTPUT 0: name %s.\n", debugstr_a(output_info->name));
391
392     if (!output_info->nmode)
393     {
394         ERR("Output has no modes.\n");
395         goto done;
396     }
397
398     if (!(xrandr12_modes = HeapAlloc( GetProcessHeap(), 0, sizeof(*xrandr12_modes) * output_info->nmode )))
399     {
400         ERR("Failed to allocate xrandr mode info array.\n");
401         goto done;
402     }
403
404     dd_modes = X11DRV_Settings_SetHandlers( "XRandR 1.2",
405                                             xrandr12_get_current_mode,
406                                             xrandr12_set_current_mode,
407                                             output_info->nmode, 1 );
408
409     xrandr_mode_count = 0;
410     for (i = 0; i < output_info->nmode; ++i)
411     {
412         for (j = 0; j < resources->nmode; ++j)
413         {
414             XRRModeInfo *mode = &resources->modes[j];
415
416             if (mode->id == output_info->modes[i])
417             {
418                 unsigned int dots = mode->hTotal * mode->vTotal;
419                 unsigned int refresh = dots ? (mode->dotClock + dots / 2) / dots : 0;
420
421                 TRACE("Adding mode %#lx: %ux%u@%u.\n", mode->id, mode->width, mode->height, refresh);
422                 X11DRV_Settings_AddOneMode( mode->width, mode->height, 0, refresh );
423                 xrandr12_modes[xrandr_mode_count++] = mode->id;
424                 break;
425             }
426         }
427     }
428
429     X11DRV_Settings_AddDepthModes();
430
431 done:
432     pXRRFreeOutputInfo( output_info );
433     pXRRFreeCrtcInfo( crtc_info );
434     pXRRFreeScreenResources( resources );
435 }
436
437 #endif /* HAVE_XRRGETSCREENRESOURCES */
438
439 void X11DRV_XRandR_Init(void)
440 {
441     int event_base, error_base, minor, ret;
442     static int major;
443     Bool ok;
444
445     if (major) return; /* already initialized? */
446     if (!usexrandr) return; /* disabled in config */
447     if (root_window != DefaultRootWindow( gdi_display )) return;
448     if (!(ret = load_xrandr())) return;  /* can't load the Xrandr library */
449
450     /* see if Xrandr is available */
451     wine_tsx11_lock();
452     if (!pXRRQueryExtension( gdi_display, &event_base, &error_base )) goto done;
453     X11DRV_expect_error( gdi_display, XRandRErrorHandler, NULL );
454     ok = pXRRQueryVersion( gdi_display, &major, &minor );
455     if (X11DRV_check_error() || !ok) goto done;
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             xrandr_get_screen_resources = wine_dlsym( xrandr_handle, "XRRGetScreenResourcesCurrent", NULL, 0 );
464         if (!xrandr_get_screen_resources)
465             xrandr_get_screen_resources = wine_dlsym( xrandr_handle, "XRRGetScreenResources", NULL, 0 );
466     }
467
468     if (xrandr_get_screen_resources)
469         xrandr12_init_modes();
470     else
471 #endif
472         xrandr10_init_modes();
473
474 done:
475     wine_tsx11_unlock();
476 }
477
478 #else /* SONAME_LIBXRANDR */
479
480 void X11DRV_XRandR_Init(void)
481 {
482     TRACE("XRandR support not compiled in.\n");
483 }
484
485 #endif /* SONAME_LIBXRANDR */