From e5ac5678dc4a4d22ad9ca1e088c656c4e5f9b1e9 Mon Sep 17 00:00:00 2001 From: ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> Date: Mon, 15 May 2023 12:40:24 +0200 Subject: [PATCH] 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. --- src/Fl_Menu.cxx | 8 ++++++++ src/Fl_Window_Driver.H | 1 + .../Wayland/Fl_Wayland_Screen_Driver.cxx | 2 ++ .../Wayland/Fl_Wayland_Window_Driver.cxx | 19 +++++++++++++++++++ 4 files changed, 30 insertions(+) diff --git a/src/Fl_Menu.cxx b/src/Fl_Menu.cxx index d67b116d0..f3400bd9a 100644 --- a/src/Fl_Menu.cxx +++ b/src/Fl_Menu.cxx @@ -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; diff --git a/src/Fl_Window_Driver.H b/src/Fl_Window_Driver.H index 0149d28b0..9d7588d9d 100644 --- a/src/Fl_Window_Driver.H +++ b/src/Fl_Window_Driver.H @@ -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 *); diff --git a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx index a95cce8fe..2a379d57a 100644 --- a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx +++ b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx @@ -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; } diff --git a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx index 7ea0b948f..b4a5f6ebc 100644 --- a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx +++ b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx @@ -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;