Fix handling of tall menu windows with the KDE Wayland compositor

Unfortunately (sigh), the KDE Wayland compositor does not seem to support
correctly, that is, as described by the Wayland protocol, popup windows
that are taller than the display : there is no means to make it draw such popup
so that part of it is above the screen top, whereas the 3 other tested compositors
(Mutter, Weston, Sway) don't have this problem.

This commit implements a new approach to draw tall menu windows, and uses
it only with the KDE compositor : instead of asking the compositor to slide the
menu window up, the menu window remains at a fixed position and the graphics
inside the window is slided up.

This requires to add a member variable, int offset_y, to class menuwindow, that
gets used only for the Wayland platform and that contains the vertical offset by which
graphics to the menu window is moved, expressed in FLTK units. An accessor
to the address of this member variable is added to class Fl_Window_Driver.
This commit is contained in:
ManoloFLTK 2023-05-15 12:40:24 +02:00
parent 1555132df1
commit e5ac5678dc
4 changed files with 30 additions and 0 deletions

View File

@ -163,6 +163,7 @@ public:
menuwindow* as_menuwindow() FL_OVERRIDE { return this; }
int menubartitle;
menuwindow *origin;
int offset_y;
};
Fl_Window *menuwindow::parent_ = NULL;
@ -224,6 +225,12 @@ int Fl_Window_Driver::menu_selected(Fl_Window *win) {
return (mwin ? mwin->selected : -1);
}
/** Accessor to the address of the offset_y member variable of class menuwindow */
int *Fl_Window_Driver::menu_offset_y(Fl_Window *win) {
menuwindow *mwin = to_menuwindow(win);
return (mwin ? &(mwin->offset_y) : NULL);
}
/** Returns whether win is a non-menubar menutitle */
bool Fl_Window_Driver::is_floating_title(Fl_Window *win) {
if (!win->menu_window()) return false;
@ -371,6 +378,7 @@ menuwindow::menuwindow(const Fl_Menu_Item* m, int X, int Y, int Wp, int Hp,
int tx = X, ty = Y;
menubartitle = menubar_title;
origin = NULL;
offset_y = 0;
Fl_Window_Driver::driver(this)->menu_window_area(scr_x, scr_y, scr_w, scr_h);
if (!right_edge || right_edge > scr_x+scr_w) right_edge = scr_x+scr_w;

View File

@ -200,6 +200,7 @@ public:
static int menu_itemheight(Fl_Window*);
static int menu_bartitle(Fl_Window*);
static int menu_selected(Fl_Window*);
static int *menu_offset_y(Fl_Window*);
static bool is_floating_title(Fl_Window *);
static void scroll_to_selected_item(Fl_Window *);
static bool is_menutitle(Fl_Window *);

View File

@ -219,6 +219,8 @@ static Fl_Window *event_coords_from_surface(struct wl_surface *surface,
Fl::e_x = wl_fixed_to_int(surface_x) / f + delta_x;
Fl::e_x_root = Fl::e_x + win->x();
Fl::e_y = wl_fixed_to_int(surface_y) / f + delta_y;
int *poffset = Fl_Window_Driver::menu_offset_y(win);
if (poffset) Fl::e_y -= *poffset;
Fl::e_y_root = Fl::e_y + win->y();
return win;
}

View File

@ -356,6 +356,10 @@ void Fl_Wayland_Window_Driver::make_current() {
&window->buffer->draw_buffer_needs_commit);
}
((Fl_Wayland_Graphics_Driver*)fl_graphics_driver)->set_buffer(window->buffer, f * wld_s);
int *poffset = Fl_Window_Driver::menu_offset_y(pWindow);
if (poffset) { // for tall menu windows under KDE to offset drawing inside window
cairo_translate(window->buffer->cairo_, 0, *poffset);
}
cairo_rectangle_int_t *extents = subRect();
if (extents) { // make damage-to-buffer not to leak outside parent
Fl_Region clip_region = fl_graphics_driver->XRectangleRegion(extents->x, extents->y,
@ -1741,6 +1745,21 @@ void Fl_Wayland_Window_Driver::subRect(cairo_rectangle_int_t *r) {
void Fl_Wayland_Window_Driver::reposition_menu_window(int x, int y) {
if (y == pWindow->y()) return;
if (Fl_Wayland_Screen_Driver::compositor == Fl_Wayland_Screen_Driver::KDE) {
// The KDE compositor refuses to position a popup such that it extends above
// the top of the screen. Therefore, instead of sliding the popup window
// on the display, we slide the drawing inside the fixed popup via
// member variable offset_y of the menuwindow class, and we redraw the popup
// content. It's also useful to make such tall popup window transparent.
*Fl_Window_Driver::menu_offset_y(pWindow) += (y - pWindow->y());
struct wld_window *xid = fl_wl_xid(pWindow);
wl_surface_set_opaque_region(xid->wl_surface, NULL);
memset(xid->buffer->draw_buffer, 0, xid->buffer->data_size);
//printf("offset_y=%d\n", *Fl_Window_Driver::menu_offset_y(pWindow));
this->y(y);
pWindow->redraw();
return;
}
struct wld_window * xid_menu = fl_wl_xid(pWindow);
//printf("reposition %dx%d[cur=%d] menu->state=%d\n", x, y, pWindow->y(), xid_menu->state);
struct xdg_popup *old_popup = xid_menu->xdg_popup;