// // "$Id: Fl_WinAPI_Screen_Driver.cxx 12628 2018-01-09 07:26:49Z manolo $" // // Definition of MSWindows Win32/64 Screen interface // // Copyright 1998-2017 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 #include #include #include #include #include // 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 #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; //extern FILE*LOG;fprintf(LOG,"screen_cb ns=%d\n",num_screens);fflush(LOG); /*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() { open_display(); // 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]; scale_of_screen[0] = 1; } float Fl_WinAPI_Screen_Driver::desktop_scale_factor() { return 0; //indicates each screen has already been assigned its scale factor value } 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/scale_of_screen[n]; Y = work_area[n].top/scale_of_screen[n]; W = (work_area[n].right - X)/scale_of_screen[n]; H = (work_area[n].bottom - Y)/scale_of_screen[n]; } 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/scale_of_screen[n]; Y = screens[n].top/scale_of_screen[n]; W = (screens[n].right - screens[n].left)/scale_of_screen[n]; H = (screens[n].bottom - screens[n].top)/scale_of_screen[n]; } 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(); } } } 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 fl_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) { float s = Fl_Surface_Device::surface()->driver()->scale(); return read_win_rectangle_unscaled(p, X*s, Y*s, w*s, h*s, alpha); } Fl_RGB_Image *Fl_WinAPI_Screen_Driver::read_win_rectangle_unscaled(uchar *p, int X, int Y, int w, int h, int alpha) { 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; jalloc_array = 1; return rgb; } #ifndef FLTK_HIDPI_SUPPORT /* Returns the current desktop scaling factor for screen_num (1.75 for example) */ float Fl_WinAPI_Screen_Driver::DWM_scaling_factor() { // 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; } } //NOTICE: returns -1 if x,y is not in any screen int Fl_WinAPI_Screen_Driver::screen_num_unscaled(int x, int y) { int screen = -1; if (num_screens < 0) init(); for (int i = 0; i < num_screens; i ++) { if (x >= screens[i].left && x < screens[i].right && y >= screens[i].top && y < screens[i].bottom) { screen = i; break; } } return screen; } // // End of "$Id: Fl_WinAPI_Screen_Driver.cxx 12628 2018-01-09 07:26:49Z manolo $". //