Fl::flush() calls, if the window is damaged:
\e Fl_Wayland_Window_Driver::flush() calls, if the window's \c frame_cb is NULL:
\e Fl_Wayland_Graphics_Driver::buffer_commit() calls, if the window's \c buffer->wl_buffer is NULL:
\e Fl_Wayland_Graphics_Driver::create_shm_buffer() creates a Wayland buffer.
Member function \c Fl_Wayland_Graphics_Driver::create_shm_buffer()
follows this 3-step procedure to create one or more shared memory pools and to
construct Wayland buffers from them:
- Libdecor function libdecor_os_create_anonymous_file(off_t size) creates an adequate file
and mmap's it. This file lives in RAM because it is created by function \c memfd_create().
FLTK sets this file size to 10 MB unless the size of the buffer to be created
is larger; in that case the anonymous file is sized to twice the buffer size.
- Wayland function \c wl_shm_create_pool() shares this mmap'ed memory with the
Wayland compositor and returns an object of type struct wl_shm_pool, a shared memory pool,
which encapsulates this memory. A record of type
struct Fl_Wayland_Graphics_Driver::wld_shm_pool_data is created and associated to
the newly created \c wl_shm_pool by \c wl_shm_pool_set_user_data(). This record stores
the starting address (\c pool_memory) and size (\c pool_size) of the pool's encapsulated
memory. The record also contains member \c buffers of type struct wl_list which
stores the access point to the linked list of \c wl_buffer objects that will be created from
the \c wl_shm_pool.
- A variable named \c chunk_offset represents the offset within the pool's shared
memory available for the buffer being constructed. It equals 0 when the pool has just been
created and is updated as detailed below each time a buffer is created
from the pool. A record of type struct Fl_Wayland_Graphics_Driver::wld_buffer is created. This record will
contain (member \c wl_buffer) the address of a \c wl_buffer object that's created by function
\c wl_shm_pool_create_buffer(). This \c wl_buffer object encapsulates a section of a given
size of the pool's shared memory beginning at offset \c chunk_offset in it.
Quantity pool_memory + chunk_offset is therefore the address of the
beginning of the mmap'ed memory section encapsulated by this \c wl_buffer.
Member \c shm_pool of the newly constructed \c Fl_Wayland_Graphics_Driver::wld_buffer object is set to the address of
the current \c wl_shm_pool object. This record is added to the head of the linked list of
current pool's buffers by a call to \c wl_list_insert().
At that point, a struct Fl_Wayland_Graphics_Driver::wld_buffer record is part of the linked list of all
such records corresponding to \c wl_buffer objects created from the same \c wl_shm_pool
object, and member \c shm_pool of this record gives the address of this \c wl_shm_pool.
When a new struct Fl_Wayland_Graphics_Driver::wld_buffer record is to be created,
\code
struct wld_shm_pool_data *pool_data =
(struct wld_shm_pool_data *)wl_shm_pool_get_user_data(pool);
struct Fl_Wayland_Graphics_Driver::wld_buffer *record = wl_container_of(pool_data->buffers.next, record, link);
int chunk_offset = ((char*)record->data - pool_data->pool_memory) + record->data_size;
\endcode
gives the offset within the current pool's mmap'ed memory available for a new \c wl_buffer.
Macro \c wl_container_of() gives the address of a record belonging to a linked list of
records of the same type.
A window's \c wl_buffer is re-filled by graphics data and committed each time
the window gets redrawn, and is set to be destroyed by function
\c Fl_Wayland_Graphics_Driver::buffer_release() when \c Fl_Window::hide() runs or
the window is resized. When the \c wl_buffer is no longer in use, function
\c do_buffer_release() gets called as explained above. It destroys the
\c wl_buffer with \c wl_buffer_destroy(), and removes the corresponding
\c Fl_Wayland_Graphics_Driver::wld_buffer record from the linked list of buffers from the same \c wl_shm_pool.
Since new \c Fl_Wayland_Graphics_Driver::wld_buffer records are added at the head of the linked list, and since
the record at the head of this list is used to compute the offset within the pool's mmap'ed
memory available for a new \c wl_buffer, destruction of the last created \c wl_buffer
allows to re-use the destroyed buffer's pool's memory for a new \c wl_buffer.
When function \c do_buffer_release() finds the list of buffers from a given pool empty,
two situations can occur. 1) This pool is the current pool. Its mmap'ed memory will be
re-used from offset 0 to create future \c wl_buffer objects. 2) This pool is not
current. It gets destroyed with \c wl_shm_pool_destroy(), the pool's mmap'ed memory
is munmap'ed, and the pool's associated struct wld_shm_pool_data is freed.
In situation 1) above, the next \c wl_buffer to be created can need more memory than
the current pool's memory size. If so, the current pool gets destroyed and replaced
by a new, larger pool.
If the sum of \c chunk_offset plus the buffer size is larger than the current pool's size
when function \c create_shm_buffer() is called, \c chunk_offset is reset
to 0, and a new \c wl_shm_pool object is created and used by FLTK's "buffer factory".
This mechanism allows to access new mmap'ed memory when \c chunk_offset reaches the end of
the previous mmap'ed section.
Wayland uses also \c wl_buffer objects to support cursors. FLTK uses the "buffer factory"
described here when creating custom cursors (see \ref custom-cursor) with
function Fl_Wayland_Window_Driver::set_cursor(const Fl_RGB_Image *,…) which
calls \c create_shm_buffer() via \c set_cursor_4args(), \c custom_offscreen()
and \c create_wld_buffer().
In contrast, standard shaped-cursors (e.g., FL_CURSOR_INSERT)
use their own "buffer factory" inside Wayland functions such as
\c wl_cursor_theme_get_cursor().
Therefore, the fact that the \c wl_buffer objects behind standard cursors are never destroyed
doesn't prevent disused struct wl_shm_pool objects from being freed because those
buffers come from a distinct "buffer factory".
The "buffer factory" described here is also used by function \c offscreen_from_text() when
displaying dragged text in a DnD operation.
\section wayland-display Displays and HighDPI support
Wayland uses the concept of seat of type struct wl_seat 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 outputs, of type struct wl_output.
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's member functions run
at program startup when Wayland discovers its displays (see \ref wayland-connection).
Member \c output_mode runs also when the display is resized and member
\c output_scale also when the Wayland scale factor (see below) is changed.
FLTK defines type \c struct \ref wayland-output "Fl_Wayland_Screen_Driver::output"
to store display size and scaling information.
One such record is created for each display. These records are put in a
struct wl_list accessible from member \c outputs of the single
\c Fl_Wayland_Screen_Driver object.
FLTK uses 2 distinct scaling parameters for each display:
- int wld_scale;. This member variable of the
\c struct \ref wayland-output "Fl_Wayland_Screen_Driver::output" record
typically equals 1 for standard, and 2 for HighDPI displays.
The effect of value \c n of variable \c wld_scale is
that 1 Wayland graphics unit represents a block of \c nxn pixels.
Another effect is that a drawing buffer for a surface of size WxH units
contains W * n * H * n * 4 bytes.
Member function \c output_scale() mentioned above sets this value for
each system's display at startup time. Member function \c
Fl_Wayland_Graphics_Driver::buffer_commit() informs the Wayland compositor
of the value of \c wld_scale calling \c wl_surface_set_buffer_scale()
which is enough to make FLTK apps HighDPI-aware.
Under the gnome and KDE desktops, this parameter is visible in the "Settings" app,
"Displays" section, "Scale" parameter which is 200% on HighDPI displays.
- float gui_scale;. This other member variable is where FLTK's own GUI scaling mechanism
with ctrl/+/-/0/ keystrokes and with environment variable FLTK_SCALING_FACTOR operates:
when FLTK is scaled at 150%, \c gui_scale is assigned value 1.5. Function
Fl_Wayland_Screen_Driver::scale(int n, float f) assigns value \c f to the \c gui_scale
member variable of display # \c n. This variable is used by function
\c Fl_Wayland_Window_Driver::make_current() when it calls \c Fl_Wayland_Graphics_Driver::set_buffer()
that scales the graphics driver by this factor with \c cairo_scale().
Overall, an FLTK object, say an Fl_Window, of size \c WxH FLTK units occupies
int(W * gui_scale) * wld_scale x int(H * gui_scale) * wld_scale pixels
on the display.
When an \c Fl_Window is to be show()'n, \c Fl_Wayland_Window_Driver::makeWindow() creates
a struct wl_surface with \c wl_compositor_create_surface() and associates it
calling \c wl_surface_add_listener() with a 2-member listener called \c surface_listener
encharged of managing as follows the list of displays where this \c wl_surface will map.
The \c Fl_Window possesses an initially empty linked list of displays accessible at
member \c outputs of the window's \ref wld_window record.
When the \c Fl_Window, or more exactly its associated struct wl_surface is mapped
on a display, member \c surface_enter() of \c surface_listener runs.
This function adds the display where the surface belongs to the end of the linked
list of displays for this surface.
When a surface is dragged or enlarged across the edge of a display
in a multi-display system and expands on a second display, \c surface_enter() runs again,
and this surface's list of displays contains 2 items.
When a surface leaves a display, member \c surface_leave() of \c surface_listener runs.
It removes that display from the surface's list of displays.
Each time the first item of a surface's list of displays
changes, function \c change_scale() is called and applies that display's \c gui_scale
value to that surface calling \c Fl_Window_Driver::screen_num(int). When a window
is unmapped by function \c Fl_Wayland_Window_Driver::hide(), the surface's list of displays
is emptied.
| listener function | called by Wayland when | resulting FLTK events |
|---|---|---|
| \c pointer_enter | pointer enters a window | FL_ENTER |
| \c pointer_leave | pointer leaves a window | FL_LEAVE |
| \c pointer_motion | pointer moves inside a window | FL_MOVE |
| \c pointer_button | state of mouse buttons changes | FL_PUSH, FL_RELEASE |
| \c pointer_axis | trackpad is moved vertically or horizontally | FL_MOUSEWHEEL |
| The Wayland book | Extensive introduction to Wayland programming written by the author of the sway compositor, unfortunately unachieved. | |
| Wayland Explorer | Documentation of all Wayland protocols, both stable and unstable. | |
| Wayland Protocol Specification | Documentation for all functions of the Wayland core protocol. | |
| Wayland clipboard and drag & drop | Detailed explanation of how clipboard and drag-and-drop work under Wayland. | |
| Wayland and input methods | Blog article introducing to the issue of text input methods under Wayland. | |
| Input Method Hub | Entry page for input method support giving newcomers a first understanding of what input methods are and how they are implemented in Wayland. |