Windows: improve moving window across screens having distinct scale factors.

This commit is contained in:
ManoloFLTK 2026-01-04 16:45:11 +01:00
parent 0e570fb672
commit 46e6815612
4 changed files with 82 additions and 62 deletions

View File

@ -1208,11 +1208,35 @@ extern HPALETTE fl_select_palette(void); // in fl_color_win32.cxx
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.
@ -1234,7 +1258,7 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
switch (uMsg) {
case WM_DPICHANGED: { // 0x02E0, after display re-scaling and followed by WM_DISPLAYCHANGE
if (is_dpi_aware && !Fl_WinAPI_Window_Driver::data_for_resize_window_between_screens_.busy) {
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;
@ -1251,13 +1275,11 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar
sd->update_scaling_capability();
} else if (ns != old_ns) {
// jump window with Windows+Shift+L|R-arrow to other screen with other DPI
float scale = Fl::screen_driver()->scale(ns);
int bt, bx, by;
Fl_WinAPI_Window_Driver *wdr = (Fl_WinAPI_Window_Driver*)Fl_Window_Driver::driver(window);
wdr->border_width_title_bar_height(bx, by, bt);
window->position(int(round(lParam_rect->left/scale)),
int(round((lParam_rect->top + bt)/scale)));
wdr->resize_after_scale_change(ns, scale, scale);
if (ns >= 0) 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);
}
}
return 0;
@ -1704,26 +1726,34 @@ content key keyboard layout
if (wParam == SIZE_MINIMIZED || wParam == SIZE_MAXHIDE) {
Fl::handle(FL_HIDE, window);
} else {
Fl::handle(FL_SHOW, window);
resize_bug_fix = window;
window->size(int(ceil(LOWORD(lParam) / scale)), int(ceil(HIWORD(lParam) / scale)));
// fprintf(LOG,"WM_SIZE size(%.0f,%.0f) graph(%d,%d) s=%.2f\n",
// ceil(LOWORD(lParam)/scale),ceil(HIWORD(lParam)/scale),
// LOWORD(lParam),HIWORD(lParam),scale);
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();
}
}
}
break;
return 0;
case WM_MOVING:
moving_window = true;
return 1;
case WM_CAPTURECHANGED:
moving_window = false;
return 0;
case WM_MOVE: {
if (IsIconic(hWnd)) {
if (IsIconic(hWnd) || window->parent()) {
break;
}
resize_bug_fix = window;
int nx = LOWORD(lParam);
int ny = HIWORD(lParam);
if (nx & 0x8000) nx -= 65536;
if (ny & 0x8000) ny -= 65536;
// fprintf(LOG,"WM_MOVE position(%d,%d) s=%.2f\n",int(nx/scale),int(ny/scale),scale);
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);
@ -1740,30 +1770,15 @@ content key keyboard layout
int news = sd->screen_num_unscaled(nx + int(trueW * scale / 2), ny + int(trueH * scale / 2));
if (news == -1)
news = olds;
float s = sd->scale(news);
// fprintf(LOG,"WM_MOVE olds=%d(%.2f) news=%d(%.2f) busy=%d\n",olds,
// sd->scale(olds),news, s,
// Fl_WinAPI_Window_Driver::data_for_resize_window_between_screens_.busy);
// fflush(LOG);
if (!window->parent()) {
if (olds != news) {
if (s != sd->scale(olds) &&
!Fl_WinAPI_Window_Driver::data_for_resize_window_between_screens_.busy &&
window->user_data() != &Fl_WinAPI_Screen_Driver::transient_scale_display) {
Fl_WinAPI_Window_Driver::data_for_resize_window_between_screens_.busy = true;
Fl_WinAPI_Window_Driver::data_for_resize_window_between_screens_.screen = news;
Fl::add_timeout(1, Fl_WinAPI_Window_Driver::resize_after_screen_change, window);
}
else if (!Fl_WinAPI_Window_Driver::data_for_resize_window_between_screens_.busy)
wd->screen_num(news);
} else if (Fl_WinAPI_Window_Driver::data_for_resize_window_between_screens_.busy) {
Fl::remove_timeout(Fl_WinAPI_Window_Driver::resize_after_screen_change, window);
Fl_WinAPI_Window_Driver::data_for_resize_window_between_screens_.busy = false;
}
scale = sd->scale(news);
if (olds != news) {
wd->screen_num(news);
if (window->as_gl_window()) invalidate_gl_win(window);
}
window->position(int(round(nx/scale)), int(round(ny/scale)));
break;
} // case WM_MOVE
wd->x(int(round(nx/scale)));
wd->y(int(round(ny/scale)));
}
return 0;
case WM_SETCURSOR:
if (LOWORD(lParam) == HTCLIENT) {

View File

@ -454,6 +454,19 @@ void Fl_WinAPI_Gl_Window_Driver::switch_back() {
}
class Fl_WinAPI_Gl_Plugin : public Fl_WinAPI_Plugin {
public:
Fl_WinAPI_Gl_Plugin() : Fl_WinAPI_Plugin(name()) { }
const char *name() override { return "gl.winapi.fltk.org"; }
void invalidate(Fl_Window *w) override {
w->as_gl_window()->valid(0);
}
};
static Fl_WinAPI_Gl_Plugin Gl_Invalidate_Plugin;
FL_EXPORT HGLRC fl_win32_glcontext(GLContext rc) { return (HGLRC)rc; }
#endif // HAVE_GL

View File

@ -23,6 +23,7 @@
#ifndef FL_WINAPI_WINDOW_DRIVER_H
#define FL_WINAPI_WINDOW_DRIVER_H
#include <FL/Fl_Plugin.H>
#include "../../Fl_Window_Driver.H"
#include <windows.h>
@ -71,11 +72,6 @@ public:
struct icon_data *icon_;
HCURSOR cursor;
int custom_cursor;
struct type_for_resize_window_between_screens {
int screen;
bool busy;
};
static type_for_resize_window_between_screens data_for_resize_window_between_screens_;
void set_minmax(LPMINMAXINFO minmax);
int fake_X_wm(int &X, int &Y, int &bt, int &bx, int &by, DWORD style = 0, DWORD styleEx = 0);
void make_fullscreen(int X, int Y, int W, int H);
@ -120,7 +116,15 @@ public:
void capture_titlebar_and_borders(Fl_RGB_Image*& top, Fl_RGB_Image*& left, Fl_RGB_Image*& bottom, Fl_RGB_Image*& right) FL_OVERRIDE;
int scroll(int src_x, int src_y, int src_w, int src_h, int dest_x, int dest_y,
void (*draw_area)(void*, int,int,int,int), void* data) FL_OVERRIDE;
static void resize_after_screen_change(void *data);
};
class Fl_WinAPI_Plugin : public Fl_Plugin {
public:
Fl_WinAPI_Plugin(const char *pluginName) : Fl_Plugin(klass(), pluginName) { }
virtual const char *klass() { return "winapi.fltk.org"; }
virtual const char *name() = 0;
virtual void invalidate(Fl_Window*) = 0;
};

