Corresponds to STR #3320 1) HiDPI support consists in detecting the adequate scaling factor for the screen on which FLTK maps a window, and scaling all FLTK units by this factor. FLTK tries to detect the correct value of this factor at startup (see more details below). Environment variable FLTK_SCALING_FACTOR can also be used to set this value. 2) Rescaling support consists in changing the scaling factor of all FLTK windows in reply to ctrl/+/-/0/ keystrokes. More details for the various platforms : - X11: Support is very advanced. Some details need still to be improved. Automatic detection of the correct starting value of the scaling factor works well with the gnome desktop. The present code contains no support for this on other desktops. FLTK_SCALING_FACTOR provides a workaround. -WIN32: Support is incomplete at this point, although many test applications have partial or complete HiDPI and scaling support. The current value of the system's scaling factor is correctly detected at application startup. Apps respond to changes of this value in real time. Support needs to define the FLTK_HIDPI_SUPPORT preprocessor variable at compile time. This way, standard builds produce a code with the default WIN32 HiDPI support, that is, where all graphics goes to an internal buffer that gets enlarged by the system and then mapped to the HiDPI display. To experiment with (or develop) the new HiDPI support requires a modified build procedure in which FLTK_HIDPI_SUPPORT is defined at compile time. When the support will be complete, the requirement for the definition of this preprocessor variable will be removed. The present commit contains support for a single scaling factor. Eventually, per-screen scaling factors should be implemented, as done for X11. - MacOS: this commit does not give new HiDPI for this platform. Eventually, window rescaling in reply to command/+/-/0/ is desirable. Per-screen scaling factor makes no sense on this platform because the OS itself takes care of the difference between the resolutions of traditional and retina displays. git-svn-id: file:///fltk/svn/fltk/branches/branch-1.4@12239 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
657 lines
18 KiB
C++
657 lines
18 KiB
C++
//
|
|
// "$Id$"
|
|
//
|
|
// Definition of MSWindows Win32/64 Screen interface
|
|
//
|
|
// 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 "../../config_lib.h"
|
|
#include "Fl_WinAPI_Screen_Driver.H"
|
|
#include "../GDI/Fl_Font.H"
|
|
#include <FL/Fl.H>
|
|
#include <FL/x.H>
|
|
#include <FL/Fl_Graphics_Driver.H>
|
|
#include <FL/Fl_RGB_Image.H>
|
|
#include <FL/fl_ask.H>
|
|
#include <stdio.h>
|
|
|
|
|
|
// these are set by Fl::args() and override any system colors: from Fl_get_system_colors.cxx
|
|
extern const char *fl_fg;
|
|
extern const char *fl_bg;
|
|
extern const char *fl_bg2;
|
|
// end of extern additions workaround
|
|
|
|
|
|
#if !defined(HMONITOR_DECLARED) && (_WIN32_WINNT < 0x0500)
|
|
# define COMPILE_MULTIMON_STUBS
|
|
# include <multimon.h>
|
|
#endif // !HMONITOR_DECLARED && _WIN32_WINNT < 0x0500
|
|
|
|
|
|
/*
|
|
Creates a driver that manages all screen and display related calls.
|
|
|
|
This function must be implemented once for every platform.
|
|
*/
|
|
Fl_Screen_Driver *Fl_Screen_Driver::newScreenDriver()
|
|
{
|
|
return new Fl_WinAPI_Screen_Driver();
|
|
}
|
|
|
|
|
|
int Fl_WinAPI_Screen_Driver::visual(int flags)
|
|
{
|
|
fl_GetDC(0);
|
|
if (flags & FL_DOUBLE) return 0;
|
|
HDC gc = (HDC)Fl_Graphics_Driver::default_driver().gc();
|
|
if (!(flags & FL_INDEX) &&
|
|
GetDeviceCaps(gc,BITSPIXEL) <= 8) return 0;
|
|
if ((flags & FL_RGB8) && GetDeviceCaps(gc,BITSPIXEL)<24) return 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
// 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
|
|
|
|
|
|
BOOL Fl_WinAPI_Screen_Driver::screen_cb(HMONITOR mon, HDC hdc, LPRECT r, LPARAM d)
|
|
{
|
|
Fl_WinAPI_Screen_Driver *drv = (Fl_WinAPI_Screen_Driver*)d;
|
|
return drv->screen_cb(mon, hdc, r);
|
|
}
|
|
|
|
|
|
BOOL Fl_WinAPI_Screen_Driver::screen_cb(HMONITOR mon, HDC, LPRECT r)
|
|
{
|
|
if (num_screens >= MAX_SCREENS) 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);
|
|
}
|
|
DeleteDC(screen);
|
|
}
|
|
|
|
num_screens++;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void Fl_WinAPI_Screen_Driver::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, (LPARAM)this);
|
|
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];
|
|
}
|
|
|
|
|
|
void Fl_WinAPI_Screen_Driver::screen_work_area(int &X, int &Y, int &W, int &H, int n)
|
|
{
|
|
if (num_screens < 0) init();
|
|
if (n < 0 || n >= num_screens) n = 0;
|
|
X = work_area[n].left;
|
|
Y = work_area[n].top;
|
|
W = work_area[n].right - X;
|
|
H = work_area[n].bottom - Y;
|
|
}
|
|
|
|
|
|
void Fl_WinAPI_Screen_Driver::screen_xywh(int &X, int &Y, int &W, int &H, int n)
|
|
{
|
|
if (num_screens < 0) init();
|
|
|
|
if ((n < 0) || (n >= num_screens))
|
|
n = 0;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
void Fl_WinAPI_Screen_Driver::screen_dpi(float &h, float &v, int n)
|
|
{
|
|
if (num_screens < 0) init();
|
|
h = v = 0.0f;
|
|
if (n >= 0 && n < num_screens) {
|
|
h = float(dpi[n][0]);
|
|
v = float(dpi[n][1]);
|
|
}
|
|
}
|
|
|
|
|
|
int Fl_WinAPI_Screen_Driver::x()
|
|
{
|
|
RECT r;
|
|
|
|
SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
|
|
return r.left;
|
|
}
|
|
|
|
|
|
int Fl_WinAPI_Screen_Driver::y()
|
|
{
|
|
RECT r;
|
|
|
|
SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
|
|
return r.top;
|
|
}
|
|
|
|
|
|
int Fl_WinAPI_Screen_Driver::h()
|
|
{
|
|
RECT r;
|
|
|
|
SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
|
|
return r.bottom - r.top;
|
|
}
|
|
|
|
|
|
int Fl_WinAPI_Screen_Driver::w()
|
|
{
|
|
RECT r;
|
|
|
|
SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
|
|
return r.right - r.left;
|
|
}
|
|
|
|
|
|
void Fl_WinAPI_Screen_Driver::beep(int type)
|
|
{
|
|
switch (type) {
|
|
case FL_BEEP_QUESTION :
|
|
case FL_BEEP_PASSWORD :
|
|
MessageBeep(MB_ICONQUESTION);
|
|
break;
|
|
case FL_BEEP_MESSAGE :
|
|
MessageBeep(MB_ICONASTERISK);
|
|
break;
|
|
case FL_BEEP_NOTIFICATION :
|
|
MessageBeep(MB_ICONASTERISK);
|
|
break;
|
|
case FL_BEEP_ERROR :
|
|
MessageBeep(MB_ICONERROR);
|
|
break;
|
|
default :
|
|
MessageBeep(0xFFFFFFFF);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void Fl_WinAPI_Screen_Driver::flush()
|
|
{
|
|
GdiFlush();
|
|
}
|
|
|
|
|
|
extern void fl_fix_focus(); // in Fl.cxx
|
|
|
|
// We have to keep track of whether we have captured the mouse, since
|
|
// MSWindows shows little respect for this... Grep for fl_capture to
|
|
// see where and how this is used.
|
|
extern HWND fl_capture;
|
|
|
|
|
|
void Fl_WinAPI_Screen_Driver::grab(Fl_Window* win)
|
|
{
|
|
if (win) {
|
|
if (!Fl::grab_) {
|
|
SetActiveWindow(fl_capture = fl_xid(Fl::first_window()));
|
|
SetCapture(fl_capture);
|
|
}
|
|
Fl::grab_ = win;
|
|
} else {
|
|
if (Fl::grab_) {
|
|
fl_capture = 0;
|
|
ReleaseCapture();
|
|
Fl::grab_ = 0;
|
|
fl_fix_focus();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// simulation of XParseColor:
|
|
int Fl_WinAPI_Screen_Driver::parse_color(const char* p, uchar& r, uchar& g, uchar& b)
|
|
{
|
|
if (*p == '#') p++;
|
|
size_t n = strlen(p);
|
|
size_t m = n/3;
|
|
const char *pattern = 0;
|
|
switch(m) {
|
|
case 1: pattern = "%1x%1x%1x"; break;
|
|
case 2: pattern = "%2x%2x%2x"; break;
|
|
case 3: pattern = "%3x%3x%3x"; break;
|
|
case 4: pattern = "%4x%4x%4x"; break;
|
|
default: return 0;
|
|
}
|
|
int R,G,B; if (sscanf(p,pattern,&R,&G,&B) != 3) return 0;
|
|
switch(m) {
|
|
case 1: R *= 0x11; G *= 0x11; B *= 0x11; break;
|
|
case 3: R >>= 4; G >>= 4; B >>= 4; break;
|
|
case 4: R >>= 8; G >>= 8; B >>= 8; break;
|
|
}
|
|
r = (uchar)R; g = (uchar)G; b = (uchar)B;
|
|
return 1;
|
|
}
|
|
|
|
|
|
static void set_selection_color(uchar r, uchar g, uchar b)
|
|
{
|
|
Fl::set_color(FL_SELECTION_COLOR,r,g,b);
|
|
}
|
|
|
|
|
|
static void getsyscolor(int what, const char* arg, void (*func)(uchar,uchar,uchar))
|
|
{
|
|
if (arg) {
|
|
uchar r,g,b;
|
|
if (!fl_parse_color(arg, r,g,b))
|
|
Fl::error("Unknown color: %s", arg);
|
|
else
|
|
func(r,g,b);
|
|
} else {
|
|
DWORD x = GetSysColor(what);
|
|
func(uchar(x&255), uchar(x>>8), uchar(x>>16));
|
|
}
|
|
}
|
|
|
|
|
|
void Fl_WinAPI_Screen_Driver::get_system_colors()
|
|
{
|
|
if (!bg2_set) getsyscolor(COLOR_WINDOW, fl_bg2,Fl::background2);
|
|
if (!fg_set) getsyscolor(COLOR_WINDOWTEXT, fl_fg, Fl::foreground);
|
|
if (!bg_set) getsyscolor(COLOR_BTNFACE, fl_bg, Fl::background);
|
|
getsyscolor(COLOR_HIGHLIGHT, 0, set_selection_color);
|
|
}
|
|
|
|
|
|
const char *Fl_WinAPI_Screen_Driver::get_system_scheme()
|
|
{
|
|
return getenv("FLTK_SCHEME");
|
|
}
|
|
|
|
|
|
// ---- timers
|
|
|
|
|
|
struct Win32Timer
|
|
{
|
|
UINT_PTR handle;
|
|
Fl_Timeout_Handler callback;
|
|
void *data;
|
|
};
|
|
static Win32Timer* win32_timers;
|
|
static int win32_timer_alloc;
|
|
static int win32_timer_used;
|
|
static HWND s_TimerWnd;
|
|
|
|
|
|
static void realloc_timers()
|
|
{
|
|
if (win32_timer_alloc == 0) {
|
|
win32_timer_alloc = 8;
|
|
}
|
|
win32_timer_alloc *= 2;
|
|
Win32Timer* new_timers = new Win32Timer[win32_timer_alloc];
|
|
memset(new_timers, 0, sizeof(Win32Timer) * win32_timer_used);
|
|
memcpy(new_timers, win32_timers, sizeof(Win32Timer) * win32_timer_used);
|
|
Win32Timer* delete_me = win32_timers;
|
|
win32_timers = new_timers;
|
|
delete [] delete_me;
|
|
}
|
|
|
|
|
|
static void delete_timer(Win32Timer& t)
|
|
{
|
|
KillTimer(s_TimerWnd, t.handle);
|
|
memset(&t, 0, sizeof(Win32Timer));
|
|
}
|
|
|
|
|
|
static LRESULT CALLBACK s_TimerProc(HWND hwnd, UINT msg,
|
|
WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (msg) {
|
|
case WM_TIMER:
|
|
{
|
|
unsigned int id = (unsigned) (wParam - 1);
|
|
if (id < (unsigned int)win32_timer_used && win32_timers[id].handle) {
|
|
Fl_Timeout_Handler cb = win32_timers[id].callback;
|
|
void* data = win32_timers[id].data;
|
|
delete_timer(win32_timers[id]);
|
|
if (cb) {
|
|
(*cb)(data);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
|
|
void Fl_WinAPI_Screen_Driver::add_timeout(double time, Fl_Timeout_Handler cb, void* data)
|
|
{
|
|
repeat_timeout(time, cb, data);
|
|
}
|
|
|
|
|
|
void Fl_WinAPI_Screen_Driver::repeat_timeout(double time, Fl_Timeout_Handler cb, void* data)
|
|
{
|
|
int timer_id = -1;
|
|
for (int i = 0; i < win32_timer_used; ++i) {
|
|
if ( !win32_timers[i].handle ) {
|
|
timer_id = i;
|
|
break;
|
|
}
|
|
}
|
|
if (timer_id == -1) {
|
|
if (win32_timer_used == win32_timer_alloc) {
|
|
realloc_timers();
|
|
}
|
|
timer_id = win32_timer_used++;
|
|
}
|
|
unsigned int elapsed = (unsigned int)(time * 1000);
|
|
|
|
if ( !s_TimerWnd ) {
|
|
const char* timer_class = "FLTimer";
|
|
WNDCLASSEX wc;
|
|
memset(&wc, 0, sizeof(wc));
|
|
wc.cbSize = sizeof (wc);
|
|
wc.style = CS_CLASSDC;
|
|
wc.lpfnWndProc = (WNDPROC)s_TimerProc;
|
|
wc.hInstance = fl_display;
|
|
wc.lpszClassName = timer_class;
|
|
/*ATOM atom =*/ RegisterClassEx(&wc);
|
|
// create a zero size window to handle timer events
|
|
s_TimerWnd = CreateWindowEx(WS_EX_LEFT | WS_EX_TOOLWINDOW,
|
|
timer_class, "",
|
|
WS_POPUP,
|
|
0, 0, 0, 0,
|
|
NULL, NULL, fl_display, NULL);
|
|
// just in case this OS won't let us create a 0x0 size window:
|
|
if (!s_TimerWnd)
|
|
s_TimerWnd = CreateWindowEx(WS_EX_LEFT | WS_EX_TOOLWINDOW,
|
|
timer_class, "",
|
|
WS_POPUP,
|
|
0, 0, 1, 1,
|
|
NULL, NULL, fl_display, NULL);
|
|
ShowWindow(s_TimerWnd, SW_SHOWNOACTIVATE);
|
|
}
|
|
|
|
win32_timers[timer_id].callback = cb;
|
|
win32_timers[timer_id].data = data;
|
|
|
|
win32_timers[timer_id].handle =
|
|
SetTimer(s_TimerWnd, timer_id + 1, elapsed, NULL);
|
|
}
|
|
|
|
|
|
int Fl_WinAPI_Screen_Driver::has_timeout(Fl_Timeout_Handler cb, void* data)
|
|
{
|
|
for (int i = 0; i < win32_timer_used; ++i) {
|
|
Win32Timer& t = win32_timers[i];
|
|
if (t.handle && t.callback == cb && t.data == data) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void Fl_WinAPI_Screen_Driver::remove_timeout(Fl_Timeout_Handler cb, void* data)
|
|
{
|
|
int i;
|
|
for (i = 0; i < win32_timer_used; ++i) {
|
|
Win32Timer& t = win32_timers[i];
|
|
if (t.handle && t.callback == cb &&
|
|
(t.data == data || data == NULL)) {
|
|
delete_timer(t);
|
|
}
|
|
}
|
|
}
|
|
|
|
int Fl_WinAPI_Screen_Driver::compose(int &del) {
|
|
unsigned char ascii = (unsigned char)Fl::e_text[0];
|
|
int condition = (Fl::e_state & (FL_ALT | FL_META)) && !(ascii & 128) ;
|
|
if (condition) { // this stuff is to be treated as a function key
|
|
del = 0;
|
|
return 0;
|
|
}
|
|
del = Fl::compose_state;
|
|
Fl::compose_state = 0;
|
|
// Only insert non-control characters:
|
|
if ( (!Fl::compose_state) && ! (ascii & ~31 && ascii!=127)) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
Fl_RGB_Image * // O - image or NULL if failed
|
|
Fl_WinAPI_Screen_Driver::read_win_rectangle(uchar *p, // I - Pixel buffer or NULL to allocate
|
|
int X, // I - Left position
|
|
int Y, // I - Top position
|
|
int w, // I - Width of area to read
|
|
int h, // I - Height of area to read
|
|
int alpha) // I - Alpha value for image (0 for none)
|
|
{
|
|
int d; // Depth of image
|
|
|
|
// Allocate the image data array as needed...
|
|
d = alpha ? 4 : 3;
|
|
|
|
const uchar *oldp = p;
|
|
if (!p) p = new uchar[w * h * d];
|
|
|
|
// Initialize the default colors/alpha in the whole image...
|
|
memset(p, alpha, w * h * d);
|
|
|
|
// Grab all of the pixels in the image...
|
|
|
|
// Assure that we are not trying to read non-existing data. If it is so, the
|
|
// function should still work, but the out-of-bounds part of the image is
|
|
// untouched (initialized with the alpha value or 0 (black), resp.).
|
|
|
|
int ww = w; // We need the original width for output data line size
|
|
|
|
int shift_x = 0; // X target shift if X modified
|
|
int shift_y = 0; // Y target shift if X modified
|
|
|
|
if (X < 0) {
|
|
shift_x = -X;
|
|
w += X;
|
|
X = 0;
|
|
}
|
|
if (Y < 0) {
|
|
shift_y = -Y;
|
|
h += Y;
|
|
Y = 0;
|
|
}
|
|
|
|
if (h < 1 || w < 1) return 0/*p*/; // nothing to copy
|
|
|
|
int line_size = ((3*w+3)/4) * 4; // each line is aligned on a DWORD (4 bytes)
|
|
uchar *dib = new uchar[line_size*h]; // create temporary buffer to read DIB
|
|
|
|
// fill in bitmap info for GetDIBits
|
|
|
|
BITMAPINFO bi;
|
|
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
bi.bmiHeader.biWidth = w;
|
|
bi.bmiHeader.biHeight = -h; // negative => top-down DIB
|
|
bi.bmiHeader.biPlanes = 1;
|
|
bi.bmiHeader.biBitCount = 24; // 24 bits RGB
|
|
bi.bmiHeader.biCompression = BI_RGB;
|
|
bi.bmiHeader.biSizeImage = 0;
|
|
bi.bmiHeader.biXPelsPerMeter = 0;
|
|
bi.bmiHeader.biYPelsPerMeter = 0;
|
|
bi.bmiHeader.biClrUsed = 0;
|
|
bi.bmiHeader.biClrImportant = 0;
|
|
|
|
// copy bitmap from original DC (Window, Fl_Offscreen, ...)
|
|
HDC gc = (HDC)fl_graphics_driver->gc();
|
|
HDC hdc = CreateCompatibleDC(gc);
|
|
HBITMAP hbm = CreateCompatibleBitmap(gc,w,h);
|
|
|
|
int save_dc = SaveDC(hdc); // save context for cleanup
|
|
SelectObject(hdc,hbm); // select bitmap
|
|
BitBlt(hdc,0,0,w,h,gc,X,Y,SRCCOPY); // copy image section to DDB
|
|
|
|
// copy RGB image data to the allocated DIB
|
|
|
|
GetDIBits(hdc, hbm, 0, h, dib, (BITMAPINFO *)&bi, DIB_RGB_COLORS);
|
|
|
|
// finally copy the image data to the user buffer
|
|
|
|
for (int j = 0; j<h; j++) {
|
|
const uchar *src = dib + j * line_size; // source line
|
|
uchar *tg = p + (j + shift_y) * d * ww + shift_x * d; // target line
|
|
for (int i = 0; i<w; i++) {
|
|
uchar b = *src++;
|
|
uchar g = *src++;
|
|
*tg++ = *src++; // R
|
|
*tg++ = g; // G
|
|
*tg++ = b; // B
|
|
if (alpha)
|
|
*tg++ = alpha; // alpha
|
|
}
|
|
}
|
|
|
|
// free used GDI and other structures
|
|
|
|
RestoreDC(hdc,save_dc); // reset DC
|
|
DeleteDC(hdc);
|
|
DeleteObject(hbm);
|
|
delete[] dib; // delete DIB temporary buffer
|
|
|
|
Fl_RGB_Image *rgb = new Fl_RGB_Image(p, w, h, d);
|
|
if (!oldp) rgb->alloc_array = 1;
|
|
return rgb;
|
|
}
|
|
|
|
/** Returns the current desktop scaling factor (1.75 for example)
|
|
*/
|
|
float Fl_WinAPI_Screen_Driver::desktop_scaling_factor() {
|
|
#ifdef FLTK_HIDPI_SUPPORT
|
|
return 1;// this becomes useless if FLTK app are made DPI-aware by calling SetProcessDpiAwareness()
|
|
#else
|
|
// Compute the global desktop scaling factor: 1, 1.25, 1.5, 1.75, etc...
|
|
// This factor can be set in Windows 10 by
|
|
// "Change the size of text, apps and other items" in display settings.
|
|
// We don't cache this value because it can change while the app is running.
|
|
HDC hdc = GetDC(NULL);
|
|
int hr = GetDeviceCaps(hdc, HORZRES); // pixels visible to the app
|
|
#ifndef DESKTOPHORZRES
|
|
#define DESKTOPHORZRES 118
|
|
/* As of 27 august 2016, the DESKTOPHORZRES flag for GetDeviceCaps()
|
|
has disappeared from Microsoft online doc, but is quoted in numerous coding examples
|
|
e.g., https://social.msdn.microsoft.com/Forums/en-US/6acc3b21-23a4-4a00-90b4-968a43e1ccc8/capture-screen-with-high-dpi?forum=vbgeneral
|
|
It is necessary for the computation of the scaling factor at runtime as done here.
|
|
*/
|
|
#endif
|
|
int dhr = GetDeviceCaps(hdc, DESKTOPHORZRES); // true number of pixels on display
|
|
ReleaseDC(NULL, hdc);
|
|
float scaling = dhr/float(hr);
|
|
scaling = int(scaling * 100 + 0.5)/100.; // round to 2 digits after decimal point
|
|
return scaling;
|
|
#endif // FLTK_HIDPI_SUPPORT
|
|
}
|
|
|
|
void Fl_WinAPI_Screen_Driver::offscreen_size(Fl_Offscreen off, int &width, int &height)
|
|
{
|
|
BITMAP bitmap;
|
|
if ( GetObject(off, sizeof(BITMAP), &bitmap) ) {
|
|
width = bitmap.bmWidth;
|
|
height = bitmap.bmHeight;
|
|
}
|
|
}
|
|
|
|
//
|
|
// End of "$Id$".
|
|
//
|