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
30 #ifdef HAVE_X11_EXTENSIONS_XINERAMA_H
31 #include <X11/extensions/Xinerama.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)
71 if (!(handle
= dlopen(SONAME_LIBXINERAMA
, RTLD_NOW
)))
73 WARN( "failed to open %s\n", SONAME_LIBXINERAMA
);
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 */
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
) )))
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
;
117 #else /* SONAME_LIBXINERAMA */
119 static inline int query_screens(void)
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
;
134 pthread_mutex_lock( &xinerama_mutex
);
135 if (nb_monitors
== 1)
137 memset( indices
, 0, sizeof(*indices
) * 4 );
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 */
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
);
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
)
172 if (indices
[1] == -1 || monitors
[i
].rcMonitor
.bottom
> monitors
[indices
[1]].rcMonitor
.bottom
)
174 if (indices
[2] == -1 || monitors
[i
].rcMonitor
.left
< monitors
[indices
[2]].rcMonitor
.left
)
176 if (indices
[3] == -1 || monitors
[i
].rcMonitor
.right
> monitors
[indices
[3]].rcMonitor
.right
)
181 if (indices
[0] == -1 || indices
[1] == -1 || indices
[2] == -1 || indices
[3] == -1)
182 ERR("Failed to get xinerama fullscreen monitor indices.\n");
187 pthread_mutex_unlock( &xinerama_mutex
);
189 TRACE( "fullscreen monitors: %ld,%ld,%ld,%ld.\n", indices
[0], indices
[1], indices
[2], indices
[3] );
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
) );
203 lstrcpyW( gpus
[0].name
, wine_adapterW
);
211 static void xinerama_free_gpus( struct gdi_gpu
*gpus
)
216 static BOOL
xinerama_get_adapters( ULONG_PTR gpu_id
, struct gdi_adapter
**new_adapters
, int *count
)
218 struct gdi_adapter
*adapters
= NULL
;
227 /* Being lazy, actual adapter count may be less */
228 pthread_mutex_lock( &xinerama_mutex
);
229 adapters
= calloc( nb_monitors
, sizeof(*adapters
) );
232 pthread_mutex_unlock( &xinerama_mutex
);
236 primary_index
= primary_monitor
;
237 if (primary_index
>= nb_monitors
)
240 for (i
= 0; i
< nb_monitors
; i
++)
243 for (j
= 0; j
< i
; j
++)
245 if (EqualRect( &monitors
[i
].rcMonitor
, &monitors
[j
].rcMonitor
) && !IsRectEmpty( &monitors
[j
].rcMonitor
))
252 /* Mirrored monitors share the same adapter */
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
;
268 /* Primary adapter has to be first */
271 struct gdi_adapter tmp
;
272 tmp
= adapters
[primary_index
];
273 adapters
[primary_index
] = adapters
[0];
277 *new_adapters
= adapters
;
279 pthread_mutex_unlock( &xinerama_mutex
);
283 static void xinerama_free_adapters( struct gdi_adapter
*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;
296 pthread_mutex_lock( &xinerama_mutex
);
298 for (i
= first
; i
< nb_monitors
; i
++)
301 || (EqualRect( &monitors
[i
].rcMonitor
, &monitors
[first
].rcMonitor
)
302 && !IsRectEmpty( &monitors
[first
].rcMonitor
)))
306 monitor
= calloc( monitor_count
, sizeof(*monitor
) );
309 pthread_mutex_unlock( &xinerama_mutex
);
313 for (i
= first
; i
< nb_monitors
; i
++)
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
;
332 *new_monitors
= monitor
;
333 *count
= monitor_count
;
334 pthread_mutex_unlock( &xinerama_mutex
);
338 static void xinerama_free_monitors( struct gdi_monitor
*monitors
, int count
)
343 void xinerama_init( unsigned int width
, unsigned int height
)
345 struct x11drv_display_device_handler handler
;
346 MONITORINFOEXW
*primary
;
350 if (is_virtual_desktop())
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
);
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
);