blob: 9873634ea1ab30cd005297b8103ceda886416025 [file] [log] [blame]
DRC2ff39b82011-07-28 08:38:59 +00001//
2// "$Id: screen_xywh.cxx 8783 2011-06-06 09:37:21Z AlbrechtS $"
3//
4// Screen/monitor bounding box API for the Fast Light Tool Kit (FLTK).
5//
6// Copyright 1998-2010 by Bill Spitzak and others.
7//
8// This library is free software; you can redistribute it and/or
9// modify it under the terms of the GNU Library General Public
10// License as published by the Free Software Foundation; either
11// version 2 of the License, or (at your option) any later version.
12//
13// This library is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// Library General Public License for more details.
17//
18// You should have received a copy of the GNU Library General Public
19// License along with this library; if not, write to the Free Software
20// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21// USA.
22//
23// Please report all bugs and problems on the following page:
24//
25// http://www.fltk.org/str.php
26//
27
28
29#include <FL/Fl.H>
30#include <FL/x.H>
31#include <config.h>
32
Henrik Andersson485f4192011-09-16 11:51:32 +000033#define MAX_SCREENS 16
DRC2ff39b82011-07-28 08:38:59 +000034
35// Number of screens returned by multi monitor aware API; -1 before init
36static int num_screens = -1;
37
38#ifdef WIN32
39# if !defined(HMONITOR_DECLARED) && (_WIN32_WINNT < 0x0500)
40# define COMPILE_MULTIMON_STUBS
41# include <multimon.h>
42# endif // !HMONITOR_DECLARED && _WIN32_WINNT < 0x0500
43
44// We go the much more difficult route of individually picking some multi-screen
45// functions from the USER32.DLL . If these functions are not available, we
46// will gracefully fall back to single monitor support.
47//
48// If we were to insist on the existence of "EnumDisplayMonitors" and
49// "GetMonitorInfoA", it would be impossible to use FLTK on Windows 2000
50// before SP2 or earlier.
51
52// BOOL EnumDisplayMonitors(HDC, LPCRECT, MONITORENUMPROC, LPARAM)
53typedef BOOL (WINAPI* fl_edm_func)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
54// BOOL GetMonitorInfo(HMONITOR, LPMONITORINFO)
55typedef BOOL (WINAPI* fl_gmi_func)(HMONITOR, LPMONITORINFO);
56
57static fl_gmi_func fl_gmi = NULL; // used to get a proc pointer for GetMonitorInfoA
58
59static RECT screens[16];
60static float dpi[16][2];
61
62static BOOL CALLBACK screen_cb(HMONITOR mon, HDC, LPRECT r, LPARAM) {
63 if (num_screens >= 16) return TRUE;
64
65 MONITORINFOEX mi;
66 mi.cbSize = sizeof(mi);
67
68// GetMonitorInfo(mon, &mi);
69// (but we use our self-aquired function pointer instead)
70 if (fl_gmi(mon, &mi)) {
71 screens[num_screens] = mi.rcMonitor;
72
73 // find the pixel size
74 if (mi.cbSize == sizeof(mi)) {
75 HDC screen = CreateDC(mi.szDevice, NULL, NULL, NULL);
76 if (screen) {
77 dpi[num_screens][0] = (float)GetDeviceCaps(screen, LOGPIXELSX);
78 dpi[num_screens][1] = (float)GetDeviceCaps(screen, LOGPIXELSY);
79 }
80 ReleaseDC(0L, screen);
81 }
82
83 num_screens ++;
84 }
85 return TRUE;
86}
87
88static void screen_init() {
89 num_screens = 0;
90 // Since not all versions of Windows include multiple monitor support,
91 // we do a run-time check for the required functions...
92 HMODULE hMod = GetModuleHandle("USER32.DLL");
93
94 if (hMod) {
95 // check that EnumDisplayMonitors is available
96 fl_edm_func fl_edm = (fl_edm_func)GetProcAddress(hMod, "EnumDisplayMonitors");
97
98 if (fl_edm) {
99 // We do have EnumDisplayMonitors, so lets find out how many monitors...
Henrik Andersson485f4192011-09-16 11:51:32 +0000100 //num_screens = GetSystemMetrics(SM_CMONITORS);
DRC2ff39b82011-07-28 08:38:59 +0000101
102// if (num_screens > 1) {
103 // If there is more than 1 monitor, enumerate them...
104 fl_gmi = (fl_gmi_func)GetProcAddress(hMod, "GetMonitorInfoA");
105
106 if (fl_gmi) {
107 // We have GetMonitorInfoA, enumerate all the screens...
108// EnumDisplayMonitors(0,0,screen_cb,0);
109// (but we use our self-aquired function pointer instead)
110 fl_edm(0, 0, screen_cb, 0);
111 return;
112 }
113// }
114 }
115 }
116
117 // If we get here, assume we have 1 monitor...
118 num_screens = 1;
119 screens[0].top = 0;
120 screens[0].left = 0;
121 screens[0].right = GetSystemMetrics(SM_CXSCREEN);
122 screens[0].bottom = GetSystemMetrics(SM_CYSCREEN);
123}
124#elif defined(__APPLE__)
125static XRectangle screens[16];
126static float dpi_h[16];
127static float dpi_v[16];
128
129static void screen_init() {
130 CGDirectDisplayID displays[16];
131 CGDisplayCount count, i;
132 CGRect r;
133 CGGetActiveDisplayList(16, displays, &count);
134 for( i = 0; i < count; i++) {
135 r = CGDisplayBounds(displays[i]);
136 screens[i].x = int(r.origin.x);
137 screens[i].y = int(r.origin.y);
138 screens[i].width = int(r.size.width);
139 screens[i].height = int(r.size.height);
140 CGSize s = CGDisplayScreenSize(displays[i]);
141 dpi_h[i] = screens[i].width / (s.width/25.4);
142 dpi_v[i] = screens[i].height / (s.height/25.4);
143 }
144 num_screens = count;
145}
Henrik Andersson485f4192011-09-16 11:51:32 +0000146#else
DRC2ff39b82011-07-28 08:38:59 +0000147
Henrik Andersson485f4192011-09-16 11:51:32 +0000148#if HAVE_XINERAMA
149# include <X11/extensions/Xinerama.h>
150#endif
151typedef struct {
152 short x_org;
153 short y_org;
154 short width;
155 short height;
156} FLScreenInfo;
157static FLScreenInfo screens[MAX_SCREENS];
158static float dpi[MAX_SCREENS][2];
DRC2ff39b82011-07-28 08:38:59 +0000159
160static void screen_init() {
161 if (!fl_display) fl_open_display();
162
Henrik Andersson485f4192011-09-16 11:51:32 +0000163 // FIXME: Rewrite using randr instead
164#if HAVE_XINERAMA
DRC2ff39b82011-07-28 08:38:59 +0000165 if (XineramaIsActive(fl_display)) {
Henrik Andersson485f4192011-09-16 11:51:32 +0000166 static XineramaScreenInfo *xsi = XineramaQueryScreens(fl_display, &num_screens);
167 if (num_screens > MAX_SCREENS) num_screens = MAX_SCREENS;
168
169 /* There's no way to use different DPI for different Xinerama
170 screens. */
171 for (int i=0; i<num_screens; i++) {
172 screens[i].x_org = xsi[i].x_org;
173 screens[i].y_org = xsi[i].y_org;
174 screens[i].width = xsi[i].width;
175 screens[i].height = xsi[i].height;
176
177 int mm = DisplayWidthMM(fl_display, fl_screen);
178 dpi[i][0] = mm ? screens[i].width*25.4f/mm : 0.0f;
179 mm = DisplayHeightMM(fl_display, fl_screen);
180 dpi[i][1] = mm ? screens[i].height*25.4f/mm : 0.0f;
DRC2ff39b82011-07-28 08:38:59 +0000181 }
Henrik Andersson485f4192011-09-16 11:51:32 +0000182 } else
183#endif
184 { // ! XineramaIsActive()
185 num_screens = ScreenCount(fl_display);
186 if (num_screens > MAX_SCREENS) num_screens = MAX_SCREENS;
187
188 for (int i=0; i<num_screens; i++) {
189 screens[i].x_org = 0;
190 screens[i].y_org = 0;
191 screens[i].width = DisplayWidth(fl_display, i);
192 screens[i].height = DisplayHeight(fl_display, i);
193
194 int mm = DisplayWidthMM(fl_display, i);
195 dpi[i][0] = mm ? DisplayWidth(fl_display, i)*25.4f/mm : 0.0f;
196 mm = DisplayHeightMM(fl_display, i);
197 dpi[i][1] = mm ? DisplayHeight(fl_display, i)*25.4f/mm : 0.0f;
198 }
DRC2ff39b82011-07-28 08:38:59 +0000199 }
200}
Henrik Andersson485f4192011-09-16 11:51:32 +0000201
DRC2ff39b82011-07-28 08:38:59 +0000202#endif // WIN32
203
204
205/**
206 Gets the number of available screens.
207*/
208int Fl::screen_count() {
209 if (num_screens < 0) screen_init();
210
211 return num_screens ? num_screens : 1;
212}
213
214/**
215 Gets the bounding box of a screen
216 that contains the specified screen position \p mx, \p my
217 \param[out] X,Y,W,H the corresponding screen bounding box
218 \param[in] mx, my the absolute screen position
219*/
220void Fl::screen_xywh(int &X, int &Y, int &W, int &H, int mx, int my) {
221 int screen = 0;
222 int i;
223
224 if (num_screens < 0) screen_init();
225
226 for (i = 0; i < num_screens; i ++) {
227 int sx, sy, sw, sh;
228 Fl::screen_xywh(sx, sy, sw, sh, i);
229 if ((mx >= sx) && (mx < (sx+sw)) && (my >= sy) && (my < (sy+sh))) {
230 screen = i;
231 break;
232 }
233 }
234
235 screen_xywh(X, Y, W, H, screen);
236}
237
238/**
239 Gets the screen bounding rect for the given screen.
240 \param[out] X,Y,W,H the corresponding screen bounding box
241 \param[in] n the screen number (0 to Fl::screen_count() - 1)
242 \see void screen_xywh(int &x, int &y, int &w, int &h, int mx, int my)
243*/
244void Fl::screen_xywh(int &X, int &Y, int &W, int &H, int n) {
245 if (num_screens < 0) screen_init();
246
247 if ((n < 0) || (n >= num_screens))
248 n = 0;
249
250#ifdef WIN32
251 if (num_screens > 0) {
252 X = screens[n].left;
253 Y = screens[n].top;
254 W = screens[n].right - screens[n].left;
255 H = screens[n].bottom - screens[n].top;
256 } else {
257 /* Fallback if something is broken... */
258 X = 0;
259 Y = 0;
260 W = GetSystemMetrics(SM_CXSCREEN);
261 H = GetSystemMetrics(SM_CYSCREEN);
262 }
263#elif defined(__APPLE__)
264 if (num_screens > 0) {
265 X = screens[n].x;
266 Y = screens[n].y;
267 W = screens[n].width;
268 H = screens[n].height;
269 } else {
270 /* Fallback if something is broken... */
271 X = Fl::x();
272 Y = Fl::y();
273 W = Fl::w();
274 H = Fl::h();
275 }
276#else
Henrik Andersson485f4192011-09-16 11:51:32 +0000277 if (num_screens > 0) {
DRC2ff39b82011-07-28 08:38:59 +0000278 X = screens[n].x_org;
279 Y = screens[n].y_org;
280 W = screens[n].width;
281 H = screens[n].height;
DRC2ff39b82011-07-28 08:38:59 +0000282 }
283#endif // WIN32
284}
285
286static inline float fl_intersection(int x1, int y1, int w1, int h1,
287 int x2, int y2, int w2, int h2) {
288 if(x1+w1 < x2 || x2+w2 < x1 || y1+h1 < y2 || y2+h2 < y1)
289 return 0.;
290 int int_left = x1 > x2 ? x1 : x2;
291 int int_right = x1+w1 > x2+w2 ? x2+w2 : x1+w1;
292 int int_top = y1 > y2 ? y1 : y2;
293 int int_bottom = y1+h1 > y2+h2 ? y2+h2 : y1+h1;
294 return (float)(int_right - int_left) * (int_bottom - int_top);
295}
296
297/**
298 Gets the screen bounding rect for the screen
299 which intersects the most with the rectangle
300 defined by \p mx, \p my, \p mw, \p mh.
301 \param[out] X,Y,W,H the corresponding screen bounding box
302 \param[in] mx, my, mw, mh the rectangle to search for intersection with
303 \see void screen_xywh(int &X, int &Y, int &W, int &H, int n)
304 */
305void Fl::screen_xywh(int &X, int &Y, int &W, int &H, int mx, int my, int mw, int mh) {
306 int best_screen = 0;
307 float best_intersection = 0.;
308 for(int i = 0; i < Fl::screen_count(); i++) {
309 int sx, sy, sw, sh;
310 Fl::screen_xywh(sx, sy, sw, sh, i);
311 float sintersection = fl_intersection(mx, my, mw, mh, sx, sy, sw, sh);
312 if(sintersection > best_intersection) {
313 best_screen = i;
314 best_intersection = sintersection;
315 }
316 }
317 screen_xywh(X, Y, W, H, best_screen);
318}
319
320
321
322/**
323 Gets the screen resolution in dots-per-inch for the given screen.
324 \param[out] h, v horizontal and vertical resolution
325 \param[in] n the screen number (0 to Fl::screen_count() - 1)
326 \see void screen_xywh(int &x, int &y, int &w, int &h, int mx, int my)
327 */
328void Fl::screen_dpi(float &h, float &v, int n)
329{
330 if (num_screens < 0) screen_init();
331 h = v = 0.0f;
332
333#ifdef WIN32
334 if (n >= 0 && n < num_screens) {
335 h = float(dpi[n][0]);
336 v = float(dpi[n][1]);
337 }
338#elif defined(__APPLE__)
339 if (n >= 0 && n < num_screens) {
340 h = dpi_h[n];
341 v = dpi_v[n];
342 }
Henrik Andersson485f4192011-09-16 11:51:32 +0000343#else
DRC2ff39b82011-07-28 08:38:59 +0000344 if (n >= 0 && n < num_screens) {
345 h = dpi[n][0];
346 v = dpi[n][1];
347 }
DRC2ff39b82011-07-28 08:38:59 +0000348#endif // WIN32
349}
350
351
352
353//
354// End of "$Id: screen_xywh.cxx 8783 2011-06-06 09:37:21Z AlbrechtS $".
355//