Wayland: add support for multiple high or low DPI displays

This commit is contained in:
ManoloFLTK 2023-03-13 10:38:28 +01:00
parent af4789077f
commit eeb9267e6e
7 changed files with 106 additions and 45 deletions

View File

@ -526,17 +526,6 @@ public:
*/ */
void make_current(); void make_current();
/**
Changes the cursor for this window.
This always calls the system. If you are changing the cursor a lot
you may want to keep track of how you set it in a static variable
and call this only if the new cursor is different.
The type Fl_Cursor is an enumeration defined in <FL/Enumerations.H>.
\see cursor(const Fl_RGB_Image*, int, int), default_cursor()
*/
void cursor(Fl_Cursor); void cursor(Fl_Cursor);
void cursor(const Fl_RGB_Image*, int, int); void cursor(const Fl_RGB_Image*, int, int);
void default_cursor(Fl_Cursor); void default_cursor(Fl_Cursor);

View File

@ -96,12 +96,9 @@ a minimized window has no effect.
it's currently not possible for an app to be notified of changes to the content of it's currently not possible for an app to be notified of changes to the content of
the system clipboard, that is, Fl::add_clipboard_notify() has no effect. the system clipboard, that is, Fl::add_clipboard_notify() has no effect.
* With GTK-style window titlebars, narrow windows are silently forced to be wide enough * Narrow windows with a titlebar are silently forced to be wide enough
for the titlebar to display window buttons and a few letters of the title. for the titlebar to display window buttons and a few letters of the title.
* The library should support multi-display configurations in principle, but has not been
tested in that situation.
* Text input methods have been tested without any understanding of the writing systems, * Text input methods have been tested without any understanding of the writing systems,
so feedback on this subject would be helpful. so feedback on this subject would be helpful.

View File

@ -451,19 +451,19 @@ displaying dragged text in a DnD operation.
\section wayland-display Displays and HighDPI support \section wayland-display Displays and HighDPI support
Wayland uses the concept of seat of type <tt>struct wl_seat</tt> which encompasses displays, Wayland uses the concept of <em>seat</em> of type <tt>struct wl_seat</tt> which encompasses displays,
a keyboard, a mouse, and a trackpad. It might be possible for an app to deal with several seats, a keyboard, a mouse, and a trackpad. Although Wayland may be in principle able to deal with several
but that has not been tested with FLTK yet. Each seat may contain one or more displays, which seats, FLTK's Wayland platform is conceived for one seat only. That seat may contain one or more
Wayland calls outputs, of type <tt>struct wl_output</tt>. displays, which Wayland calls <em>outputs</em>, of type <tt>struct wl_output</tt>.
As written above, function \c registry_handle_global() discovers available seats at start-up time. As written above, function \c registry_handle_global() discovers the available seat at start-up time.
This function also associates a 'listener' to each display This function also associates a listener to each display connected to the system
by calling function \c wl_output_add_listener(). This 'listener' is an array of callback function by calling function \c wl_output_add_listener(). This listener is an array of callback function
pointers among which one (\c output_mode) runs when the display is resized and another pointers among which one (\c output_mode) runs when the display is resized and another
(\c output_scale) when the Wayland scale factor (see below) is changed. (\c output_scale) when the Wayland scale factor (see below) is changed.
FLTK defines type <tt>struct Fl_Wayland_Screen_Driver::output</tt> (see \ref output) FLTK defines type <tt>struct Fl_Wayland_Screen_Driver::output</tt> (see \ref output)
to store display size and scaling information. to store display size and scaling information.
One such record is created for each display. FLTK uses 2 distinct scaling parameters under Wayland: One such record is created for each display. FLTK uses 2 distinct scaling parameters for each display:
- <tt>int wld_scale;</tt>. This member variable of - <tt>int wld_scale;</tt>. This member variable of
<tt>struct Fl_Wayland_Screen_Driver::output</tt> typically equals 1 for standard, and 2 for <tt>struct Fl_Wayland_Screen_Driver::output</tt> typically equals 1 for standard, and 2 for
HighDPI displays. Its value is set by the Wayland compositor for each display with the effect HighDPI displays. Its value is set by the Wayland compositor for each display with the effect

