This function sets the screen dimensions and tries to use Xrandr, Xinerama, and basic X11 functions (in this order) to get the screen sizes and resolutions (dpi). This commit simplifies conditional compilation and fixes "unused variable" compiler warnings under certain conditions as mentioned by Greg in fltk.general, thread "Can not change font size in Kubuntu 16.04" on Mon, 15 Aug 2016 00:25:32 -0700. Also merged back small improvements of branch-1.3-porting. git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@11889 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
462 lines
14 KiB
C++
462 lines
14 KiB
C++
//
|
|
// "$Id$"
|
|
//
|
|
// Screen/monitor bounding box API for the Fast Light Tool Kit (FLTK).
|
|
//
|
|
// Copyright 1998-2016 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-acquired 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-acquired 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 // X11
|
|
|
|
#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];
|
|
|
|
#define USE_XRANDR (HAVE_DLSYM && HAVE_DLFCN_H) // means attempt to dynamically load libXrandr.so
|
|
#if USE_XRANDR
|
|
#include <dlfcn.h>
|
|
typedef struct {
|
|
int width, height;
|
|
int mwidth, mheight;
|
|
} XRRScreenSize;
|
|
typedef XRRScreenSize* (*XRRSizes_type)(Display *dpy, int screen, int *nsizes);
|
|
#endif // USE_XRANDR
|
|
|
|
static void screen_init() {
|
|
if (!fl_display) fl_open_display();
|
|
|
|
int dpi_by_randr = 0;
|
|
float dpih = 0.0f, dpiv = 0.0f;
|
|
|
|
#if USE_XRANDR
|
|
|
|
static XRRSizes_type XRRSizes_f = NULL;
|
|
if (!XRRSizes_f) {
|
|
void *libxrandr_addr = dlopen("libXrandr.so.2", RTLD_LAZY);
|
|
if (!libxrandr_addr) libxrandr_addr = dlopen("libXrandr.so", RTLD_LAZY);
|
|
# ifdef __APPLE_CC__ // allows testing on Darwin + X11
|
|
if (!libxrandr_addr) libxrandr_addr = dlopen("/opt/X11/lib/libXrandr.dylib", RTLD_LAZY);
|
|
# endif
|
|
if (libxrandr_addr) XRRSizes_f = (XRRSizes_type)dlsym(libxrandr_addr, "XRRSizes");
|
|
}
|
|
if (XRRSizes_f) {
|
|
int nscreens;
|
|
XRRScreenSize *ssize = XRRSizes_f(fl_display, fl_screen, &nscreens);
|
|
|
|
//for (int i=0; i<nscreens; i++)
|
|
// printf("width=%d height=%d mwidth=%d mheight=%d\n",
|
|
// ssize[i].width,ssize[i].height,ssize[i].mwidth,ssize[i].mheight);
|
|
|
|
if (nscreens > 0) { // Note: XRRSizes() *may* return nscreens == 0, see docs
|
|
int mm = ssize[0].mwidth;
|
|
dpih = mm ? ssize[0].width*25.4f/mm : 0.0f;
|
|
mm = ssize[0].mheight;
|
|
dpiv = mm ? ssize[0].height*25.4f/mm : 0.0f;
|
|
dpi_by_randr = 1;
|
|
}
|
|
}
|
|
|
|
#endif // USE_XRANDR
|
|
|
|
#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;
|
|
|
|
if (dpi_by_randr) {
|
|
dpi[i][0] = dpih;
|
|
dpi[i][1] = dpiv;
|
|
} else {
|
|
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 // HAVE_XINERAMA
|
|
|
|
{ // ! HAVE_XINERAMA || ! 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);
|
|
|
|
if (dpi_by_randr) {
|
|
dpi[i][0] = dpih;
|
|
dpi[i][1] = dpiv;
|
|
} else {
|
|
int mm = DisplayWidthMM(fl_display, i);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // ( WIN32 || __APPLE__ || ) X11
|
|
|
|
#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;
|
|
}
|
|
|
|
/**
|
|
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, screen_num(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, screen_num(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
|
|
}
|
|
|
|
/**
|
|
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) {
|
|
screen_xywh(X, Y, W, H, screen_num(mx, my, mw, mh));
|
|
}
|
|
|
|
/**
|
|
Gets the screen number of a screen
|
|
that contains the specified screen position \p x, \p y
|
|
\param[in] x, y the absolute screen position
|
|
*/
|
|
int Fl::screen_num(int x, int y) {
|
|
int screen = 0;
|
|
if (num_screens < 0) screen_init();
|
|
|
|
for (int i = 0; i < num_screens; i ++) {
|
|
int sx = 0, sy = 0, sw = 0, sh = 0;
|
|
Fl::screen_xywh(sx, sy, sw, sh, i);
|
|
if ((x >= sx) && (x < (sx+sw)) && (y >= sy) && (y < (sy+sh))) {
|
|
screen = i;
|
|
break;
|
|
}
|
|
}
|
|
return screen;
|
|
}
|
|
|
|
// Return the number of pixels common to the two rectangular areas
|
|
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 number for the screen
|
|
which intersects the most with the rectangle
|
|
defined by \p x, \p y, \p w, \p h.
|
|
\param[in] x, y, w, h the rectangle to search for intersection with
|
|
*/
|
|
int Fl::screen_num(int x, int y, int w, int h) {
|
|
int best_screen = 0;
|
|
float best_intersection = 0.;
|
|
for (int i = 0; i < Fl::screen_count(); i++) {
|
|
int sx = 0, sy = 0, sw = 0, sh = 0;
|
|
Fl::screen_xywh(sx, sy, sw, sh, i);
|
|
float sintersection = fl_intersection(x, y, w, h, sx, sy, sw, sh);
|
|
if (sintersection > best_intersection) {
|
|
best_screen = i;
|
|
best_intersection = sintersection;
|
|
}
|
|
}
|
|
return 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$".
|
|
//
|