From 570c0cce066d2baab9504aece94a91c3e3a6405c Mon Sep 17 00:00:00 2001 From: Ciaran Anscomb Date: Mon, 14 Aug 2023 11:59:20 +0100 Subject: [PATCH 1/3] Set window gravity hint correctly for X11 Removes some window positioning bodges, which mess with coordinates. Window Managers should be responsible for placing windows correctly if they're adding title bars and such to them. Note that negative X and Y will only end up truly accurate if the screen scale is (or has been set to) 1.0. This is because information about the actual screen dimensions has been lost scaling integers. Updated patch fixes use of uninitialised variable, with help from Albrecht Schlosser. --- src/Fl_Window_Driver.H | 20 ++++++++++++++++++++ src/Fl_Window_Driver.cxx | 1 + src/Fl_arg.cxx | 30 ++++++++++++++++++++++++++++++ src/Fl_x.cxx | 19 ++++++++++++++++++- 4 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/Fl_Window_Driver.H b/src/Fl_Window_Driver.H index 9d7588d9d..29c09bea8 100644 --- a/src/Fl_Window_Driver.H +++ b/src/Fl_Window_Driver.H @@ -58,6 +58,8 @@ private: protected: Fl_Window *pWindow; int screen_num_; // number of screen where window is mapped + int win_gravity_; + public: Fl_Window_Driver(Fl_Window *); virtual ~Fl_Window_Driver(); @@ -69,6 +71,24 @@ public: int screen_num(); void screen_num(int n) { screen_num_ = n; } + // Window gravity + enum WIN_GRAVITY { + WIN_GRAVITY_UNMAP = 0, + WIN_GRAVITY_NORTHWEST = 1, + WIN_GRAVITY_NORTH = 2, + WIN_GRAVITY_NORTHEAST = 3, + WIN_GRAVITY_WEST = 4, + WIN_GRAVITY_CENTER = 5, + WIN_GRAVITY_EAST = 6, + WIN_GRAVITY_SOUTHWEST = 7, + WIN_GRAVITY_SOUTH = 8, + WIN_GRAVITY_SOUTHEAST = 9, + WIN_GRAVITY_STATIC = 10, + }; + /** + Sets the desired window gravity. Almost certainly only useful in X11. + */ + virtual void win_gravity(int g) { win_gravity_ = g; } // --- frequently used accessors to public window data /** returns the x coordinate of the window. */ diff --git a/src/Fl_Window_Driver.cxx b/src/Fl_Window_Driver.cxx index 25d2554c6..92d1d7343 100644 --- a/src/Fl_Window_Driver.cxx +++ b/src/Fl_Window_Driver.cxx @@ -43,6 +43,7 @@ Fl_Window_Driver::Fl_Window_Driver(Fl_Window *win) wait_for_expose_value = 0; other_xid = 0; screen_num_ = 0; + win_gravity_ = WIN_GRAVITY_STATIC; } diff --git a/src/Fl_arg.cxx b/src/Fl_arg.cxx index 08254c3e9..79777e5cb 100644 --- a/src/Fl_arg.cxx +++ b/src/Fl_arg.cxx @@ -284,6 +284,36 @@ void Fl_Window::show(int argc, char **argv) { fl = Fl::screen_driver()->XParseGeometry(geometry, &gx, &gy, &gw, &gh); if (fl & Fl_Screen_Driver::fl_XNegative) gx = Fl::w()-w()+gx; if (fl & Fl_Screen_Driver::fl_YNegative) gy = Fl::h()-h()+gy; + + // Determine window gravity from geometry. Note that negative X and Y + // will only end up truly accurate if the screen scale is (or has been + // set to) 1.0. This is because information about the actual screen + // dimensions has been lost scaling integers. + static const Fl_Window_Driver::WIN_GRAVITY gravity_table[16] = { + Fl_Window_Driver::WIN_GRAVITY_STATIC, + Fl_Window_Driver::WIN_GRAVITY_WEST, + Fl_Window_Driver::WIN_GRAVITY_NORTH, + Fl_Window_Driver::WIN_GRAVITY_NORTHWEST, + Fl_Window_Driver::WIN_GRAVITY_STATIC, + Fl_Window_Driver::WIN_GRAVITY_EAST, + Fl_Window_Driver::WIN_GRAVITY_NORTH, + Fl_Window_Driver::WIN_GRAVITY_NORTHEAST, + Fl_Window_Driver::WIN_GRAVITY_STATIC, + Fl_Window_Driver::WIN_GRAVITY_WEST, + Fl_Window_Driver::WIN_GRAVITY_SOUTH, + Fl_Window_Driver::WIN_GRAVITY_SOUTHWEST, + Fl_Window_Driver::WIN_GRAVITY_STATIC, + Fl_Window_Driver::WIN_GRAVITY_EAST, + Fl_Window_Driver::WIN_GRAVITY_SOUTH, + Fl_Window_Driver::WIN_GRAVITY_SOUTHEAST + }; + int gravity_index = 0; + gravity_index |= (fl & Fl_Screen_Driver::fl_XValue) ? 1 : 0; + gravity_index |= (fl & Fl_Screen_Driver::fl_YValue) ? 2 : 0; + gravity_index |= (fl & Fl_Screen_Driver::fl_XNegative) ? 4 : 0; + gravity_index |= (fl & Fl_Screen_Driver::fl_YNegative) ? 8 : 0; + pWindowDriver->win_gravity(gravity_table[gravity_index]); + // int mw,mh; minsize(mw,mh); // if (mw > gw) gw = mw; // if (mh > gh) gh = mh; diff --git a/src/Fl_x.cxx b/src/Fl_x.cxx index 1dffa6e1c..1704be57e 100644 --- a/src/Fl_x.cxx +++ b/src/Fl_x.cxx @@ -2375,6 +2375,11 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap) int scr_x, scr_y, scr_w, scr_h; Fl::screen_xywh(scr_x, scr_y, scr_w, scr_h, X, Y, W, H); +#if 0 + // Munging the coordinates here just makes it impossible to put windows in + // the right place. With win_gravity hints now being set, this should all + // be down to the window manager. + if (win->border()) { // ensure border is on screen: // (assume extremely minimal dimensions for this border) @@ -2392,6 +2397,7 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap) if (X < scr_x) X = scr_x; if (Y+H > scr_y+scr_h) Y = scr_y+scr_h-H; if (Y < scr_y) Y = scr_y; +#endif } // if the window is a subwindow and our parent is not mapped yet, we @@ -2647,7 +2653,18 @@ void Fl_X11_Window_Driver::sendxjunk() { hints->height_inc = 0; } - hints->win_gravity = StaticGravity; + switch (win_gravity_) { + case WIN_GRAVITY_NORTHWEST: hints->win_gravity = NorthWestGravity; break; + case WIN_GRAVITY_NORTH: hints->win_gravity = NorthGravity; break; + case WIN_GRAVITY_NORTHEAST: hints->win_gravity = NorthEastGravity; break; + case WIN_GRAVITY_WEST: hints->win_gravity = WestGravity; break; + case WIN_GRAVITY_CENTER: hints->win_gravity = CenterGravity; break; + case WIN_GRAVITY_EAST: hints->win_gravity = EastGravity; break; + case WIN_GRAVITY_SOUTHWEST: hints->win_gravity = SouthWestGravity; break; + case WIN_GRAVITY_SOUTH: hints->win_gravity = SouthGravity; break; + case WIN_GRAVITY_SOUTHEAST: hints->win_gravity = SouthEastGravity; break; + default: hints->win_gravity = StaticGravity; break; + } // see the file /usr/include/X11/Xm/MwmUtil.h: // fill all fields to avoid bugs in kwm and perhaps other window managers: From 209f348ca406416a66cf02567d4af0bcc9435a12 Mon Sep 17 00:00:00 2001 From: Ciaran Anscomb Date: Mon, 14 Aug 2023 14:18:10 +0100 Subject: [PATCH 2/3] Offset position in X11 for east and south gravity Adds new virtual functions x_from_right() and y_from_bottom() to Fl_Screen_Driver. These are overridden in Fl_X11_Screen_Driver to return the offset between the actual screen edge (right or bottom) and the scaled up screen edge. The calculation is: scaled delta = scaled limit - scaled coordinate real coordinate = real limit - (scaled delta * screen scale) These offsets then applied when creating a window with east or south gravity to adjust for the loss of precision in the screen dimensions when they were scaled. --- src/Fl_Screen_Driver.H | 2 ++ src/Fl_x.cxx | 26 +++++++++++++++++++++++- src/drivers/X11/Fl_X11_Screen_Driver.H | 2 ++ src/drivers/X11/Fl_X11_Screen_Driver.cxx | 17 ++++++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/Fl_Screen_Driver.H b/src/Fl_Screen_Driver.H index 1b6e057ed..89a0e1c48 100644 --- a/src/Fl_Screen_Driver.H +++ b/src/Fl_Screen_Driver.H @@ -111,6 +111,8 @@ public: virtual int y() { return 0; } virtual int w() { return 800; } // default, FL_OVERRIDE in driver! virtual int h() { return 600; } // default, FL_OVERRIDE in driver! + virtual int x_from_right(int x) { return x * scale(0); } + virtual int y_from_bottom(int y) { return y * scale(0); } virtual int screen_count(); void screen_xywh(int &X, int &Y, int &W, int &H, int mx, int my); virtual void screen_xywh(int &X, int &Y, int &W, int &H, int /*n*/) { diff --git a/src/Fl_x.cxx b/src/Fl_x.cxx index 1704be57e..800c27e90 100644 --- a/src/Fl_x.cxx +++ b/src/Fl_x.cxx @@ -2487,10 +2487,34 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap) s = Fl::screen_driver()->scale(nscreen); // if (!win->parent()) printf("win creation on screen #%d\n", nscreen); #endif + + int win_x = rint(X*s); + int win_y = rint(Y*s); + + switch (Fl_X11_Window_Driver::driver(win)->win_gravity_) { + case Fl_X11_Window_Driver::WIN_GRAVITY_NORTHEAST: + case Fl_X11_Window_Driver::WIN_GRAVITY_EAST: + case Fl_X11_Window_Driver::WIN_GRAVITY_SOUTHEAST: + win_x = Fl::screen_driver()->x_from_right(X); + break; + default: + break; + } + + switch (Fl_X11_Window_Driver::driver(win)->win_gravity_) { + case Fl_X11_Window_Driver::WIN_GRAVITY_SOUTHWEST: + case Fl_X11_Window_Driver::WIN_GRAVITY_SOUTH: + case Fl_X11_Window_Driver::WIN_GRAVITY_SOUTHEAST: + win_y = Fl::screen_driver()->y_from_bottom(Y); + break; + default: + break; + } + Fl_X* xp = set_xid(win, XCreateWindow(fl_display, root, - rint(X*s), rint(Y*s), W*s, H*s, + win_x, win_y, W*s, H*s, 0, // borderwidth visual->depth, InputOutput, diff --git a/src/drivers/X11/Fl_X11_Screen_Driver.H b/src/drivers/X11/Fl_X11_Screen_Driver.H index 17826a94d..89bbca2dd 100644 --- a/src/drivers/X11/Fl_X11_Screen_Driver.H +++ b/src/drivers/X11/Fl_X11_Screen_Driver.H @@ -81,6 +81,8 @@ public: int y() FL_OVERRIDE; int w() FL_OVERRIDE; int h() FL_OVERRIDE; + int x_from_right(int x) FL_OVERRIDE; + int y_from_bottom(int y) FL_OVERRIDE; void screen_xywh(int &X, int &Y, int &W, int &H, int n) FL_OVERRIDE; void screen_dpi(float &h, float &v, int n=0) FL_OVERRIDE; void screen_work_area(int &X, int &Y, int &W, int &H, int n) FL_OVERRIDE; diff --git a/src/drivers/X11/Fl_X11_Screen_Driver.cxx b/src/drivers/X11/Fl_X11_Screen_Driver.cxx index 9e3465f13..4debda429 100644 --- a/src/drivers/X11/Fl_X11_Screen_Driver.cxx +++ b/src/drivers/X11/Fl_X11_Screen_Driver.cxx @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "../../Fl_Timeout.h" @@ -252,6 +253,22 @@ int Fl_X11_Screen_Driver::h() { ; } +// Returns a real (unscaled) X coordinate calculated relative to the right of +// of the screen. Used when positioning window with "east" gravity. + +int Fl_X11_Screen_Driver::x_from_right(int x) { + int dx = w() - x; + return (fl_workarea_xywh[0] + fl_workarea_xywh[2]) - rint(dx * screens[0].scale); +} + +// Returns a real (unscaled) Y coordinate calculated relative to the bottom of +// the screen. Used when positioning window with "south" gravity. + +int Fl_X11_Screen_Driver::y_from_bottom(int y) { + int dy = h() - y; + return (fl_workarea_xywh[1] + fl_workarea_xywh[3]) - rint(dy * screens[0].scale); +} + #define USE_XRANDR (HAVE_DLSYM && HAVE_DLFCN_H) // means attempt to dynamically load libXrandr.so #if USE_XRANDR #include From b0de49b28062e2f729c850465e1f6144226bbbcc Mon Sep 17 00:00:00 2001 From: Ciaran Anscomb Date: Wed, 16 Aug 2023 15:16:09 +0100 Subject: [PATCH 3/3] X11 geometry: use parsed width and height in negative calculations If the user has specified a new dimension, that's how much we need to offset the coordinates. Using gw, gh is safe here: Fl_Screen_Driver::XParseGeometry() will only update their values if there was a user-specified value. --- src/Fl_arg.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Fl_arg.cxx b/src/Fl_arg.cxx index 79777e5cb..415e826c1 100644 --- a/src/Fl_arg.cxx +++ b/src/Fl_arg.cxx @@ -282,8 +282,8 @@ void Fl_Window::show(int argc, char **argv) { if (geometry) { int fl = 0, gx = x(), gy = y(); unsigned int gw = w(), gh = h(); fl = Fl::screen_driver()->XParseGeometry(geometry, &gx, &gy, &gw, &gh); - if (fl & Fl_Screen_Driver::fl_XNegative) gx = Fl::w()-w()+gx; - if (fl & Fl_Screen_Driver::fl_YNegative) gy = Fl::h()-h()+gy; + if (fl & Fl_Screen_Driver::fl_XNegative) gx = Fl::w()-gw+gx; + if (fl & Fl_Screen_Driver::fl_YNegative) gy = Fl::h()-gh+gy; // Determine window gravity from geometry. Note that negative X and Y // will only end up truly accurate if the screen scale is (or has been