diff options
| author | maxim nikonov <maxim.nikonov@hqo.co> | 2026-02-05 15:21:34 +0500 |
|---|---|---|
| committer | maxim nikonov <maxim.nikonov@hqo.co> | 2026-02-05 15:21:34 +0500 |
| commit | db214d1145e46d527a46d1fc2519548d2c4d23f1 (patch) | |
| tree | cf0fd9922e4d54f6beb63888f9b28c8e2a787bdf /src/Fl_win32.cxx | |
| parent | 75fc94d6c71fe686f6dde5b41ad91cba2f6bdd6f (diff) | |
wip: fork
Diffstat (limited to 'src/Fl_win32.cxx')
| -rw-r--r-- | src/Fl_win32.cxx | 2976 |
1 files changed, 0 insertions, 2976 deletions
diff --git a/src/Fl_win32.cxx b/src/Fl_win32.cxx deleted file mode 100644 index b5aca1c78..000000000 --- a/src/Fl_win32.cxx +++ /dev/null @@ -1,2976 +0,0 @@ -// -// Windows-specific code for the Fast Light Tool Kit (FLTK). -// -// Copyright 1998-2025 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: -// -// https://www.fltk.org/COPYING.php -// -// Please see the following page on how to report bugs and issues: -// -// https://www.fltk.org/bugs.php -// - -// Note: this file contains platform specific code and will therefore -// not be processed by doxygen (see Doxyfile.in). - -// This file contains Windows-specific code for FLTK which is always linked -// in. Search other files for "_WIN32" or filenames ending in _win32.cxx -// for other system-specific code. - -/* We require Windows 2000 features (e.g. VK definitions) */ -# if !defined(WINVER) || (WINVER < 0x0500) -# ifdef WINVER -# undef WINVER -# endif -# define WINVER 0x0500 -# endif -# if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) -# ifdef _WIN32_WINNT -# undef _WIN32_WINNT -# endif -# define _WIN32_WINNT 0x0500 -# endif - -// recent versions of MinGW warn: "Please include winsock2.h before windows.h" -#if !defined(__CYGWIN__) -# include <winsock2.h> -#endif -#include <windows.h> -#include <ole2.h> -#include <shellapi.h> -// Some versions of MinGW now require us to explicitly include winerror to get S_OK defined -#include <winerror.h> -#include <math.h> // for ceil() and round() -#include <algorithm> // for min and max (clamp is C++17) - -void fl_free_fonts(void); -void fl_release_dc(HWND, HDC); -void fl_cleanup_dc_list(void); - -#include <config.h> -#include <FL/Fl.H> -#include <FL/platform.H> -#include "Fl_Window_Driver.H" -#include "Fl_Screen_Driver.H" -#include "Fl_Timeout.h" -#include "print_button.h" -#include <FL/Fl_Graphics_Driver.H> // for fl_graphics_driver -#include "drivers/WinAPI/Fl_WinAPI_Window_Driver.H" -#include "drivers/WinAPI/Fl_WinAPI_System_Driver.H" -#include "drivers/WinAPI/Fl_WinAPI_Screen_Driver.H" -#include "drivers/GDI/Fl_GDI_Graphics_Driver.H" -#include <FL/fl_utf8.h> -#include <FL/fl_string_functions.h> -#include <FL/Fl_Window.H> -#include <FL/fl_draw.H> -#include <FL/Enumerations.H> -#include <FL/Fl_Tooltip.H> -#include <FL/Fl_Paged_Device.H> -#include <FL/Fl_Image_Surface.H> -#include "flstring.h" -#include "drivers/GDI/Fl_Font.H" -#include <stdio.h> -#include <stdlib.h> -#include <sys/types.h> -#include <time.h> -#include <signal.h> -#ifdef __CYGWIN__ -# include <sys/time.h> -# include <unistd.h> -#endif - -#if !defined(NO_TRACK_MOUSE) -# include <commctrl.h> // TrackMouseEvent -#endif - -#if defined(__GNUC__) -# include <wchar.h> -#endif - -// old versions of MinGW lack definition of GET_XBUTTON_WPARAM: - -#ifndef GET_XBUTTON_WPARAM -#define GET_XBUTTON_WPARAM(wParam) (HIWORD(wParam)) -#endif - -static bool is_dpi_aware = false; - -extern bool fl_clipboard_notify_empty(void); -extern void fl_trigger_clipboard_notify(int source); -extern HBRUSH fl_brush_action(int action); -extern void fl_cleanup_pens(void); - -// MSVC 2010 can't find round() although <math.h> is included above, -// which is surprising because ceil() works fine. -// We could (should?) probably add CMake feature tests for round() -// and ceil() rather than depending on MSVC version numbers. -// AlbrechtS, 02/2010 - Note: we don't know about MSVC 2012 - 2015, see -// https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros - -#if defined(_MSC_VER) && _MSC_VER <= 1600 -#define round(A) int((A) + 0.5) -#endif // _MSC_VER <= 1600 - -// Internal functions -static void fl_clipboard_notify_target(HWND wnd); -static void fl_clipboard_notify_untarget(HWND wnd); -static int clamp(int v, int a, int b) { - if (v < a) return a; - if (v > b) return b; - return v; -} - -// Internal variables -static HWND clipboard_wnd = 0; -static HWND next_clipboard_wnd = 0; - -static bool initial_clipboard = true; - -// dynamic wsock dll handling api: -#if defined(__CYGWIN__) && !defined(SOCKET) -# define SOCKET int -#endif - -/* - Dynamic linking of imm32.dll - This library is only needed for a hand full (four ATM) functions relating to - international text rendering and locales. Dynamically loading reduces initial - size and link dependencies. -*/ -static HMODULE s_imm_module = 0; -typedef BOOL(WINAPI *flTypeImmAssociateContextEx)(HWND, HIMC, DWORD); -static flTypeImmAssociateContextEx flImmAssociateContextEx = 0; -typedef HIMC(WINAPI *flTypeImmGetContext)(HWND); -static flTypeImmGetContext flImmGetContext = 0; -typedef BOOL(WINAPI *flTypeImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM); -static flTypeImmSetCompositionWindow flImmSetCompositionWindow = 0; -typedef BOOL(WINAPI *flTypeImmReleaseContext)(HWND, HIMC); -static flTypeImmReleaseContext flImmReleaseContext = 0; - -static void get_imm_module() { - s_imm_module = LoadLibrary("IMM32.DLL"); - if (!s_imm_module) - Fl::fatal("FLTK Lib Error: IMM32.DLL file not found!\n\n" - "Please check your input method manager library accessibility."); - flImmAssociateContextEx = (flTypeImmAssociateContextEx)GetProcAddress(s_imm_module, "ImmAssociateContextEx"); - flImmGetContext = (flTypeImmGetContext)GetProcAddress(s_imm_module, "ImmGetContext"); - flImmSetCompositionWindow = (flTypeImmSetCompositionWindow)GetProcAddress(s_imm_module, "ImmSetCompositionWindow"); - flImmReleaseContext = (flTypeImmReleaseContext)GetProcAddress(s_imm_module, "ImmReleaseContext"); -} - -// USE_TRACK_MOUSE - define NO_TRACK_MOUSE if you don't have -// TrackMouseEvent()... -// -// Now (Dec. 2008) we can assume that current Cygwin/MinGW versions -// support the TrackMouseEvent() function, but WinCE obviously doesn't -// support it (STR 2095). Therefore, USE_TRACK_MOUSE is enabled by -// default, but you can disable it by defining NO_TRACK_MOUSE. -// -// TrackMouseEvent is only used to support window leave notifications -// under Windows. It can be used to get FL_LEAVE events, when the -// mouse leaves the _main_ application window (FLTK detects subwindow -// leave events by using normal move events). -// -// Implementation note: If the mouse cursor leaves one subwindow and -// enters another window, then Windows sends a WM_MOUSEMOVE message to -// the new window before it sends a WM_MOUSELEAVE message to the old -// (just left) window. We save the current mouse window in a static variable, -// and if we get a WM_MOUSELEAVE event for the current mouse window, this -// means that the top level window has been left (otherwise we would have -// got another WM_MOUSEMOVE message before). - -// #define NO_TRACK_MOUSE - -#if !defined(NO_TRACK_MOUSE) -# define USE_TRACK_MOUSE -#endif // NO_TRACK_MOUSE - -static Fl_Window *track_mouse_win = 0; // current TrackMouseEvent() window - -// USE_CAPTURE_MOUSE_WIN - this must be defined for TrackMouseEvent to work -// correctly with subwindows - otherwise a single mouse click and release -// (without a move) would generate phantom leave events. -// This defines, if the current mouse window (maybe a subwindow) or the -// main window should get mouse events after pushing (and holding) a mouse -// button, i.e. when dragging the mouse. This is done by calling SetCapture -// (see below). - -#ifdef USE_TRACK_MOUSE -#define USE_CAPTURE_MOUSE_WIN -#endif // USE_TRACK_MOUSE - -// -// WM_SYNCPAINT is an "undocumented" message, which is finally defined in -// VC++ 6.0. -// - -#ifndef WM_SYNCPAINT -# define WM_SYNCPAINT 0x0088 -#endif - -#ifndef WM_MOUSELEAVE -# define WM_MOUSELEAVE 0x02a3 -#endif - -#ifndef WM_MOUSEWHEEL -# define WM_MOUSEWHEEL 0x020a -#endif - -#ifndef WHEEL_DELTA -# define WHEEL_DELTA 120 // according to MSDN. -#endif - -// This is only defined on Vista and upwards... -#ifndef WM_MOUSEHWHEEL -# define WM_MOUSEHWHEEL 0x020E -#endif - -#ifndef SM_CXPADDEDBORDER -# define SM_CXPADDEDBORDER (92) // STR #3061 -#endif - -// https://msdn.microsoft.com/en-us/library/windows/desktop/dn312083(v=vs.85).aspx -#ifndef WM_DPICHANGED -# define WM_DPICHANGED 0x02E0 -#endif - -// -// WM_FLSELECT is the user-defined message that we get when one of -// the sockets has pending data, etc. -// - -#define WM_FLSELECT (WM_APP + 1) // WM_APP is used for hide-window - - -//////////////////////////////////////////////////////////////// -// interface to poll/select call: - -// fd's are only implemented for sockets. Microsoft Windows does not -// have a unified IO system, so it doesn't support select() on files, -// devices, or pipes... -// -// Microsoft provides the Berkeley select() call and an asynchronous -// select function that sends a Windows message when the select condition -// exists. However, we don't try to use the asynchronous WSAAsyncSelect() -// any more for good reasons (see above). -// -// A.S. Dec 2009: We got reports that current winsock2.h files define -// POLLIN, POLLOUT, and POLLERR with conflicting values WRT what we -// used before (STR #2301). Therefore we must not use these values -// for our internal purposes, but use FL_READ, FL_WRITE, and -// FL_EXCEPT, as defined for use in Fl::add_fd(). -// -static int maxfd = 0; -static fd_set fdsets[3]; - -extern IDropTarget *flIDropTarget; - -static int nfds = 0; -static int fd_array_size = 0; -static struct FD { - int fd; - short events; - void (*cb)(FL_SOCKET, void *); // keep socket api opaque at this level to reduce multiplatform deps headaches - void *arg; -} *fd = 0; - -extern unsigned int fl_codepage; - -void Fl_WinAPI_System_Driver::add_fd(int n, int events, void (*cb)(FL_SOCKET, void *), void *v) { - remove_fd(n, events); - int i = nfds++; - if (i >= fd_array_size) { - fd_array_size = 2 * fd_array_size + 1; - fd = (FD *)realloc(fd, fd_array_size * sizeof(FD)); - } - fd[i].fd = n; - fd[i].events = (short)events; - fd[i].cb = cb; - fd[i].arg = v; - - if (events & FL_READ) - FD_SET((unsigned)n, &fdsets[0]); - if (events & FL_WRITE) - FD_SET((unsigned)n, &fdsets[1]); - if (events & FL_EXCEPT) - FD_SET((unsigned)n, &fdsets[2]); - if (n > maxfd) - maxfd = n; -} - -void Fl_WinAPI_System_Driver::add_fd(int fd, void (*cb)(FL_SOCKET, void *), void *v) { - add_fd(fd, FL_READ, cb, v); -} - -void Fl_WinAPI_System_Driver::remove_fd(int n, int events) { - int i, j; - for (i = j = 0; i < nfds; i++) { - if (fd[i].fd == n) { - short e = fd[i].events & ~events; - if (!e) - continue; // if no events left, delete this fd - fd[i].events = e; - } - // move it down in the array if necessary: - if (j < i) { - fd[j] = fd[i]; - } - j++; - } - nfds = j; - - if (events & FL_READ) - FD_CLR(unsigned(n), &fdsets[0]); - if (events & FL_WRITE) - FD_CLR(unsigned(n), &fdsets[1]); - if (events & FL_EXCEPT) - FD_CLR(unsigned(n), &fdsets[2]); -} - -void Fl_WinAPI_System_Driver::remove_fd(int n) { - remove_fd(n, -1); -} - -// these pointers are set by the Fl::lock() function: -static void nothing() {} -void (*fl_lock_function)() = nothing; -void (*fl_unlock_function)() = nothing; - -static void *thread_message_; -void *Fl_WinAPI_System_Driver::thread_message() { - void *r = thread_message_; - thread_message_ = 0; - return r; -} - -extern int fl_send_system_handlers(void *e); - -MSG fl_msg; - -// A local helper function to flush any pending callback requests -// from the awake ring-buffer -static void process_awake_handler_requests(void) { - Fl_Awake_Handler func; - void *data; - while (Fl_WinAPI_System_Driver::pop_awake_handler(func, data) == 0) { - func(data); - } -} - -// This is never called with time_to_wait < 0.0. -// It *should* return negative on error, 0 if nothing happens before -// timeout, and >0 if any callbacks were done. This version -// always returns 1. -double Fl_WinAPI_System_Driver::wait(double time_to_wait) { - - time_to_wait = Fl_System_Driver::wait(time_to_wait); - - int have_message = 0; - - if (nfds) { - // For Windows we need to poll for socket input FIRST, since - // the event queue is not something we can select() on... - timeval t; - t.tv_sec = 0; - t.tv_usec = 0; - - fd_set fdt[3]; - memcpy(fdt, fdsets, sizeof fdt); // one shot faster fdt init - if (select(maxfd + 1, &fdt[0], &fdt[1], &fdt[2], &t)) { - // We got something - do the callback! - for (int i = 0; i < nfds; i++) { - SOCKET f = fd[i].fd; - short revents = 0; - if (FD_ISSET(f, &fdt[0])) - revents |= FL_READ; - if (FD_ISSET(f, &fdt[1])) - revents |= FL_WRITE; - if (FD_ISSET(f, &fdt[2])) - revents |= FL_EXCEPT; - if (fd[i].events & revents) - fd[i].cb(f, fd[i].arg); - } - time_to_wait = 0.0; // just peek for any messages - } else { - // we need to check them periodically, so set a short timeout: - if (time_to_wait > .001) - time_to_wait = .001; - } - } - - if (Fl::idle() || Fl::damage()) - time_to_wait = 0.0; - - // if there are no more windows and this timer is set - // to FOREVER, continue through or look up indefinitely - if (!Fl::first_window() && time_to_wait == 1e20) - time_to_wait = 0.0; - - fl_unlock_function(); - - time_to_wait = (time_to_wait > 10000 ? 10000 : time_to_wait); - - time_to_wait = Fl_Timeout::time_to_wait(time_to_wait); - - int t_msec = (int)(time_to_wait * 1000.0 + 0.5); - MsgWaitForMultipleObjects(0, NULL, FALSE, t_msec, QS_ALLINPUT); - - fl_lock_function(); - - // Execute the message we got, and all other pending messages: - // have_message = PeekMessage(&fl_msg, NULL, 0, 0, PM_REMOVE); - while ((have_message = PeekMessageW(&fl_msg, NULL, 0, 0, PM_REMOVE)) > 0) { - if (fl_send_system_handlers(&fl_msg)) - continue; - - // Let applications treat WM_QUIT identical to SIGTERM on *nix - if (fl_msg.message == WM_QUIT) - raise(SIGTERM); - - if (fl_msg.message == fl_wake_msg) { - // Used for awaking wait() from another thread - thread_message_ = (void *)fl_msg.wParam; - process_awake_handler_requests(); - } - - TranslateMessage(&fl_msg); - DispatchMessageW(&fl_msg); - } - - // The following conditional test: !Fl_System_Driver::awake_ring_empty() - // equivalent to: - // (Fl::awake_ring_head_ != Fl::awake_ring_tail_) - // is a workaround / fix for STR #3143. This works, but a better solution - // would be to understand why the PostThreadMessage() messages are not - // seen by the main window if it is being dragged/ resized at the time. - // If a worker thread posts an awake callback to the ring buffer - // whilst the main window is unresponsive (if a drag or resize operation - // is in progress) we may miss the PostThreadMessage(). So here, we check if - // there is anything pending in the awake ring buffer and if so process - // it. This is not strictly thread safe (for speed it compares the head - // and tail indices without first locking the ring buffer) but is intended - // only as a fall-back recovery mechanism if the awake processing stalls. - // If the test erroneously returns true (may happen if we test the indices - // whilst they are being modified) we will call process_awake_handler_requests() - // unnecessarily, but this has no harmful consequences so is safe to do. - // Note also that if we miss the PostThreadMessage(), then thread_message_ - // will not be updated, so this is not a perfect solution, but it does - // recover and process any pending awake callbacks. - // Normally the ring buffer head and tail indices will match and this - // comparison will do nothing. Addresses STR #3143 - if (!Fl_System_Driver::awake_ring_empty()) { - process_awake_handler_requests(); - } - - Fl::flush(); - - // This should return 0 if only timer events were handled: - return 1; -} - -// just like Fl_WinAPI_System_Driver::wait(0.0) except no callbacks are done: -int Fl_WinAPI_System_Driver::ready() { - if (PeekMessage(&fl_msg, NULL, 0, 0, PM_NOREMOVE)) - return 1; - if (!nfds) - return 0; - timeval t; - t.tv_sec = 0; - t.tv_usec = 0; - fd_set fdt[3]; - memcpy(fdt, fdsets, sizeof fdt); - return select(0, &fdt[0], &fdt[1], &fdt[2], &t); -} - -static void delayed_create_print_window(void *) { - Fl::remove_check(delayed_create_print_window); - fl_create_print_window(); -} - -void Fl_WinAPI_Screen_Driver::open_display_platform() { - static char beenHereDoneThat = 0; - - if (beenHereDoneThat) - return; - - beenHereDoneThat = 1; - // test whether DpiAwareness has been set before via a manifest - /*enum PROCESS_DPI_AWARENESS { // in shellscalingapi.h from Window 8.1 - PROCESS_DPI_UNAWARE, - PROCESS_SYSTEM_DPI_AWARE, - PROCESS_PER_MONITOR_DPI_AWARE - };*/ - typedef HRESULT(WINAPI * GetProcessDpiAwareness_type)(HANDLE, int *); - GetProcessDpiAwareness_type fl_GetProcessDpiAwareness = - (GetProcessDpiAwareness_type)GetProcAddress(LoadLibrary("Shcore.DLL"), "GetProcessDpiAwareness"); - int awareness; - if (!fl_GetProcessDpiAwareness || fl_GetProcessDpiAwareness(NULL, &awareness) != S_OK) { - awareness = 0; //corresponds to PROCESS_DPI_UNAWARE; - } - if (awareness == 2 /*PROCESS_PER_MONITOR_DPI_AWARE*/) is_dpi_aware = true; - if (awareness == 0 /*PROCESS_DPI_UNAWARE*/) { // DpiAwareness has not been set via a manifest - typedef void *fl_DPI_AWARENESS_CONTEXT; - typedef BOOL(WINAPI * SetProcessDpiAwarenessContext_type)(fl_DPI_AWARENESS_CONTEXT); - SetProcessDpiAwarenessContext_type fl_SetProcessDpiAwarenessContext = - (SetProcessDpiAwarenessContext_type)GetProcAddress(LoadLibrary("User32.DLL"), "SetProcessDpiAwarenessContext"); - if (fl_SetProcessDpiAwarenessContext) { - const fl_DPI_AWARENESS_CONTEXT fl_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = (fl_DPI_AWARENESS_CONTEXT)(-4); - is_dpi_aware = fl_SetProcessDpiAwarenessContext(fl_DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - } - if (!is_dpi_aware) { - typedef HRESULT(WINAPI * SetProcessDpiAwareness_type)(int); - SetProcessDpiAwareness_type fl_SetProcessDpiAwareness = - (SetProcessDpiAwareness_type)GetProcAddress(LoadLibrary("Shcore.DLL"), "SetProcessDpiAwareness"); - if (fl_SetProcessDpiAwareness) { - const int fl_PROCESS_PER_MONITOR_DPI_AWARE = 2; - if (fl_SetProcessDpiAwareness(fl_PROCESS_PER_MONITOR_DPI_AWARE) == S_OK) is_dpi_aware = true; - } - } - } - OleInitialize(0L); - get_imm_module(); - Fl::add_check(delayed_create_print_window); -} - - -void Fl_WinAPI_Screen_Driver::update_scaling_capability() { - scaling_capability = SYSTEMWIDE_APP_SCALING; - for (int ns = 1; ns < screen_count(); ns++) { - if (scale(ns) != scale(0)) { - scaling_capability = PER_SCREEN_APP_SCALING; - break; - } - } -} - -void Fl_WinAPI_Screen_Driver::desktop_scale_factor() { - typedef HRESULT(WINAPI * GetDpiForMonitor_type)(HMONITOR, int, UINT *, UINT *); - typedef HMONITOR(WINAPI * MonitorFromRect_type)(LPCRECT, DWORD); - GetDpiForMonitor_type fl_GetDpiForMonitor = NULL; - MonitorFromRect_type fl_MonitorFromRect = NULL; - if (is_dpi_aware) { - fl_GetDpiForMonitor = (GetDpiForMonitor_type)GetProcAddress(LoadLibrary("Shcore.DLL"), "GetDpiForMonitor"); - if (fl_GetDpiForMonitor) - fl_MonitorFromRect = (MonitorFromRect_type)GetProcAddress(LoadLibrary("User32.DLL"), "MonitorFromRect"); - } - for (int ns = 0; ns < screen_count(); ns++) { - UINT dpiX, dpiY; - HRESULT r = E_INVALIDARG; - if (fl_GetDpiForMonitor && fl_MonitorFromRect) { - HMONITOR hm = fl_MonitorFromRect(&screens[ns], MONITOR_DEFAULTTONEAREST); - r = fl_GetDpiForMonitor(hm, 0, &dpiX, &dpiY); - } - if (r != S_OK) { dpiX = dpiY = 96; } - dpi[ns][0] = float(dpiX); - dpi[ns][1] = float(dpiY); - scale(ns, dpiX / 96.f); - // fprintf(LOG, "desktop_scale_factor ns=%d factor=%.2f dpi=%.1f\n", ns, scale(ns), dpi[ns][0]); - } - update_scaling_capability(); -} - - -class Fl_Win32_At_Exit { -public: - Fl_Win32_At_Exit() {} - ~Fl_Win32_At_Exit() { - fl_free_fonts(); // do some Windows cleanup - fl_cleanup_pens(); - OleUninitialize(); - if (fl_graphics_driver) fl_brush_action(1); - fl_cleanup_dc_list(); - // This is actually too late in the cleanup process to remove the - // clipboard notifications, but we have no earlier hook so we try - // to work around it anyway. - if (clipboard_wnd != NULL) - fl_clipboard_notify_untarget(clipboard_wnd); -#if USE_GDIPLUS - Fl_GDIplus_Graphics_Driver::shutdown(); -#endif - } -}; -static Fl_Win32_At_Exit win32_at_exit; - -static char im_enabled = 1; - -void Fl_WinAPI_Screen_Driver::enable_im() { - open_display(); - - Fl_X *i = Fl_X::first; - while (i) { - flImmAssociateContextEx((HWND)i->xid, 0, IACE_DEFAULT); - i = i->next; - } - - im_enabled = 1; -} - -void Fl_WinAPI_Screen_Driver::disable_im() { - open_display(); - - Fl_X *i = Fl_X::first; - while (i) { - flImmAssociateContextEx((HWND)i->xid, 0, 0); - i = i->next; - } - - im_enabled = 0; -} - -void Fl_WinAPI_Screen_Driver::set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win) -{ - if (!win) return; - Fl_Window* tw = win->top_window(); - - if (!tw->shown()) - return; - - HIMC himc = flImmGetContext(fl_xid(tw)); - - if (himc) { - COMPOSITIONFORM cfs; - float s = Fl_Graphics_Driver::default_driver().scale(); - cfs.dwStyle = CFS_POINT; - cfs.ptCurrentPos.x = int(X * s); - cfs.ptCurrentPos.y = int(Y * s) - int(tw->labelsize() * s); - // Attempt to have temporary text entered by input method use scaled font. - // Does good, but still not always effective. - Fl_GDI_Font_Descriptor *desc = (Fl_GDI_Font_Descriptor*)Fl_Graphics_Driver::default_driver().font_descriptor(); - if (desc) SelectObject((HDC)Fl_Graphics_Driver::default_driver().gc(), desc->fid); - MapWindowPoints(fl_xid(win), fl_xid(tw), &cfs.ptCurrentPos, 1); - flImmSetCompositionWindow(himc, &cfs); - flImmReleaseContext(fl_xid(tw), himc); - } -} - - -//////////////////////////////////////////////////////////////// - -int Fl_WinAPI_Screen_Driver::get_mouse_unscaled(int &mx, int &my) { - POINT p; - GetCursorPos(&p); - mx = p.x; - my = p.y; - int screen = screen_num_unscaled(mx, my); - return screen >= 0 ? screen : 0; -} - -int Fl_WinAPI_Screen_Driver::get_mouse(int &x, int &y) { - int n = get_mouse_unscaled(x, y); - float s = scale(n); - x = int(x / s); - y = int(y / s); - return n; -} - -//////////////////////////////////////////////////////////////// -// code used for selections: - -char *fl_selection_buffer[2]; -int fl_selection_length[2]; -int fl_selection_buffer_length[2]; -char fl_i_own_selection[2]; - -UINT fl_get_lcid_codepage(LCID id) { - char buf[8]; - buf[GetLocaleInfo(id, LOCALE_IDEFAULTANSICODEPAGE, buf, 8)] = 0; - return atol(buf); -} - -// Convert \n -> \r\n -class Lf2CrlfConvert { - char *out; - int outlen; - -public: - Lf2CrlfConvert(const char *in, int inlen) { - outlen = 0; - const char *i; - char *o; - int lencount; - // Predict size of \r\n conversion buffer - for (i = in, lencount = inlen; lencount > 0; lencount--) { - if (*i == '\r' && *(i + 1) == '\n' && lencount >= 2) { // leave \r\n untranslated - i += 2; - outlen += 2; - lencount--; - } else if (*i == '\n') { // \n by itself? leave room to insert \r - i++; - outlen += 2; - } else { - ++i; - ++outlen; - } - } - // Alloc conversion buffer + NULL - out = new char[outlen + 1]; - // Handle \n -> \r\n conversion - for (i = in, o = out, lencount = inlen; lencount > 0; lencount--) { - if (*i == '\r' && *(i + 1) == '\n' && lencount >= 2) { // leave \r\n untranslated - *o++ = *i++; - *o++ = *i++; - lencount--; - } else if (*i == '\n') { // \n by itself? insert \r - *o++ = '\r'; - *o++ = *i++; - } else { - *o++ = *i++; - } - } - *o++ = 0; - } - ~Lf2CrlfConvert() { - delete[] out; - } - int GetLength() const { return (outlen); } - const char *GetValue() const { return (out); } -}; - -void fl_update_clipboard(void) { - Fl_Window *w1 = Fl::first_window(); - if (!w1) - return; - - HWND hwnd = fl_xid(w1); - - if (!OpenClipboard(hwnd)) - return; - - EmptyClipboard(); - - int utf16_len = fl_utf8toUtf16(fl_selection_buffer[1], fl_selection_length[1], 0, 0); - - HGLOBAL hMem = GlobalAlloc(GHND, utf16_len * 2 + 2); // moveable and zero'ed mem alloc. - LPVOID memLock = GlobalLock(hMem); - - fl_utf8toUtf16(fl_selection_buffer[1], fl_selection_length[1], (unsigned short *)memLock, utf16_len + 1); - - GlobalUnlock(hMem); - SetClipboardData(CF_UNICODETEXT, hMem); - - CloseClipboard(); - - // In case Windows managed to lob of a WM_DESTROYCLIPBOARD during - // the above. - fl_i_own_selection[1] = 1; -} - -// call this when you create a selection: -void Fl_WinAPI_Screen_Driver::copy(const char *stuff, int len, int clipboard, const char *type) { - if (!stuff || len < 0) - return; - if (clipboard >= 2) - clipboard = 1; // Only on X11 do multiple clipboards make sense. - - // Convert \n -> \r\n (for old apps like Notepad, DOS) - Lf2CrlfConvert buf(stuff, len); - len = buf.GetLength(); - stuff = buf.GetValue(); - - if (len + 1 > fl_selection_buffer_length[clipboard]) { - delete[] fl_selection_buffer[clipboard]; - fl_selection_buffer[clipboard] = new char[len + 100]; - fl_selection_buffer_length[clipboard] = len + 100; - } - memcpy(fl_selection_buffer[clipboard], stuff, len); - fl_selection_buffer[clipboard][len] = 0; // needed for direct paste - fl_selection_length[clipboard] = len; - fl_i_own_selection[clipboard] = 1; - if (clipboard) - fl_update_clipboard(); -} - -// Call this when a "paste" operation happens: -void Fl_WinAPI_Screen_Driver::paste(Fl_Widget &receiver, int clipboard, const char *type) { - if (!clipboard || (fl_i_own_selection[clipboard] && strcmp(type, Fl::clipboard_plain_text) == 0)) { - // We already have it, do it quickly without window server. - // Notice that the text is clobbered if set_selection is - // called in response to FL_PASTE! - char *i = fl_selection_buffer[clipboard]; - if (i == 0L) { - Fl::e_text = 0; - return; - } - char *clip_text = new char[fl_selection_length[clipboard] + 1]; - char *o = clip_text; - while (*i) { // Convert \r\n -> \n - if (*i == '\r' && *(i + 1) == '\n') - i++; - else - *o++ = *i++; - } - *o = 0; - Fl::e_text = clip_text; - Fl::e_length = (int)(o - Fl::e_text); - Fl::e_clipboard_type = Fl::clipboard_plain_text; - receiver.handle(FL_PASTE); // this may change Fl::e_text - delete[] clip_text; - Fl::e_text = 0; - } else if (clipboard) { - HANDLE h; - if (!OpenClipboard(NULL)) - return; - if (strcmp(type, Fl::clipboard_plain_text) == 0) { // we want plain text from clipboard - if ((h = GetClipboardData(CF_UNICODETEXT))) { // there's text in the clipboard - wchar_t *memLock = (wchar_t *)GlobalLock(h); - size_t utf16_len = wcslen(memLock); - char *clip_text = new char[utf16_len * 4 + 1]; - unsigned utf8_len = fl_utf8fromwc(clip_text, (unsigned)(utf16_len * 4), memLock, (unsigned)utf16_len); - *(clip_text + utf8_len) = 0; - GlobalUnlock(h); - LPSTR a, b; - a = b = clip_text; - while (*a) { // strip the CRLF pairs ($%$#@^) - if (*a == '\r' && a[1] == '\n') - a++; - else - *b++ = *a++; - } - *b = 0; - Fl::e_text = clip_text; - Fl::e_length = (int)(b - Fl::e_text); - Fl::e_clipboard_type = Fl::clipboard_plain_text; // indicates that the paste event is for plain UTF8 text - receiver.handle(FL_PASTE); // send the FL_PASTE event to the widget. May change Fl::e_text - delete[] clip_text; - Fl::e_text = 0; - } - } else if (strcmp(type, Fl::clipboard_image) == 0) { // we want an image from clipboard - uchar *rgb = NULL; - Fl_RGB_Image *image = NULL; - int width = 0, height = 0, depth = 0; - if ((h = GetClipboardData(CF_DIB))) { // if there's a DIB in clipboard - LPBITMAPINFO lpBI = (LPBITMAPINFO)GlobalLock(h); - width = lpBI->bmiHeader.biWidth; // bitmap width & height - height = lpBI->bmiHeader.biHeight; // is < 0 for top-down DIB - if ((lpBI->bmiHeader.biBitCount == 24 || lpBI->bmiHeader.biBitCount == 32) && - lpBI->bmiHeader.biCompression == BI_RGB && - lpBI->bmiHeader.biClrUsed == 0) { // direct use of the DIB data if it's RGB or RGBA - int linewidth; // row length - depth = lpBI->bmiHeader.biBitCount / 8; // 3 or 4 - if (depth == 3) - linewidth = 4 * ((3 * width + 3) / 4); // row length: series of groups of 3 bytes, rounded to multiple of 4 bytes - else - linewidth = 4 * width; - rgb = new uchar[width * abs(height) * depth]; // will hold the image data - uchar *p = rgb, *r, rr, gg, bb; - int step = (height > 0 ? -1 : +1); - int from = (height > 0 ? height-1 : 0); - int to = (height > 0 ? 0 : -height-1); - for (int i = from; (height > 0 ? i>=to : i <=to); i += step) {// for each row, from last to first - r = (uchar *)(lpBI->bmiColors) + i * linewidth; // beginning of pixel data for the ith row - for (int j = 0; j < width; j++) { // for each pixel in a row - bb = *r++; // BGR is in DIB - gg = *r++; - rr = *r++; - *p++ = rr; // we want RGB - *p++ = gg; - *p++ = bb; - if (depth == 4) - *p++ = *r++; // copy alpha if present - } - } - } else { // the system will decode a complex DIB - void *pDIBBits = (void *)(lpBI->bmiColors + 256); - if (lpBI->bmiHeader.biCompression == BI_BITFIELDS) - pDIBBits = (void *)(lpBI->bmiColors + 3); - else if (lpBI->bmiHeader.biClrUsed > 0) - pDIBBits = (void *)(lpBI->bmiColors + lpBI->bmiHeader.biClrUsed); - Fl_Image_Surface *surf = new Fl_Image_Surface(width, abs(height)); - Fl_Surface_Device::push_current(surf); - SetDIBitsToDevice((HDC)fl_graphics_driver->gc(), 0, 0, width, abs(height), 0, 0, 0, abs(height), pDIBBits, lpBI, DIB_RGB_COLORS); - rgb = fl_read_image(NULL, 0, 0, width, abs(height)); - depth = 3; - Fl_Surface_Device::pop_current(); - delete surf; - } - GlobalUnlock(h); - } else if ((h = GetClipboardData(CF_ENHMETAFILE))) { // if there's an enhanced metafile in clipboard - ENHMETAHEADER header; - GetEnhMetaFileHeader((HENHMETAFILE)h, sizeof(header), &header); // get structure containing metafile dimensions - width = (header.rclFrame.right - header.rclFrame.left + 1); // in .01 mm units - height = (header.rclFrame.bottom - header.rclFrame.top + 1); - HDC hdc = GetDC(NULL); // get unit correspondance between .01 mm and screen pixels - int hmm = GetDeviceCaps(hdc, HORZSIZE); - int hdots = GetDeviceCaps(hdc, HORZRES); - ReleaseDC(NULL, hdc); - float factor = (100.f * hmm) / hdots; - float scaling = Fl::screen_driver()->scale(Fl_Window_Driver::driver(receiver.top_window())->screen_num()); - if (!Fl_Window::current()) { - Fl_GDI_Graphics_Driver *d = (Fl_GDI_Graphics_Driver*)&Fl_Graphics_Driver::default_driver(); - d->scale(scaling);// may run early at app startup before Fl_Window::make_current() scales d - } - width = int(width / (scaling * factor)); // convert to screen pixel unit - height = int(height / (scaling * factor)); - RECT rect = {0, 0, width, height}; - Fl_Image_Surface *surf = new Fl_Image_Surface(width, height, 1); - Fl_Surface_Device::push_current(surf); - fl_color(FL_WHITE); // draw white background - fl_rectf(0, 0, width, height); - rect.right = LONG(rect.right * scaling); // apply scaling to the metafile draw operation - rect.bottom = LONG(rect.bottom * scaling); - PlayEnhMetaFile((HDC)fl_graphics_driver->gc(), (HENHMETAFILE)h, &rect); // draw metafile to offscreen buffer - image = surf->image(); - Fl_Surface_Device::pop_current(); - delete surf; - } - if (rgb || image) { - if (!image) { - image = new Fl_RGB_Image(rgb, width, abs(height), depth); // create new image from pixel data - image->alloc_array = 1; - } - Fl::e_clipboard_data = image; - Fl::e_clipboard_type = Fl::clipboard_image; // indicates that the paste event is for image data - int done = receiver.handle(FL_PASTE); // send FL_PASTE event to widget - Fl::e_clipboard_type = ""; - if (done == 0) { // if widget did not handle the event, delete the image - Fl::e_clipboard_data = NULL; - delete image; - } - } - } - CloseClipboard(); - } -} - -int Fl_WinAPI_Screen_Driver::clipboard_contains(const char *type) { - int retval = 0; - if (!OpenClipboard(NULL)) - return 0; - if (strcmp(type, Fl::clipboard_plain_text) == 0 || type[0] == 0) { - retval = IsClipboardFormatAvailable(CF_UNICODETEXT); - } else if (strcmp(type, Fl::clipboard_image) == 0) { - retval = IsClipboardFormatAvailable(CF_DIB) || IsClipboardFormatAvailable(CF_ENHMETAFILE); - } - CloseClipboard(); - return retval; -} - -static void fl_clipboard_notify_target(HWND wnd) { - if (clipboard_wnd) - return; - - // We get one fake WM_DRAWCLIPBOARD immediately, which we therefore - // need to ignore. - initial_clipboard = true; - - clipboard_wnd = wnd; - next_clipboard_wnd = SetClipboardViewer(wnd); -} - -static void fl_clipboard_notify_untarget(HWND wnd) { - if (wnd != clipboard_wnd) - return; - - // We might be called late in the cleanup where Windows has already - // implicitly destroyed our clipboard window. At that point we need - // to do some extra work to manually repair the clipboard chain. - if (IsWindow(wnd)) - ChangeClipboardChain(wnd, next_clipboard_wnd); - else { - HWND tmp, head; - - tmp = CreateWindow("STATIC", "Temporary FLTK Clipboard Window", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); - if (tmp == NULL) - return; - - head = SetClipboardViewer(tmp); - if (head == NULL) - ChangeClipboardChain(tmp, next_clipboard_wnd); - else { - SendMessage(head, WM_CHANGECBCHAIN, (WPARAM)wnd, (LPARAM)next_clipboard_wnd); - ChangeClipboardChain(tmp, head); - } - - DestroyWindow(tmp); - } - - clipboard_wnd = next_clipboard_wnd = 0; -} - -void fl_clipboard_notify_retarget(HWND wnd) { - // The given window is getting destroyed. If it's part of the - // clipboard chain then we need to unregister it and find a - // replacement window. - if (wnd != clipboard_wnd) - return; - - fl_clipboard_notify_untarget(wnd); - - if (Fl::first_window()) - fl_clipboard_notify_target(fl_xid(Fl::first_window())); -} - -void Fl_WinAPI_Screen_Driver::clipboard_notify_change() { - // untarget clipboard monitor if no handlers are registered - if (clipboard_wnd != NULL && fl_clipboard_notify_empty()) { - fl_clipboard_notify_untarget(clipboard_wnd); - return; - } - - // if there are clipboard notify handlers but no window targeted - // target first window if available - if (clipboard_wnd == NULL && Fl::first_window()) - fl_clipboard_notify_target(fl_xid(Fl::first_window())); -} - -//////////////////////////////////////////////////////////////// -void fl_get_codepage() { - HKL hkl = GetKeyboardLayout(0); - TCHAR ld[8]; - - GetLocaleInfo(LOWORD(hkl), LOCALE_IDEFAULTANSICODEPAGE, ld, 6); - DWORD ccp = atol(ld); - fl_codepage = ccp; -} - -HWND fl_capture; - -// \param[in] window The FLTK window that generated the event -// \param[in] what 0 = down, 1 = double down, 2 = up, 3 = move -// \param[in] button 1 = left, 2 = middle, 3 = right, 4 = XBUTTON1, 5 = any other XBUTTON -// \param[in] wParam depends on event, for example MK_LBUTTON, MK_MBUTTON, MK_RBUTTON, MK_XBUTTON1, MK_XBUTTON2 -// \param[in] lParam 32 bit value, top 16 bit are the signed y coordinate, bottom 16 bits is x in window space -// WM_KILLFOCUS calls this with (Fl::grab(), 0, 1, MK_LBUTTON, MAKELPARAM(32767, 0)) -static int mouse_event(Fl_Window *window, int what, int button, - WPARAM wParam, LPARAM lParam) { - static int px, py, pmx, pmy; - POINT pt; - float scale = Fl::screen_driver()->scale(window->screen_num()); - Fl::e_x = pt.x = (signed short)LOWORD(lParam); - Fl::e_y = pt.y = (signed short)HIWORD(lParam); - Fl::e_x = int(Fl::e_x / scale); - Fl::e_y = int(Fl::e_y / scale); - ClientToScreen(fl_xid(window), &pt); - Fl::e_x_root = int(pt.x / scale); - Fl::e_y_root = int(pt.y / scale); -#ifdef USE_CAPTURE_MOUSE_WIN - Fl_Window *mouse_window = window; // save "mouse window" -#endif - while (window->parent()) { - Fl::e_x += window->x(); - Fl::e_y += window->y(); - window = window->window(); - } - - ulong state = Fl::e_state & 0xff0000; // keep shift key states -#if 0 - // mouse event reports some shift flags, perhaps save them? - if (wParam & MK_SHIFT) state |= FL_SHIFT; - if (wParam & MK_CONTROL) state |= FL_CTRL; -#endif - if (wParam & MK_LBUTTON) state |= FL_BUTTON1; // left - if (wParam & MK_MBUTTON) state |= FL_BUTTON2; // right - if (wParam & MK_RBUTTON) state |= FL_BUTTON3; // middle - if (wParam & MK_XBUTTON1) state |= FL_BUTTON4; // side button 1 (back) - if (wParam & MK_XBUTTON2) state |= FL_BUTTON5; // side button 2 (forward) - - Fl::e_state = state; - - switch (what) { - case 1: // double-click - if (Fl::e_is_click) { - Fl::e_clicks++; - goto J1; - } - case 0: // single-click - Fl::e_clicks = 0; - J1: -#ifdef USE_CAPTURE_MOUSE_WIN - if (!fl_capture) - SetCapture(fl_xid(mouse_window)); // use mouse window -#else - if (!fl_capture) - SetCapture(fl_xid(window)); // use main window -#endif - Fl::e_keysym = FL_Button + button; - Fl::e_is_click = 1; - px = pmx = Fl::e_x_root; - py = pmy = Fl::e_y_root; - return Fl::handle(FL_PUSH, window); - - case 2: // release: - if (!fl_capture) - ReleaseCapture(); - Fl::e_keysym = FL_Button + button; - return Fl::handle(FL_RELEASE, window); - - case 3: // move: - default: // avoid compiler warning - // Windows produces extra events even if the mouse does not move, ignore em: - if (Fl::e_x_root == pmx && Fl::e_y_root == pmy) - return 1; - pmx = Fl::e_x_root; - pmy = Fl::e_y_root; - if (abs(Fl::e_x_root - px) > 5 || abs(Fl::e_y_root - py) > 5) - Fl::e_is_click = 0; - return Fl::handle(FL_MOVE, window); - } -} - -// Convert a Windows VK_x to an FLTK (X) Keysym: -// See also the inverse converter in Fl_get_key_win32.cxx -// This table is in numeric order by VK: -static const struct { - unsigned short vk, fltk, extended; -} vktab[] = { - {VK_BACK, FL_BackSpace}, - {VK_TAB, FL_Tab}, - {VK_CLEAR, FL_KP+'5', 0xff0b/*XK_Clear*/}, - {VK_RETURN, FL_Enter, FL_KP_Enter}, - {VK_SHIFT, FL_Shift_L, FL_Shift_R}, - {VK_CONTROL, FL_Control_L, FL_Control_R}, - {VK_MENU, FL_Alt_L, FL_Alt_R}, - {VK_PAUSE, FL_Pause}, - {VK_CAPITAL, FL_Caps_Lock}, - {VK_ESCAPE, FL_Escape}, - {VK_SPACE, ' '}, - {VK_PRIOR, FL_KP+'9', FL_Page_Up}, - {VK_NEXT, FL_KP+'3', FL_Page_Down}, - {VK_END, FL_KP+'1', FL_End}, - {VK_HOME, FL_KP+'7', FL_Home}, - {VK_LEFT, FL_KP+'4', FL_Left}, - {VK_UP, FL_KP+'8', FL_Up}, - {VK_RIGHT, FL_KP+'6', FL_Right}, - {VK_DOWN, FL_KP+'2', FL_Down}, - {VK_SNAPSHOT, FL_Print}, // does not work on NT - {VK_INSERT, FL_KP+'0', FL_Insert}, - {VK_DELETE, FL_KP+'.', FL_Delete}, - {VK_LWIN, FL_Meta_L}, - {VK_RWIN, FL_Meta_R}, - {VK_APPS, FL_Menu}, - {VK_SLEEP, FL_Sleep}, - {VK_MULTIPLY, FL_KP+'*'}, - {VK_ADD, FL_KP+'+'}, - {VK_SUBTRACT, FL_KP+'-'}, - {VK_DECIMAL, FL_KP+'.'}, - {VK_DIVIDE, FL_KP+'/'}, - {VK_NUMLOCK, FL_Num_Lock}, - {VK_SCROLL, FL_Scroll_Lock}, -#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0500) - {VK_BROWSER_BACK, FL_Back}, - {VK_BROWSER_FORWARD, FL_Forward}, - {VK_BROWSER_REFRESH, FL_Refresh}, - {VK_BROWSER_STOP, FL_Stop}, - {VK_BROWSER_SEARCH, FL_Search}, - {VK_BROWSER_FAVORITES, FL_Favorites}, - {VK_BROWSER_HOME, FL_Home_Page}, - {VK_VOLUME_MUTE, FL_Volume_Mute}, - {VK_VOLUME_DOWN, FL_Volume_Down}, - {VK_VOLUME_UP, FL_Volume_Up}, - {VK_MEDIA_NEXT_TRACK, FL_Media_Next}, - {VK_MEDIA_PREV_TRACK, FL_Media_Prev}, - {VK_MEDIA_STOP, FL_Media_Stop}, - {VK_MEDIA_PLAY_PAUSE, FL_Media_Play}, - {VK_LAUNCH_MAIL, FL_Mail}, -#endif - {0xba, ';'}, - {0xbb, '='}, // 0xbb == VK_OEM_PLUS (see #1086) - {0xbc, ','}, - {0xbd, '-'}, - {0xbe, '.'}, - {0xbf, '/'}, - {0xc0, '`'}, - {0xdb, '['}, - {0xdc, '\\'}, - {0xdd, ']'}, - {0xde, '\''}, - {VK_OEM_102, FL_Iso_Key} -}; -static int ms2fltk(WPARAM vk, int extended) { - static unsigned short vklut[256]; - static unsigned short extendedlut[256]; - if (!vklut[1]) { // init the table - unsigned int i; - for (i = 0; i < 256; i++) - vklut[i] = tolower(i); - for (i = VK_F1; i <= VK_F16; i++) - vklut[i] = i + (FL_F - (VK_F1 - 1)); - for (i = VK_NUMPAD0; i <= VK_NUMPAD9; i++) - vklut[i] = i + (FL_KP + '0' - VK_NUMPAD0); - for (i = 0; i < sizeof(vktab) / sizeof(*vktab); i++) { - vklut[vktab[i].vk] = vktab[i].fltk; - extendedlut[vktab[i].vk] = vktab[i].extended; - } - for (i = 0; i < 256; i++) - if (!extendedlut[i]) - extendedlut[i] = vklut[i]; - } - return extended ? extendedlut[vk] : vklut[vk]; -} - -#if USE_COLORMAP -extern HPALETTE fl_select_palette(void); // in fl_color_win32.cxx -#endif - - -static Fl_Window *resize_bug_fix; -static bool moving_window = false; // true when dragging a window with the mouse on the titlebar - -extern void fl_save_pen(void); -extern void fl_restore_pen(void); -extern LRESULT fl_win32_tablet_handler(MSG& msg); - - -static void invalidate_gl_win(Fl_Window *glwin) { - static Fl_WinAPI_Plugin *plugin = NULL; - if (!plugin) { - Fl_Plugin_Manager pm("winapi.fltk.org"); - plugin = (Fl_WinAPI_Plugin*)pm.plugin("gl.winapi.fltk.org"); - } - plugin->invalidate(glwin); -} - - -static BOOL CALLBACK child_window_cb(HWND child_xid, LPARAM data) { - Fl_Window *child = fl_find(child_xid); - if (data) { - float s = *(float*)data; - SetWindowPos(child_xid, 0, int(round(child->x() * s)), int(round(child->y() * s)), - int(round(child->w() * s)), int(round(child->h() * s)), 0); - } - if (child->as_gl_window()) invalidate_gl_win(child); - return TRUE; -} - - -static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - - // Copy the message to fl_msg so add_handler code can see it. - // It is already there if this is called by DispatchMessage, - // but not if Windows calls this directly. - - fl_msg.hwnd = hWnd; - fl_msg.message = uMsg; - fl_msg.wParam = wParam; - fl_msg.lParam = lParam; - // fl_msg.time = ??? - // fl_msg.pt = ??? - // fl_msg.lPrivate = ??? - - Fl_Window *window = fl_find(hWnd); - float scale = (window ? Fl::screen_driver()->scale(Fl_Window_Driver::driver(window)->screen_num()) : 1); - - if (window) { - switch (uMsg) { - - case WM_DPICHANGED: { // 0x02E0, after display re-scaling and followed by WM_DISPLAYCHANGE - if (is_dpi_aware) { - RECT r, *lParam_rect = (RECT*)lParam; - Fl_WinAPI_Screen_Driver *sd = (Fl_WinAPI_Screen_Driver*)Fl::screen_driver(); - int centerX = (lParam_rect->left + lParam_rect->right)/2; - int centerY = (lParam_rect->top + lParam_rect->bottom)/2; - int ns = sd->screen_num_unscaled(centerX, centerY); - int old_ns = Fl_Window_Driver::driver(window)->screen_num(); - if (sd->dpi[ns][0] != HIWORD(wParam) && ns == old_ns) { // change DPI of a screen - sd->dpi[ns][0] = sd->dpi[ns][1] = HIWORD(wParam); - float f = HIWORD(wParam) / 96.f; - GetClientRect(hWnd, &r); - float old_f = float(r.right) / window->w(); - Fl::screen_driver()->scale(ns, f); - Fl_Window_Driver::driver(window)->resize_after_scale_change(ns, old_f, f); - sd->update_scaling_capability(); - } else if (ns != old_ns) { - // jump window with Windows+Shift+L|R-arrow to other screen with other DPI - if (ns >= 0) Fl_Window_Driver::driver(window)->screen_num(ns); - UINT flags = SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOCOPYBITS; - SetWindowPos(hWnd, NULL, lParam_rect->left, lParam_rect->top, - lParam_rect->right - lParam_rect->left, - lParam_rect->bottom - lParam_rect->top, flags); - if (ns >= 0) { - scale = Fl::screen_driver()->scale(ns); - EnumChildWindows(hWnd, child_window_cb, (LPARAM)&scale); - } - } - } - return 0; - } - - case WM_QUIT: // this should not happen? - Fl::fatal("WM_QUIT message"); - - case WM_CLOSE: // user clicked close box - Fl::handle(FL_CLOSE, window); - return 0; - - case WM_SYNCPAINT: - case WM_NCPAINT: - case WM_ERASEBKGND: - // Andreas Weitl - WM_SYNCPAINT needs to be passed to DefWindowProc - // so that Windows can generate the proper paint messages... - // Similarly, WM_NCPAINT and WM_ERASEBKGND need this, too... - break; - - case WM_PAINT: { - HRGN R, R2; - Fl_X *i = Fl_X::flx(window); - Fl_Window_Driver::driver(window)->wait_for_expose_value = 0; - char redraw_whole_window = false; - if (!i->region && window->damage()) { - // Redraw the whole window... - i->region = CreateRectRgn(0, 0, window->w(), window->h()); - redraw_whole_window = true; - } - - // We need to merge Windows' damage into FLTK's damage. - R = CreateRectRgn(0, 0, 0, 0); - int r = GetUpdateRgn(hWnd, R, 0); - if (r == NULLREGION && !redraw_whole_window) { - DeleteObject(R); - break; - } - - // convert i->region in FLTK units to R2 in drawing units - R2 = Fl_GDI_Graphics_Driver::scale_region((HRGN)i->region, scale, NULL); - - RECT r_box; - if (scale != 1 && GetRgnBox(R, &r_box) != NULLREGION) { - // add de-scaled update region to i->region in FLTK units - r_box.left = LONG(r_box.left / scale); - r_box.right = LONG(r_box.right / scale); - r_box.top = LONG(r_box.top / scale); - r_box.bottom = LONG(r_box.bottom / scale); - HRGN R3 = CreateRectRgn(r_box.left, r_box.top, r_box.right + 1, r_box.bottom + 1); - if (!i->region) i->region = R3; - else { - CombineRgn((HRGN)i->region, (HRGN)i->region, R3, RGN_OR); - DeleteObject(R3); - } - } - if (R2) { - // Also tell Windows that we are drawing someplace else as well... - CombineRgn(R2, R2, R, RGN_OR); - DeleteObject(R); - } else { - R2 = R; - } - if (window->type() == FL_DOUBLE_WINDOW) - ValidateRgn(hWnd, 0); - else { - ValidateRgn(hWnd, R2); - } - - if (scale != 1) DeleteObject(R2); - - window->clear_damage((uchar)(window->damage() | FL_DAMAGE_EXPOSE)); - // These next two statements should not be here, so that all update - // is deferred until Fl::flush() is called during idle. However Windows - // apparently is very unhappy if we don't obey it and draw right now. - // Very annoying! - fl_GetDC(hWnd); // Make sure we have a DC for this window... - fl_save_pen(); - Fl_Window_Driver::driver(window)->flush(); - fl_restore_pen(); - window->clear_damage(); - return 0; - } // case WM_PAINT - - case WM_LBUTTONDOWN: - mouse_event(window, 0, 1, wParam, lParam); - return 0; - case WM_LBUTTONDBLCLK: - mouse_event(window, 1, 1, wParam, lParam); - return 0; - case WM_LBUTTONUP: - mouse_event(window, 2, 1, wParam, lParam); - return 0; - case WM_MBUTTONDOWN: - mouse_event(window, 0, 2, wParam, lParam); - return 0; - case WM_MBUTTONDBLCLK: - mouse_event(window, 1, 2, wParam, lParam); - return 0; - case WM_MBUTTONUP: - mouse_event(window, 2, 2, wParam, lParam); - return 0; - case WM_RBUTTONDOWN: - mouse_event(window, 0, 3, wParam, lParam); - return 0; - case WM_RBUTTONDBLCLK: - mouse_event(window, 1, 3, wParam, lParam); - return 0; - case WM_RBUTTONUP: - mouse_event(window, 2, 3, wParam, lParam); - return 0; - case WM_XBUTTONDOWN: { - int xbutton = GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? 4 : 5; - mouse_event(window, 0, xbutton, wParam, lParam); - return 0; - } - case WM_XBUTTONDBLCLK: { - int xbutton = GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? 4 : 5; - mouse_event(window, 1, xbutton, wParam, lParam); - return 0; - } - case WM_XBUTTONUP: { - int xbutton = GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? 4 : 5; - mouse_event(window, 2, xbutton, wParam, lParam); - return 0; - } - - case WM_MOUSEMOVE: -#ifdef USE_TRACK_MOUSE - if (track_mouse_win != window) { - TRACKMOUSEEVENT tme; - tme.cbSize = sizeof(TRACKMOUSEEVENT); - tme.dwFlags = TME_LEAVE; - tme.hwndTrack = hWnd; - _TrackMouseEvent(&tme); - track_mouse_win = window; - } -#endif // USE_TRACK_MOUSE - mouse_event(window, 3, 0, wParam, lParam); - return 0; - - case WM_MOUSELEAVE: - if (track_mouse_win == window) { // we left the top level window ! - Fl_Window *tw = window; - while (tw->parent()) // find top level window - tw = tw->window(); - // Get a better mouse position for FL_LEAVE event (#87) - POINT pt; - if (GetCursorPos(&pt)) { - float scale = Fl::screen_driver()->scale(tw->screen_num()); - Fl::e_x_root = int(pt.x / scale); - Fl::e_y_root = int(pt.y / scale); - ScreenToClient(fl_xid(tw), &pt); - Fl::e_x = clamp(int(pt.x / scale), 0, window->w() - 1); - Fl::e_y = clamp(int(pt.y / scale), 0, window->h() - 1); - } - Fl::belowmouse(0); - Fl::handle(FL_LEAVE, tw); - } - track_mouse_win = 0; // force TrackMouseEvent() restart - break; - - case WM_SETFOCUS: - if ((Fl::modal_) && (Fl::modal_ != window)) { - SetFocus(fl_xid(Fl::modal_)); - return 0; - } - Fl::handle(FL_FOCUS, window); - break; - - case WM_KILLFOCUS: - if (Fl::grab() && (Fl::grab() != window) && Fl::grab()->menu_window()) { - // simulate click at remote location (see issue #1166), coordinates are signed 16 bit values - mouse_event(Fl::grab(), 0, 1, MK_LBUTTON, MAKELPARAM(32767, 0)); - } - Fl::handle(FL_UNFOCUS, window); - Fl::flush(); // it never returns to main loop when deactivated... - break; - - case WM_SHOWWINDOW: - if (!window->parent()) { - Fl::handle(wParam ? FL_SHOW : FL_HIDE, window); - } - break; - - case WM_ACTIVATEAPP: - // From eric@vfx.sel.sony.com, we should process WM_ACTIVATEAPP - // messages to restore the correct state of the shift/ctrl/alt/lock - // keys... Added control, shift, alt, and meta keys, and changed - // to use GetAsyncKeyState and do it when wParam is 1 - // (that means we have focus...) - if (wParam) { - ulong state = 0; - if (GetAsyncKeyState(VK_CAPITAL)) - state |= FL_CAPS_LOCK; - if (GetAsyncKeyState(VK_NUMLOCK)) - state |= FL_NUM_LOCK; - if (GetAsyncKeyState(VK_SCROLL)) - state |= FL_SCROLL_LOCK; - if (GetAsyncKeyState(VK_CONTROL) & ~1) - state |= FL_CTRL; - if (GetAsyncKeyState(VK_SHIFT) & ~1) - state |= FL_SHIFT; - if (GetAsyncKeyState(VK_MENU)) - state |= FL_ALT; - if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & ~1) - state |= FL_META; - Fl::e_state = state; - Fl::handle(FL_APP_ACTIVATE, nullptr); - } else { - Fl::handle(FL_APP_DEACTIVATE, nullptr); - } - break; - - case WM_INPUTLANGCHANGE: - fl_get_codepage(); - break; - case WM_IME_COMPOSITION: - // if (!fl_is_nt4() && lParam & GCS_RESULTCLAUSE) { - // HIMC himc = ImmGetContext(hWnd); - // wlen = ImmGetCompositionStringW(himc, GCS_RESULTSTR, - // wbuf, sizeof(wbuf)) / sizeof(short); - // if (wlen < 0) wlen = 0; - // wbuf[wlen] = 0; - // ImmReleaseContext(hWnd, himc); - // } - break; - - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - case WM_KEYUP: - case WM_SYSKEYUP: - // save the keysym until we figure out the characters: - Fl::e_keysym = Fl::e_original_keysym = ms2fltk(wParam, lParam & (1 << 24)); - // Kludge to allow recognizing ctrl+'-' on keyboards with digits in uppercase positions (e.g. French) - if (Fl::e_keysym == '6' && (VkKeyScanA('-') & 0xff) == '6') { - Fl::e_keysym = '-'; - } - // See if TranslateMessage turned it into a WM_*CHAR message: - if (PeekMessageW(&fl_msg, hWnd, WM_CHAR, WM_SYSDEADCHAR, PM_REMOVE)) { - uMsg = fl_msg.message; - wParam = fl_msg.wParam; - lParam = fl_msg.lParam; - } - // FALLTHROUGH ... - - case WM_DEADCHAR: - case WM_SYSDEADCHAR: - case WM_CHAR: - case WM_SYSCHAR: { - ulong state = Fl::e_state & 0xff000000; // keep the mouse button state - // if GetKeyState is expensive we might want to comment some of these out: - if (GetKeyState(VK_SHIFT) & ~1) - state |= FL_SHIFT; - if (GetKeyState(VK_CAPITAL)) - state |= FL_CAPS_LOCK; - if (GetKeyState(VK_CONTROL) & ~1) - state |= FL_CTRL; - // Alt gets reported for the Alt-GR switch on non-English keyboards. - // so we need to check the event as well to get it right: - if ((lParam & (1 << 29)) // same as GetKeyState(VK_MENU) - && uMsg != WM_CHAR) - state |= FL_ALT; - if (GetKeyState(VK_NUMLOCK)) - state |= FL_NUM_LOCK; - if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & ~1) { - // Windows bug? GetKeyState returns garbage if the user hit the - // meta key to pop up start menu. Sigh. - if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & ~1) - state |= FL_META; - } - if (GetKeyState(VK_SCROLL)) - state |= FL_SCROLL_LOCK; - Fl::e_state = state; - static char buffer[1024]; - if (uMsg == WM_CHAR || uMsg == WM_SYSCHAR) { - wchar_t u = (wchar_t)wParam; - // Windows emoji palette triggered with Windows + dot sends 2 or more WM_CHAR messages: - // the 2 components of a surrogate pair, or variation selectors, or zero-width joiner, - // or emoji modifiers FITZPATRICK or extra Unicode points. - if (u >= 0xD800 && u <= 0xDFFF) { // handle the 2 components of a surrogate pair - static wchar_t surrogate_pair[2]; - if (IS_HIGH_SURROGATE(u)) { - surrogate_pair[0] = u; // memorize the 1st member of the pair - Fl::e_length = 0; - return 0; // and wait for next WM_CHAR message that will give the 2nd member - } else { - surrogate_pair[1] = u; // memorize the 2nd member of the pair - Fl::e_length = fl_utf8fromwc(buffer, 1024, surrogate_pair, 2); // transform to UTF-8 - } - } else { - Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1); // process regular Unicode point - } - buffer[Fl::e_length] = 0; - } else if (Fl::e_keysym >= FL_KP && Fl::e_keysym <= FL_KP_Last) { - if (state & FL_NUM_LOCK) { - // Convert to regular keypress... - buffer[0] = Fl::e_keysym - FL_KP; - Fl::e_length = 1; - } else { - // Convert to special keypress... - buffer[0] = 0; - Fl::e_length = 0; - switch (Fl::e_keysym) { - case FL_KP + '0': - Fl::e_keysym = FL_Insert; - break; - case FL_KP + '1': - Fl::e_keysym = FL_End; - break; - case FL_KP + '2': - Fl::e_keysym = FL_Down; - break; - case FL_KP + '3': - Fl::e_keysym = FL_Page_Down; - break; - case FL_KP + '4': - Fl::e_keysym = FL_Left; - break; - case FL_KP + '6': - Fl::e_keysym = FL_Right; - break; - case FL_KP + '7': - Fl::e_keysym = FL_Home; - break; - case FL_KP + '8': - Fl::e_keysym = FL_Up; - break; - case FL_KP + '9': - Fl::e_keysym = FL_Page_Up; - break; - case FL_KP + '.': - Fl::e_keysym = FL_Delete; - break; - case FL_KP + '/': - case FL_KP + '*': - case FL_KP + '-': - case FL_KP + '+': - buffer[0] = Fl::e_keysym - FL_KP; - Fl::e_length = 1; - break; - } - } - } else if ((lParam & (1 << 31)) == 0) { -#ifdef FLTK_PREVIEW_DEAD_KEYS - if ((lParam & (1 << 24)) == 0) { // clear if dead key (always?) - wchar_t u = (wchar_t)wParam; - Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1); - buffer[Fl::e_length] = 0; - } else { // set if "extended key" (never printable?) - buffer[0] = 0; - Fl::e_length = 0; - } -#else - buffer[0] = 0; - Fl::e_length = 0; -#endif - } - Fl::e_text = buffer; - - // Kludge to process the +-containing key in cross-platform way when used with Ctrl -/* Table of how Windows processes the '+'-containing key by keyboard layout - key virtual -content key keyboard layout - =|+ 0xbb US/UK/Fr/Arabic/Chinese/Hebrew/Brazil/Russian/Vietnam/Japan/Korean/Persian - +|* 0xbb German/Spanish/Italy/Greek/Portugal - +|? 0xbb Swedish/Finish/Norway - +|± 0xbb Dutch - 1|+ '1' Swiss/Luxemburg - 3|+ '3' Hungarian - 4|+ '4' Turkish -*/ - if ((Fl::e_state & FL_CTRL) && !(GetAsyncKeyState(VK_MENU) >> 15)) { - // extra processing necessary only when Ctrl is down and Alt is up - int vk_plus_key = (VkKeyScanA('+') & 0xff); // virtual key of '+'-containing key - bool plus_shift_pos = ((VkKeyScanA('+') & 0x100) != 0); // true means '+' in shifted position - int plus_other_char; // the other char on same key as '+' - if (plus_shift_pos) plus_other_char = ms2fltk(vk_plus_key, 0); - else if ((VkKeyScanA('*') & 0xff) == vk_plus_key) plus_other_char = '*'; // German - else if ((VkKeyScanA('?') & 0xff) == vk_plus_key) plus_other_char = '?'; // Swedish - else if ((VkKeyScanW(L'±') & 0xff) == vk_plus_key) plus_other_char = L'±'; // Dutch - else plus_other_char = '='; // fallback -//fprintf(stderr, "plus_shift_pos=%d plus_other_char='%c' vk+=0x%x\n", plus_shift_pos, -// plus_other_char, vk_plus_key); - if ( (vk_plus_key == 0xbb && Fl::e_keysym == '=') || // the '+'-containing key is down - (plus_shift_pos && Fl::e_keysym == plus_other_char) ) { - Fl::e_keysym = (plus_shift_pos ? plus_other_char : '+'); - static char plus_other_char_utf8[4]; - int lutf8 = fl_utf8encode(plus_other_char, plus_other_char_utf8); - plus_other_char_utf8[lutf8] = 0; - if (plus_shift_pos) { - Fl::e_text = ( (Fl::e_state & FL_SHIFT) ? (char*)"+" : plus_other_char_utf8 ); - } else { - Fl::e_text = ( (Fl::e_state & FL_SHIFT) ? plus_other_char_utf8 : (char*)"+" ); - } - Fl::e_length = (int)strlen(Fl::e_text); - } - } - // end of processing of the +-containing key - - if (lParam & (1 << 31)) { // key up events. - if (Fl::handle(FL_KEYUP, window)) - return 0; - break; - } - while (window->parent()) - window = window->window(); - if (Fl::handle(FL_KEYBOARD, window)) { - if (uMsg == WM_DEADCHAR || uMsg == WM_SYSDEADCHAR) - Fl::compose_state = 1; - return 0; - } - break; // WM_KEYDOWN ... WM_SYSKEYUP, WM_DEADCHAR ... WM_SYSCHAR - } // case WM_DEADCHAR ... WM_SYSCHAR - - case WM_MOUSEWHEEL: { - static int delta = 0; // running total of all vertical mousewheel motion - delta += (SHORT)(HIWORD(wParam)); - int dy = -delta / WHEEL_DELTA; - delta += dy * WHEEL_DELTA; - if (dy == 0) // nothing to do - return 0; - if (Fl::event_shift()) { // shift key pressed: send horizontal mousewheel event - Fl::e_dx = dy; - Fl::e_dy = 0; - } else { // shift key not pressed (normal behavior): send vertical mousewheel event - Fl::e_dx = 0; - Fl::e_dy = dy; - } - Fl::handle(FL_MOUSEWHEEL, window); - return 0; - } - - case WM_MOUSEHWHEEL: { - static int delta = 0; // running total of all horizontal mousewheel motion - delta += (SHORT)(HIWORD(wParam)); - int dx = delta / WHEEL_DELTA; - delta -= dx * WHEEL_DELTA; - if (dx == 0) // nothing to do - return 0; - if (Fl::event_shift()) { // shift key pressed: send *vertical* mousewheel event - Fl::e_dx = 0; - Fl::e_dy = dx; - } else { // shift key not pressed (normal behavior): send horizontal mousewheel event - Fl::e_dx = dx; - Fl::e_dy = 0; - } - Fl::handle(FL_MOUSEWHEEL, window); - return 0; - } - - case WM_GETMINMAXINFO: - Fl_WinAPI_Window_Driver::driver(window)->set_minmax((LPMINMAXINFO)lParam); - break; - - case WM_SIZE: - if (!window->parent()) { - Fl_Window_Driver::driver(window)->is_maximized(wParam == SIZE_MAXIMIZED); - if (wParam == SIZE_MINIMIZED || wParam == SIZE_MAXHIDE) { - Fl::handle(FL_HIDE, window); - } else { - if (!moving_window) { - Fl::handle(FL_SHOW, window); - resize_bug_fix = window; - window->size(int(ceil(LOWORD(lParam) / scale)), int(ceil(HIWORD(lParam) / scale))); - } else { - window->size(int(ceil(LOWORD(lParam) / scale)), int(ceil(HIWORD(lParam) / scale))); - EnumChildWindows(hWnd, child_window_cb, (LPARAM)&scale); - window->redraw(); - } - } - } - return 0; - - case WM_MOVING: - moving_window = true; - return 1; - - case WM_CAPTURECHANGED: - moving_window = false; - resize_bug_fix = 0; - return 0; - - case WM_MOVE: { - if (IsIconic(hWnd) || window->parent()) { - break; - } - if (moving_window) resize_bug_fix = window; - POINTS pts = MAKEPOINTS(lParam); - int nx = pts.x, ny = pts.y; - // detect when window centre changes screen - Fl_WinAPI_Screen_Driver *sd = (Fl_WinAPI_Screen_Driver *)Fl::screen_driver(); - Fl_WinAPI_Window_Driver *wd = Fl_WinAPI_Window_Driver::driver(window); - int olds = wd->screen_num(); - // Issue #1097: when a fullscreen window is restored to its size, it receives first a WM_MOVE - // and then a WM_SIZE, so it still has its fullscreen size at the WM_MOVE event, which defeats - // using window->w()|h() to compute the center of the (small) window. We detect this situation - // with condition: !window->fullscreen_active() && *wd->no_fullscreen_w() - // and use *wd->no_fullscreen_w()|h() instead of window->w()|h(). - int trueW = window->w(), trueH = window->h(); - if (!window->fullscreen_active() && *wd->no_fullscreen_w()) { - trueW = *wd->no_fullscreen_w(); trueH = *wd->no_fullscreen_h(); - } - int news = sd->screen_num_unscaled(nx + int(trueW * scale / 2), ny + int(trueH * scale / 2)); - if (news == -1) - news = olds; - scale = sd->scale(news); - wd->x(int(round(nx/scale))); - wd->y(int(round(ny/scale))); - } - return 0; - - case WM_SETCURSOR: - if (LOWORD(lParam) == HTCLIENT) { - while (window->parent()) - window = window->window(); - SetCursor(Fl_WinAPI_Window_Driver::driver(window)->cursor); - return 0; - } - break; - -#if USE_COLORMAP - case WM_QUERYNEWPALETTE: - fl_GetDC(hWnd); - if (fl_select_palette()) - InvalidateRect(hWnd, NULL, FALSE); - break; - - case WM_PALETTECHANGED: - if ((HWND)wParam != hWnd && fl_select_palette()) - UpdateColors(fl_GetDC(hWnd)); - break; - - case WM_CREATE: - fl_GetDC(hWnd); - fl_select_palette(); - break; -#endif - - case WM_DESTROYCLIPBOARD: - fl_i_own_selection[1] = 0; - return 1; - - case WM_DISPLAYCHANGE: {// when screen configuration (number, size, position) changes - Fl::call_screen_init(); - Fl::handle(FL_SCREEN_CONFIGURATION_CHANGED, NULL); - return 0; - } - case WM_CHANGECBCHAIN: - if ((hWnd == clipboard_wnd) && (next_clipboard_wnd == (HWND)wParam)) - next_clipboard_wnd = (HWND)lParam; - else - SendMessage(next_clipboard_wnd, WM_CHANGECBCHAIN, wParam, lParam); - return 0; - - case WM_DRAWCLIPBOARD: - // When the clipboard moves between two FLTK windows, - // fl_i_own_selection will temporarily be false as we are - // processing this message. Hence the need to use fl_find(). - if (!initial_clipboard && !fl_find(GetClipboardOwner())) - fl_trigger_clipboard_notify(1); - initial_clipboard = false; - - if (next_clipboard_wnd) - SendMessage(next_clipboard_wnd, WM_DRAWCLIPBOARD, wParam, lParam); - - return 0; - - default: { -#if defined(FLTK_HAVE_PEN_SUPPORT) - LRESULT ret = fl_win32_tablet_handler(fl_msg); - if (ret != -1) - return ret; -#endif - if (Fl::handle(0, 0)) - return 0; - break; } - } // switch (uMsg) - } // if (window) - return DefWindowProcW(hWnd, uMsg, wParam, lParam); -} - -/* Implementation note about the API to get the dimensions of the top/left borders and the title bar - - Function fake_X_wm() below is used before calling CreateWindowExW() to create - a window and before calling SetWindowPos(). Both of these Windows functions need the window size - including borders and title bar. Function fake_X_wm() uses AdjustWindowRectExForDpi() or - AdjustWindowRectEx() to get the sizes of borders and title bar. The gotten values don't always match - what is seen on the display, but they are the **required** values so the subsequent calls to - CreateWindowExW() or SetWindowPos() correctly size the window. - The Windows doc of AdjustWindowRectExForDpi/AdjustWindowRectEx makes this very clear: - Calculates the required size of the window rectangle, based on the desired size of the client - rectangle [and the provided DPI]. This window rectangle can then be passed to the CreateWindowEx - function to create a window with a client area of the desired size. - - Conversely, Fl_WinAPI_Window_Driver::border_width_title_bar_height() is used to get - the true sizes of borders and title bar of a mapped window. The correct API for that is - DwmGetWindowAttribute(). - */ - -// ///////////////////////////////////////////////////////////////// -// This function gets the dimensions of the top/left borders and -// the title bar, if there is one, based on the FL_BORDER, FL_MODAL -// and FL_NONMODAL flags, and on the window's size range. -// It returns the following values: -// -// value | border | title bar -// 0 | none | no -// 1 | fix | yes -// 2 | size | yes - -int Fl_WinAPI_Window_Driver::fake_X_wm(int &X, int &Y, int &bt, int &bx, int &by, DWORD style, DWORD styleEx) { - - const Fl_Window *w = pWindow; - - int W = 0, H = 0, xoff = 0, yoff = 0, dx = 0, dy = 0; - int ret = bx = by = bt = 0; - - int fallback = 1; - float s = Fl::screen_driver()->scale(screen_num()); - int minw, minh, maxw, maxh; - pWindow->get_size_range(&minw, &minh, &maxw, &maxh, NULL, NULL, NULL); - if (!w->parent()) { - if (fl_xid(w) || style) { - // The block below calculates the window borders by requesting the - // required decorated window rectangle for a desired client rectangle. - // If any part of the function above fails, we will drop to a - // fallback to get the best guess which is always available. - - if (!style) { - HWND hwnd = fl_xid(w); - // request the style flags of this window, as Windows sees them - style = GetWindowLong(hwnd, GWL_STYLE); - styleEx = GetWindowLong(hwnd, GWL_EXSTYLE); - } - - RECT r; - int drawingX, drawingY; // drawing coordinates of window top-left - r.left = drawingX = int(round(w->x() * s)); - r.top = drawingY = int(round(w->y() * s)); - r.right = drawingX + int(w->w() * s); - r.bottom = drawingY + int(w->h() * s); - // get the decoration rectangle for the desired client rectangle - - typedef BOOL(WINAPI* AdjustWindowRectExForDpi_type)(LPRECT, DWORD, BOOL, DWORD, UINT); - static AdjustWindowRectExForDpi_type fl_AdjustWindowRectExForDpi = - (AdjustWindowRectExForDpi_type)GetProcAddress(LoadLibrary("User32.DLL"), "AdjustWindowRectExForDpi"); - BOOL ok; - if (is_dpi_aware && fl_AdjustWindowRectExForDpi) { - Fl_WinAPI_Screen_Driver *sd = (Fl_WinAPI_Screen_Driver*)Fl::screen_driver(); - UINT dpi = UINT(sd->dpi[screen_num()][0]); - ok = fl_AdjustWindowRectExForDpi(&r, style, FALSE, styleEx, dpi); - } else - ok = AdjustWindowRectEx(&r, style, FALSE, styleEx); - if (ok) { - X = r.left; - Y = r.top; - W = r.right - r.left; - H = r.bottom - r.top; - bx = drawingX - r.left; - by = r.bottom - int(drawingY + w->h() * s); // height of the bottom frame - bt = drawingY - r.top - by; // height of top caption bar - xoff = bx; - yoff = by + bt; - dx = W - int(w->w() * s); - dy = H - int(w->h() * s); - if (maxw != minw || maxh != minh) - ret = 2; - else - ret = 1; - fallback = 0; - } - } - } - // This is the original (pre 1.1.7) routine to calculate window border sizes. - if (fallback) { - if (w->border() && !w->parent()) { - if (maxw != minw || maxh != minh) { - ret = 2; - bx = GetSystemMetrics(SM_CXSIZEFRAME); - by = GetSystemMetrics(SM_CYSIZEFRAME); - } else { - ret = 1; - int padding = GetSystemMetrics(SM_CXPADDEDBORDER); - NONCLIENTMETRICS ncm; - ncm.cbSize = sizeof(NONCLIENTMETRICS); - SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0); - bx = GetSystemMetrics(SM_CXFIXEDFRAME) + (padding ? padding + ncm.iBorderWidth : 0); - by = GetSystemMetrics(SM_CYFIXEDFRAME) + (padding ? padding + ncm.iBorderWidth : 0); - } - bt = GetSystemMetrics(SM_CYCAPTION); - } - // The coordinates of the whole window, including non-client area - xoff = bx; - yoff = by + bt; - dx = 2 * bx; - dy = 2 * by + bt; - X = w->x() - xoff; - Y = w->y() - yoff; - W = w->w() + dx; - H = w->h() + dy; - } - - // Proceed to positioning the window fully inside the screen, if possible - // Find screen that contains most of the window - // FIXME: this ought to be the "work area" instead of the entire screen ! - int scr_x = 0, scr_y = 0, scr_w = 0, scr_h = 0; - int ns = Fl::screen_num(int(round(X / s)), int(round(Y / s)), int(W / s), int(H / s)); - ((Fl_WinAPI_Screen_Driver*)Fl::screen_driver())->screen_xywh_unscaled(scr_x, scr_y, scr_w, scr_h, ns); - // Make border's lower right corner visible - if (scr_x + scr_w < X + W) - X = scr_x + scr_w - W; - if (scr_y + scr_h < Y + H) - Y = scr_y + scr_h - H; - // Make border's upper left corner visible - if (X < scr_x) - X = scr_x; - if (Y < scr_y) - Y = scr_y; - // Make client area's lower right corner visible - if (scr_x + scr_w < X + dx + w->w()) - X = scr_x + scr_w - int(w->w() * s) - dx; - if (scr_y + scr_h < Y + dy + w->h()) - Y = scr_y + scr_h - int(w->h() * s) - dy; - // Make client area's upper left corner visible - if (X + xoff < scr_x) - X = scr_x - xoff; - if (Y + yoff < scr_y) - Y = scr_y - yoff; - // Return the client area's top left corner in (X,Y) - X += xoff; - Y += yoff; - - if (w->fullscreen_active()) { - bx = by = bt = 0; - } - - return ret; -} - -//////////////////////////////////////////////////////////////// - -static void delayed_fullscreen(Fl_Window *win) { - Fl::remove_check((Fl_Timeout_Handler)delayed_fullscreen, win); - win->fullscreen_off(); - win->fullscreen(); -} - - -static void delayed_maximize(Fl_Window *win) { - Fl::remove_check((Fl_Timeout_Handler)delayed_maximize, win); - win->un_maximize(); - win->maximize(); -} - - -void Fl_WinAPI_Window_Driver::resize(int X, int Y, int W, int H) { -//fprintf(stderr, "resize w()=%d W=%d h()=%d H=%d\n",pWindow->w(), W,pWindow->h(), H); - if (Fl_Window::is_a_rescale() && pWindow->fullscreen_active()) { - Fl::add_check((Fl_Timeout_Handler)delayed_fullscreen, pWindow); - } else if (Fl_Window::is_a_rescale() && pWindow->maximize_active()) { - Fl::add_check((Fl_Timeout_Handler)delayed_maximize, pWindow); - } - UINT flags = SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER; - int is_a_resize = (W != w() || H != h() || Fl_Window::is_a_rescale()); - int resize_from_program = (pWindow != resize_bug_fix); - if (!resize_from_program) - resize_bug_fix = 0; - if (X != x() || Y != y() || Fl_Window::is_a_rescale()) { - force_position(1); - } else { - if (!is_a_resize) - return; - flags |= SWP_NOMOVE; - } - if (is_a_resize) { - if (resize_from_program && shown()) { - // don't obey "resize from program" when window is maximized - WINDOWPLACEMENT wplace; - wplace.length = sizeof(WINDOWPLACEMENT); - BOOL ok = GetWindowPlacement(fl_xid(pWindow), &wplace); - if (ok && wplace.showCmd == SW_SHOWMAXIMIZED) return; - } - pWindow->Fl_Group::resize(X, Y, W, H); - if (visible_r()) { - pWindow->redraw(); - // only wait for exposure if this window has a size - a window - // with no width or height will never get an exposure event - Fl_X *i = Fl_X::flx(pWindow); - if (i && W > 0 && H > 0) - wait_for_expose_value = 1; - } - } else { - x(X); - y(Y); - flags |= SWP_NOSIZE; - } - if (!border()) - flags |= SWP_NOACTIVATE; - if (resize_from_program && shown()) { - int dummy_x, dummy_y, bt, bx, by; - // compute window position and size in scaled units - float s = Fl::screen_driver()->scale(screen_num()); - int scaledX = int(round(X * s)), scaledY = int(round(Y * s)), scaledW = int(W * s), scaledH = int(H * s); - // Ignore window managing when resizing, so that windows (and more - // specifically menus) can be moved offscreen. - if (fake_X_wm(dummy_x, dummy_y, bt, bx, by)) { - scaledX -= bx; - scaledY -= by + bt; - scaledW += 2 * bx; - scaledH += 2 * by + bt; - } - // avoid zero size windows. A zero sized window on Win32 - // will cause continouly new redraw events. - if (scaledW <= 0) - scaledW = 1; - if (scaledH <= 0) - scaledH = 1; - SetWindowPos(fl_xid(pWindow), 0, scaledX, scaledY, scaledW, scaledH, flags); - } -} - - -//////////////////////////////////////////////////////////////// - -/* - This silly little class remembers the name of all window classes - we register to avoid double registration. It has the added bonus - of freeing everything on application close as well. - */ -class NameList { -public: - NameList() { - name = (char **)malloc(sizeof(char **)); - NName = 1; - nName = 0; - } - ~NameList() { - int i; - for (i = 0; i < nName; i++) - free(name[i]); - if (name) - free(name); - } - void add_name(const char *n) { - if (NName == nName) { - NName += 5; - name = (char **)realloc(name, NName * sizeof(char *)); - } - name[nName++] = fl_strdup(n); - } - char has_name(const char *n) { - int i; - for (i = 0; i < nName; i++) { - if (strcmp(name[i], n) == 0) - return 1; - } - return 0; - } - -private: - char **name; - int nName, NName; -}; - -void fl_fix_focus(); // in Fl.cxx - -UINT fl_wake_msg = 0; -int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR - -void Fl_WinAPI_Window_Driver::makeWindow() { - Fl_Group::current(0); // get rid of very common user bug: forgot end() - - fl_open_display(); - - // if the window is a subwindow and our parent is not mapped yet, we - // mark this window visible, so that mapping the parent at a later - // point in time will call this function again to finally map the subwindow. - Fl_Window *w = pWindow; - if (w->parent() && !Fl_X::flx(w->window())) { - w->set_visible(); - return; - } - - static NameList class_name_list; - static const char *first_class_name = 0L; - const char *class_name = w->xclass(); - if (!class_name) - class_name = first_class_name; // reuse first class name used - if (!class_name) - class_name = "FLTK"; // default to create a "FLTK" WNDCLASS - if (!first_class_name) { - first_class_name = class_name; - } -// Prefix user-set window class name by "FLTK", unless it's already here, -// to avoid collision with system-defined window class names (example "edit") - if (strncmp(class_name, "FLTK", 4)) { - static char new_class_name[100]; - snprintf(new_class_name, sizeof(new_class_name), "FLTK-%s", class_name); - class_name = new_class_name; - } - //fprintf(stderr,"makeWindow: class_name=%s\n",class_name);fflush(stderr); - - wchar_t class_namew[100]; // (limited) buffer for Windows class name - - // convert UTF-8 class_name to wchar_t for RegisterClassExW and CreateWindowExW - - fl_utf8toUtf16(class_name, - (unsigned)strlen(class_name), // in - (unsigned short *)class_namew, // out - (unsigned)sizeof(class_namew) / sizeof(wchar_t)); // max. size - - if (!class_name_list.has_name(class_name)) { - WNDCLASSEXW wcw; - memset(&wcw, 0, sizeof(wcw)); - wcw.cbSize = sizeof(WNDCLASSEXW); - - // Documentation states a device context consumes about 800 bytes - // of memory... so who cares? If 800 bytes per window is what it - // takes to speed things up, I'm game. - wcw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; - wcw.lpfnWndProc = (WNDPROC)WndProc; - wcw.cbClsExtra = wcw.cbWndExtra = 0; - wcw.hInstance = fl_display; - if (!w->icon() && !icon_->count) - w->icon((void *)LoadIcon(NULL, IDI_APPLICATION)); - wcw.hIcon = wcw.hIconSm = (HICON)w->icon(); - wcw.hCursor = LoadCursor(NULL, IDC_ARROW); - wcw.hbrBackground = NULL; - wcw.lpszMenuName = NULL; - wcw.lpszClassName = class_namew; - RegisterClassExW(&wcw); - class_name_list.add_name(class_name); - } - - const wchar_t *message_namew = L"FLTK::ThreadWakeup"; - if (!fl_wake_msg) - fl_wake_msg = RegisterWindowMessageW(message_namew); - - HWND parent; - DWORD style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS; - DWORD styleEx = WS_EX_LEFT; - - // compute adequate screen where to put the window - int nscreen = 0; - if (w->parent()) { - nscreen = Fl_Window_Driver::driver(w->top_window())->screen_num(); - } else if (Fl_Window_Driver::driver(w)->force_position() && Fl_WinAPI_Window_Driver::driver(w)->screen_num_ >= 0) { - nscreen = Fl_Window_Driver::driver(w)->screen_num(); - } else { - Fl_Window *hint = Fl::first_window(); - if (hint) { - nscreen = Fl_Window_Driver::driver(hint->top_window())->screen_num(); - } else if (Fl::screen_driver()->screen_count() > 1 ) { - // put the new window on same screen as mouse - int mx, my, X, Y, W, H; - nscreen = Fl::screen_driver()->get_mouse(mx, my); - Fl::screen_xywh(X, Y, W, H, nscreen); - if (mx + w->w() >= X + W) mx = X + W - w->w(); - if (my + w->h() >= Y + H) my = Y + H - w->h(); - w->position(mx, my); - } - } - Fl_Window_Driver::driver(w)->screen_num(nscreen); - float s = Fl::screen_driver()->scale(nscreen); - int xp = int(round(w->x() * s)); // these are in graphical units - int yp = int(round(w->y() * s)); - int wp = int(w->w() * s); - int hp = int(w->h() * s); - - int showit = 1; - - if (w->parent()) { - style |= WS_CHILD; - styleEx |= WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT; - parent = fl_xid(w->window()); - } else { // top level window - styleEx |= WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT; - - int wintype = 0; - if (w->border()) { - if (is_resizable()) - wintype = 2; - else - wintype = 1; - } - - switch (wintype) { - // No border (used for menus) - case 0: - style |= WS_POPUP; - styleEx |= WS_EX_TOOLWINDOW; - break; - - // Thin border and title bar - case 1: - style |= WS_DLGFRAME | WS_CAPTION; - if (!w->modal()) - style |= WS_SYSMENU | WS_MINIMIZEBOX; - break; - - // Thick, resizable border and title bar, with maximize button - case 2: - style |= WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_CAPTION; - if (!w->modal()) - style |= WS_MINIMIZEBOX; - break; - } - - int xwm = xp, ywm = yp, bt, bx, by; // these are in graphical units - - fake_X_wm(xwm, ywm, bt, bx, by, style, styleEx); - - if (by + bt) { - wp += 2 * bx; - hp += 2 * by + bt; - } - if (!force_position()) { - xp = yp = CW_USEDEFAULT; - } else { - if (!Fl::grab()) { - xp = xwm; - yp = ywm; - x(int(round(xp / s))); - y(int(round(yp / s))); - } - xp -= bx; - yp -= by + bt; - } - - parent = 0; - if (w->non_modal() && Fl_X::first && !fl_disable_transient_for) { - // find some other window to be "transient for": - Fl_Window *w = Fl_X::first->w; - while (w->parent()) - w = w->window(); - parent = fl_xid(w); - if (!w->visible()) - showit = 0; -// https://www.fltk.org/str.php?L1115 -// Mike added the code below to fix issues with tooltips that unfortunately -// he does not specify in detail. After extensive testing, I can't see -// how this fixes things, but I do see how a window opened by a timer will -// link that window to the current popup, which is wrong. -// Matt, Apr 30th, 2023 -// } else if (Fl::grab()) { -// parent = fl_xid(Fl::grab()); - } - } - - Fl_X *x = new Fl_X; - other_xid = 0; - x->w = w; - flx(x); - x->region = 0; - Fl_WinAPI_Window_Driver::driver(w)->private_dc = 0; - cursor = LoadCursor(NULL, IDC_ARROW); - custom_cursor = 0; - if (!fl_codepage) - fl_get_codepage(); - - WCHAR *lab = NULL; - if (w->label()) { - size_t l = strlen(w->label()); - unsigned wlen = fl_utf8toUtf16(w->label(), (unsigned)l, NULL, 0); // Pass NULL to query length - wlen++; - lab = (WCHAR *)malloc(sizeof(WCHAR) * wlen); - wlen = fl_utf8toUtf16(w->label(), (unsigned)l, (unsigned short *)lab, wlen); - lab[wlen] = 0; - } - x->xid = (fl_uintptr_t)CreateWindowExW(styleEx, - class_namew, lab, style, - xp, yp, wp, hp, - parent, - NULL, // menu - fl_display, - NULL // creation parameters - ); - if (lab) - free(lab); - - x->next = Fl_X::first; - Fl_X::first = x; - - set_icons(); - - if (w->fullscreen_active()) { - /* We need to make sure that the fullscreen is created on the - default monitor, ie the desktop where the shortcut is located - etc. This requires that CreateWindow is called with CW_USEDEFAULT - for x and y. We can then use GetWindowRect to determine which - monitor the window was placed on. */ - RECT rect; - GetWindowRect((HWND)x->xid, &rect); - make_fullscreen(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); - } - - // Setup clipboard monitor target if there are registered handlers and - // no window is targeted. - if (!fl_clipboard_notify_empty() && clipboard_wnd == NULL) - fl_clipboard_notify_target((HWND)x->xid); - - wait_for_expose_value = ((wp == 0 || hp == 0) && !w->border() && !w->parent() ? 0 : 1); // issue #985 - if (show_iconic()) { - showit = 0; - show_iconic(0); - } - if (showit) { - w->set_visible(); - int old_event = Fl::e_number; - w->handle(Fl::e_number = FL_SHOW); // get child windows to appear - Fl::e_number = old_event; - w->redraw(); // force draw to happen - } - - // Needs to be done before ShowWindow() to get the correct behavior - // when we get WM_SETFOCUS. - if (w->modal()) { - Fl::modal_ = w; - fl_fix_focus(); - } - - // If we've captured the mouse, we don't want to activate any - // other windows from the code, or we lose the capture. - ShowWindow((HWND)x->xid, !showit ? SW_SHOWMINNOACTIVE : - (Fl::grab() || (styleEx & WS_EX_TOOLWINDOW)) ? SW_SHOWNOACTIVATE : SW_SHOWNORMAL); - - // Register all windows for potential drag'n'drop operations - RegisterDragDrop((HWND)x->xid, flIDropTarget); - - if (!im_enabled) - flImmAssociateContextEx((HWND)x->xid, 0, 0); - - if (w->fullscreen_active()) Fl::handle(FL_FULLSCREEN, w); -} - - -//////////////////////////////////////////////////////////////// - -HINSTANCE fl_display = GetModuleHandle(NULL); - -HINSTANCE fl_win32_display() { return fl_display; } - -void Fl_WinAPI_Window_Driver::set_minmax(LPMINMAXINFO minmax) { - int td, wd, hd, dummy_x, dummy_y; - - fake_X_wm(dummy_x, dummy_y, td, wd, hd); - wd *= 2; - hd *= 2; - hd += td; - - int minw, minh, maxw, maxh; - pWindow->get_size_range(&minw, &minh, &maxw, &maxh, NULL, NULL, NULL); - float s = Fl::screen_driver()->scale(screen_num()); - minmax->ptMinTrackSize.x = LONG(s * minw) + wd; - minmax->ptMinTrackSize.y = LONG(s * minh) + hd; - if (maxw) { - minmax->ptMaxTrackSize.x = LONG(s * maxw) + wd; - minmax->ptMaxSize.x = LONG(s * maxw) + wd; - } - if (maxh) { - minmax->ptMaxTrackSize.y = LONG(s * maxh) + hd; - minmax->ptMaxSize.y = LONG(s * maxh) + hd; - } -} - - -//////////////////////////////////////////////////////////////// - -// returns pointer to the filename, or null if name ends with '/' -const char *Fl_WinAPI_System_Driver::filename_name(const char *name) { - const char *p, *q; - if (!name) - return (0); - q = name; - if (q[0] && q[1] == ':') - q += 2; // skip leading drive letter - for (p = q; *p; p++) { - if (*p == '/' || *p == '\\') - q = p + 1; - } - return q; -} - - -//////////////////////////////////////////////////////////////// - -static HICON image_to_icon(const Fl_RGB_Image *image, bool is_icon, int hotx, int hoty) { - BITMAPV5HEADER bi; - HBITMAP bitmap, mask; - DWORD *bits; - HICON icon; - - if (!is_icon) { - if ((hotx < 0) || (hotx >= image->data_w())) - return NULL; - if ((hoty < 0) || (hoty >= image->data_h())) - return NULL; - } - - memset(&bi, 0, sizeof(BITMAPV5HEADER)); - - bi.bV5Size = sizeof(BITMAPV5HEADER); - bi.bV5Width = image->data_w(); - bi.bV5Height = -image->data_h(); // Negative for top-down - bi.bV5Planes = 1; - bi.bV5BitCount = 32; - bi.bV5Compression = BI_BITFIELDS; - bi.bV5RedMask = 0x00FF0000; - bi.bV5GreenMask = 0x0000FF00; - bi.bV5BlueMask = 0x000000FF; - bi.bV5AlphaMask = 0xFF000000; - - HDC hdc; - - hdc = GetDC(NULL); - bitmap = CreateDIBSection(hdc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&bits, NULL, 0); - ReleaseDC(NULL, hdc); - - if (bits == NULL) - return NULL; - - const uchar *i = (const uchar *)*image->data(); - const int extra_data = image->ld() ? (image->ld() - image->data_w() * image->d()) : 0; - - for (int y = 0; y < image->data_h(); y++) { - for (int x = 0; x < image->data_w(); x++) { - switch (image->d()) { - case 1: - *bits = (0xff << 24) | (i[0] << 16) | (i[0] << 8) | i[0]; - break; - case 2: - *bits = (i[1] << 24) | (i[0] << 16) | (i[0] << 8) | i[0]; - break; - case 3: - *bits = (0xff << 24) | (i[0] << 16) | (i[1] << 8) | i[2]; - break; - case 4: - *bits = (i[3] << 24) | (i[0] << 16) | (i[1] << 8) | i[2]; - break; - } - i += image->d(); - bits++; - } - i += extra_data; - } - - // A mask bitmap is still needed even though it isn't used - mask = CreateBitmap(image->data_w(), image->data_h(), 1, 1, NULL); - if (mask == NULL) { - DeleteObject(bitmap); - return NULL; - } - - ICONINFO ii; - - ii.fIcon = is_icon; - ii.xHotspot = hotx; - ii.yHotspot = hoty; - ii.hbmMask = mask; - ii.hbmColor = bitmap; - - icon = CreateIconIndirect(&ii); - - DeleteObject(bitmap); - DeleteObject(mask); - - return icon; -} - -//////////////////////////////////////////////////////////////// - -static HICON default_big_icon = NULL; -static HICON default_small_icon = NULL; - -static const Fl_RGB_Image *find_best_icon(int ideal_width, const Fl_RGB_Image *icons[], int count) { - const Fl_RGB_Image *best; - - best = NULL; - - for (int i = 0; i < count; i++) { - if (best == NULL) - best = icons[i]; - else { - if (best->w() < ideal_width) { - if (icons[i]->w() > best->w()) - best = icons[i]; - } else { - if ((icons[i]->w() >= ideal_width) && (icons[i]->w() < best->w())) - best = icons[i]; - } - } - } - - return best; -} - -void Fl_WinAPI_Screen_Driver::default_icons(const Fl_RGB_Image *icons[], int count) { - const Fl_RGB_Image *best_big, *best_small; - - if (default_big_icon != NULL) - DestroyIcon(default_big_icon); - if (default_small_icon != NULL) - DestroyIcon(default_small_icon); - - default_big_icon = NULL; - default_small_icon = NULL; - - best_big = find_best_icon(GetSystemMetrics(SM_CXICON), icons, count); - best_small = find_best_icon(GetSystemMetrics(SM_CXSMICON), icons, count); - - bool need_delete; - if (best_big != NULL) { - need_delete = false; - if (best_big->w() != best_big->data_w() || best_big->h() != best_big->data_h()) { - best_big = (Fl_RGB_Image *)best_big->copy(); - need_delete = true; - } - default_big_icon = image_to_icon(best_big, true, 0, 0); - if (need_delete) delete best_big; - } - - if (best_small != NULL) { - need_delete = false; - if (best_small->w() != best_small->data_w() || - best_small->h() != best_small->data_h()) { - best_small = (Fl_RGB_Image *)best_small->copy(); - need_delete = true; - } - default_small_icon = image_to_icon(best_small, true, 0, 0); - if (need_delete) delete best_small; - } -} - - -void Fl_Window::icons(HICON big_icon, HICON small_icon) { - free_icons(); - if (big_icon != NULL) - Fl_WinAPI_Window_Driver::driver(this)->icon_->big_icon = CopyIcon(big_icon); - if (small_icon != NULL) - Fl_WinAPI_Window_Driver::driver(this)->icon_->small_icon = CopyIcon(small_icon); - if (Fl_X::flx(this)) - Fl_WinAPI_Window_Driver::driver(this)->set_icons(); -} - -void Fl_Window::default_icons(HICON big_icon, HICON small_icon) { - if (default_big_icon != NULL) - DestroyIcon(default_big_icon); - if (default_small_icon != NULL) - DestroyIcon(default_small_icon); - - default_big_icon = NULL; - default_small_icon = NULL; - - if (big_icon != NULL) - default_big_icon = CopyIcon(big_icon); - if (small_icon != NULL) - default_small_icon = CopyIcon(small_icon); -} - -void Fl_WinAPI_Window_Driver::set_icons() { - HICON big_icon, small_icon; - - // Windows doesn't copy the icons, so we have to "leak" them when - // setting, and clean up when we change to some other icons. - big_icon = (HICON)SendMessage(fl_xid(pWindow), WM_GETICON, ICON_BIG, 0); - if ((big_icon != NULL) && (big_icon != default_big_icon)) - DestroyIcon(big_icon); - small_icon = (HICON)SendMessage(fl_xid(pWindow), WM_GETICON, ICON_SMALL, 0); - if ((small_icon != NULL) && (small_icon != default_small_icon)) - DestroyIcon(small_icon); - - big_icon = NULL; - small_icon = NULL; - - if (icon_->count) { - const Fl_RGB_Image *best_big, *best_small; - - best_big = find_best_icon(GetSystemMetrics(SM_CXICON), - (const Fl_RGB_Image **)icon_->icons, - icon_->count); - best_small = find_best_icon(GetSystemMetrics(SM_CXSMICON), - (const Fl_RGB_Image **)icon_->icons, - icon_->count); - - if (best_big != NULL) - big_icon = image_to_icon(best_big, true, 0, 0); - if (best_small != NULL) - small_icon = image_to_icon(best_small, true, 0, 0); - } else { - if ((icon_->big_icon != NULL) || (icon_->small_icon != NULL)) { - big_icon = icon_->big_icon; - small_icon = icon_->small_icon; - } else { - big_icon = default_big_icon; - small_icon = default_small_icon; - } - } - - SendMessage(fl_xid(pWindow), WM_SETICON, ICON_BIG, (LPARAM)big_icon); - SendMessage(fl_xid(pWindow), WM_SETICON, ICON_SMALL, (LPARAM)small_icon); -} - - -//////////////////////////////////////////////////////////////// - -#ifndef IDC_HAND -#define IDC_HAND MAKEINTRESOURCE(32649) -#endif // !IDC_HAND - -int Fl_WinAPI_Window_Driver::set_cursor(Fl_Cursor c) { - LPSTR n; - HCURSOR new_cursor; - - if (c == FL_CURSOR_NONE) - new_cursor = NULL; - else { - switch (c) { - case FL_CURSOR_ARROW: - n = IDC_ARROW; - break; - case FL_CURSOR_CROSS: - n = IDC_CROSS; - break; - case FL_CURSOR_WAIT: - n = IDC_WAIT; - break; - case FL_CURSOR_INSERT: - n = IDC_IBEAM; - break; - case FL_CURSOR_HAND: - n = IDC_HAND; - break; - case FL_CURSOR_HELP: - n = IDC_HELP; - break; - case FL_CURSOR_MOVE: - n = IDC_SIZEALL; - break; - case FL_CURSOR_N: - case FL_CURSOR_S: - // FIXME: Should probably have fallbacks for these instead - case FL_CURSOR_NS: - n = IDC_SIZENS; - break; - case FL_CURSOR_NE: - case FL_CURSOR_SW: - // FIXME: Dito. - case FL_CURSOR_NESW: - n = IDC_SIZENESW; - break; - case FL_CURSOR_E: - case FL_CURSOR_W: - // FIXME: Dito. - case FL_CURSOR_WE: - n = IDC_SIZEWE; - break; - case FL_CURSOR_SE: - case FL_CURSOR_NW: - // FIXME: Dito. - case FL_CURSOR_NWSE: - n = IDC_SIZENWSE; - break; - default: - return 0; - } - - new_cursor = LoadCursor(NULL, n); - if (new_cursor == NULL) - return 0; - } - - if ((cursor != NULL) && custom_cursor) - DestroyIcon(cursor); - - cursor = new_cursor; - custom_cursor = 0; - - SetCursor(cursor); - - return 1; -} - -int Fl_WinAPI_Window_Driver::set_cursor(const Fl_RGB_Image *image, int hotx, int hoty) { - HCURSOR new_cursor; - Fl_RGB_Image *scaled_image = (Fl_RGB_Image*)image->copy(); - scaled_image->normalize(); - new_cursor = image_to_icon(scaled_image, false, hotx, hoty); - delete scaled_image; - if (new_cursor == NULL) - return 0; - - if ((cursor != NULL) && custom_cursor) - DestroyIcon(cursor); - - cursor = new_cursor; - custom_cursor = 1; - - SetCursor(cursor); - - return 1; -} - - -//////////////////////////////////////////////////////////////// -// Implement the virtual functions for the base Fl_Window class: - -void Fl_WinAPI_Window_Driver::show() { - if (!shown()) { - makeWindow(); - } else { - // Once again, we would lose the capture if we activated the window. - Fl_X *i = Fl_X::flx(pWindow); - if (IsIconic((HWND)i->xid)) - OpenIcon((HWND)i->xid); - if (!fl_capture) - BringWindowToTop((HWND)i->xid); - // ShowWindow(i->xid,fl_capture?SW_SHOWNOACTIVATE:SW_RESTORE); - } -} - -// the current context -// the current window handle, initially set to -1 so we can correctly -// allocate fl_GetDC(0) -HWND fl_window = NULL; - -// Here we ensure only one GetDC is ever in place. -HDC fl_GetDC(HWND w) { - HDC gc = (HDC)Fl_Graphics_Driver::default_driver().gc(); - if (gc) { - if (w == fl_window && fl_window != NULL) - return gc; - if (fl_window) - fl_release_dc(fl_window, gc); // ReleaseDC - } - gc = GetDC(w); - Fl_Graphics_Driver::default_driver().gc(gc); - fl_save_dc(w, gc); - fl_window = w; - // calling GetDC seems to always reset these: (?) - SetTextAlign(gc, TA_BASELINE | TA_LEFT); - SetBkMode(gc, TRANSPARENT); - - return gc; -} - - -/* Make sure that all allocated fonts are released. This works only if - Fl::run() is allowed to exit by closing all windows. Calling 'exit(int)' - will not automatically free any fonts. */ -void fl_free_fonts(void) { - // remove the Fl_Font_Descriptor chains - int i; - Fl_Fontdesc *s; - Fl_Font_Descriptor *f; - Fl_Font_Descriptor *ff; - for (i = 0; i < FL_FREE_FONT; i++) { - s = fl_fonts + i; - for (f = s->first; f; f = ff) { - ff = f->next; - delete (Fl_GDI_Font_Descriptor*)f; - s->first = ff; - } - } -} - - -/////////////////////////////////////////////////////////////////////// -// -// The following routines help fix a problem with the leaking of Windows -// Device Context (DC) objects. The 'proper' protocol is for a program to -// acquire a DC, save its state, do the modifications needed for drawing, -// perform the drawing, restore the initial state, and release the DC. In -// FLTK, the save and restore steps have previously been omitted and DCs are -// not properly released, leading to a great number of DC leaks. As some -// Windows "OSs" will hang when any process exceeds roughly 10,000 GDI objects, -// it is important to control GDI leaks, which are much more important than memory -// leaks. The following struct, global variable, and routines help implement -// the above protocol for those cases where the GetDC and RestoreDC are not in -// the same routine. For each GetDC, fl_save_dc is used to create an entry in -// a linked list that saves the window handle, the DC handle, and the initial -// state. When the DC is to be released, 'fl_release_dc' is called. It restores -// the initial state and releases the DC. When the program exits, 'fl_cleanup_dc_list' -// frees any remaining nodes in the list. - -struct Win_DC_List { // linked list - HWND window; // window handle - HDC dc; // device context handle - int saved_dc; // initial state of DC - Win_DC_List *next; // pointer to next item -}; - -static Win_DC_List *win_DC_list = 0; - -void fl_save_dc(HWND w, HDC dc) { - Win_DC_List *t; - t = new Win_DC_List; - t->window = w; - t->dc = dc; - t->saved_dc = SaveDC(dc); - if (win_DC_list) - t->next = win_DC_list; - else - t->next = NULL; - win_DC_list = t; -} - -void fl_release_dc(HWND w, HDC dc) { - Win_DC_List *t = win_DC_list; - Win_DC_List *prev = 0; - if (!t) - return; - do { - if (t->dc == dc) { - RestoreDC(dc, t->saved_dc); - ReleaseDC(w, dc); - if (!prev) { - win_DC_list = t->next; // delete first item - } else { - prev->next = t->next; // one in the middle - } - delete (t); - return; - } - prev = t; - t = t->next; - } while (t); -} - -void fl_cleanup_dc_list(void) { // clean up the list - Win_DC_List *t = win_DC_list; - if (!t) - return; - do { - RestoreDC(t->dc, t->saved_dc); - ReleaseDC(t->window, t->dc); - win_DC_list = t->next; - delete (t); - t = win_DC_list; - } while (t); -} - -/* Returns images of the captures of the window title-bar, and the left, bottom and right window borders. - This function exploits a feature of Fl_WinAPI_Screen_Driver::read_win_rectangle() which, - when fl_gc is set to the screen device context, captures the window decoration. - */ -void Fl_WinAPI_Window_Driver::capture_titlebar_and_borders(Fl_RGB_Image *&top, Fl_RGB_Image *&left, - Fl_RGB_Image *&bottom, Fl_RGB_Image *&right) { - top = left = bottom = right = NULL; - if (!shown() || parent() || !border() || !visible()) - return; - int wsides, hbottom, bt; - float scaling = Fl::screen_driver()->scale(screen_num()); - RECT r = border_width_title_bar_height(wsides, hbottom, bt); - int htop = bt + hbottom; - Fl_Surface_Device::push_current(Fl_Display_Device::display_device()); - pWindow->show(); - while (Fl::ready()) - Fl::check(); - HDC save_gc = (HDC)fl_graphics_driver->gc(); - fl_graphics_driver->gc(GetDC(NULL)); - int ww = int(w() * scaling) + 2 * wsides; - wsides = int(wsides / scaling); - if (wsides < 1) - wsides = 1; - ww = int(ww / scaling); - if (wsides <= 1) - ww = w() + 2 * wsides; - // capture the 4 window sides from screen - int offset = r.left < 0 ? -r.left : 0; - Fl_WinAPI_Screen_Driver *dr = (Fl_WinAPI_Screen_Driver *)Fl::screen_driver(); - if (htop && r.right - r.left > offset) { - top = dr->read_win_rectangle_unscaled(r.left+offset, r.top, r.right - r.left-offset, htop, 0); - if (scaling != 1 && top) - top->scale(ww, int(htop / scaling), 0, 1); - } - if (wsides) { - left = dr->read_win_rectangle_unscaled(r.left + offset, r.top + htop, wsides, int(h() * scaling), 0); - right = dr->read_win_rectangle_unscaled(r.right - wsides, r.top + htop, wsides, int(h() * scaling), 0); - bottom = dr->read_win_rectangle_unscaled(r.left+offset, r.bottom - hbottom, ww, hbottom, 0); - if (scaling != 1) { - if (left) left->scale(wsides, h(), 0, 1); - if (right) right->scale(wsides, h(), 0, 1); - if (bottom) bottom->scale(ww, hbottom, 0, 1); - } - } - ReleaseDC(NULL, (HDC)fl_graphics_driver->gc()); - fl_graphics_driver->gc(save_gc); - Fl_Surface_Device::pop_current(); -} |
