
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@9299 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
394 lines
12 KiB
C++
394 lines
12 KiB
C++
//
|
|
// "$Id$"
|
|
//
|
|
// Screen/monitor bounding box API for the Fast Light Tool Kit (FLTK).
|
|
//
|
|
// Copyright 1998-2010 by Bill Spitzak and others.
|
|
//
|
|
// This library is free software. Distribution and use rights are outlined in
|
|
// the file "COPYING" which should have been included with this file. If this
|
|
// file is missing or damaged, see the license at:
|
|
//
|
|
// http://www.fltk.org/COPYING.php
|
|
//
|
|
// Please report all bugs and problems on the following page:
|
|
//
|
|
// http://www.fltk.org/str.php
|
|
//
|
|
|
|
|
|
#include <FL/Fl.H>
|
|
#include <FL/x.H>
|
|
#include <config.h>
|
|
|
|
#define MAX_SCREENS 16
|
|
|
|
// Number of screens returned by multi monitor aware API; -1 before init
|
|
static int num_screens = -1;
|
|
|
|
#ifdef WIN32
|
|
# if !defined(HMONITOR_DECLARED) && (_WIN32_WINNT < 0x0500)
|
|
# define COMPILE_MULTIMON_STUBS
|
|
# include <multimon.h>
|
|
# endif // !HMONITOR_DECLARED && _WIN32_WINNT < 0x0500
|
|
|
|
// We go the much more difficult route of individually picking some multi-screen
|
|
// functions from the USER32.DLL . If these functions are not available, we
|
|
// will gracefully fall back to single monitor support.
|
|
//
|
|
// If we were to insist on the existence of "EnumDisplayMonitors" and
|
|
// "GetMonitorInfoA", it would be impossible to use FLTK on Windows 2000
|
|
// before SP2 or earlier.
|
|
|
|
// BOOL EnumDisplayMonitors(HDC, LPCRECT, MONITORENUMPROC, LPARAM)
|
|
typedef BOOL (WINAPI* fl_edm_func)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
|
|
// BOOL GetMonitorInfo(HMONITOR, LPMONITORINFO)
|
|
typedef BOOL (WINAPI* fl_gmi_func)(HMONITOR, LPMONITORINFO);
|
|
|
|
static fl_gmi_func fl_gmi = NULL; // used to get a proc pointer for GetMonitorInfoA
|
|
|
|
static RECT screens[16];
|
|
static RECT work_area[16];
|
|
static float dpi[16][2];
|
|
|
|
static BOOL CALLBACK screen_cb(HMONITOR mon, HDC, LPRECT r, LPARAM) {
|
|
if (num_screens >= 16) return TRUE;
|
|
|
|
MONITORINFOEX mi;
|
|
mi.cbSize = sizeof(mi);
|
|
|
|
// GetMonitorInfo(mon, &mi);
|
|
// (but we use our self-aquired function pointer instead)
|
|
if (fl_gmi(mon, &mi)) {
|
|
screens[num_screens] = mi.rcMonitor;
|
|
// If we also want to record the work area, we would also store mi.rcWork at this point
|
|
work_area[num_screens] = mi.rcWork;
|
|
/*fl_alert("screen %d %d,%d,%d,%d work %d,%d,%d,%d",num_screens,
|
|
screens[num_screens].left,screens[num_screens].right,screens[num_screens].top,screens[num_screens].bottom,
|
|
work_area[num_screens].left,work_area[num_screens].right,work_area[num_screens].top,work_area[num_screens].bottom);
|
|
*/
|
|
// find the pixel size
|
|
if (mi.cbSize == sizeof(mi)) {
|
|
HDC screen = CreateDC(mi.szDevice, NULL, NULL, NULL);
|
|
if (screen) {
|
|
dpi[num_screens][0] = (float)GetDeviceCaps(screen, LOGPIXELSX);
|
|
dpi[num_screens][1] = (float)GetDeviceCaps(screen, LOGPIXELSY);
|
|
}
|
|
ReleaseDC(0L, screen);
|
|
}
|
|
|
|
num_screens ++;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void screen_init() {
|
|
// Since not all versions of Windows include multiple monitor support,
|
|
// we do a run-time check for the required functions...
|
|
HMODULE hMod = GetModuleHandle("USER32.DLL");
|
|
|
|
if (hMod) {
|
|
// check that EnumDisplayMonitors is available
|
|
fl_edm_func fl_edm = (fl_edm_func)GetProcAddress(hMod, "EnumDisplayMonitors");
|
|
|
|
if (fl_edm) {
|
|
// we have EnumDisplayMonitors - do we also have GetMonitorInfoA ?
|
|
fl_gmi = (fl_gmi_func)GetProcAddress(hMod, "GetMonitorInfoA");
|
|
if (fl_gmi) {
|
|
// We have GetMonitorInfoA, enumerate all the screens...
|
|
// EnumDisplayMonitors(0,0,screen_cb,0);
|
|
// (but we use our self-aquired function pointer instead)
|
|
// NOTE: num_screens is incremented in screen_cb so we must first reset it here...
|
|
num_screens = 0;
|
|
fl_edm(0, 0, screen_cb, 0);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we get here, assume we have 1 monitor...
|
|
num_screens = 1;
|
|
screens[0].top = 0;
|
|
screens[0].left = 0;
|
|
screens[0].right = GetSystemMetrics(SM_CXSCREEN);
|
|
screens[0].bottom = GetSystemMetrics(SM_CYSCREEN);
|
|
work_area[0] = screens[0];
|
|
}
|
|
#elif defined(__APPLE__)
|
|
static XRectangle screens[16];
|
|
static float dpi_h[16];
|
|
static float dpi_v[16];
|
|
|
|
static void screen_init() {
|
|
CGDirectDisplayID displays[16];
|
|
CGDisplayCount count, i;
|
|
CGRect r;
|
|
CGGetActiveDisplayList(16, displays, &count);
|
|
for( i = 0; i < count; i++) {
|
|
r = CGDisplayBounds(displays[i]);
|
|
screens[i].x = int(r.origin.x);
|
|
screens[i].y = int(r.origin.y);
|
|
screens[i].width = int(r.size.width);
|
|
screens[i].height = int(r.size.height);
|
|
//fprintf(stderr,"screen %d %dx%dx%dx%d\n",i,screens[i].x,screens[i].y,screens[i].width,screens[i].height);
|
|
if (CGDisplayScreenSize != NULL) {
|
|
CGSize s = CGDisplayScreenSize(displays[i]); // from 10.3
|
|
dpi_h[i] = screens[i].width / (s.width/25.4);
|
|
dpi_v[i] = screens[i].height / (s.height/25.4);
|
|
}
|
|
else {
|
|
dpi_h[i] = dpi_v[i] = 75.;
|
|
}
|
|
}
|
|
num_screens = count;
|
|
}
|
|
|
|
#else
|
|
|
|
#if HAVE_XINERAMA
|
|
# include <X11/extensions/Xinerama.h>
|
|
#endif
|
|
typedef struct {
|
|
short x_org;
|
|
short y_org;
|
|
short width;
|
|
short height;
|
|
} FLScreenInfo;
|
|
static FLScreenInfo screens[MAX_SCREENS];
|
|
static float dpi[MAX_SCREENS][2];
|
|
|
|
static void screen_init() {
|
|
if (!fl_display) fl_open_display();
|
|
// FIXME: Rewrite using RandR instead
|
|
#if HAVE_XINERAMA
|
|
if (XineramaIsActive(fl_display)) {
|
|
XineramaScreenInfo *xsi = XineramaQueryScreens(fl_display, &num_screens);
|
|
if (num_screens > MAX_SCREENS) num_screens = MAX_SCREENS;
|
|
|
|
/* There's no way to use different DPI for different Xinerama screens. */
|
|
for (int i=0; i<num_screens; i++) {
|
|
screens[i].x_org = xsi[i].x_org;
|
|
screens[i].y_org = xsi[i].y_org;
|
|
screens[i].width = xsi[i].width;
|
|
screens[i].height = xsi[i].height;
|
|
|
|
int mm = DisplayWidthMM(fl_display, fl_screen);
|
|
dpi[i][0] = mm ? screens[i].width*25.4f/mm : 0.0f;
|
|
mm = DisplayHeightMM(fl_display, fl_screen);
|
|
dpi[i][1] = mm ? screens[i].height*25.4f/mm : 0.0f;
|
|
}
|
|
if (xsi) XFree(xsi);
|
|
} else
|
|
#endif
|
|
{ // ! XineramaIsActive()
|
|
num_screens = ScreenCount(fl_display);
|
|
if (num_screens > MAX_SCREENS) num_screens = MAX_SCREENS;
|
|
|
|
for (int i=0; i<num_screens; i++) {
|
|
screens[i].x_org = 0;
|
|
screens[i].y_org = 0;
|
|
screens[i].width = DisplayWidth(fl_display, i);
|
|
screens[i].height = DisplayHeight(fl_display, i);
|
|
|
|
int mm = DisplayWidthMM(fl_display, i);
|
|
dpi[i][0] = mm ? DisplayWidth(fl_display, i)*25.4f/mm : 0.0f;
|
|
mm = DisplayHeightMM(fl_display, i);
|
|
dpi[i][1] = mm ? DisplayHeight(fl_display, i)*25.4f/mm : 0.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // WIN32
|
|
|
|
#ifndef FL_DOXYGEN
|
|
void Fl::call_screen_init() {
|
|
screen_init();
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
Gets the number of available screens.
|
|
*/
|
|
int Fl::screen_count() {
|
|
if (num_screens < 0) screen_init();
|
|
|
|
return num_screens ? num_screens : 1;
|
|
}
|
|
|
|
static int find_screen_with_point(int mx, int my) {
|
|
int screen = 0;
|
|
if (num_screens < 0) screen_init();
|
|
|
|
for (int i = 0; i < num_screens; i ++) {
|
|
int sx, sy, sw, sh;
|
|
Fl::screen_xywh(sx, sy, sw, sh, i);
|
|
if ((mx >= sx) && (mx < (sx+sw)) && (my >= sy) && (my < (sy+sh))) {
|
|
screen = i;
|
|
break;
|
|
}
|
|
}
|
|
return screen;
|
|
}
|
|
|
|
/**
|
|
Gets the bounding box of a screen
|
|
that contains the specified screen position \p mx, \p my
|
|
\param[out] X,Y,W,H the corresponding screen bounding box
|
|
\param[in] mx, my the absolute screen position
|
|
*/
|
|
void Fl::screen_xywh(int &X, int &Y, int &W, int &H, int mx, int my) {
|
|
screen_xywh(X, Y, W, H, find_screen_with_point(mx, my));
|
|
}
|
|
|
|
|
|
/**
|
|
Gets the bounding box of the work area of a screen
|
|
that contains the specified screen position \p mx, \p my
|
|
\param[out] X,Y,W,H the work area bounding box
|
|
\param[in] mx, my the absolute screen position
|
|
*/
|
|
void Fl::screen_work_area(int &X, int &Y, int &W, int &H, int mx, int my) {
|
|
screen_work_area(X, Y, W, H, find_screen_with_point(mx, my));
|
|
}
|
|
|
|
/**
|
|
Gets the bounding box of the work area of the given screen.
|
|
\param[out] X,Y,W,H the work area bounding box
|
|
\param[in] n the screen number (0 to Fl::screen_count() - 1)
|
|
\see void screen_xywh(int &x, int &y, int &w, int &h, int mx, int my)
|
|
*/
|
|
void Fl::screen_work_area(int &X, int &Y, int &W, int &H, int n) {
|
|
if (num_screens < 0) screen_init();
|
|
if (n < 0 || n >= num_screens) n = 0;
|
|
#ifdef WIN32
|
|
X = work_area[n].left;
|
|
Y = work_area[n].top;
|
|
W = work_area[n].right - X;
|
|
H = work_area[n].bottom - Y;
|
|
#elif defined(__APPLE__)
|
|
Fl_X::screen_work_area(X, Y, W, H, n);
|
|
#else
|
|
if (n == 0) { // for the main screen, these return the work area
|
|
X = Fl::x();
|
|
Y = Fl::y();
|
|
W = Fl::w();
|
|
H = Fl::h();
|
|
}
|
|
else { // for other screens, work area is full screen,
|
|
screen_xywh(X, Y, W, H, n);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
Gets the screen bounding rect for the given screen.
|
|
Under MSWindows, Mac OS X, and the Gnome desktop, screen #0 contains the menubar/taskbar
|
|
\param[out] X,Y,W,H the corresponding screen bounding box
|
|
\param[in] n the screen number (0 to Fl::screen_count() - 1)
|
|
\see void screen_xywh(int &x, int &y, int &w, int &h, int mx, int my)
|
|
*/
|
|
void Fl::screen_xywh(int &X, int &Y, int &W, int &H, int n) {
|
|
if (num_screens < 0) screen_init();
|
|
|
|
if ((n < 0) || (n >= num_screens))
|
|
n = 0;
|
|
|
|
#ifdef WIN32
|
|
if (num_screens > 0) {
|
|
X = screens[n].left;
|
|
Y = screens[n].top;
|
|
W = screens[n].right - screens[n].left;
|
|
H = screens[n].bottom - screens[n].top;
|
|
} else {
|
|
/* Fallback if something is broken... */
|
|
X = 0;
|
|
Y = 0;
|
|
W = GetSystemMetrics(SM_CXSCREEN);
|
|
H = GetSystemMetrics(SM_CYSCREEN);
|
|
}
|
|
#elif defined(__APPLE__)
|
|
X = screens[n].x;
|
|
Y = screens[n].y;
|
|
W = screens[n].width;
|
|
H = screens[n].height;
|
|
#else
|
|
if (num_screens > 0) {
|
|
X = screens[n].x_org;
|
|
Y = screens[n].y_org;
|
|
W = screens[n].width;
|
|
H = screens[n].height;
|
|
}
|
|
#endif // WIN32
|
|
}
|
|
|
|
static inline float fl_intersection(int x1, int y1, int w1, int h1,
|
|
int x2, int y2, int w2, int h2) {
|
|
if(x1+w1 < x2 || x2+w2 < x1 || y1+h1 < y2 || y2+h2 < y1)
|
|
return 0.;
|
|
int int_left = x1 > x2 ? x1 : x2;
|
|
int int_right = x1+w1 > x2+w2 ? x2+w2 : x1+w1;
|
|
int int_top = y1 > y2 ? y1 : y2;
|
|
int int_bottom = y1+h1 > y2+h2 ? y2+h2 : y1+h1;
|
|
return (float)(int_right - int_left) * (int_bottom - int_top);
|
|
}
|
|
|
|
/**
|
|
Gets the screen bounding rect for the screen
|
|
which intersects the most with the rectangle
|
|
defined by \p mx, \p my, \p mw, \p mh.
|
|
\param[out] X,Y,W,H the corresponding screen bounding box
|
|
\param[in] mx, my, mw, mh the rectangle to search for intersection with
|
|
\see void screen_xywh(int &X, int &Y, int &W, int &H, int n)
|
|
*/
|
|
void Fl::screen_xywh(int &X, int &Y, int &W, int &H, int mx, int my, int mw, int mh) {
|
|
int best_screen = 0;
|
|
float best_intersection = 0.;
|
|
for(int i = 0; i < Fl::screen_count(); i++) {
|
|
int sx, sy, sw, sh;
|
|
Fl::screen_xywh(sx, sy, sw, sh, i);
|
|
float sintersection = fl_intersection(mx, my, mw, mh, sx, sy, sw, sh);
|
|
if(sintersection > best_intersection) {
|
|
best_screen = i;
|
|
best_intersection = sintersection;
|
|
}
|
|
}
|
|
screen_xywh(X, Y, W, H, best_screen);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Gets the screen resolution in dots-per-inch for the given screen.
|
|
\param[out] h, v horizontal and vertical resolution
|
|
\param[in] n the screen number (0 to Fl::screen_count() - 1)
|
|
\see void screen_xywh(int &x, int &y, int &w, int &h, int mx, int my)
|
|
*/
|
|
void Fl::screen_dpi(float &h, float &v, int n)
|
|
{
|
|
if (num_screens < 0) screen_init();
|
|
h = v = 0.0f;
|
|
|
|
#ifdef WIN32
|
|
if (n >= 0 && n < num_screens) {
|
|
h = float(dpi[n][0]);
|
|
v = float(dpi[n][1]);
|
|
}
|
|
#elif defined(__APPLE__)
|
|
if (n >= 0 && n < num_screens) {
|
|
h = dpi_h[n];
|
|
v = dpi_v[n];
|
|
}
|
|
#else
|
|
if (n >= 0 && n < num_screens) {
|
|
h = dpi[n][0];
|
|
v = dpi[n][1];
|
|
}
|
|
#endif // WIN32
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// End of "$Id$".
|
|
//
|