Fix: Wayland crashes when dragging a heavy window from 100% scale factor to 200% (#1428)

This commit is contained in:
ManoloFLTK
2026-05-17 19:25:01 +02:00
parent 21e6e48bf6
commit cf87ae9c3b
4 changed files with 14 additions and 31 deletions
@@ -42,7 +42,6 @@ public:
struct wl_buffer *wl_buffer;
void *data;
struct wl_shm_pool *shm_pool;
int allowed_wld_scale; // the wld_scale active when this buffer was sized
bool draw_buffer_needs_commit;
bool in_use; // true while being committed
bool released; // true after buffer_release() was called
@@ -58,7 +57,7 @@ public:
void copy_offscreen(int x, int y, int w, int h, Fl_Offscreen osrc,
int srcx, int srcy) FL_OVERRIDE;
void cache_size(Fl_Image *img, int &width, int &height) FL_OVERRIDE;
static struct wld_buffer *create_wld_buffer(int width, int height, bool with_shm, int wld_s);
static struct wld_buffer *create_wld_buffer(int width, int height, bool with_shm);
static void create_shm_buffer(wld_buffer *buffer);
static void buffer_release(struct wld_window *window);
static void buffer_commit(struct wld_window *window, cairo_region_t *r = NULL);
@@ -67,7 +66,7 @@ public:
// used by class Fl_Wayland_Gl_Window_Driver
static FL_EXPORT struct draw_buffer *offscreen_buffer(Fl_Offscreen);
static const cairo_user_data_key_t key;
static Fl_Image_Surface *custom_offscreen(int w, int h, struct wld_buffer **buffer, int scale);
static Fl_Image_Surface *custom_offscreen(int w, int h, struct wld_buffer **buffer);
};
#endif // FL_WAYLAND_GRAPHICS_DRIVER_H
@@ -108,13 +108,12 @@ void Fl_Wayland_Graphics_Driver::create_shm_buffer(Fl_Wayland_Graphics_Driver::w
struct Fl_Wayland_Graphics_Driver::wld_buffer *
Fl_Wayland_Graphics_Driver::create_wld_buffer(int width, int height, bool with_shm, int wld_s) {
Fl_Wayland_Graphics_Driver::create_wld_buffer(int width, int height, bool with_shm) {
struct wld_buffer *buffer = (struct wld_buffer*)calloc(1, sizeof(struct wld_buffer));
int stride = cairo_format_stride_for_width(cairo_format, width);
cairo_init(&buffer->draw_buffer, width, height, stride, cairo_format);
buffer->draw_buffer_needs_commit = true;
if (with_shm) create_shm_buffer(buffer);
buffer->allowed_wld_scale = wld_s;
return buffer;
}
@@ -174,17 +173,6 @@ static void copy_region(struct wld_window *window, cairo_region_t *r) {
void Fl_Wayland_Graphics_Driver::buffer_commit(struct wld_window *window, cairo_region_t *r)
{
if (!window->buffer->wl_buffer) create_shm_buffer(window->buffer);
if (window->buffer->allowed_wld_scale != Fl_Wayland_Window_Driver::driver(window->fl_win)->wld_scale()) {
// surface_enter has updated wld_scale() but the buffer was sized for the
// old scale. Committing now would violate the Wayland protocol (buffer
// dimensions must be multiples of buffer_scale). Let delayed_rescale
// rebuild the buffer at the correct size first. (#1428)
buffer_release(window);
window->fl_win->redraw();
return;
}
cairo_surface_t *surf = cairo_get_target(window->buffer->draw_buffer.cairo_);
cairo_surface_flush(surf);
if (r) copy_region(window, r);
@@ -307,8 +295,8 @@ Fl_Wayland_Graphics_Driver::offscreen_buffer(Fl_Offscreen offscreen) {
Fl_Image_Surface *Fl_Wayland_Graphics_Driver::custom_offscreen(int w, int h,
struct Fl_Wayland_Graphics_Driver::wld_buffer **p_off, int scale) {
struct wld_buffer *off = create_wld_buffer(w, h, true, scale);
struct Fl_Wayland_Graphics_Driver::wld_buffer **p_off) {
struct wld_buffer *off = create_wld_buffer(w, h, true);
*p_off = off;
cairo_set_user_data(off->draw_buffer.cairo_, &key, &off->draw_buffer, NULL);
return new Fl_Image_Surface(w, h, 0, (Fl_Offscreen)off->draw_buffer.cairo_);
@@ -370,7 +370,7 @@ void Fl_Wayland_Window_Driver::make_current() {
int wld_s = wld_scale();
if (!window->buffer) {
window->buffer = Fl_Wayland_Graphics_Driver::create_wld_buffer(
int(pWindow->w() * f) * wld_s, int(pWindow->h() * f) * wld_s, false, wld_s);
int(pWindow->w() * f) * wld_s, int(pWindow->h() * f) * wld_s, false);
((Fl_Cairo_Graphics_Driver*)fl_graphics_driver)->needs_commit_tag(
&window->buffer->draw_buffer_needs_commit);
}
@@ -660,14 +660,7 @@ int Fl_Wayland_Window_Driver::scroll(int src_x, int src_y, int src_w, int src_h,
}
static void delayed_rescale(Fl_Window *win) {
Fl_Window_Driver::driver(win)->is_a_rescale(true);
win->size(win->w(), win->h());
Fl_Window_Driver::driver(win)->is_a_rescale(false);
}
void change_scale(Fl_Wayland_Screen_Driver::output *output, struct wld_window *window,
static void change_scale(Fl_Wayland_Screen_Driver::output *output, struct wld_window *window,
float pre_scale) {
Fl_Wayland_Window_Driver *win_driver = Fl_Wayland_Window_Driver::driver(window->fl_win);
if (!window->fl_win->parent()) {
@@ -690,8 +683,11 @@ void change_scale(Fl_Wayland_Screen_Driver::output *output, struct wld_window *w
Fl_Wayland_Graphics_Driver::buffer_release(window);
window->fl_win->redraw();
} else {
// delaying the rescaling is necessary to set first the window's size_range according to the new screen
Fl::add_timeout(0, (Fl_Timeout_Handler)delayed_rescale, window->fl_win);
// set first the window's size_range according to the new screen
win_driver->size_range();
win_driver->is_a_rescale(true);
window->fl_win->size(window->fl_win->w(), window->fl_win->h());
win_driver->is_a_rescale(false);
}
}
}
@@ -1872,7 +1868,7 @@ int Fl_Wayland_Window_Driver::set_cursor_4args(const Fl_RGB_Image *rgb, int hotx
//create a Wayland buffer and have it used as an image of the new cursor
struct Fl_Wayland_Graphics_Driver::wld_buffer *offscreen;
Fl_Image_Surface *img_surf = Fl_Wayland_Graphics_Driver::custom_offscreen(
new_image->image.width, new_image->image.height, &offscreen, scale);
new_image->image.width, new_image->image.height, &offscreen);
new_image->buffer = offscreen->wl_buffer;
wl_buffer_set_user_data(new_image->buffer, offscreen);
new_cursor->image_count = 1;
@@ -215,7 +215,7 @@ static struct Fl_Wayland_Graphics_Driver::wld_buffer *offscreen_from_text(const
height = ceil(height/float(scale)) * scale;
struct Fl_Wayland_Graphics_Driver::wld_buffer *off;
Fl_Image_Surface *surf = Fl_Wayland_Graphics_Driver::custom_offscreen(
width, height, &off, scale);
width, height, &off);
Fl_Surface_Device::push_current(surf);
p = text;
fl_font(FL_HELVETICA, 10 * scale);