diff --git a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx index 2c45df2c8..3c9fb3c65 100644 --- a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx +++ b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx @@ -1771,7 +1771,36 @@ int Fl_Wayland_Window_Driver::set_cursor_4args(const Fl_RGB_Image *rgb, int hotx } +struct xid_and_rect { + struct wld_window *xid; + Fl_Window *win; + int X, Y, W, H; + bool need_resize; +}; + + +static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) { + struct xid_and_rect *xid_rect = (xid_and_rect *)data; + wl_callback_destroy(cb); + xid_rect->xid->frame_cb = NULL; + if (xid_rect->need_resize) { + xid_rect->win->Fl_Group::resize(xid_rect->X, xid_rect->Y, xid_rect->W, xid_rect->H); + xid_rect->win->redraw(); + } else { + xid_rect->win->Fl_Widget::resize(xid_rect->X, xid_rect->Y, xid_rect->W, xid_rect->H); + wl_surface_commit(xid_rect->xid->wl_surface); + } + delete xid_rect; +} + + +static const struct wl_callback_listener surface_frame_listener = { + .done = surface_frame_done, +}; + + void Fl_Wayland_Window_Driver::resize(int X, int Y, int W, int H) { + static int depth = 0; struct wld_window *fl_win = fl_wl_xid(pWindow); if (fl_win && fl_win->kind == DECORATED && !xdg_toplevel()) { pWindow->wait_for_expose(); @@ -1789,20 +1818,32 @@ void Fl_Wayland_Window_Driver::resize(int X, int Y, int W, int H) { int is_a_resize = (W != w() || H != h() || true_rescale); if (is_a_move) force_position(1); else if (!is_a_resize && !is_a_move) return; + depth++; if (shown() && !(parent() || popup_window())) { X = Y = 0; } - if (is_a_resize) { - if (pWindow->parent()) { - if (W < 1) W = 1; - if (H < 1) H = 1; + struct wld_window *parent_xid = parent() ? fl_wl_xid(pWindow->window()) : NULL; + xid_and_rect *xid_rect = NULL; + if (parent_xid && parent_xid->frame_cb && wl_proxy_get_listener((struct wl_proxy*)parent_xid->frame_cb) == &surface_frame_listener) { + xid_rect = (xid_and_rect*)wl_callback_get_user_data(parent_xid->frame_cb); + if (xid_rect->win != pWindow) xid_rect = NULL; + } + // When moving or resizing a non-GL subwindow independently from its parent, this condition + // delays application of X,Y,W,H values until the compositor signals + // it's ready for a new frame using the frame callback mechanism. + if (depth > 1 || pWindow->as_gl_window() || !parent_xid || wait_for_expose_value || (parent_xid->frame_cb && !xid_rect)) { + if (is_a_resize) { + if (pWindow->parent()) { + if (W < 1) W = 1; + if (H < 1) H = 1; + } + pWindow->Fl_Group::resize(X,Y,W,H); + //fprintf(stderr, "resize: win=%p to %dx%d\n", pWindow, W, H); + if (shown()) {pWindow->redraw();} + } else { + x(X); y(Y); + //fprintf(stderr, "move win=%p to %dx%d\n", pWindow, X, Y); } - pWindow->Fl_Group::resize(X,Y,W,H); - //fprintf(stderr, "resize: win=%p to %dx%d\n", pWindow, W, H); - if (shown()) {pWindow->redraw();} - } else { - x(X); y(Y); - //fprintf(stderr, "move win=%p to %dx%d\n", pWindow, X, Y); } if (shown()) { @@ -1860,14 +1901,35 @@ void Fl_Wayland_Window_Driver::resize(int X, int Y, int W, int H) { } } - struct wld_window *parent_xid = parent() ? fl_wl_xid(pWindow->window()) : NULL; if (fl_win && parent_xid) { - if (pWindow->as_gl_window() && fl_win->frame_cb) { - wl_callback_destroy(fl_win->frame_cb); - fl_win->frame_cb = NULL; - } else wl_surface_commit(parent_xid->wl_surface); + if (pWindow->as_gl_window()) { + if (fl_win->frame_cb) { + wl_callback_destroy(fl_win->frame_cb); + fl_win->frame_cb = NULL; + } + Fl_Wayland_Graphics_Driver::buffer_commit(parent_xid); + } else { + if (!parent_xid->frame_cb) { + // use the frame callback mechanism and memorize current X,Y,W,H values + xid_rect = new xid_and_rect; + xid_rect->xid = parent_xid; + xid_rect->win = pWindow; + parent_xid->frame_cb = wl_surface_frame(parent_xid->wl_surface); + wl_callback_add_listener(parent_xid->frame_cb, &surface_frame_listener, xid_rect); + xid_rect->X = X; xid_rect->Y = Y; xid_rect->W = W; xid_rect->H = H; + xid_rect->need_resize = is_a_resize; + wl_surface_commit(parent_xid->wl_surface); + } else if (xid_rect) { + // update the active frame callback with new X,Y,W,H values + xid_rect->X = X; xid_rect->Y = Y; xid_rect->W = W; xid_rect->H = H; + xid_rect->need_resize |= is_a_resize; + } else { + wl_surface_commit(parent_xid->wl_surface); + } + } checkSubwindowFrame(); // make sure subwindow doesn't leak outside parent } + depth--; }