Wayland: partial implementation of multi-screen fullscreen windows (#1397)
Build and Test / build-linux (push) Has been cancelled
Build and Test / build-wayland (push) Has been cancelled
Build and Test / build-macos (push) Has been cancelled
Build and Test / build-windows (push) Has been cancelled

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.
This commit is contained in:
ManoloFLTK
2026-04-08 10:45:50 +02:00
parent 24b66b2b31
commit dfc930d346
4 changed files with 49 additions and 26 deletions
-14
View File
@@ -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();
+16
View File
@@ -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;
@@ -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
@@ -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);
}