Release 6.15.
[wine.git] / dlls / winex11.drv / xinerama.c
blobc9863482534598ce0584d2e6bb7b9bc1a794e655
1 /*
2 * Xinerama support
4 * Copyright 2006 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "config.h"
22 #include "wine/port.h"
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <X11/Xlib.h>
27 #ifdef HAVE_X11_EXTENSIONS_XINERAMA_H
28 #include <X11/extensions/Xinerama.h>
29 #endif
30 #include "x11drv.h"
31 #include "wine/debug.h"
32 #include "wine/heap.h"
33 #include "wine/unicode.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
37 static MONITORINFOEXW default_monitor =
39 sizeof(default_monitor), /* cbSize */
40 { 0, 0, 0, 0 }, /* rcMonitor */
41 { 0, 0, 0, 0 }, /* rcWork */
42 MONITORINFOF_PRIMARY, /* dwFlags */
43 { '\\','\\','.','\\','D','I','S','P','L','A','Y','1',0 } /* szDevice */
46 static MONITORINFOEXW *monitors;
47 static int nb_monitors;
49 static inline MONITORINFOEXW *get_primary(void)
51 /* default to 0 if specified primary is invalid */
52 int idx = primary_monitor;
53 if (idx >= nb_monitors) idx = 0;
54 return &monitors[idx];
57 #ifdef SONAME_LIBXINERAMA
59 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
61 MAKE_FUNCPTR(XineramaQueryExtension);
62 MAKE_FUNCPTR(XineramaQueryScreens);
64 static void load_xinerama(void)
66 void *handle;
68 if (!(handle = dlopen(SONAME_LIBXINERAMA, RTLD_NOW)))
70 WARN( "failed to open %s\n", SONAME_LIBXINERAMA );
71 return;
73 pXineramaQueryExtension = dlsym( handle, "XineramaQueryExtension" );
74 if (!pXineramaQueryExtension) WARN( "XineramaQueryScreens not found\n" );
75 pXineramaQueryScreens = dlsym( handle, "XineramaQueryScreens" );
76 if (!pXineramaQueryScreens) WARN( "XineramaQueryScreens not found\n" );
79 static int query_screens(void)
81 int i, count, event_base, error_base;
82 XineramaScreenInfo *screens;
84 if (!monitors) /* first time around */
85 load_xinerama();
87 if (!pXineramaQueryExtension || !pXineramaQueryScreens ||
88 !pXineramaQueryExtension( gdi_display, &event_base, &error_base ) ||
89 !(screens = pXineramaQueryScreens( gdi_display, &count ))) return 0;
91 if (monitors != &default_monitor) HeapFree( GetProcessHeap(), 0, monitors );
92 if ((monitors = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*monitors) )))
94 nb_monitors = count;
95 for (i = 0; i < nb_monitors; i++)
97 monitors[i].cbSize = sizeof( monitors[i] );
98 monitors[i].rcMonitor.left = screens[i].x_org;
99 monitors[i].rcMonitor.top = screens[i].y_org;
100 monitors[i].rcMonitor.right = screens[i].x_org + screens[i].width;
101 monitors[i].rcMonitor.bottom = screens[i].y_org + screens[i].height;
102 monitors[i].dwFlags = 0;
103 monitors[i].rcWork = get_work_area( &monitors[i].rcMonitor );
106 get_primary()->dwFlags |= MONITORINFOF_PRIMARY;
108 else count = 0;
110 XFree( screens );
111 return count;
114 #else /* SONAME_LIBXINERAMA */
116 static inline int query_screens(void)
118 return 0;
121 #endif /* SONAME_LIBXINERAMA */
123 static BOOL xinerama_get_gpus( struct x11drv_gpu **new_gpus, int *count )
125 static const WCHAR wine_adapterW[] = {'W','i','n','e',' ','A','d','a','p','t','e','r',0};
126 struct x11drv_gpu *gpus;
128 /* Xinerama has no support for GPU, faking one */
129 gpus = heap_calloc( 1, sizeof(*gpus) );
130 if (!gpus)
131 return FALSE;
133 lstrcpyW( gpus[0].name, wine_adapterW );
135 *new_gpus = gpus;
136 *count = 1;
138 return TRUE;
141 static void xinerama_free_gpus( struct x11drv_gpu *gpus )
143 heap_free( gpus );
146 static BOOL xinerama_get_adapters( ULONG_PTR gpu_id, struct x11drv_adapter **new_adapters, int *count )
148 struct x11drv_adapter *adapters = NULL;
149 INT index = 0;
150 INT i, j;
151 INT primary_index;
152 BOOL mirrored;
154 if (gpu_id)
155 return FALSE;
157 /* Being lazy, actual adapter count may be less */
158 adapters = heap_calloc( nb_monitors, sizeof(*adapters) );
159 if (!adapters)
160 return FALSE;
162 primary_index = primary_monitor;
163 if (primary_index >= nb_monitors)
164 primary_index = 0;
166 for (i = 0; i < nb_monitors; i++)
168 mirrored = FALSE;
169 for (j = 0; j < i; j++)
171 if (EqualRect( &monitors[i].rcMonitor, &monitors[j].rcMonitor) && !IsRectEmpty( &monitors[j].rcMonitor ))
173 mirrored = TRUE;
174 break;
178 /* Mirrored monitors share the same adapter */
179 if (mirrored)
180 continue;
182 /* Use monitor index as id */
183 adapters[index].id = (ULONG_PTR)i;
185 if (i == primary_index)
186 adapters[index].state_flags |= DISPLAY_DEVICE_PRIMARY_DEVICE;
188 if (!IsRectEmpty( &monitors[i].rcMonitor ))
189 adapters[index].state_flags |= DISPLAY_DEVICE_ATTACHED_TO_DESKTOP;
191 index++;
194 /* Primary adapter has to be first */
195 if (primary_index)
197 struct x11drv_adapter tmp;
198 tmp = adapters[primary_index];
199 adapters[primary_index] = adapters[0];
200 adapters[0] = tmp;
203 *new_adapters = adapters;
204 *count = index;
205 return TRUE;
208 static void xinerama_free_adapters( struct x11drv_adapter *adapters )
210 heap_free( adapters );
213 static BOOL xinerama_get_monitors( ULONG_PTR adapter_id, struct x11drv_monitor **new_monitors, int *count )
215 static const WCHAR generic_nonpnp_monitorW[] = {
216 'G','e','n','e','r','i','c',' ',
217 'N','o','n','-','P','n','P',' ','M','o','n','i','t','o','r',0};
218 struct x11drv_monitor *monitor;
219 INT first = (INT)adapter_id;
220 INT monitor_count = 0;
221 INT index = 0;
222 INT i;
224 for (i = first; i < nb_monitors; i++)
226 if (i == first
227 || (EqualRect( &monitors[i].rcMonitor, &monitors[first].rcMonitor )
228 && !IsRectEmpty( &monitors[first].rcMonitor )))
229 monitor_count++;
232 monitor = heap_calloc( monitor_count, sizeof(*monitor) );
233 if (!monitor)
234 return FALSE;
236 for (i = first; i < nb_monitors; i++)
238 if (i == first
239 || (EqualRect( &monitors[i].rcMonitor, &monitors[first].rcMonitor )
240 && !IsRectEmpty( &monitors[first].rcMonitor )))
242 lstrcpyW( monitor[index].name, generic_nonpnp_monitorW );
243 monitor[index].rc_monitor = monitors[i].rcMonitor;
244 monitor[index].rc_work = monitors[i].rcWork;
245 /* Xinerama only reports monitors already attached */
246 monitor[index].state_flags = DISPLAY_DEVICE_ATTACHED;
247 if (!IsRectEmpty( &monitors[i].rcMonitor ))
248 monitor[index].state_flags |= DISPLAY_DEVICE_ACTIVE;
250 index++;
254 *new_monitors = monitor;
255 *count = monitor_count;
256 return TRUE;
259 static void xinerama_free_monitors( struct x11drv_monitor *monitors )
261 heap_free( monitors );
264 void xinerama_init( unsigned int width, unsigned int height )
266 struct x11drv_display_device_handler handler;
267 MONITORINFOEXW *primary;
268 int i;
269 RECT rect;
271 if (is_virtual_desktop())
272 return;
274 SetRect( &rect, 0, 0, width, height );
275 if (!query_screens())
277 default_monitor.rcMonitor = rect;
278 default_monitor.rcWork = get_work_area( &default_monitor.rcMonitor );
279 nb_monitors = 1;
280 monitors = &default_monitor;
283 primary = get_primary();
285 /* coordinates (0,0) have to point to the primary monitor origin */
286 OffsetRect( &rect, -primary->rcMonitor.left, -primary->rcMonitor.top );
287 for (i = 0; i < nb_monitors; i++)
289 OffsetRect( &monitors[i].rcMonitor, rect.left, rect.top );
290 OffsetRect( &monitors[i].rcWork, rect.left, rect.top );
291 TRACE( "monitor 0x%x: %s work %s%s\n",
292 i, wine_dbgstr_rect(&monitors[i].rcMonitor),
293 wine_dbgstr_rect(&monitors[i].rcWork),
294 (monitors[i].dwFlags & MONITORINFOF_PRIMARY) ? " (primary)" : "" );
297 handler.name = "Xinerama";
298 handler.priority = 100;
299 handler.get_gpus = xinerama_get_gpus;
300 handler.get_adapters = xinerama_get_adapters;
301 handler.get_monitors = xinerama_get_monitors;
302 handler.free_gpus = xinerama_free_gpus;
303 handler.free_adapters = xinerama_free_adapters;
304 handler.free_monitors = xinerama_free_monitors;
305 handler.register_event_handlers = NULL;
306 X11DRV_DisplayDevices_SetHandler( &handler );