From dfc930d346beda4d9751a2d890d1e6e785114620 Mon Sep 17 00:00:00 2001 From: ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:45:50 +0200 Subject: [PATCH] Wayland: partial implementation of multi-screen fullscreen windows (#1397) Wayland doesn't seem to allow creation of true multi-screen fullscreen windows. This commit implements an approximation of a multi-screen fullscreen window. It resizes a window to the total width and the total height of targetted screens. Under Mutter, this produces a window that covers all screens except the main screen's menubar. Other compositors may reply differently to this request. KWin produces a multi-screen fullscreen window only if the window is initially decorated and in the main screen. --- FL/Fl_Window.H | 14 ------ src/Fl_Window_fullscreen.cxx | 16 +++++++ .../Wayland/Fl_Wayland_Window_Driver.H | 1 + .../Wayland/Fl_Wayland_Window_Driver.cxx | 44 ++++++++++++++----- 4 files changed, 49 insertions(+), 26 deletions(-) diff --git a/FL/Fl_Window.H b/FL/Fl_Window.H index 54ecf0f5d..5bee47bd4 100644 --- a/FL/Fl_Window.H +++ b/FL/Fl_Window.H @@ -506,25 +506,11 @@ public: Turns off any side effects of fullscreen() */ void fullscreen_off(); - /** - Turns off any side effects of fullscreen() and does - resize(x,y,w,h). - */ void fullscreen_off(int X,int Y,int W,int H); /** Returns non zero if FULLSCREEN flag is set, 0 otherwise. */ unsigned int fullscreen_active() const { return flags() & FULLSCREEN; } - /** - Sets which screens should be used when this window is in fullscreen - mode. The window will be resized to the top of the screen with index - \p top, the bottom of the screen with index \p bottom, etc. - - If this method is never called, or if any argument is < 0, then the - window will be resized to fill the screen it is currently on. - - \see void Fl_Window::fullscreen() - */ void fullscreen_screens(int top, int bottom, int left, int right); void maximize(); diff --git a/src/Fl_Window_fullscreen.cxx b/src/Fl_Window_fullscreen.cxx index 79e57a0c8..b0b9db10d 100644 --- a/src/Fl_Window_fullscreen.cxx +++ b/src/Fl_Window_fullscreen.cxx @@ -50,6 +50,9 @@ void Fl_Window::fullscreen() { } } +/** + Turns off any side effects of fullscreen() and does resize(X,Y,W,H). +*/ void Fl_Window::fullscreen_off(int X,int Y,int W,int H) { if (shown() && (flags() & Fl_Widget::FULLSCREEN)) { pWindowDriver->fullscreen_off(X, Y, W, H); @@ -69,6 +72,19 @@ void Fl_Window::fullscreen_off() { fullscreen_off(no_fullscreen_x, no_fullscreen_y, no_fullscreen_w, no_fullscreen_h); } +/** + Sets which screens should be used when this window is in fullscreen + mode. The window will be resized to the top of the screen with index + \p top, the bottom of the screen with index \p bottom, etc. + + If this method is never called, or if any argument is < 0, then the + window will be resized to fill the screen it is currently on. + + Wayland compositors may support only partially or not at all multi-screen + fullscreen windows. + + \see void Fl_Window::fullscreen() + */ void Fl_Window::fullscreen_screens(int top, int bottom, int left, int right) { if ((top < 0) || (bottom < 0) || (left < 0) || (right < 0)) { fullscreen_screen_top = -1; diff --git a/src/drivers/Wayland/Fl_Wayland_Window_Driver.H b/src/drivers/Wayland/Fl_Wayland_Window_Driver.H index c5c1bee50..dea77c561 100644 --- a/src/drivers/Wayland/Fl_Wayland_Window_Driver.H +++ b/src/drivers/Wayland/Fl_Wayland_Window_Driver.H @@ -63,6 +63,7 @@ private: Fl_Cursor standard_cursor_; // window's standard custom kind struct gl_start_support *gl_start_support_; // for support of gl_start/gl_finish bool is_popup_window_; + bool previous_border_; // records whether window before being turned fullscreen had a border public: inline Fl_Cursor standard_cursor() { return standard_cursor_; } bool in_handle_configure; // distinguish OS and user window resize diff --git a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx index c41b4d08a..e79c3a86f 100644 --- a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx +++ b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx @@ -75,6 +75,7 @@ Fl_Wayland_Window_Driver::Fl_Wayland_Window_Driver(Fl_Window *win) : Fl_Window_D subRect_ = NULL; is_popup_window_ = false; can_expand_outside_parent_ = false; + previous_border_ = false; } @@ -912,7 +913,8 @@ static void handle_configure(struct libdecor_frame *frame, #endif struct wl_output *wl_output = NULL; if (window->fl_win->fullscreen_active()) { - if (!(window->state & LIBDECOR_WINDOW_STATE_FULLSCREEN)) { + if (is_1st_run && !(window->state & LIBDECOR_WINDOW_STATE_FULLSCREEN)) { + // Used to turn fullscreen a window while showing it if (Fl_Window_Driver::driver(window->fl_win)->force_position()) { struct Fl_Wayland_Screen_Driver::output *output = screen_num_to_output(window->fl_win->screen_num()); @@ -954,13 +956,6 @@ static void handle_configure(struct libdecor_frame *frame, scan_subwindows(window->fl_win, does_window_cover_parent); // issue #878 } - if (window->fl_win->fullscreen_active() && - Fl_Window_Driver::driver(window->fl_win)->force_position()) { - int X, Y, W, H; - Fl::screen_xywh(X, Y, W, H, window->fl_win->screen_num()); - width = W * f; height = H * f; - } - if (width == 0) { width = window->floating_width; height = window->floating_height; @@ -1034,6 +1029,7 @@ static void handle_configure(struct libdecor_frame *frame, if (Fl_Wayland_Screen_Driver::compositor != Fl_Wayland_Screen_Driver::WESTON || !is_1st_run) { window->fl_win->clear_damage(); } + if (is_2nd_run) driver->force_position(0); } @@ -1148,7 +1144,10 @@ static void xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel // under Weston: width & height are 0 during both calls, except if fullscreen struct wld_window *window = (struct wld_window*)data; //fprintf(stderr, "xdg_toplevel_configure: surface=%p size: %dx%d\n", window->wl_surface, width, height); - if (window->fl_win->fullscreen_active() && !parse_states_fullscreen(states)) { + Fl_Window_Driver *dr = Fl_Window_Driver::driver(window->fl_win); + if (window->fl_win->fullscreen_active() && !parse_states_fullscreen(states) && + dr->fullscreen_screen_top() == dr->fullscreen_screen_bottom() && + dr->fullscreen_screen_left() == dr->fullscreen_screen_right()) { struct wl_output *wl_output = NULL; if (Fl_Window_Driver::driver(window->fl_win)->force_position()) { struct Fl_Wayland_Screen_Driver::output *output = @@ -1746,7 +1745,7 @@ int Fl_Wayland_Window_Driver::set_cursor(Fl_Cursor c) { void Fl_Wayland_Window_Driver::use_border() { if (!shown() || pWindow->parent()) return; - pWindow->wait_for_expose(); // useful for border(0) just after show() + if (!xdg_toplevel()) pWindow->wait_for_expose(); // useful for border(0) just after show() struct libdecor_frame *frame = fl_wl_xid(pWindow)->frame; if (frame && Fl_Wayland_Screen_Driver::compositor != Fl_Wayland_Screen_Driver::KWIN) { if (fl_wl_xid(pWindow)->kind == DECORATED) { @@ -1777,9 +1776,29 @@ void Fl_Wayland_Window_Driver::fullscreen_on() { left = top; right = top; } - pWindow->wait_for_expose(); // make sure ->xdg_toplevel is initialized + if (!xdg_toplevel()) pWindow->wait_for_expose(); // make sure ->xdg_toplevel is initialized if (xdg_toplevel()) { - xdg_toplevel_set_fullscreen(xdg_toplevel(), NULL); + if (top == bottom && left == right) { // single-screen fullscreen + if (!pWindow->fullscreen_active()) previous_border_ = pWindow->border(); + xdg_toplevel_set_fullscreen(xdg_toplevel(), NULL); + } else { // multi-screen fullscreen + if (pWindow->fullscreen_active()) { + xdg_toplevel_unset_fullscreen(xdg_toplevel()); + wl_display_roundtrip(Fl_Wayland_Screen_Driver::wl_display); // Mutter needs this + } else { + previous_border_ = pWindow->border(); + } + if (previous_border_) pWindow->border(0); + int XL,XR,YY,WL,WR,HH; + Fl::screen_xywh(XL, YY, WL, HH, left); + Fl::screen_xywh(XR, YY, WR, HH, right); + int W = XR + WR - XL; + int XX,YT,YB,WW,HB,HT; + Fl::screen_xywh(XX, YT, WW, HT, top); + Fl::screen_xywh(XX, YB, WW, HB, bottom); + int H = YB + HB - YT; + pWindow->size(W, H); + } pWindow->_set_fullscreen(); Fl::handle(FL_FULLSCREEN, pWindow); } @@ -1793,6 +1812,7 @@ void Fl_Wayland_Window_Driver::fullscreen_off(int X, int Y, int W, int H) { if (!W) W = w(); if (!H) H = h(); pWindow->resize(X, Y, W, H); + if (previous_border_) { pWindow->border(1); previous_border_ = false; } pWindow->show(); Fl::handle(FL_FULLSCREEN, pWindow); }