View File

@ -50,7 +50,6 @@ Fl_WinAPI_Window_Driver::Fl_WinAPI_Window_Driver(Fl_Window *win)
Fl_WinAPI_Window_Driver::~Fl_WinAPI_Window_Driver()
{
Fl::remove_timeout(resize_after_screen_change, pWindow);
if (shape_data_) {
delete shape_data_->effective_bitmap_;
delete shape_data_;
@ -716,17 +715,6 @@ int Fl_WinAPI_Window_Driver::scroll(int src_x, int src_y, int src_w, int src_h,
return 0;
}
Fl_WinAPI_Window_Driver::type_for_resize_window_between_screens Fl_WinAPI_Window_Driver::data_for_resize_window_between_screens_ = {0, false};
void Fl_WinAPI_Window_Driver::resize_after_screen_change(void *data) {
Fl_Window *win = (Fl_Window*)data;
RECT r;
GetClientRect(fl_xid(win), &r);
float old_f = float(r.right)/win->w();
int ns = data_for_resize_window_between_screens_.screen;
Fl_Window_Driver::driver(win)->resize_after_scale_change(ns, old_f, Fl::screen_driver()->scale(ns));
data_for_resize_window_between_screens_.busy = false;
}
const Fl_Image* Fl_WinAPI_Window_Driver::shape() {
return shape_data_ ? shape_data_->shape_ : NULL;