View File

@ -179,7 +179,6 @@ static void do_set_cursor(struct seat *seat, struct wl_cursor *wl_cursor = NULL)
image->hotspot_x / scale, image->hotspot_x / scale,
image->hotspot_y / scale); image->hotspot_y / scale);
wl_surface_attach(seat->cursor_surface, buffer, 0, 0); wl_surface_attach(seat->cursor_surface, buffer, 0, 0);
wl_surface_set_buffer_scale(seat->cursor_surface, scale);
wl_surface_damage_buffer(seat->cursor_surface, 0, 0, wl_surface_damage_buffer(seat->cursor_surface, 0, 0,
image->width, image->height); image->width, image->height);
wl_surface_commit(seat->cursor_surface); wl_surface_commit(seat->cursor_surface);
@ -258,7 +257,8 @@ static void pointer_enter(void *data,
{ {
Fl_Window *win = event_coords_from_surface(surface, surface_x, surface_y); Fl_Window *win = event_coords_from_surface(surface, surface_x, surface_y);
if (!win) return; if (!win) return;
struct wl_cursor *cursor = fl_wl_xid(win)->custom_cursor;// use custom cursor if present // use custom cursor if present
struct wl_cursor *cursor = fl_wl_xid(win)->custom_cursor ? fl_wl_xid(win)->custom_cursor->wl_cursor : NULL;
struct seat *seat = (struct seat*)data; struct seat *seat = (struct seat*)data;
do_set_cursor(seat, cursor); do_set_cursor(seat, cursor);
seat->serial = serial; seat->serial = serial;
@ -412,6 +412,7 @@ static void cursor_surface_enter(void *data,
struct wl_surface *wl_surface, struct wl_surface *wl_surface,
struct wl_output *wl_output) struct wl_output *wl_output)
{ {
// Runs when the seat's cursor_surface enters a display
struct seat *seat = (struct seat*)data; struct seat *seat = (struct seat*)data;
struct pointer_output *pointer_output; struct pointer_output *pointer_output;
@ -428,9 +429,13 @@ static void cursor_surface_enter(void *data,
if (win) { if (win) {
Fl_Wayland_Window_Driver *driver = Fl_Wayland_Window_Driver::driver(win); Fl_Wayland_Window_Driver *driver = Fl_Wayland_Window_Driver::driver(win);
//fprintf(stderr, "cursor_surface_enter: cursor_default=%d standard_cursor=%d\n", driver->cursor_default(), driver->standard_cursor()); //fprintf(stderr, "cursor_surface_enter: cursor_default=%d standard_cursor=%d\n", driver->cursor_default(), driver->standard_cursor());
struct wl_cursor *cursor = fl_wl_xid(win)->custom_cursor; struct wld_window *xid = fl_wl_xid(win);
if (cursor) do_set_cursor(seat, cursor); struct wld_window::custom_cursor *custom = xid->custom_cursor;
else if (driver->cursor_default()) driver->set_cursor(driver->cursor_default()); if (custom) {
// Change custom cursor's width & height according to display's wld_scale
driver->set_cursor_4args(custom->rgb, custom->hotx, custom->hoty, false);
do_set_cursor(seat, xid->custom_cursor->wl_cursor);
} else if (driver->cursor_default()) driver->set_cursor(driver->cursor_default());
else win->cursor(driver->standard_cursor()); else win->cursor(driver->standard_cursor());
} }
} }
@ -448,6 +453,14 @@ static void cursor_surface_leave(void *data,
free(pointer_output); free(pointer_output);
} }
} }
try_update_cursor(seat);
// maintain custom window cursor
Fl_Window *win = Fl::first_window();
if (win) {
struct wld_window *xid = fl_wl_xid(win);
if (xid->custom_cursor) do_set_cursor(seat, xid->custom_cursor->wl_cursor);
}
} }
static struct wl_surface_listener cursor_surface_listener = { static struct wl_surface_listener cursor_surface_listener = {
@ -962,6 +975,17 @@ static void output_scale(void *data, struct wl_output *wl_output, int32_t factor
Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data; Fl_Wayland_Screen_Driver::output *output = (Fl_Wayland_Screen_Driver::output*)data;
output->wld_scale = factor; output->wld_scale = factor;
//fprintf(stderr,"output_scale: wl_output=%p factor=%d\n",wl_output, factor); //fprintf(stderr,"output_scale: wl_output=%p factor=%d\n",wl_output, factor);
// rescale cursors of windows that map here and have a custom cursor
Fl_Window *win = Fl::first_window();
while (win) {
struct wld_window *xid = fl_wl_xid(win);
if (xid->custom_cursor && wl_output_get_user_data(wl_output) == xid->output) {
Fl_Wayland_Window_Driver *driver = Fl_Wayland_Window_Driver::driver(win);
driver->set_cursor_4args(xid->custom_cursor->rgb,
xid->custom_cursor->hotx, xid->custom_cursor->hoty, false);
};
win = Fl::next_window(win);
}
} }

View File

@ -60,7 +60,7 @@ private:
static bool in_flush; // useful for progressive window drawing static bool in_flush; // useful for progressive window drawing
static Fl_Wayland_Plugin *gl_plugin(); static Fl_Wayland_Plugin *gl_plugin();
Fl_Cursor standard_cursor_; // window's standard custom kind Fl_Cursor standard_cursor_; // window's standard custom kind
void delete_cursor_(struct wld_window *); void delete_cursor_(struct wld_window *, bool keep_rgb = false);
struct gl_start_support *gl_start_support_; // for support of gl_start/gl_finish struct gl_start_support *gl_start_support_; // for support of gl_start/gl_finish
public: public:
inline Fl_Cursor standard_cursor() { return standard_cursor_; }; inline Fl_Cursor standard_cursor() { return standard_cursor_; };
@ -116,6 +116,7 @@ public:
// --- window cursor stuff // --- window cursor stuff
int set_cursor(Fl_Cursor) FL_OVERRIDE; int set_cursor(Fl_Cursor) FL_OVERRIDE;
int set_cursor(const Fl_RGB_Image*, int, int) FL_OVERRIDE; int set_cursor(const Fl_RGB_Image*, int, int) FL_OVERRIDE;
int set_cursor_4args(const Fl_RGB_Image*, int, int, bool);
void shape(const Fl_Image* img) FL_OVERRIDE; void shape(const Fl_Image* img) FL_OVERRIDE;
void capture_titlebar_and_borders(Fl_RGB_Image*& top, Fl_RGB_Image*& left, Fl_RGB_Image*& bottom, Fl_RGB_Image*& right) FL_OVERRIDE; void capture_titlebar_and_borders(Fl_RGB_Image*& top, Fl_RGB_Image*& left, Fl_RGB_Image*& bottom, Fl_RGB_Image*& right) FL_OVERRIDE;
@ -136,14 +137,18 @@ struct wld_window {
struct wl_surface *wl_surface; struct wl_surface *wl_surface;
struct fl_wld_buffer *buffer; struct fl_wld_buffer *buffer;
struct xdg_surface *xdg_surface; struct xdg_surface *xdg_surface;
union { enum Fl_Wayland_Window_Driver::kind kind;
union { // for each value of kind
struct libdecor_frame *frame; struct libdecor_frame *frame;
struct wl_subsurface *subsurface; struct wl_subsurface *subsurface;
struct xdg_popup *xdg_popup; struct xdg_popup *xdg_popup;
struct xdg_toplevel *xdg_toplevel; struct xdg_toplevel *xdg_toplevel;
}; };
struct wl_cursor *custom_cursor; // non-null when using custom cursor struct custom_cursor {
enum Fl_Wayland_Window_Driver::kind kind; struct wl_cursor *wl_cursor;
const Fl_RGB_Image *rgb;
int hotx, hoty;
} *custom_cursor; // non-null when using custom cursor
int configured_width; int configured_width;
int configured_height; int configured_height;
int floating_width; int floating_width;

View File

@ -78,9 +78,10 @@ Fl_Wayland_Window_Driver::Fl_Wayland_Window_Driver(Fl_Window *win) : Fl_Window_D
subRect_ = NULL; subRect_ = NULL;
} }
void Fl_Wayland_Window_Driver::delete_cursor_(struct wld_window *xid) { void Fl_Wayland_Window_Driver::delete_cursor_(struct wld_window *xid, bool keep_rgb) {
struct wl_cursor *wl_cursor = xid->custom_cursor; struct wld_window::custom_cursor *custom = xid->custom_cursor;
if (wl_cursor) { if (custom) {
struct wl_cursor *wl_cursor = custom->wl_cursor;
struct cursor_image *new_image = (struct cursor_image*)wl_cursor->images[0]; struct cursor_image *new_image = (struct cursor_image*)wl_cursor->images[0];
struct fl_wld_buffer *offscreen = (struct fl_wld_buffer *)wl_buffer_get_user_data(new_image->buffer); struct fl_wld_buffer *offscreen = (struct fl_wld_buffer *)wl_buffer_get_user_data(new_image->buffer);
struct wld_window fake_xid; struct wld_window fake_xid;
@ -92,6 +93,8 @@ void Fl_Wayland_Window_Driver::delete_cursor_(struct wld_window *xid) {
free(wl_cursor); free(wl_cursor);
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
if (scr_driver->default_cursor() == wl_cursor) scr_driver->default_cursor(scr_driver->xc_arrow); if (scr_driver->default_cursor() == wl_cursor) scr_driver->default_cursor(scr_driver->xc_arrow);
if (!keep_rgb) delete custom->rgb;
delete custom;
xid->custom_cursor = NULL; xid->custom_cursor = NULL;
} }
} }
@ -637,6 +640,7 @@ static void surface_enter(void *data, struct wl_surface *wl_surface, struct wl_o
if (output == NULL) if (output == NULL)
return; return;
//printf("surface_enter win=%p wl_output=%p wld_scale=%d\n", window->fl_win, wl_output, output->wld_scale);
window->output = output; window->output = output;
Fl_Wayland_Window_Driver *win_driver = Fl_Wayland_Window_Driver::driver(window->fl_win); Fl_Wayland_Window_Driver *win_driver = Fl_Wayland_Window_Driver::driver(window->fl_win);
if (!window->fl_win->parent()) { // for top-level, set its screen number if (!window->fl_win->parent()) { // for top-level, set its screen number
@ -652,15 +656,23 @@ static void surface_enter(void *data, struct wl_surface *wl_surface, struct wl_o
i++; i++;
} }
} }
if (window->kind == Fl_Wayland_Window_Driver::POPUP) {
Fl_Wayland_Graphics_Driver::buffer_release(window);
window->fl_win->redraw();
} else {
win_driver->is_a_rescale(true);
window->fl_win->size(window->fl_win->w(), window->fl_win->h());
win_driver->is_a_rescale(false);
if (window->fl_win->as_gl_window())
wl_surface_set_buffer_scale(window->wl_surface, output->wld_scale);
}
} }
static void surface_leave(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) static void surface_leave(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output)
{ {
struct wld_window *window = (struct wld_window*)data; // Do nothing because surface_leave old display arrives **after** surface_enter new display
if (! window->wl_surface) return; //struct wld_window *window = (struct wld_window*)data;
if (window->output->wl_output == wl_output) { //printf("surface_leave win=%p wl_output=%p\n", window->fl_win, wl_output);
window->output = NULL;
}
} }
static struct wl_surface_listener surface_listener = { static struct wl_surface_listener surface_listener = {
@ -1453,6 +1465,21 @@ void Fl_Wayland_Window_Driver::label(const char *name, const char *iname) {
int Fl_Wayland_Window_Driver::set_cursor(const Fl_RGB_Image *rgb, int hotx, int hoty) { int Fl_Wayland_Window_Driver::set_cursor(const Fl_RGB_Image *rgb, int hotx, int hoty) {
return set_cursor_4args(rgb, hotx, hoty, true);
}
int Fl_Wayland_Window_Driver::set_cursor_4args(const Fl_RGB_Image *rgb, int hotx, int hoty,
bool keep_copy) {
if (keep_copy) {
int ld = rgb->ld() ? rgb->ld() : rgb->data_w() * rgb->d();
uchar *data = new uchar[ld * rgb->data_h()];
memcpy(data, rgb->array, ld * rgb->data_h());
Fl_RGB_Image *rgb2 = new Fl_RGB_Image(data, rgb->data_w(), rgb->data_h(), rgb->d(), rgb->ld());
rgb2->alloc_array = 1;
rgb2->scale(rgb->w(), rgb->h());
rgb = rgb2;
}
// build a new wl_cursor and its image // build a new wl_cursor and its image
struct wld_window *xid = (struct wld_window *)Fl_Window_Driver::xid(pWindow); struct wld_window *xid = (struct wld_window *)Fl_Window_Driver::xid(pWindow);
struct wl_cursor *new_cursor = (struct wl_cursor*)malloc(sizeof(struct wl_cursor)); struct wl_cursor *new_cursor = (struct wl_cursor*)malloc(sizeof(struct wl_cursor));
@ -1482,12 +1509,15 @@ int Fl_Wayland_Window_Driver::set_cursor(const Fl_RGB_Image *rgb, int hotx, int
Fl_Surface_Device::pop_current(); Fl_Surface_Device::pop_current();
delete img_surf; delete img_surf;
memcpy(offscreen->data, offscreen->draw_buffer, offscreen->data_size); memcpy(offscreen->data, offscreen->draw_buffer, offscreen->data_size);
// delete the previous custom cursor, if there was one // delete the previous custom cursor, if there was one, but keep its Fl_RGB_Image
delete_cursor_(xid); delete_cursor_(xid, true);
//have this new cursor used //have this new cursor used
xid->custom_cursor = new_cursor; xid->custom_cursor = new struct wld_window::custom_cursor;
xid->custom_cursor->wl_cursor = new_cursor;
xid->custom_cursor->rgb = rgb;
xid->custom_cursor->hotx = hotx;
xid->custom_cursor->hoty = hoty;
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver(); Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
scr_driver->default_cursor(xid->custom_cursor);
return 1; return 1;
} }

View File

@ -107,6 +107,18 @@ static void fallback_cursor(Fl_Window *w, Fl_Cursor c) {
} }
/**
Changes the cursor for this window.
The window must be show()'n for this function to have any effect.
This always calls the system. If you are changing the cursor a lot
you may want to keep track of how you set it in a static variable
and call this only if the new cursor is different.
The type Fl_Cursor is an enumeration defined in <FL/Enumerations.H>.
\see cursor(const Fl_RGB_Image*, int, int), default_cursor()
*/
void Fl_Window::cursor(Fl_Cursor c) { void Fl_Window::cursor(Fl_Cursor c) {
int ret; int ret;
@ -137,13 +149,17 @@ void Fl_Window::cursor(Fl_Cursor c) {
} }
/** /**
Changes the cursor for this window. This always calls the system. If Changes the cursor for this window using the provided image as cursor's shape.
The window must be show()'n for this function to have any effect.
This always calls the system. If
you are changing the cursor a lot you may want to keep track of how you are changing the cursor a lot you may want to keep track of how
you set it in a static variable and call this only if the new cursor you set it in a static variable and call this only if the new cursor
is different. is different.
The default cursor will be used if the provided image cannot be used The default cursor will be used if the provided image cannot be used
as a cursor. as a cursor.
\param image Sets the cursor size and shape
\param hotx,hoty Sets the cursor's active location relatively to top-left of \c image when clicking
\see cursor(Fl_Cursor), default_cursor() \see cursor(Fl_Cursor), default_cursor()
*/ */