summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com>2023-03-13 10:38:28 +0100
committerManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com>2023-03-13 10:38:28 +0100
commiteeb9267e6ea1a8481bef36186e36c02189924f3b (patch)
treecbcac46d56d4bf4da55c240dd650235991335f30
parentaf4789077f44d0391b80432a8e0fcfbf4865a924 (diff)
Wayland: add support for multiple high or low DPI displays
-rw-r--r--FL/Fl_Window.H11
-rw-r--r--README.Wayland.txt5
-rw-r--r--documentation/src/wayland.dox18
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx34
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Window_Driver.H13
-rw-r--r--src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx54
-rw-r--r--src/fl_cursor.cxx18
7 files changed, 107 insertions, 46 deletions
diff --git a/FL/Fl_Window.H b/FL/Fl_Window.H
index 7ea3fd8ec..ef4d78007 100644
--- a/FL/Fl_Window.H
+++ b/FL/Fl_Window.H
@@ -526,17 +526,6 @@ public:
*/
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(const Fl_RGB_Image*, int, int);
void default_cursor(Fl_Cursor);
diff --git a/README.Wayland.txt b/README.Wayland.txt
index 65a5c3a36..a07969b7e 100644
--- a/README.Wayland.txt
+++ b/README.Wayland.txt
@@ -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
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.
-* 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,
so feedback on this subject would be helpful.
diff --git a/documentation/src/wayland.dox b/documentation/src/wayland.dox
index 1e38611ef..c99dac9b1 100644
--- a/documentation/src/wayland.dox
+++ b/documentation/src/wayland.dox
@@ -451,19 +451,19 @@ displaying dragged text in a DnD operation.
\section wayland-display Displays and HighDPI support
-Wayland uses the concept of seat 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,
-but that has not been tested with FLTK yet. Each seat may contain one or more displays, which
-Wayland calls outputs, of type <tt>struct wl_output</tt>.
-
-As written above, function \c registry_handle_global() discovers available seats at start-up time.
-This function also associates a 'listener' to each display
-by calling function \c wl_output_add_listener(). This 'listener' is an array of callback function
+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. Although Wayland may be in principle able to deal with several
+seats, FLTK's Wayland platform is conceived for one seat only. That seat may contain one or more
+displays, which Wayland calls <em>outputs</em>, of type <tt>struct wl_output</tt>.
+
+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 connected to the system
+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
(\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)
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>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
diff --git a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx
index 4e67f3943..0875f4e1f 100644
--- a/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx
+++ b/src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx
@@ -179,7 +179,6 @@ static void do_set_cursor(struct seat *seat, struct wl_cursor *wl_cursor = NULL)
image->hotspot_x / scale,
image->hotspot_y / scale);
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,
image->width, image->height);
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);
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;
do_set_cursor(seat, cursor);
seat->serial = serial;
@@ -412,6 +412,7 @@ static void cursor_surface_enter(void *data,
struct wl_surface *wl_surface,
struct wl_output *wl_output)
{
+ // Runs when the seat's cursor_surface enters a display
struct seat *seat = (struct seat*)data;
struct pointer_output *pointer_output;
@@ -428,9 +429,13 @@ static void cursor_surface_enter(void *data,
if (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());
- struct wl_cursor *cursor = fl_wl_xid(win)->custom_cursor;
- if (cursor) do_set_cursor(seat, cursor);
- else if (driver->cursor_default()) driver->set_cursor(driver->cursor_default());
+ struct wld_window *xid = fl_wl_xid(win);
+ struct wld_window::custom_cursor *custom = xid->custom_cursor;
+ 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());
}
}
@@ -448,6 +453,14 @@ static void cursor_surface_leave(void *data,
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 = {
@@ -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;
output->wld_scale = 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);
+ }
}
diff --git a/src/drivers/Wayland/Fl_Wayland_Window_Driver.H b/src/drivers/Wayland/Fl_Wayland_Window_Driver.H
index 3ae9ed320..1bf85a1c1 100644
--- a/src/drivers/Wayland/Fl_Wayland_Window_Driver.H
+++ b/src/drivers/Wayland/Fl_Wayland_Window_Driver.H
@@ -60,7 +60,7 @@ private:
static bool in_flush; // useful for progressive window drawing
static Fl_Wayland_Plugin *gl_plugin();
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
public:
inline Fl_Cursor standard_cursor() { return standard_cursor_; };
@@ -116,6 +116,7 @@ public:
// --- window cursor stuff
int set_cursor(Fl_Cursor) 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 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 fl_wld_buffer *buffer;
struct xdg_surface *xdg_surface;
- union {
+ enum Fl_Wayland_Window_Driver::kind kind;
+ union { // for each value of kind
struct libdecor_frame *frame;
struct wl_subsurface *subsurface;
struct xdg_popup *xdg_popup;
struct xdg_toplevel *xdg_toplevel;
};
- struct wl_cursor *custom_cursor; // non-null when using custom cursor
- enum Fl_Wayland_Window_Driver::kind kind;
+ struct custom_cursor {
+ 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_height;
int floating_width;
diff --git a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx
index 82186560e..ac634fa5c 100644
--- a/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx
+++ b/src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx
@@ -78,9 +78,10 @@ Fl_Wayland_Window_Driver::Fl_Wayland_Window_Driver(Fl_Window *win) : Fl_Window_D
subRect_ = NULL;
}
-void Fl_Wayland_Window_Driver::delete_cursor_(struct wld_window *xid) {
- struct wl_cursor *wl_cursor = xid->custom_cursor;
- if (wl_cursor) {
+void Fl_Wayland_Window_Driver::delete_cursor_(struct wld_window *xid, bool keep_rgb) {
+ struct wld_window::custom_cursor *custom = xid->custom_cursor;
+ if (custom) {
+ struct wl_cursor *wl_cursor = custom->wl_cursor;
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 wld_window fake_xid;
@@ -92,6 +93,8 @@ void Fl_Wayland_Window_Driver::delete_cursor_(struct wld_window *xid) {
free(wl_cursor);
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 (!keep_rgb) delete custom->rgb;
+ delete custom;
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)
return;
+//printf("surface_enter win=%p wl_output=%p wld_scale=%d\n", window->fl_win, wl_output, output->wld_scale);
window->output = output;
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
@@ -652,15 +656,23 @@ static void surface_enter(void *data, struct wl_surface *wl_surface, struct wl_o
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)
{
- struct wld_window *window = (struct wld_window*)data;
- if (! window->wl_surface) return;
- if (window->output->wl_output == wl_output) {
- window->output = NULL;
- }
+ // Do nothing because surface_leave old display arrives **after** surface_enter new display
+ //struct wld_window *window = (struct wld_window*)data;
+ //printf("surface_leave win=%p wl_output=%p\n", window->fl_win, wl_output);
}
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) {
+ 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
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));
@@ -1482,12 +1509,15 @@ int Fl_Wayland_Window_Driver::set_cursor(const Fl_RGB_Image *rgb, int hotx, int
Fl_Surface_Device::pop_current();
delete img_surf;
memcpy(offscreen->data, offscreen->draw_buffer, offscreen->data_size);
- // delete the previous custom cursor, if there was one
- delete_cursor_(xid);
+ // delete the previous custom cursor, if there was one, but keep its Fl_RGB_Image
+ delete_cursor_(xid, true);
//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();
- scr_driver->default_cursor(xid->custom_cursor);
return 1;
}
diff --git a/src/fl_cursor.cxx b/src/fl_cursor.cxx
index 84ef9ff1e..681a98255 100644
--- a/src/fl_cursor.cxx
+++ b/src/fl_cursor.cxx
@@ -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) {
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 set it in a static variable and call this only if the new cursor
is different.
The default cursor will be used if the provided image cannot be used
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()
*/