diff options
| author | ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> | 2023-03-13 10:38:28 +0100 |
|---|---|---|
| committer | ManoloFLTK <41016272+ManoloFLTK@users.noreply.github.com> | 2023-03-13 10:38:28 +0100 |
| commit | eeb9267e6ea1a8481bef36186e36c02189924f3b (patch) | |
| tree | cbcac46d56d4bf4da55c240dd650235991335f30 | |
| parent | af4789077f44d0391b80432a8e0fcfbf4865a924 (diff) | |
Wayland: add support for multiple high or low DPI displays
| -rw-r--r-- | FL/Fl_Window.H | 11 | ||||
| -rw-r--r-- | README.Wayland.txt | 5 | ||||
| -rw-r--r-- | documentation/src/wayland.dox | 18 | ||||
| -rw-r--r-- | src/drivers/Wayland/Fl_Wayland_Screen_Driver.cxx | 34 | ||||
| -rw-r--r-- | src/drivers/Wayland/Fl_Wayland_Window_Driver.H | 13 | ||||
| -rw-r--r-- | src/drivers/Wayland/Fl_Wayland_Window_Driver.cxx | 54 | ||||
| -rw-r--r-- | src/fl_cursor.cxx | 18 |
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() */ |
