Fix "Wayland: Huge menu hiding unexpectedly when moving a mouse (#1115)

This commit also computes work-area size for single-display settings.
This commit is contained in:
ManoloFLTK 2025-03-11 17:35:29 +01:00
parent 1054e81504
commit 9b5d40ad97
3 changed files with 102 additions and 12 deletions

View File

@ -67,8 +67,10 @@ public:
struct output { // one record for each screen
uint32_t id;
int x, y; // logical position of screen
int width; // in pixels
int height; // in pixels
int pixel_width; // in pixels
int pixel_height; // in pixels
int width; // in pixels, account for fractional scaling
int height; // in pixels, account for fractional scaling
float dpi;
struct wl_output *wl_output;
int wld_scale; // Wayland scale factor

View File

@ -1106,8 +1106,8 @@ static void output_mode(void *data, struct wl_output *wl_output, uint32_t flags,
int32_t width, int32_t height, int32_t refresh)
{
Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data;
output->width = int(width);
output->height = int(height);
output->pixel_width = int(width);
output->pixel_height = int(height);
//fprintf(stderr, "output_mode: [%p]=%dx%d\n",output->wl_output,width,height);
}
@ -1531,18 +1531,107 @@ void Fl_Wayland_Screen_Driver::close_display() {
}
struct pair_s { int W, H; };
static void xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
int32_t width, int32_t height, struct wl_array *states)
{
struct pair_s *pair = (struct pair_s*)data;
pair->W = width;
pair->H = height;
}
static void xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel) {}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
.configure = xdg_toplevel_configure,
.close = xdg_toplevel_close,
};
static void compute_full_and_maximized_areas(Fl_Wayland_Screen_Driver::output *output,
int& Wfullscreen, int& Hfullscreen,
int& Wworkarea, int& Hworkarea,
bool need_workarea) {
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
struct wl_surface *wl_surface = wl_compositor_create_surface(scr_driver->wl_compositor);
wl_surface_set_opaque_region(wl_surface, NULL);
struct xdg_surface *xdg_surface = xdg_wm_base_get_xdg_surface(scr_driver->xdg_wm_base, wl_surface);
struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
struct pair_s pair = {0, 0};
xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, &pair);
xdg_toplevel_set_fullscreen(xdg_toplevel, output->wl_output);
wl_surface_commit(wl_surface);
while (!pair.H) wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display);
Wfullscreen = pair.W;
Hfullscreen = pair.H;
if (need_workarea) {
xdg_toplevel_unset_fullscreen(xdg_toplevel);
xdg_toplevel_set_maximized(xdg_toplevel);
pair.H = 0;
wl_surface_commit(wl_surface);
while (!pair.H) wl_display_dispatch(Fl_Wayland_Screen_Driver::wl_display);
}
Wworkarea = pair.W;
Hworkarea = pair.H;
xdg_toplevel_destroy(xdg_toplevel);
xdg_surface_destroy(xdg_surface);
wl_surface_destroy(wl_surface);
//int H = output->pixel_height / output->wld_scale;
//float fractional_scale = Hfullscreen / float(H);
//printf("fullscreen=%dx%d workarea=%dx%d apparentH=%d fractional_scale=%g wld_s=%d\n",
// Wfullscreen,Hfullscreen,Wworkarea,Hworkarea,H,fractional_scale,output->wld_scale);
}
static int workarea_xywh[4] = { -1, -1, -1, -1 };
/* Implementation note about computing work area and about handling fractional scaling.
FLTK computes 2 pairs of (WxH) values for each display:
1) (pixel_width x pixel_height) gives the size in pixel of a display. It's unchanged by
any scaling applied by the compositor; it's assigned by function output_mode().
2) (width x height) gives the size in pixels of a buffer that would fully cover the display.
When the active scaling is non-fractional, these equations hold:
pixel_width = width = wld_scale * configured-width-of-fullscreen-window
pixel_height = height = wld_scale * configured-height-of-fullscreen-window
When fractional scaling is active, buffers received from client are scaled down
by the compositor and mapped to screen. These equations hold:
pixel_width < width = wld_scale * configured-width-of-fullscreen-window
pixel_height < height = wld_scale * configured-height-of-fullscreen-window
One way for a client to discover that fractional scaling is active on a given display
is to ask for a fullscreen window on that display, get its configured size and compare
it to the display pixel size. That's what function compute_full_and_maximized_areas() does.
One way for a client to discover the work area size is to get the configured size
of a maximized window on a given display. But it's not possible to control on what display
the compositor puts the maximized window. Therefore, FLTK computes an exact work area size
only when the system contains a single display. That's also done by function
compute_full_and_maximized_areas().
FLTK didn't find how to recognize the primary display within the list of displays
received from the compositor. That's another reason why FLTK doesn't attempt
to compute work area sizes when there are multiple displays.
*/
void Fl_Wayland_Screen_Driver::init_workarea()
{
Fl_Wayland_Screen_Driver::output *output;
int count = 0;
wl_list_for_each(output, &outputs, link) ++count;
bool need_workarea = (count == 1);
bool first = true;
wl_list_for_each(output, &outputs, link) {
workarea_xywh[0] = output->x; // pixels
workarea_xywh[1] = output->y; // pixels
workarea_xywh[2] = output->width; // pixels
workarea_xywh[3] = output->height; // pixels
break;
if (first) workarea_xywh[0] = output->x; // pixels
if (first) workarea_xywh[1] = output->y; // pixels
int Wfullscreen, Hfullscreen, Wworkarea, Hworkarea;
compute_full_and_maximized_areas(output, Wfullscreen, Hfullscreen, Wworkarea, Hworkarea, need_workarea);
output->width = Wfullscreen * output->wld_scale; // pixels
if (first) workarea_xywh[2] = Wworkarea * output->wld_scale; // pixels
output->height = Hfullscreen * output->wld_scale; // pixels
if (first) workarea_xywh[3] = Hworkarea * output->wld_scale; // pixels
first = false;
}
}

View File

@ -95,9 +95,8 @@ void Fl::screen_work_area(int &X, int &Y, int &W, int &H, int mx, int my)
\param[out] X,Y,W,H the work area bounding box
\param[in] n the screen number (0 to Fl::screen_count() - 1)
\see void screen_xywh(int &x, int &y, int &w, int &h, int mx, int my)
\note Under X11, the screen work area is given values that differ from that screen's bounding box
only if the system contains a single screen. Under Wayland, a screen work area is always
equal to that screen's bounding box.
\note Under X11 and Wayland, the screen work area is given values that differ
from that screen's bounding box only if the system contains a single screen.
\note Like all quantities accessible via public APIs of FLTK, values of \p X,Y,W,H
are given in FLTK units, that is, in drawing units divided by the scaling factor of screen \p n.
*/