wow32: Use spec file imports.
[wine.git] / dlls / winex11.drv / xinerama.c
bloba508141d8c4a38f23307c8f26b1cdfbaa618e272
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 #if 0
22 #pragma makedep unix
23 #endif
25 #include "config.h"
27 #include <stdarg.h>
28 #include <stdlib.h>
29 #include <X11/Xlib.h>
30 #ifdef HAVE_X11_EXTENSIONS_XINERAMA_H
31 #include <X11/extensions/Xinerama.h>
32 #endif
33 #include <dlfcn.h>
34 #include "x11drv.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(x11drv);
39 static MONITORINFOEXW default_monitor =
41 sizeof(default_monitor), /* cbSize */
42 { 0, 0, 0, 0 }, /* rcMonitor */
43 { 0, 0, 0, 0 }, /* rcWork */
44 MONITORINFOF_PRIMARY, /* dwFlags */
45 { '\\','\\','.','\\','D','I','S','P','L','A','Y','1',0 } /* szDevice */
48 static pthread_mutex_t xinerama_mutex = PTHREAD_MUTEX_INITIALIZER;
49 static MONITORINFOEXW *monitors;
50 static int nb_monitors;
52 static inline MONITORINFOEXW *get_primary(void)
54 /* default to 0 if specified primary is invalid */
55 int idx = primary_monitor;
56 if (idx >= nb_monitors) idx = 0;
57 return &monitors[idx];
60 #ifdef SONAME_LIBXINERAMA
62 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
64 MAKE_FUNCPTR(XineramaQueryExtension);
65 MAKE_FUNCPTR(XineramaQueryScreens);
67 static void load_xinerama(void)
69 void *handle;
71 if (!(handle = dlopen(SONAME_LIBXINERAMA, RTLD_NOW)))
73 WARN( "failed to open %s\n", SONAME_LIBXINERAMA );
74 return;
76 pXineramaQueryExtension = dlsym( handle, "XineramaQueryExtension" );
77 if (!pXineramaQueryExtension) WARN( "XineramaQueryScreens not found\n" );
78 pXineramaQueryScreens = dlsym( handle, "XineramaQueryScreens" );
79 if (!pXineramaQueryScreens) WARN( "XineramaQueryScreens not found\n" );
82 static int query_screens(void)
84 int i, count, event_base, error_base;
85 XineramaScreenInfo *screens;
87 if (!monitors) /* first time around */
88 load_xinerama();
90 if (!pXineramaQueryExtension || !pXineramaQueryScreens ||
91 !pXineramaQueryExtension( gdi_display, &event_base, &error_base ) ||
92 !(screens = pXineramaQueryScreens( gdi_display, &count ))) return 0;
94 if (monitors != &default_monitor) free( monitors );
95 if ((monitors = malloc( count * sizeof(*monitors) )))
97 nb_monitors = count;
98 for (i = 0; i < nb_monitors; i++)
100 monitors[i].cbSize = sizeof( monitors[i] );
101 monitors[i].rcMonitor.left = screens[i].x_org;
102 monitors[i].rcMonitor.top = screens[i].y_org;
103 monitors[i].rcMonitor.right = screens[i].x_org + screens[i].width;
104 monitors[i].rcMonitor.bottom = screens[i].y_org + screens[i].height;
105 monitors[i].dwFlags = 0;
106 monitors[i].rcWork = get_work_area( &monitors[i].rcMonitor );
109 get_primary()->dwFlags |= MONITORINFOF_PRIMARY;
111 else count = 0;
113 XFree( screens );
114 return count;
117 #else /* SONAME_LIBXINERAMA */
119 static inline int query_screens(void)
121 return 0;
124 #endif /* SONAME_LIBXINERAMA */
126 /* Get xinerama monitor indices required for _NET_WM_FULLSCREEN_MONITORS */
127 BOOL xinerama_get_fullscreen_monitors( const RECT *rect, long *indices )
129 RECT window_rect, intersected_rect, monitor_rect;
130 BOOL ret = FALSE;
131 POINT offset;
132 INT i;
134 pthread_mutex_lock( &xinerama_mutex );
135 if (nb_monitors == 1)
137 memset( indices, 0, sizeof(*indices) * 4 );
138 ret = TRUE;
139 goto done;
142 /* Convert window rectangle to root coordinates */
143 offset = virtual_screen_to_root( rect->left, rect->top );
144 window_rect.left = offset.x;
145 window_rect.top = offset.y;
146 window_rect.right = window_rect.left + rect->right - rect->left;
147 window_rect.bottom = window_rect.top + rect->bottom - rect->top;
149 /* Compare to xinerama monitor rectangles in root coordinates */
150 offset.x = INT_MAX;
151 offset.y = INT_MAX;
152 for (i = 0; i < nb_monitors; ++i)
154 offset.x = min( offset.x, monitors[i].rcMonitor.left );
155 offset.y = min( offset.y, monitors[i].rcMonitor.top );
158 indices[0] = -1;
159 indices[1] = -1;
160 indices[2] = -1;
161 indices[3] = -1;
162 for (i = 0; i < nb_monitors; ++i)
164 SetRect( &monitor_rect, monitors[i].rcMonitor.left - offset.x,
165 monitors[i].rcMonitor.top - offset.y, monitors[i].rcMonitor.right - offset.x,
166 monitors[i].rcMonitor.bottom - offset.y );
167 intersect_rect( &intersected_rect, &window_rect, &monitor_rect );
168 if (EqualRect( &intersected_rect, &monitor_rect ))
170 if (indices[0] == -1 || monitors[i].rcMonitor.top < monitors[indices[0]].rcMonitor.top)
171 indices[0] = i;
172 if (indices[1] == -1 || monitors[i].rcMonitor.bottom > monitors[indices[1]].rcMonitor.bottom)
173 indices[1] = i;
174 if (indices[2] == -1 || monitors[i].rcMonitor.left < monitors[indices[2]].rcMonitor.left)
175 indices[2] = i;
176 if (indices[3] == -1 || monitors[i].rcMonitor.right > monitors[indices[3]].rcMonitor.right)
177 indices[3] = i;
181 if (indices[0] == -1 || indices[1] == -1 || indices[2] == -1 || indices[3] == -1)
182 ERR("Failed to get xinerama fullscreen monitor indices.\n");
183 else
184 ret = TRUE;
186 done:
187 pthread_mutex_unlock( &xinerama_mutex );
188 if (ret)
189 TRACE( "fullscreen monitors: %ld,%ld,%ld,%ld.\n", indices[0], indices[1], indices[2], indices[3] );
190 return ret;
193 static BOOL xinerama_get_gpus( struct gdi_gpu **new_gpus, int *count )
195 static const WCHAR wine_adapterW[] = {'W','i','n','e',' ','A','d','a','p','t','e','r',0};
196 struct gdi_gpu *gpus;
198 /* Xinerama has no support for GPU, faking one */
199 gpus = calloc( 1, sizeof(*gpus) );
200 if (!gpus)
201 return FALSE;
203 lstrcpyW( gpus[0].name, wine_adapterW );
205 *new_gpus = gpus;
206 *count = 1;
208 return TRUE;
211 static void xinerama_free_gpus( struct gdi_gpu *gpus )
213 free( gpus );
216 static BOOL xinerama_get_adapters( ULONG_PTR gpu_id, struct gdi_adapter **new_adapters, int *count )
218 struct gdi_adapter *adapters = NULL;
219 INT index = 0;
220 INT i, j;
221 INT primary_index;
222 BOOL mirrored;
224 if (gpu_id)
225 return FALSE;
227 /* Being lazy, actual adapter count may be less */
228 pthread_mutex_lock( &xinerama_mutex );
229 adapters = calloc( nb_monitors, sizeof(*adapters) );
230 if (!adapters)
232 pthread_mutex_unlock( &xinerama_mutex );
233 return FALSE;
236 primary_index = primary_monitor;
237 if (primary_index >= nb_monitors)
238 primary_index = 0;
240 for (i = 0; i < nb_monitors; i++)
242 mirrored = FALSE;
243 for (j = 0; j < i; j++)
245 if (EqualRect( &monitors[i].rcMonitor, &monitors[j].rcMonitor) && !IsRectEmpty( &monitors[j].rcMonitor ))
247 mirrored = TRUE;
248 break;
252 /* Mirrored monitors share the same adapter */
253 if (mirrored)
254 continue;
256 /* Use monitor index as id */
257 adapters[index].id = (ULONG_PTR)i;
259 if (i == primary_index)
260 adapters[index].state_flags |= DISPLAY_DEVICE_PRIMARY_DEVICE;
262 if (!IsRectEmpty( &monitors[i].rcMonitor ))
263 adapters[index].state_flags |= DISPLAY_DEVICE_ATTACHED_TO_DESKTOP;
265 index++;
268 /* Primary adapter has to be first */
269 if (primary_index)
271 struct gdi_adapter tmp;
272 tmp = adapters[primary_index];
273 adapters[primary_index] = adapters[0];
274 adapters[0] = tmp;
277 *new_adapters = adapters;
278 *count = index;
279 pthread_mutex_unlock( &xinerama_mutex );
280 return TRUE;
283 static void xinerama_free_adapters( struct gdi_adapter *adapters )
285 free( adapters );
288 static BOOL xinerama_get_monitors( ULONG_PTR adapter_id, struct gdi_monitor **new_monitors, int *count )
290 struct gdi_monitor *monitor;
291 INT first = (INT)adapter_id;
292 INT monitor_count = 0;
293 INT index = 0;
294 INT i;
296 pthread_mutex_lock( &xinerama_mutex );
298 for (i = first; i < nb_monitors; i++)
300 if (i == first
301 || (EqualRect( &monitors[i].rcMonitor, &monitors[first].rcMonitor )
302 && !IsRectEmpty( &monitors[first].rcMonitor )))
303 monitor_count++;
306 monitor = calloc( monitor_count, sizeof(*monitor) );
307 if (!monitor)
309 pthread_mutex_unlock( &xinerama_mutex );
310 return FALSE;
313 for (i = first; i < nb_monitors; i++)
315 if (i == first
316 || (EqualRect( &monitors[i].rcMonitor, &monitors[first].rcMonitor )
317 && !IsRectEmpty( &monitors[first].rcMonitor )))
319 monitor[index].rc_monitor = monitors[i].rcMonitor;
320 monitor[index].rc_work = monitors[i].rcWork;
321 /* Xinerama only reports monitors already attached */
322 monitor[index].state_flags = DISPLAY_DEVICE_ATTACHED;
323 monitor[index].edid_len = 0;
324 monitor[index].edid = NULL;
325 if (!IsRectEmpty( &monitors[i].rcMonitor ))
326 monitor[index].state_flags |= DISPLAY_DEVICE_ACTIVE;
328 index++;
332 *new_monitors = monitor;
333 *count = monitor_count;
334 pthread_mutex_unlock( &xinerama_mutex );
335 return TRUE;
338 static void xinerama_free_monitors( struct gdi_monitor *monitors, int count )
340 free( monitors );
343 void xinerama_init( unsigned int width, unsigned int height )
345 struct x11drv_display_device_handler handler;
346 MONITORINFOEXW *primary;
347 int i;
348 RECT rect;
350 if (is_virtual_desktop())
351 return;
353 pthread_mutex_lock( &xinerama_mutex );
355 SetRect( &rect, 0, 0, width, height );
356 if (!query_screens())
358 default_monitor.rcMonitor = rect;
359 default_monitor.rcWork = get_work_area( &default_monitor.rcMonitor );
360 nb_monitors = 1;
361 monitors = &default_monitor;
364 primary = get_primary();
366 /* coordinates (0,0) have to point to the primary monitor origin */
367 OffsetRect( &rect, -primary->rcMonitor.left, -primary->rcMonitor.top );
368 for (i = 0; i < nb_monitors; i++)
370 OffsetRect( &monitors[i].rcMonitor, rect.left, rect.top );
371 OffsetRect( &monitors[i].rcWork, rect.left, rect.top );
372 TRACE( "monitor 0x%x: %s work %s%s\n",
373 i, wine_dbgstr_rect(&monitors[i].rcMonitor),
374 wine_dbgstr_rect(&monitors[i].rcWork),
375 (monitors[i].dwFlags & MONITORINFOF_PRIMARY) ? " (primary)" : "" );
378 pthread_mutex_unlock( &xinerama_mutex );
380 handler.name = "Xinerama";
381 handler.priority = 100;
382 handler.get_gpus = xinerama_get_gpus;
383 handler.get_adapters = xinerama_get_adapters;
384 handler.get_monitors = xinerama_get_monitors;
385 handler.free_gpus = xinerama_free_gpus;
386 handler.free_adapters = xinerama_free_adapters;
387 handler.free_monitors = xinerama_free_monitors;
388 handler.register_event_handlers = NULL;
389 X11DRV_DisplayDevices_SetHandler( &handler );