fltk/src/screen_xywh.cxx
Albrecht Schlosser c79ec99d05 Fix compiler warnings and simplify X11 screen_init().
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
2016-08-23 16:38:10 +00:00

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$".
//