1536 lines
89 KiB
Plaintext
1536 lines
89 KiB
Plaintext
/**
|
|
|
|
\page FLTK-devel Development of the FLTK library
|
|
|
|
- \subpage cmp
|
|
- \subpage wayland-devel
|
|
- \subpage bundled-libs
|
|
- \subpage development
|
|
|
|
*/
|
|
|
|
/**
|
|
\page wayland-devel The Wayland backend for its developer
|
|
|
|
This chapter describes how the Wayland backend of FLTK works from a developer's viewpoint.
|
|
|
|
|
|
|
|
\section wayland-intro Introduction to Wayland
|
|
|
|
Wayland usage involves communication via a Unix domain socket between a client
|
|
application and another process called the Wayland compositor which creates,
|
|
moves and resizes windows and displays their graphical content.
|
|
Diverse Wayland compositors exist which follow diverse strategies to size, decorate
|
|
and position windows on the screens. Compositors follow either the
|
|
client-side decoration (CSD) rule where client apps draw window titlebars, or the
|
|
server-side decoration (SSD) rule where the compositor draws titlebars. FLTK supports both
|
|
CSD and SSD compositors. It uses a library called \c libdecor charged of determining whether
|
|
a CSD or a SSD compositor is active, and of drawing titlebars in the first case.
|
|
|
|
Wayland is divided in various protocols that a given compositor may or may not support,
|
|
although they all support the \c core protocol. Each protocol adds functionality
|
|
not available in the core protocol and determines a well defined API the client must follow
|
|
to gain such functionality.
|
|
<a href=https://wayland.app/protocols/>Wayland Explorer</a> lists
|
|
all protocols and details their APIs.
|
|
Wayland compositors evolve in time by supporting more protocols or newly defined protocols.
|
|
The core protocol allows a client app
|
|
to discover what protocols the connected compositor supports. Protocols can be stable,
|
|
which means they have a defined API that will not change but can be expanded, or unstable.
|
|
For example, mapping a window on a screen is not done by the core protocol but by the
|
|
<tt>xdg shell</tt> protocol which is stable. The names of symbols used by unstable
|
|
protocols always begin with letter 'z'. For example, FLTK uses unstable protocol
|
|
<a href=https://wayland.app/protocols/text-input-unstable-v3>Text input</a>
|
|
to support CJK input methods; its symbol names begin with \c zwp_text_input_v3.
|
|
|
|
Wayland makes intensive use of the <em>listener</em> mechanism. A listener is a small array
|
|
of pointers to FLTK-defined callback functions associated to a Wayland-defined object;
|
|
Wayland calls these functions when defined events occur (more at \ref wayland-listeners
|
|
below).
|
|
|
|
Wayland differs noticeably from X11 in that rendering is left to clients: Wayland provides
|
|
no drawing API. Instead, Wayland provides objects of type <tt>struct wl_buffer</tt> which
|
|
encapsulate a memory array of pixel values shared between the client and the compositor.
|
|
The client app is expected to draw to that memory buffer with whatever means it chooses,
|
|
and to instruct the compositor to map those pixels to the screen when the buffer is
|
|
ready for display. The Wayland platform of FLTK draws with the Cairo library to \c Fl_Window's
|
|
and \c Fl_Image_Surface's, and with OpenGL to \c Fl_Gl_Window's.
|
|
|
|
Wayland differs also from X11 in that the position of a window in the display is
|
|
completely hidden to the client app. This prevents function \c Fl_Window::position() from having
|
|
any effect on a top-level window. Wayland also prevents a client app from knowing whether
|
|
a window is minimized: \c Fl_Window::show() has no effect on a minimized window.
|
|
Subwindows can be positioned as usual relatively to their parent window.
|
|
Wayland allows to create popup windows positioned relatively to a previously mapped other
|
|
window. This allows FLTK to position adequately menu and tooltip windows
|
|
(see \ref menu-windows).
|
|
FLTK uses also popups for the small, yellow windows that display
|
|
the new scale factor value when it's changed: these are created as short-lived popups
|
|
centered above \c Fl::first_window().
|
|
|
|
Wayland offers a means to handle lists of linked records. It defines type
|
|
<tt>struct wl_list</tt> and a few macros (\c wl_list_init(), \c wl_list_for_each(), \c wl_list_insert(),
|
|
\c wl_list_for_each_safe(), \c wl_list_remove()) to manage linked lists. Memory records put in these
|
|
lists must have the same type and contain a member variable of type <tt>struct wl_list</tt> used
|
|
to link records together
|
|
and often named 'link'. Access to such a list is possible memorizing a value of type
|
|
<tt>struct wl_list</tt> computed by macro \c wl_list_init().
|
|
Macro <tt>wl_list_for_each(arg1, arg2, arg3)</tt> allows to run through all list elements with:
|
|
- \c arg1 is a pointer variable to the type of elements of the linked list;
|
|
- \c arg2 is the address of a variable of type <tt>struct wl_list</tt> identifying the targeted list;
|
|
- \c arg3 is the name of the member variable of these elements used to link them together.
|
|
|
|
For example, \c wl_list_for_each() can be used as follows to scan the linked list
|
|
of all displays of the system (see \ref wayland-output "Fl_Wayland_Screen_Driver::output"):
|
|
\code
|
|
Fl_Wayland_Screen_Driver::output *output;
|
|
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
|
|
wl_list_for_each(output, &(scr_driver->outputs), link) {
|
|
// … work with output, an item of the linked list of all displays in the system …
|
|
}
|
|
\endcode
|
|
|
|
Overall, and ignoring for now OpenGL usage, FLTK interacts with Wayland as follows :
|
|
- When opening the display: FLTK calls \c Fl::add_fd() in \c FL_READ mode to associate
|
|
a callback function called \c wayland_socket_callback to the socket connecting the client
|
|
and the compositor.
|
|
- Client to compositor: FLTK calls C functions of the \c libwayland-client.so,
|
|
\c libwayland-cursor.so, \c libxkbcommon.so and \c libdecor.so shared libraries.
|
|
The names of these functions begin with \c wl_, \c xkb_ or \c libdecor_.
|
|
Functions from the first 2 of these libraries instruct the compositor to perform
|
|
various operations sending it data via the socket.
|
|
- Compositor to client: the callback function \c wayland_socket_callback runs when there are
|
|
data to read in the socket; it calls \c wl_display_dispatch() which interprets the read data
|
|
and calls corresponding listeners.
|
|
|
|
The core protocol defines also a number of mostly opaque structures whose names begin with \c wl_.
|
|
The names of symbols and types defined by the other protocols FLTK uses begin with \c xdg_,
|
|
\c zwp_text_input_v3, \c zxdg_toplevel_decoration_, \c gtk_shell1_ and \c gtk_surface1_.
|
|
FLTK defines a few structures holding Wayland-related data.
|
|
The names of FLTK-defined structures don't begin with \c wl_. For example,
|
|
<tt>struct wld_window</tt> (see \ref wld_window) is used to store all Wayland-specific data associated
|
|
to a mapped Fl_Window.
|
|
|
|
|
|
\section wayland-build Building libfltk as a Wayland client
|
|
|
|
Classes \c Fl_Wayland_Window_Driver, \c Fl_Wayland_Screen_Driver, \c Fl_Wayland_Graphics_Driver,
|
|
\c Fl_Wayland_Copy_Surface_Driver, \c Fl_Wayland_Image_Surface_Driver and
|
|
\c Fl_Wayland_Gl_Window_Driver and file \c fl_wayland_platform_init.cxx
|
|
contain all the Wayland-specific code of the FLTK library.
|
|
This code is located at \c src/drivers/Wayland/ in the FLTK source tree.
|
|
A single C++ source file generally contains all the code of a given class.
|
|
The code for class \c Fl_Wayland_Screen_Driver, however, is split in two source files,
|
|
\c Fl_Wayland_Screen_Driver.cxx and \c fl_wayland_clipboard_dnd.cxx that contains
|
|
all code related to copy, paste and drag-and-drop.
|
|
Furthermore, class \c Fl_Unix_System_Driver is used by both the Wayland and the X11
|
|
FLTK platforms. File FL/fl_config.h defines preprocessor variables
|
|
\c FLTK_USE_WAYLAND and \c FLTK_USE_CAIRO.
|
|
|
|
The public C API to Wayland, xkb, EGL and libdecor libraries are obtained with
|
|
\code
|
|
#include <wayland-client.h>
|
|
#include <wayland-cursor.h>
|
|
#include <xkbcommon/xkbcommon.h>
|
|
#include <xkbcommon/xkbcommon-compose.h>
|
|
#include <linux/input.h> // for BTN_LEFT, BTN_RIGHT, BTN_MIDDLE
|
|
#include "../../../libdecor/build/fl_libdecor.h"
|
|
#if HAVE_GL
|
|
# include <wayland-egl.h>
|
|
# include <EGL/egl.h>
|
|
#endif // HAVE_GL
|
|
\endcode
|
|
as necessary.
|
|
|
|
File \c README.Wayland.txt details what software packages are needed on Debian-based, Fedora
|
|
and FreeBSD systems for FLTK to use Wayland. Wayland protocols are packaged as XML files
|
|
accompanied by a utility program, \c wayland-scanner, able to generate a header file and a
|
|
necessary glue C source file from a given XML file. For example, for FLTK to use the
|
|
<a href=https://wayland.app/protocols/xdg-shell>XDG shell</a> protocol, these commands are
|
|
run at build time to generate, using file \c xdg-shell.xml, a .c file (\c xdg-shell-protocol.c)
|
|
that will be compiled into
|
|
\c libfltk and a header file (\c xdg-shell-client-protocol.h) that the FLTK code will include:
|
|
\code
|
|
PROTOCOLS=`pkg-config --variable=pkgdatadir wayland-protocols`
|
|
wayland-scanner private-code $PROTOCOLS/stable/xdg-shell/xdg-shell.xml xdg-shell-protocol.c
|
|
wayland-scanner client-header $PROTOCOLS/stable/xdg-shell/xdg-shell.xml xdg-shell-client-protocol.h
|
|
\endcode
|
|
Similar operations are performed for FLTK to use protocols
|
|
<a href=https://wayland.app/protocols/xdg-decoration-unstable-v1>XDG decoration</a>,
|
|
<a href=https://wayland.app/protocols/text-input-unstable-v3>Text input</a>
|
|
and <a href=https://wayland.app/protocols/gtk-shell>GTK Shell</a>.
|
|
|
|
|
|
\section wayland-x11-hybrid The hybrid Wayland/X11 platform
|
|
|
|
The Wayland platform of FLTK is normally a two-legged hybrid able to use either Wayland or X11
|
|
and to choose between these possibilities at run-time, without any change to the client
|
|
application. The Wayland/X11 hybrid is essentially a version of the FLTK library containing both all
|
|
Wayland-specific <u>and</u> all X11-specific code. That's reflected in file
|
|
FL/fl_config.h which defines both \c FLTK_USE_WAYLAND and \c FLTK_USE_X11.
|
|
This creates the constraint that FLTK's Wayland-specific and X11-specific source code cannot
|
|
use the same type name for different purposes or the same symbol name.
|
|
That is why function <tt>fl_xid(const Fl_Window*)</tt> is deprecated in FLTK 1.4 and replaced by
|
|
\c fl_wl_xid() for Wayland and \c fl_x11_xid() for X11. Also, global variable
|
|
<tt>Window fl_window</tt> is not used by the Wayland platform which instead uses
|
|
<tt>static struct wld_window *Fl_Wayland_Window_Driver:: wld_window</tt>.
|
|
The FLTK library contains also a short source file,
|
|
\c fl_wayland_platform_init.cxx, that determines, at startup time, whether
|
|
the app will run as a Wayland or as an X11 client. Function \c attempt_wayland() therein performs
|
|
this choice as follows :
|
|
- if the app defines a global bool variable called \c fl_disable_wayland and this variable is true,
|
|
the X11 leg is chosen;
|
|
- if environment variable FLTK_BACKEND is defined to string "wayland", the Wayland leg is chosen;
|
|
- if environment variable FLTK_BACKEND is defined to string "x11", the X11 leg is chosen;
|
|
- otherwise, a connection to a Wayland compositor is attempted; if it's successful, the Wayland
|
|
leg is chosen; if it's not, the X11 leg is chosen.
|
|
|
|
The first condition listed above is meant to facilitate transition to FLTK 1.4 of source code
|
|
written for FLTK 1.3 and containing X11-specific code : it's enough to put
|
|
\code
|
|
FL_EXPORT bool fl_disable_wayland = true;
|
|
\endcode
|
|
anywhere in the source code, for the app to run with 1.4, using the x11 leg of the hybrid platform,
|
|
without any other change in the source code nor to the application's environment.
|
|
|
|
Function \c attempt_wayland() must be called before the very first platform-dependent operation FLTK
|
|
performs so that operation is done the Wayland or the X11 way, as appropriate. That's why
|
|
4 locations of the FLTK source code call \c attempt_wayland():
|
|
<tt>Fl_Graphics_Driver::newMainGraphicsDriver(), Fl_Screen_Driver::newScreenDriver(),
|
|
Fl_Window_Driver::newWindowDriver(Fl_Window*), and
|
|
Fl_Image_Surface_Driver::newImageSurfaceDriver().</tt>
|
|
|
|
In special situations, such as with embedded systems equipped with the Wayland software but lacking
|
|
the X11 library, it's possible to build the FLTK library such as it contains only the Wayland backend.
|
|
This is achieved building FLTK with <tt>cmake -DFLTK_BACKEND_X11=OFF</tt>.
|
|
In that case, FL/fl_config.h does not define \c FLTK_USE_X11.
|
|
|
|
The rest of this chapter describes what happens when the Wayland leg has been chosen.
|
|
|
|
|
|
\section wayland-listeners Listeners
|
|
|
|
A Wayland 'listener' is a small array of pointers to FLTK-defined callback functions
|
|
associated to a Wayland-defined object.
|
|
A call to a specific Wayland function with a name of the form \c wl_XXX_add_listener
|
|
associates the Wayland object and its listener, usually right after creation of this object.
|
|
After defined events have occurred, the Wayland compositor sends appropriate commands
|
|
to the client through the socket; the event loop detects the availability of data in the
|
|
socket and calls function \c wayland_socket_callback(); this function calls the appropriate
|
|
member of the listener and transmits relevant information to the client app as parameters of this call.
|
|
For example, this code:
|
|
\code
|
|
static void surface_enter(……) { …… } // called when a surface enters a display
|
|
static void surface_leave(……) { …… } // called when a surface leaves a display
|
|
|
|
static struct wl_surface_listener surface_listener = {
|
|
surface_enter,
|
|
surface_leave,
|
|
};
|
|
|
|
some_pointer_type pter_to_data;
|
|
struct wl_surface *my_wl_surface;
|
|
my_wl_surface = wl_compositor_create_surface(scr_driver->wl_compositor);
|
|
wl_surface_add_listener(my_wl_surface, &surface_listener, pter_to_data);
|
|
\endcode
|
|
creates a Wayland object of type <tt>struct wl_surface</tt> (roughly, a window), and associates it with
|
|
a 2-member listener called \c surface_listener. After this, Wayland is expected to call
|
|
the 2 listener members, \c surface_enter or \c surface_leave, each time \c my_wl_surface will
|
|
enter or leave, respectively, a display. The arguments of these calls, not detailed here,
|
|
allow the member functions to identify which surface enters or leaves which display.
|
|
The \c wl_surface_add_listener() call above also associates \c pter_to_data to
|
|
\c my_wl_surface as <em>user data</em>. The \c wl_surface object's
|
|
"user data" can be obtained later calling function \c wl_surface_get_user_data().
|
|
|
|
Wayland function \c wl_proxy_get_listener() returns a pointer
|
|
to a Wayland object's listener provided that object is transmitted cast to type
|
|
<tt>struct wl_proxy *</tt>. This gives a handy way to distinguish FLTK-created Wayland
|
|
objects from objects of other origin: the listener of an FLTK-created object is a known
|
|
FLTK listener. For example, function \c Fl_Wayland_Window_Driver::surface_to_window()
|
|
uses this possibility calling <tt>wl_proxy_get_listener( (struct wl_proxy *)wl_surface )</tt>
|
|
for any object of type <tt>struct wl_surface</tt>: if that object was created as in the
|
|
example above, this call returns a pointer to FLTK's \c surface_listener static variable.
|
|
|
|
|
|
\section wayland-connection Opening a Wayland connection
|
|
|
|
Establishing a Wayland connection requires environment variable \c XDG_RUNTIME_DIR to be
|
|
defined and to point to a directory containing a socket connected to a Wayland
|
|
compositor. This variable is usually set by the login procedure of Wayland-friendly desktops.
|
|
Which socket-file to use within that directory is determined as follows:
|
|
- the client may call <tt>Fl::display(const char *display_name)</tt> before
|
|
\c fl_open_display() runs or use the \c -display command line argument and transmit there the
|
|
socket name;
|
|
- environment variable \c WAYLAND_DISPLAY can be defined to the socket name;
|
|
- otherwise, \c "wayland-0" is used.
|
|
|
|
Which socket is selected determines the compositor used by the client application: that at the other
|
|
end of the socket.
|
|
|
|
Establishing the connection begins with a call to <tt>wl_display_connect(const char *display_name)
|
|
</tt>. That call is done inside function \c attempt_wayland() mentioned before with a NULL argument,
|
|
or when a non default Wayland display name is specified as explained above. That call returns a
|
|
<tt>struct wl_display</tt> pointer or NULL in case of failure. Such NULL return is the hint
|
|
that allows the FLTK display opening procedure of the Wayland/X11 hybrid to recognize when Wayland
|
|
access is not possible and to fallback to X11. If the call is successful, its non-NULL return
|
|
is assigned to class variable \c Fl_Wayland_Screen_Driver::wl_display.
|
|
|
|
The rest of the work is done in function \c Fl_Wayland_Screen_Driver::open_display_platform().
|
|
A call to \c wl_registry_add_listener() associates a 2-member listener, whose 1st member,
|
|
\c registry_handle_global(), will be called by Wayland a number of times to indicate each time
|
|
a protocol supported by the compositor or a system feature such as displays and keyboards.
|
|
|
|
FLTK runs this code to receive calls to \c registry_handle_global():
|
|
\code
|
|
static void sync_done(void *data, struct wl_callback *cb, uint32_t time) {
|
|
*(struct wl_callback **)data = NULL;
|
|
wl_callback_destroy(cb);
|
|
}
|
|
|
|
static const struct wl_callback_listener sync_listener = {
|
|
sync_done
|
|
};
|
|
|
|
struct wl_callback *registry_cb = wl_display_sync(wl_display);
|
|
wl_callback_add_listener(registry_cb, &sync_listener, ®istry_cb);
|
|
while (registry_cb) wl_display_dispatch(wl_display);
|
|
\endcode
|
|
A pointer to an object of type <tt>struct wl_callback</tt> created by function
|
|
\c wl_display_sync() is assigned to variable \c registry_cb.
|
|
Then a 1-member listener is attached to this object. Wayland will run this listener's
|
|
member function, \c sync_done(), after all calls to \c registry_handle_global() have
|
|
occurred. Function \c sync_done() sets to null variable \c registry_cb and destroys
|
|
the \c wl_callback.
|
|
Finally, function \c wl_display_dispatch() is called as long as variable \c registry_cb is
|
|
not null. Thus, when \c sync_done() runs, FLTK has received all due calls to
|
|
\c registry_handle_global().
|
|
|
|
The prototype of function \c registry_handle_global is:
|
|
\code
|
|
static void registry_handle_global(void *user_data, struct wl_registry *wl_registry,
|
|
uint32_t id, const char *interface, uint32_t version)
|
|
\endcode
|
|
Each time Wayland calls \c registry_handle_global(), \c interface and \c version give the name
|
|
and version of a component or feature of the Wayland system.
|
|
Here is the list of the \c interface value for all protocols and system features relevant for FLTK:
|
|
<table>
|
|
<tr><th>Interface </th><th>How FLTK uses this interface</th> </tr>
|
|
<tr><td>wl_compositor </td><td> create wl_surface objects </td> </tr>
|
|
<tr><td>wl_subcompositor </td><td>create subwindows </td> </tr>
|
|
<tr><td>wl_shm </td><td> create shared memory pools and buffers</td> </tr>
|
|
<tr><td>wl_seat </td><td> create the unique "seat"</td> </tr>
|
|
<tr><td>wl_data_device </td><td> support of copy/paste/drag-n-drop</td> </tr>
|
|
<tr><td>wl_output </td><td>received once for each display </td> </tr>
|
|
<tr><td>xdg_wm_base </td><td>create mapped windows </td> </tr>
|
|
<tr><td>gtk_shell1 </td><td>indicates Mutter is in use + titlebar gestures</td> </tr>
|
|
<tr><td>weston_desktop_shell </td><td> indicates Weston is in use</td></tr>
|
|
<tr><td>org_kde_plasma_shell </td><td> indicates KDE/Plasma is in use</td></tr>
|
|
<tr><td>zwp_text_input_manager_v3 </td><td>interface with Text Input Methods </td></tr>
|
|
<tr><td>zxdg_decoration_manager_v1 </td><td>select between CSD and SSD modes </td></tr>
|
|
</table>
|
|
Wayland compositors typically support several other protocols (e.g., \c zxdg_output_manager_v1)
|
|
that FLTK does not use.
|
|
|
|
Each time \c registry_handle_global runs with an \c interface from the table above, FLTK calls
|
|
\c wl_registry_bind() which returns a pointer to a Wayland structure that will be the client's access
|
|
point to the corresponding Wayland protocol or system feature. This pointer is stored in a dedicated
|
|
member variable of the unique \c Fl_Wayland_Screen_Driver object of an FLTK app, or of another object
|
|
accessible from this object.
|
|
For example, when \c interface equals \c "wl_compositor", the value returned by
|
|
\c wl_registry_bind() is stored as member \c wl_compositor of the
|
|
\c Fl_Wayland_Screen_Driver object.
|
|
Function \c registry_handle_global() also identifies whether the Mutter, Weston, KWin or Wayfire compositor
|
|
is connected and stores this information in static member variable \c Fl_Wayland_Screen_Driver::compositor.
|
|
Other compositors (e.g., \c sway, \c labwc) are not specifically identified by FLTK and
|
|
store value \c unspecified in member variable \c compositor.
|
|
|
|
Wayland calls \c registry_handle_global() with its parameter \c interface equals to
|
|
\c "wl_output" once for each screen connected to the system. Each time, a pointer to a Wayland object
|
|
of type <tt>struct wl_output</tt> is obtained by a call to \c wl_registry_bind() and an FLTK
|
|
record of type \ref wayland-output "Fl_Wayland_Screen_Driver::output" is created.
|
|
This FLTK record is added to the end of the linked list of known screens that starts
|
|
at member \c outputs of the \c Fl_Wayland_Screen_Driver object.
|
|
A 4-member listener is associated to the <tt>struct wl_output</tt> by function
|
|
\c wl_output_add_listener(). The 3rd member of this 4-function listener, \c output_done(),
|
|
runs after all initialization steps of the screen have completed and turns to \c true
|
|
member \c done of the record of type \c Fl_Wayland_Screen_Driver::output.
|
|
Function \c sync_done() mentioned above therefore also calls \c wl_display_dispatch()
|
|
until the \c done member of all \c Fl_Wayland_Screen_Driver::output records are \c true.
|
|
Overall, after return from function \c sync_done(), FLTK has been made aware of all
|
|
optional protocols and features of its connected Wayland compositor, and has initialized
|
|
all screens of the system.
|
|
|
|
Finally, function \c wl_display_get_fd() is called to obtain the file descriptor of the Wayland socket
|
|
and a call to Fl::add_fd() makes FLTK listen to this descriptor in \c FL_READ mode and associates
|
|
function \c wayland_socket_callback() from file \c Fl_Wayland_Screen_Driver.cxx with it.
|
|
This function calls \c wl_display_dispatch() which reads and interprets data available from the
|
|
file descriptor, and calls corresponding listeners. Function \c wayland_socket_callback() repeats
|
|
the \c wl_display_dispatch() call as long as data are available for reading.
|
|
|
|
The event loop is run by function \c Fl_Unix_System_Driver::wait() which is used by both
|
|
the Wayland and X11 FLTK backends. Among various tasks, this function waits for data arriving
|
|
on the file descriptors FLTK is listening. Overall, the Wayland backend's event loop differs
|
|
from that of the X11 backend only in the callback function handling data read from the Wayland
|
|
connection socket and in overridden functions \c Fl_Wayland_Screen_Driver::poll_or_select_with_delay()
|
|
and \c Fl_Wayland_Screen_Driver::poll_or_select().
|
|
|
|
|
|
\section wayland-surface Wayland windows and surfaces
|
|
|
|
Wayland defines objects called surfaces of type <tt>struct wl_surface</tt>. A Wayland surface
|
|
"has a rectangular area which may be displayed on zero or more displays, present buffers,
|
|
receive user input, and define a local coordinate system". In short,
|
|
surface is the name Wayland uses for a window.
|
|
Buffers allow the client app to define the graphical content of surfaces (see \ref wayland-buffer).
|
|
FLTK creates a surface each time an Fl_Window is show()'n calling function
|
|
\c wl_compositor_create_surface().
|
|
Static member function <tt>Fl_Wayland_Window_Driver::surface_to_window(struct wl_surface *)</tt>
|
|
gives the \c Fl_Window* corresponding to the surface given in argument.
|
|
|
|
FLTK recognizes 4 mutually exclusive kinds of surfaces :
|
|
- DECORATED are toplevel windows with a titlebar;
|
|
- UNFRAMED are toplevel windows without titlebar;
|
|
- POPUP correspond to menus and tooltips;
|
|
- SUBWINDOW correspond to an Fl_Window embedded in another Fl_Window.
|
|
|
|
Function \c Fl_Wayland_Window_Driver::makeWindow() creates all these surfaces, creates for each
|
|
a record of type <tt>struct wld_window</tt> (see \ref wld_window), and stores the window kind in
|
|
member variable \c kind of this record. Member variable \c xid of the window's \c Fl_X record stores
|
|
the adress of this record.
|
|
|
|
Except for SUBWINDOW's, each surface needs a Wayland object of type <tt>struct xdg_surface</tt>
|
|
used to make it become a mapped window and stored in member \c xdg_surface of the window's
|
|
\ref wld_window record. For DECORATED windows, this object is created inside libdecor
|
|
and transmitted to FLTK by function \c libdecor_frame_get_xdg_surface(). For UNFRAMED
|
|
and POPUP windows, it's created by function \c xdg_wm_base_get_xdg_surface().
|
|
Finally, each surface is also associated to one more Wayland object whose type
|
|
varies with the window's kind. These explain this part of the \ref wld_window record:
|
|
\code
|
|
union {
|
|
struct libdecor_frame *frame; // created for DECORATED by libdecor_decorate()
|
|
struct wl_subsurface *subsurface; // created for SUBWINDOW by wl_subcompositor_get_subsurface()
|
|
struct xdg_popup *xdg_popup; // created for POPUP by xdg_surface_get_popup()
|
|
struct xdg_toplevel *xdg_toplevel; // created for UNFRAMED by xdg_surface_get_toplevel()
|
|
};
|
|
\endcode
|
|
|
|
Except for SUBWINDOW's, each surface is associated to a 'configure' function that Wayland
|
|
calls one or more times when the window is going to be mapped on the display.
|
|
The 'configure' function of DECORATED surfaces is \c handle_configure() which is the 1st
|
|
member of a 4-member listener named \c libdecor_frame_iface associated to a decorated window
|
|
when it's created calling \c libdecor_decorate(). Finally, a call to \c libdecor_frame_map()
|
|
triggers the process of mapping the newly created DECORATED surface on a display.
|
|
Wayland calls \c handle_configure() twice during this process.
|
|
The first \c handle_configure() run allows to set the window's \c xdg_surface object
|
|
which is returned by function \c libdecor_frame_get_xdg_surface().
|
|
FLTK distinguishes the first from the second run of \c handle_configure() by looking at
|
|
the \c xdg_surface member variable that's NULL at the beginning of the 1st run and not NULL later.
|
|
Wayland calls \c handle_configure() also during operations such as resizing, minimizing (see below).
|
|
With the help of a few calls to libdecor functions, FLTK obtains in this function
|
|
all needed information about the size and state of the mapped window.
|
|
The 'configure' functions of UNFRAMED and POPUP surfaces are \c xdg_surface_configure(),
|
|
\c xdg_toplevel_configure() and \c popup_configure(). The mapping process of these surfaces
|
|
is triggered by a call to \c wl_surface_commit().
|
|
These 'configure' functions transmit effective window size
|
|
information to FLTK. Also, they are where the window's
|
|
\c Fl_Window_Driver::wait_for_expose_value member variable is set to 0 to indicate that the
|
|
window has been mapped to display. \b Caution: there are some small
|
|
differences between how and when the various Wayland compositors call \c handle_configure().
|
|
|
|
When a decorated window changes size, whatever the cause of it, Wayland calls
|
|
\c handle_configure() which sets member variable \c Fl_Wayland_Window_Driver::in_handle_configure to true
|
|
and calls the window's virtual \c resize() function which ultimately runs
|
|
\c Fl_Wayland_Window_Driver::resize() which calls Fl_Group::resize() to perform FLTK's resize
|
|
operations and \c Fl_Wayland_Graphics_Driver::buffer_release()
|
|
to delete the existing window buffer that's not adequate for the new window size.
|
|
At the end of the run of \c handle_configure(), \c in_handle_configure is set back to false.
|
|
When the window size change is caused by the app itself calling the window's \c resize() function,
|
|
\c Fl_Wayland_Window_Driver::in_handle_configure is false. This allows
|
|
\c Fl_Wayland_Window_Driver::resize()
|
|
to detect that Wayland needs be informed of the desired size change, which gets done by a call
|
|
to \c libdecor_frame_commit(). Wayland later calls \c handle_configure() and events described
|
|
above unfold.
|
|
|
|
Wayland generally does not provide a way to control where the compositor should map a window
|
|
in the system displays. Nevertheless, for multi-display systems, Wayland allows to control
|
|
on what display should the compositor map a fullscreen window. That is done inside function
|
|
\c handle_configure() which calls \c libdecor_frame_set_fullscreen() for DECORATED windows
|
|
and inside function \c xdg_toplevel_configure() which calls \c xdg_toplevel_set_fullscreen()
|
|
for UNFRAMED. The <tt>struct wl_output</tt> pointer for the targeted display is transmitted
|
|
as 2nd argument of these calls.
|
|
|
|
\section menu-windows Menu windows and other popups
|
|
Menu windows, tiny menu title windows, and tooltip windows are implemented using Wayland's
|
|
popup mechanism which allows to position a popup window relatively to a previously mapped
|
|
window, itself a popup or another kind of window, with the restriction that any popup
|
|
must overlap or at least touch that other window.
|
|
Member function \c Fl_Wayland_Window_Driver::makeWindow calls member function
|
|
\c Fl_Wayland_Window_Driver::process_menu_or_tooltip to create all popups.
|
|
|
|
This function gets called after FLTK has computed using a given algorithm the desired \c (x,y)
|
|
position of the popup window's top-left corner, using coordinates centered on the top-left
|
|
corner of the toplevel window from which the popup originates.
|
|
This algorithm is able to prevent popups from
|
|
being positioned beyond the screen borders under the assumption that the position of a
|
|
toplevel window inside a screen is known. While this assumption holds for other
|
|
platforms, it does not for the Wayland platform. The FLTK code for the Wayland platform
|
|
therefore modifies the algorithm that FLTK uses to compute the position of menu windows.
|
|
The key information used by this algorithm is obtained by virtual member function
|
|
\c Fl_Window_Driver::menu_window_area which computes the coordinates of the rectangle where
|
|
menu windows are allowed to be positioned. Under other platforms, this function just returns
|
|
the origin and size of the work area of the screen in use.
|
|
In contrast, the Wayland platform handles two situations differently :
|
|
- For menu windows that are not taller than the display in use, the
|
|
Wayland-overridden member function \c Fl_Wayland_Window_Driver::menu_window_area returns
|
|
large negative origin and large width and height values. This lets the standard FLTK
|
|
algorithm position the menu relatively to its window of origin without concern about screen
|
|
limits, and relies on Wayland's constraint mechanism described below to prevent the menu from
|
|
going beyond these limits, without FLTK having to know where they are.
|
|
- Menu windows taller than the screen where they are mapped need special handling
|
|
described in detail in a comment above the source code of function
|
|
\c Fl_Wayland_Window_Driver::process_menu_or_tooltip.
|
|
|
|
Function \c Fl_Wayland_Window_Driver::process_menu_or_tooltip
|
|
first computes \c origin_win, pointer to the \c Fl_Window relatively to which the popup is to
|
|
be positioned. Window \c origin_win is the parent menu window when the popup is a sub-menu;
|
|
it's the tiny windowtitle when the popup is a menu with a title; otherwise, it's the window
|
|
containing the point of origin of the popup.
|
|
An object of type <tt>struct xdg_positioner</tt> created by function
|
|
\c xdg_wm_base_create_positioner() is used to express the rules that will determine the
|
|
popup position relatively to \c origin_win as follows:
|
|
- Function \c xdg_positioner_set_anchor_rect() determines a rectangle in \c origin_win
|
|
relatively to which the popup is to be positioned. When the popup to be created is a menu
|
|
window spawned by an Fl_Menu_Bar, that rectangle is the full area of the menu title window.
|
|
Otherwise, that rectangle is an adequately located point.
|
|
- Function \c xdg_positioner_set_size() sets the popup size.
|
|
- The <tt>xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_BOTTOM_LEFT);</tt>
|
|
and <tt>xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);</tt>
|
|
calls position the popup so that its top-left corner is initially below and at right of
|
|
the bottom-left corner of the \c origin_win 's anchor rectangle.
|
|
- The call to \c xdg_positioner_set_offset() further changes the popup vertical position.
|
|
- The call to \c xdg_positioner_set_constraint_adjustment() uses constraint flags
|
|
\c XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X and
|
|
\c XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y which mean that the compositor will move
|
|
the popup horizontally and vertically if its initial position would make it expand beyond
|
|
the edges of the screen. Furthermore, flag XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y is
|
|
added when the popup is a menu window spawned by an Fl_Menu_Bar; this has the popup flipped
|
|
above the Fl_Menu_Bar if there's not enough screen room below it for the popup.
|
|
- Finally, a call to function \c xdg_surface_get_popup() creates the popup accounting for
|
|
position rules listed above. The positioner is then deleted
|
|
by \c xdg_positioner_destroy(), a listener is associated to the popup surface with
|
|
\c xdg_popup_add_listener(), and a call to \c wl_surface_commit() triggers the mapping
|
|
of the popup on the display.
|
|
|
|
Overall, the expected coordinates of the top-left corner of the popup relatively to
|
|
\c origin_win are <tt>popup_x, popup_y</tt>. They are memorized in a record of FLTK-defined
|
|
type <tt>struct win_positioner</tt> that's associated to the popup listener.
|
|
When the compositor maps the popup, function
|
|
\c popup_configure, the first element of the popup listener, runs and receives as arguments
|
|
the coordinates of the popup top left and its size. These values account for the positioning
|
|
constraints of the popup which may have moved it to avoid screen borders. This function
|
|
can therefore detect whether constraints applied have modified the effective popup location
|
|
in comparison to the expected coordinates which are available as member variables of the
|
|
<tt>struct win_positioner</tt> record mentioned above. That's key to the handling by FLTK
|
|
of tall menu windows.
|
|
|
|
Groups of popups containing a menutitle, the associated menuwindow, and optionally
|
|
a submenu window and that don't belong to an Fl_Menu_Bar are mapped in a different order:
|
|
the menuwindow is mapped first, and the menutitle is mapped second above it as a child popup.
|
|
Function \c Fl_Window_Driver::is_floating_title() detects when such a menutitle is created,
|
|
static member variable \c previous_floatingtitle is assigned the value of this menutitle, and
|
|
the menutitle is mapped only after the menuwindow has been mapped, as a child of it.
|
|
This positions better the popup group in the display relatively to where the popup
|
|
was created.
|
|
|
|
|
|
\section wayland-graphics-driver Fl_Wayland_Graphics_Driver and Fl_Cairo_Graphics_Driver
|
|
|
|
The Wayland platform of FLTK uses an \c Fl_Wayland_Graphics_Driver object for all its on-screen
|
|
drawing operations.
|
|
This object is created by function \c Fl_Graphics_Driver::newMainGraphicsDriver() called by
|
|
\c Fl_Display_Device::display_device() when the library opens the display.
|
|
New \c Fl_Wayland_Graphics_Driver objects are also created for each \c Fl_Image_Surface and
|
|
each \c Fl_Copy_Surface used, and deleted when these objects are deleted.
|
|
|
|
Class \c Fl_Wayland_Graphics_Driver derives from class \c Fl_Cairo_Graphics_Driver which
|
|
implements all the FLTK drawing API for a Cairo surface.
|
|
Function \c Fl_Wayland_Graphics_Driver::cairo_init()
|
|
creates the Cairo surface used by each \c Fl_Wayland_Graphics_Driver object by calling \c
|
|
cairo_image_surface_create_for_data() for the window's or offscreen's \c draw_buffer (see below).
|
|
|
|
Class \c Fl_Cairo_Graphics_Driver is also used
|
|
by the X11 leg of the hybrid Wayland-X11 platform because this leg draws to the display with
|
|
an \c Fl_X11_Cairo_Graphics_Driver object which derives from class
|
|
\c Fl_Cairo_Graphics_Driver. Finally, \c Fl_Cairo_Graphics_Driver is also used, in the form of
|
|
an object from its derived class \c Fl_PostScript_Graphics_Driver, when the hybrid Wayland-X11
|
|
platform draws PDF or PostScript, or when the classic X11 platform uses Pango and draws
|
|
PDF or PostScript. This happens when classes \c Fl_PDF_File_Surface, \c Fl_PostScript_File_Device
|
|
and \c Fl_Printer are used.
|
|
|
|
|
|
\section wayland-buffer Wayland buffers
|
|
|
|
Wayland uses buffers, objects of type <tt>struct wl_buffer</tt>, to draw to surfaces. In principle,
|
|
one or more buffers can be associated to a surface, and functions \c wl_surface_attach() and
|
|
\c wl_surface_commit() are called to first attach one such buffer to the surface and then inform the
|
|
compositor to map this buffer's graphics content on the display. Wayland buffers can use various
|
|
memory layouts. FLTK uses WL_SHM_FORMAT_ARGB8888, which is the same layout as what Cairo calls
|
|
CAIRO_FORMAT_ARGB32.
|
|
|
|
FLTK calls function \c Fl_Wayland_Window_Driver::make_current() before drawing to any Fl_Window.
|
|
Member \c buffer of this Fl_Window's <tt>struct wld_window</tt> (see \ref wld_window) is NULL when the
|
|
window has just been created or resized. In that case, FLTK calls
|
|
\c Fl_Wayland_Graphics_Driver::create_wld_buffer() which
|
|
returns a pointer to a <tt>struct wld_buffer</tt> containing
|
|
- a Wayland buffer, member \c wl_buffer;
|
|
- a Cairo image surface, created by a call to \c Fl_Wayland_Graphics_Driver::cairo_init().
|
|
|
|
Each of these two objects encapsulates a byte array of the same size and the same memory layout
|
|
destined to contain the Fl_Window's graphics. The Cairo image surface object is where FLTK draws.
|
|
The Wayland buffer is what Wayland maps on the display. FLTK copies the Cairo surface's byte array
|
|
to the Wayland buffer's byte array before beginning the mapping operation.
|
|
If \c width and \c height are a window's dimensions in pixels,
|
|
\code
|
|
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
|
|
int size = stride * height;
|
|
\endcode
|
|
give \c size, the common size of both byte arrays.
|
|
|
|
The effective creation of the \c wl_buffer object is delayed until function
|
|
\c Fl_Wayland_Graphics_Driver::buffer_commit() gets called.
|
|
Section \ref wayland-buffer-factory below details how FLTK creates \c wl_buffer objects.
|
|
|
|
The <tt>struct Fl_Wayland_Graphics_Driver::wld_buffer</tt> (see \ref wld_buffer) contains
|
|
a pointer to the byte array of the Cairo image surface (member \c draw_buffer.buffer), information about the
|
|
Wayland buffer (members \c wl_buffer and \c data), the common size of the Cairo surface's and
|
|
Wayland buffer's byte arrays (member \c draw_buffer.data_size), and other information. A pointer to this
|
|
<tt>struct Fl_Wayland_Graphics_Driver::wld_buffer</tt> is memorized as member \c buffer of the Fl_Window's \ref wld_window.
|
|
All drawing operations to the Fl_Window then modify the content of the Cairo image surface.
|
|
|
|
Function \c Fl_Wayland_Window_Driver::flush() is in charge of sending FLTK
|
|
graphics data to the display. That is done by calling function \c
|
|
Fl_Wayland_Graphics_Driver::buffer_commit() which creates the <tt>struct wl_buffer</tt>
|
|
object calling \c create_shm_buffer() if that was not done before,
|
|
copies the byte array of the Cairo surface to
|
|
the Wayland buffer's starting memory address, and calls functions \c wl_surface_attach()
|
|
and \c wl_surface_commit(). Before calling Fl_Window::flush(),
|
|
FLTK has computed a damaged region. If that region is not null,
|
|
\c Fl_Wayland_Graphics_Driver::buffer_commit() copies only the damaged parts of the Cairo
|
|
surface to the Wayland buffer and calls function \c wl_surface_damage_buffer() for these
|
|
parts to inform the compositor of what parts of the surface need its attention.
|
|
|
|
<h3>Wayland buffer deletion</h3>
|
|
Each \ref wld_buffer record contains boolean member \c in_use which is set to \c true
|
|
just before the buffer gets committed, and boolean member \c released which
|
|
is set to \c true when FLTK no longer needs the buffer and calls
|
|
\c Fl_Wayland_Graphics_Driver::buffer_release().
|
|
FLTK's buffer-creating function, \c Fl_Wayland_Graphics_Driver::create_shm_buffer(),
|
|
attaches a 1-member listener to each buffer which Wayland calls after a commit
|
|
operation to indicate the client is allowed to re-use the buffer.
|
|
This listener's member function, \c buffer_release_listener(),
|
|
turns to false member \c in_use of the buffer's \ref wld_buffer record.
|
|
Since the two events 'FLTK no longer needs the buffer' and
|
|
'the client is allowed to re-use the buffer' can arrive in
|
|
any order, FLTK deletes the <tt>struct wl_buffer</tt> object by running
|
|
\c do_buffer_release() only after both events happened, that is, when \c in_use is
|
|
\c false and \c released is \c true. That's why function \c do_buffer_release()
|
|
is called by both functions \c Fl_Wayland_Graphics_Driver::buffer_release()
|
|
and \c buffer_release_listener().
|
|
|
|
|
|
\section throttling Throttling window redraws
|
|
|
|
FLTK uses Wayland's synchronization
|
|
mechanism to make sure any committed \c wl_buffer is not changed while the
|
|
compositor is using it and to refrain from calling \c wl_surface_commit()
|
|
more frequently than the system can process it.
|
|
Firstly, as seen above, Wayland calls function \c buffer_release_listener() when the client is
|
|
free to reuse or destroy a given \c wl_buffer. FLTK won't change or destroy a committed
|
|
\c wl_buffer before that call.
|
|
Second, this 2-step mechanism prevents Wayland clients from committing new buffer states
|
|
too frequently:
|
|
- \c Fl_Wayland_Graphics_Driver::buffer_commit() first calls function \c wl_surface_frame() to
|
|
obtain a pointer to a <tt>struct wl_callback</tt> object and stores it as member
|
|
\c frame_cb of the surface's \ref wld_window.
|
|
Then it calls \c wl_callback_add_listener() to associate this object to the
|
|
FLTK-defined, callback function \c surface_frame_done().
|
|
It next calls \c wl_surface_commit().
|
|
Together, these 3 calls instruct Wayland to start mapping the buffer content to the
|
|
display and to call \c surface_frame_done() later, when it will have become ready
|
|
for another mapping operation.
|
|
- Later, \c surface_frame_done() runs and destroys the \c wl_callback object by
|
|
function \c wl_callback_destroy() and sets member \c frame_cb to NULL.
|
|
|
|
Member variable \c draw_buffer_needs_commit of the \ref wld_buffer is also
|
|
important in this mechanism : it informs FLTK that the graphics buffer has
|
|
changed and needs being committed. This variable is turned \c true every time a
|
|
graphics operation changes the buffer content and turned \c false when the
|
|
buffer gets committed.
|
|
|
|
This procedure ensures that FLTK never calls \c wl_surface_commit()
|
|
before the compositor becomes ready for a new commit because
|
|
\c Fl_Wayland_Window_Driver::flush() calls
|
|
\c Fl_Wayland_Graphics_Driver::buffer_commit() only if \c frame_cb is NULL.
|
|
If it's not NULL, the exact content of function \c surface_frame_done() :
|
|
\code
|
|
static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) {
|
|
struct wld_window *window = (struct wld_window *)data;
|
|
wl_callback_destroy(cb);
|
|
window->frame_cb = NULL;
|
|
if (window->buffer && window->buffer->draw_buffer_needs_commit) {
|
|
Fl_Wayland_Graphics_Driver::buffer_commit(window);
|
|
}
|
|
}
|
|
\endcode
|
|
has the effect that when the mapping operation eventually completes, Wayland runs
|
|
\c surface_frame_done(), which, if the buffer's \c draw_buffer_needs_commit member is true,
|
|
calls \c Fl_Wayland_Graphics_Driver::buffer_commit() anew. The net result is that the screen
|
|
shows the most recent surface content.
|
|
|
|
This synchronization mechanism is also used when performing an interactive window resize
|
|
operation. During such operation, the compositor informs the client an interactive
|
|
resize is being performed and sends window resize commands at high rate (~60 Hz) to the
|
|
client via the socket. Libdecor turns on flag \c LIBDECOR_WINDOW_STATE_RESIZING
|
|
to inform the client, and runs function \c handle_configure() for each received resize
|
|
command. Before calling Fl_Group::resize() and later Fl_Window::draw(),
|
|
\c handle_configure() tests whether \c window->frame_cb is NULL. When it's not
|
|
because a previous resize operation is being performed, the current resize command is
|
|
skipped. At the end of the interactive resize, flag \c LIBDECOR_WINDOW_STATE_RESIZING
|
|
is off and Wayland sends a final resize command which is not skipped. Overall, this
|
|
ensures the client program resizes its window as frequently as it can without
|
|
falling behind resize commands sent by the compositor.
|
|
|
|
To account for a bug in Mutter (issue #878), the \c window->frame_cb object is
|
|
not created when a toplevel window is being resized and is entirely covered by
|
|
one subwindow.
|
|
|
|
<h3>Progressive window drawing</h3>
|
|
FLTK supports progressive drawing when an app calls function Fl_Window::make_current()
|
|
at any time and then calls the FLTK drawing API. This is made possible
|
|
in function \c Fl_Wayland_Window_Driver::make_current() with
|
|
\code
|
|
// to support progressive drawing
|
|
if ( (!Fl_Wayland_Window_Driver::in_flush_) && window->buffer && (!window->frame_cb)
|
|
&& (!wait_for_expose_value) ) {
|
|
Fl_Wayland_Graphics_Driver::buffer_commit(window);
|
|
}
|
|
\endcode
|
|
Thus, \c buffer_commit() runs only when \c frame_cb is NULL. If an app rapidly performs calls
|
|
to \c Fl_Window::make_current() and to drawing functions, FLTK will copy \c draw_buffer to
|
|
the Wayland buffer and instruct Wayland to map it to the display when \c frame_cb is NULL
|
|
which means that the compositor is ready to start performing a mapping operation.
|
|
This occurs when the progressive drawing operation begins. Later, \c frame_cb is generally found
|
|
non NULL when \c Fl_Wayland_Window_Driver::make_current() runs because the compositor
|
|
is busy processing the previous Wayland buffer. When the compositor has completed
|
|
this processing, the client app runs \c surface_frame_done()
|
|
which, provided member variable \c draw_buffer_needs_commit is true, calls
|
|
\c Fl_Wayland_Graphics_Driver::buffer_commit(). This makes the compositor map the
|
|
Wayland buffer in its new, more advanced, state.
|
|
|
|
An example of progressive drawing is given by FLTK's mandelbrot test app.
|
|
When maximized, this app can be seen to progressively fill its window from
|
|
top to bottom by blocks of lines, each block appearing when the compositor is ready to map
|
|
a new buffer. When the compositor is not ready, the app does not block but continues
|
|
computing and drawing in memory but not on display more lines of the desired Mandelbrot
|
|
graph.
|
|
|
|
|
|
\section wayland-buffer-factory Buffer factory
|
|
|
|
Wayland names <em>buffer factory</em> a software procedure that constructs objects of type
|
|
<tt>struct wl_buffer</tt>, that is, Wayland buffers, for use by a client application.
|
|
FLTK creates a \c wl_buffer object each time a non-GL Fl_Window is mapped on a display or resized.
|
|
FLTK triggers the creation of a Wayland buffer as follows:
|
|
<pre>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.</pre>
|
|
Member function \c Fl_Wayland_Graphics_Driver::create_shm_buffer()
|
|
follows this 3-step procedure to create one or more <em>shared memory pools</em> and to
|
|
construct Wayland buffers from them:
|
|
- Libdecor function <tt>libdecor_os_create_anonymous_file(off_t size)</tt> 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 <tt>struct wl_shm_pool</tt>, a shared memory pool,
|
|
which encapsulates this memory. A record of type
|
|
<tt>struct Fl_Wayland_Graphics_Driver::wld_shm_pool_data</tt> 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 <tt>struct wl_list</tt> 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 <tt>struct Fl_Wayland_Graphics_Driver::wld_buffer</tt> 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 <tt>pool_memory + chunk_offset</tt> 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 <tt>struct Fl_Wayland_Graphics_Driver::wld_buffer</tt> 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 <tt>struct Fl_Wayland_Graphics_Driver::wld_buffer</tt> 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 <tt>struct wld_shm_pool_data</tt> 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 <tt>Fl_Wayland_Window_Driver::set_cursor(const Fl_RGB_Image *,…)</tt> 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 <tt>struct wl_shm_pool</tt> 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 <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'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
|
|
<tt>struct wl_list</tt> accessible from member \c outputs of the single
|
|
\c Fl_Wayland_Screen_Driver object.
|
|
|
|
FLTK uses 2 distinct scaling parameters for each display:
|
|
- <tt>int wld_scale;</tt>. 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 <tt>W * n * H * n * 4</tt> 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.
|
|
- <tt>float gui_scale;</tt>. 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
|
|
<tt>Fl_Wayland_Screen_Driver::scale(int n, float f)</tt> 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
|
|
<tt>int(W * gui_scale) * wld_scale x int(H * gui_scale) * wld_scale</tt> pixels
|
|
on the display.
|
|
|
|
When an \c Fl_Window is to be show()'n, \c Fl_Wayland_Window_Driver::makeWindow() creates
|
|
a <tt>struct wl_surface</tt> 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 <tt>struct wl_surface</tt> is mapped
|
|
on a display, member \c surface_enter() of \c surface_listener runs.
|
|
This function adds the display where the surface belongs to <u>the end</u> 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 <u>the first</u> 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.
|
|
|
|
<h3>Fractional scaling</h3>
|
|
The KWin and gnome compositors allow to use <em>fractional scaling</em>
|
|
that can take values between 100% and 400% that are not a multiple of 100%.
|
|
Wayland implements this rendering all <tt>wl_surface</tt>'s as if the scaling had
|
|
the next value above that is a multiple of 100% (e.g., 175% --> 200%), and downsizing them
|
|
to the desired fractional scale value at the compositing stage.
|
|
Seen from FLTK, everything runs with <tt>wld_scale</tt> having an integer value (1, 2, 3 or 4).
|
|
|
|
Recent gnome versions natively support fractional scaling. Older ones require to use these commands
|
|
to make them accept/refuse fractional scaling:
|
|
\code
|
|
gsettings set org.gnome.mutter experimental-features "['scale-monitor-framebuffer']"
|
|
gsettings reset org.gnome.mutter experimental-features
|
|
\endcode
|
|
|
|
|
|
\section wayland-mouse Mouse and trackpad handling
|
|
|
|
FLTK receives information about mouse and pointer events via a 'listener' made up of 5
|
|
pointers to functions which Wayland calls when events listed in table below occur.
|
|
These functions receive from Wayland enough information in their parameters to generate
|
|
corresponding FLTK events, that is, calls to <tt>Fl::handle(int event_type, Fl_Window *)</tt>.
|
|
<table summary="Mouse and pointer handling" border="1">
|
|
<tr><th>listener function</th><th>called by Wayland when</th><th>resulting FLTK events</th></tr>
|
|
<tr><td>\c pointer_enter</td><td>pointer enters a window</td><td>FL_ENTER</td></tr>
|
|
<tr><td>\c pointer_leave</td><td>pointer leaves a window</td><td>FL_LEAVE</td></tr>
|
|
<tr><td>\c pointer_motion</td><td>pointer moves inside a window</td><td>FL_MOVE</td></tr>
|
|
<tr><td>\c pointer_button</td><td>state of mouse buttons changes</td><td>FL_PUSH, FL_RELEASE</td></tr>
|
|
<tr><td>\c pointer_axis</td><td>trackpad is moved vertically or horizontally</td>
|
|
<td>FL_MOUSEWHEEL</td></tr>
|
|
</table>
|
|
|
|
\c pointer_listener is installed by a call to function \c wl_pointer_add_listener()
|
|
made by function \c seat_capabilities() which is itself another 'listener' made up of 2
|
|
function pointers
|
|
\code
|
|
static struct wl_seat_listener seat_listener = {
|
|
seat_capabilities,
|
|
seat_name
|
|
};
|
|
\endcode
|
|
installed by a call to function \c wl_seat_add_listener() made by function
|
|
\c registry_handle_global() when it receives a \c "wl_seat" interface.
|
|
|
|
<h3>Handling middle mouse button clicks on window titlebars</h3>
|
|
The gnome desktop, via its \c gnome-tweaks application, allows to determine what
|
|
happens when a middle mouse button click occurs on a window titlebar. To obey this
|
|
setting, FLTK implements part of the
|
|
<a href=https://wayland.app/protocols/gtk-shell>GTK Shell</a> protocol as follows.
|
|
Mutter, gnome's Wayland compositor, declares its support of the <tt>GTK Shell</tt>
|
|
protocol calling \c registry_handle_global() with its \c interface argument equal to
|
|
\c "gtk_shell1". FLTK initializes then member variable \c seat->gtk_shell of type
|
|
<tt>struct gtk_shell1*</tt>.
|
|
|
|
Member functions of \c pointer_listener mentioned above run for all mouse events
|
|
on all \c wl_surface objects. The table above describes what these functions do for
|
|
mouse events on FLTK-created \c wl_surface objects. But they also run for the
|
|
libdecor-created \c wl_surface objects corresponding to window titlebars.
|
|
Thus, member function \c pointer_enter() runs when the mouse enters a titlebar.
|
|
It calls \c Fl_Wayland_Screen_Driver::event_coords_from_surface() which calls
|
|
\c Fl_Wayland_Window_Driver::surface_to_window() which, as mentioned above, can
|
|
distinguish FLTK-created from non FLTK-created \c wl_surface objects.
|
|
This allows \c pointer_enter() to identify the entered surface as a titlebar
|
|
and to assign static global variable \c gtk_shell_surface
|
|
with the titlebar's \c wl_surface when the mouse enters a titlebar.
|
|
Similarly, member function \c pointer_leave() sets \c gtk_shell_surface to NULL
|
|
when the mouse leaves this titlebar. When there's a click on a titlebar,
|
|
member function \c pointer_button() runs this code
|
|
\code
|
|
if (gtk_shell_surface && state == WL_POINTER_BUTTON_STATE_PRESSED && button == BTN_MIDDLE) {
|
|
struct gtk_surface1 *gtk_surface = gtk_shell1_get_gtk_surface(seat->gtk_shell, gtk_shell_surface);
|
|
gtk_surface1_titlebar_gesture(gtk_surface, serial, seat->wl_seat, GTK_SURFACE1_GESTURE_MIDDLE_CLICK);
|
|
gtk_surface1_release(gtk_surface);
|
|
return;
|
|
}
|
|
\endcode
|
|
which ensures that what \c gnome-tweaks has assigned to middle-click events is executed.
|
|
At this point in time, FLTK obeys what \c libdecor decides for right-click (display the window
|
|
menu) and double-click (maximize the window) events on titlebars which may diverge
|
|
from \c gnome-tweaks settings.
|
|
|
|
\section wayland-cursor Wayland cursors
|
|
|
|
Wayland defines types <tt>struct wl_cursor</tt> and <tt>struct wl_cursor_theme</tt> to hold
|
|
cursor-related data.
|
|
FLTK uses function \c init_cursors() from file \c Fl_Wayland_Screen_Driver.cxx to obtain the
|
|
'cursor theme' name using function \c libdecor_get_cursor_settings() of library \c libdecor.
|
|
Function \c wl_cursor_theme_load() then returns a pointer to an object of type
|
|
<tt>struct wl_cursor_theme</tt> stored in member variable \c cursor_theme of the
|
|
\ref wayland-seat "Fl_Wayland_Screen_Driver::seat" record.
|
|
Function \c init_cursors() is itself called by a 'listener' called \c seat_capabilities()
|
|
installed when function \c registry_handle_global() receives a \c "wl_seat" interface, at program
|
|
startup. It is also called when the value of the Wayland scaling factor changes:
|
|
\c output_done() calls \c try_update_cursor() calls \c init_cursors(). Function \c output_done()
|
|
belongs to a 'listener' installed when function \c registry_handle_global() receives a
|
|
\c "wl_output" interface.
|
|
|
|
Each time \c Fl_Window::cursor(Fl_Cursor) runs, FLTK calls
|
|
\c Fl_Wayland_Window_Driver::set_cursor(Fl_Cursor) which calls \c wl_cursor_theme_get_cursor()
|
|
to set the current cursor shape to one of the standard shapes from the \c Fl_Cursor enumeration.
|
|
This Wayland function selects a cursor shape based on the current \c wl_cursor_theme object
|
|
and a cursor name and returns a pointer to a <tt>struct wl_cursor</tt>.
|
|
Under the gnome desktop, cursor names are the files of directory \c /usr/share/icons/XXXX/cursors/
|
|
where \c XXXX is the 'gnome cursor theme' (default= Adwaita). For example, what FLTK calls
|
|
\c FL_CURSOR_INSERT corresponds to file \c xterm therein. The full correspondance between
|
|
\c Fl_Cursor values and names of files therein is found in function
|
|
\c Fl_Wayland_Window_Driver::set_cursor(Fl_Cursor). FLTK stores in member variable
|
|
\c default_cursor of the \ref wayland-seat "Fl_Wayland_Screen_Driver::seat" record a pointer
|
|
to the currently used \c wl_cursor object,
|
|
and the current \c Fl_Cursor value in member \c standard_cursor_ of the
|
|
\c Fl_Wayland_Window_Driver object.
|
|
|
|
Finally, function do_set_cursor() of file \c Fl_Wayland_Screen_Driver.cxx makes the system pointer use
|
|
the current \c wl_cursor object to draw its shape on screen. That's done with a call to
|
|
\c wl_pointer_set_cursor() and a few other functions.
|
|
|
|
\anchor custom-cursor
|
|
<h3>Custom cursor shapes</h3>
|
|
To support custom cursors, FLTK presently uses a <u>non-public type</u>,
|
|
<tt>struct cursor_image</tt>, defined in file \c Fl_Wayland_Window_Driver.cxx as follows:
|
|
\code
|
|
struct cursor_image {
|
|
struct wl_cursor_image image;
|
|
struct wl_cursor_theme *theme;
|
|
struct wl_buffer *buffer;
|
|
int offset;
|
|
};
|
|
\endcode
|
|
This definition has been copied to the FLTK source code from file
|
|
<a href=https://gitlab.freedesktop.org/wayland/wayland/-/blob/main/cursor/wayland-cursor.c>
|
|
wayland-cursor.c</a> of the Wayland project source code
|
|
because it's not accessible via Wayland header files.
|
|
It shows that a pointer to a \c cursor_image object can also be viewed as a pointer to the
|
|
embedded <tt>struct wl_cursor_image</tt> object, this one being part of the public Wayland API.
|
|
It also shows that a <tt>struct cursor_image</tt> object has an associated
|
|
<tt>struct wl_buffer</tt> object used to contain the cursor's graphics.
|
|
|
|
Function <tt>Fl_Wayland_Window_Driver::set_cursor(const Fl_RGB_Image *rgb, int hotx, int hoty)</tt>
|
|
gives FLTK support of custom cursor shapes. It calls \c Fl_Wayland_Window_Driver::set_cursor_4args()
|
|
that creates a \c cursor_image object, allocates the
|
|
corresponding \c wl_buffer by a call to
|
|
\c Fl_Wayland_Graphics_Driver::create_shm_buffer() via \c custom_offscreen()
|
|
and \c create_wld_buffer() and draws
|
|
the cursor shape into that buffer using the offscreen-drawing method of FLTK.
|
|
|
|
The public type <tt>struct wl_cursor</tt> is essentially an array of \c wl_cursor_image objects
|
|
and a name:
|
|
\code
|
|
struct wl_cursor {
|
|
unsigned int image_count;
|
|
struct wl_cursor_image **images;
|
|
char *name;
|
|
};
|
|
\endcode
|
|
Function \c Fl_Wayland_Window_Driver::set_cursor_4args() also creates a <tt>struct wl_cursor</tt>
|
|
object containing a single \c wl_cursor_image, which is in fact the \c cursor_image.
|
|
Finally, a <tt>struct Fl_Wayland_Window_Driver::custom_cursor</tt> (see \ref wld_window) is allocated and used to memorize
|
|
the <tt>struct wl_cursor</tt> and the cursor's image and hotspot.
|
|
A pointer to this <tt>struct Fl_Wayland_Window_Driver::custom_cursor</tt> object is stored in member \c custom_cursor of the
|
|
window's \ref wld_window.
|
|
|
|
Function \c Fl_Wayland_Window_Driver::set_cursor_4args() is also called when a window with a custom
|
|
cursor is moved between distinct displays or when a display is rescaled to adapt the cursor size
|
|
to the new display's scale factor.
|
|
|
|
Static member function \c Fl_Wayland_Window_Driver::delete_cursor() is used to delete any custom cursor
|
|
shape. This occurs when a window associated to a custom cursor is un-mapped and when such a window
|
|
gets associated to a standard cursor or to a new custom cursor.
|
|
|
|
|
|
|
|
|
|
\section wayland-text Keyboard support
|
|
|
|
The "Mouse handling" section above mentioned function \c seat_capabilities() that Wayland calls when
|
|
the app discovers its "seat". Presence of flag \c WL_SEAT_CAPABILITY_KEYBOARD in argument
|
|
\c capabilities of this function indicates that a keyboard is available. In that case, a call
|
|
to \c wl_seat_get_keyboard() returns a pointer stored in member \c wl_keyboard of the
|
|
\ref wayland-seat "Fl_Wayland_Screen_Driver::seat" object,
|
|
and a call to \c wl_keyboard_add_listener() installs a 6-member listener of type
|
|
<tt>struct wl_keyboard_listener</tt>. These 6 FLTK-defined, callback functions are used as follows.
|
|
|
|
1) Function \c wl_keyboard_keymap() runs when the app starts and also if the keyboard layout
|
|
is changed during run-time. It allows initialization of access to this keyboard.
|
|
Noticeably, member \c xkb_state of type <tt>struct xkb_state*</tt> of the current
|
|
\ref wayland-seat "Fl_Wayland_Screen_Driver::seat" record is adequately initialized.
|
|
|
|
2-3) Functions \c wl_keyboard_enter() and \c wl_keyboard_leave(), called when focus enters and
|
|
leaves a surface, send \c FL_FOCUS and \c FL_UNFOCUS events to the \c Fl_Window object corresponding
|
|
to this surface.
|
|
|
|
4) Function \c wl_keyboard_key() runs each time a keyboard key is pressed or released. Its argument \c key,
|
|
to which 8 must be added, provides the keycode via function \c xkb_state_key_get_one_sym() and then the
|
|
corresponding text via function \c xkb_state_key_get_utf8() which is put in \c Fl::e_text.
|
|
Then, a few calls to functions whose name begin with \c xkb_compose_ are necessary to support
|
|
dead and compose keys. Finally a call to \c Fl::handle() sends an \c FL_KEYDOWN or \c FL_KEYUP event to
|
|
the appropriate \c Fl_Window. Also, function \c wl_keyboard_key() uses global variable
|
|
<tt>std::vector<int> key_vector</tt> to record all currently pressed keys. This is the base of the
|
|
implementation of \c Fl_Wayland_Screen_Driver::event_key(int).
|
|
|
|
5) Function \c wl_keyboard_modifiers() runs when a modifier key (e.g., shift, control) is pressed or
|
|
released. Calls to functions \c xkb_state_update_mask() and \c xkb_state_mod_name_is_active() allow FLTK
|
|
to set \c Fl::e_state adequately.
|
|
|
|
6) Function \c wl_keyboard_repeat_info() does not run, for now, because this would require version 4 of
|
|
the <tt>wl_keyboard</tt> object which has earlier versions in all tested Wayland compositors.
|
|
|
|
|
|
\section wayland-text-input Support of text input methods
|
|
|
|
When the connected Wayland compositor supports text input methods, function
|
|
\c registry_handle_global() gets called with its \c interface argument equal to
|
|
\c zwp_text_input_manager_v3_interface.name. The following call to \c wl_registry_bind() returns a
|
|
pointer to type <tt>struct zwp_text_input_manager_v3</tt> that is stored as member
|
|
\c text_input_base of the \c Fl_Wayland_Screen_Driver object.
|
|
|
|
Later, when function \c seat_capabilities() runs, \c text_input_base is found not NULL, which triggers
|
|
a call to function \c zwp_text_input_manager_v3_get_text_input() returning a value of type
|
|
<tt>struct zwp_text_input_v3 *</tt> and stored as member \c text_input of the
|
|
\ref wayland-seat "Fl_Wayland_Screen_Driver::seat" object.
|
|
Next, a call to \c zwp_text_input_v3_add_listener() associates this \c text_input with a 6-member
|
|
listener of type <tt>struct zwp_text_input_v3_listener</tt>. These 6 FLTK-defined, callback functions
|
|
are used as follows.
|
|
|
|
1-2) Functions \c text_input_enter() and \c text_input_leave() run when text input enters or leaves a
|
|
surface.
|
|
|
|
3-4) Functions \c text_input_preedit_string() and \c text_input_commit_string() are called when the
|
|
text input method prepares the client app to later insert 'marked' text or regular text, respectively.
|
|
Complex text input often begins by inserting temporary text which is said to be 'marked' before
|
|
replacing it with the text that will stay in the document. FLTK underlines or selects marked text
|
|
to distinguish it from regular text.
|
|
|
|
5) Function \c text_input_done() runs when it's time to send either regular or marked text
|
|
to the client app. This is done by function \c send_text_to_fltk() which uses static variables
|
|
\c current_pre_edit, \c pending_pre_edit and \c pending_commit to determine the sent text.
|
|
|
|
6) Function \c text_input_delete_surrounding_text() has no effect at present, without this preventing
|
|
input methods that have been tested with FLTK from working satisfactorily.
|
|
|
|
It's necessary to inform the running text input method of the current location of the insertion
|
|
point in the active surface. This information allows the input method to map its auxiliary window
|
|
close to the insertion point. The flow of information on this topic is as follows:
|
|
- The two FLTK widgets supporting text input, Fl_Input_ and Fl_Text_Display, transmit to FLTK the window
|
|
coordinates of the bottom of the current insertion point and the line height each time they change
|
|
calling function \c fl_set_spot().
|
|
- fl_set_spot() calls the platform override of virtual member function \c Fl_Screen_Driver::set_spot().
|
|
Under Wayland, this just calls
|
|
\c Fl_Wayland_Screen_Driver::insertion_point_location(int x, int y, int height) which calls
|
|
\c zwp_text_input_v3_set_cursor_rectangle() to inform the text input method about the surface
|
|
position and size of the insertion point and also memorizes this information in static member
|
|
variables of class \c Fl_Wayland_Screen_Driver.
|
|
- Callback function \c text_input_enter() calls
|
|
\c Fl_Wayland_Screen_Driver::insertion_point_location(int *x, int *y, int *height) which gives it
|
|
the stored position information, and then calls \c zwp_text_input_v3_set_cursor_rectangle() to inform the
|
|
text input method about the position of the insertion point.
|
|
|
|
|
|
\section wayland-libdecor Interface with libdecor
|
|
|
|
FLTK uses a library called
|
|
<a href= https://gitlab.freedesktop.org/libdecor/libdecor/-/blob/master/README.md>
|
|
libdecor</a> to determine whether the Wayland compositor uses CSD or SSD mode,
|
|
and also to draw window titlebars when in CSD mode (see \ref bundled-libdecor).
|
|
\c Libdecor is conceived to be present in a shared library linked to the Wayland
|
|
client application which itself, and if the running Wayland compositor uses CSD mode,
|
|
loads another shared library intended to draw titlebars in a way that best matches the
|
|
Desktop. As of late 2023, libdecor contains two titlebar-drawing plugins:
|
|
- \c libdecor-gtk intended for the Gnome desktop;
|
|
- \c libdecor-cairo for other situations.
|
|
|
|
On recent Linux distributions, FLTK uses the system \c libdecor-0.so shared library
|
|
available via packages \c libdecor-0-dev and \c libdecor-0-plugin-1-gtk.
|
|
On earlier Linux versions, or if CMake option \c FLTK_USE_SYSTEM_LIBDECOR is set
|
|
to OFF, FLTK bundles the most recent source code of \c libdecor and its plugins.
|
|
The libdecor code bundled inside FLTK is compiled and put in libfltk.
|
|
FLTK uses \c libdecor-gtk when software package \c libgtk-3-dev
|
|
is present in the build system, and \c libdecor-cairo otherwise.
|
|
FLTK prefixes all symbols of its bundled libdecor with "fl_". This allows an FLTK client app
|
|
to link to other libraries which may use the system version of libdecor.
|
|
|
|
\c Libdecor uses the Wayland protocol
|
|
<a href=https://wayland.app/protocols/xdg-decoration-unstable-v1>XDG decoration</a>
|
|
to request being decorated by a supporting compositor.
|
|
If the running compositor supports SSD, \c libdecor doesn't draw window titlebars because
|
|
the compositor does it. That is what happens with the \c KWin and \c Sway compositors.
|
|
However, if environment variable \c LIBDECOR_FORCE_CSD is defined to value \c 1 when an
|
|
FLTK app runs, \c libdecor instructs an SSD-able compositor to refrain from decorating its
|
|
windows and decorates windows itself.
|
|
|
|
Whatever the value of \c FLTK_USE_SYSTEM_LIBDECOR, FLTK and \c libdecor use environment variable
|
|
\c LIBDECOR_PLUGIN_DIR as follows: if this variable is defined and points to the name of a directory,
|
|
this directory is searched for a potential \c libdecor plugin in the form of a shared library;
|
|
if one is found, FLTK and \c libdecor load it and use it.
|
|
|
|
The \c libdecor source code bundled in FLTK is identical to that of the \c libdecor repository.
|
|
Nevertheless, FLTK uses this code with some minor changes. For example, when \c FLTK_USE_SYSTEM_LIBDECOR
|
|
is 0, FLTK needs to modify function \c libdecor_new() charged of loading the plugin, to make it use
|
|
the plugin code that is included in libfltk if none is found as a dynamic library. This is done as
|
|
follows in file \c libdecor/build/fl_libdecor.c:
|
|
\code
|
|
#define libdecor_new libdecor_new_orig
|
|
#include "../src/libdecor.c"
|
|
#undef libdecor_new
|
|
|
|
void libdecor_new() { // FLTK rewrite of this function
|
|
……
|
|
}
|
|
\endcode
|
|
FLTK compiles file \c fl_libdecor.c which includes \c libdecor.c to the effect that all of
|
|
the \c libdecor code becomes part of libfltk except that function \c libdecor_new() is substituted by
|
|
its FLTK rewrite, without file \c libdecor.c being modified at all. This trick is also used to modify
|
|
function \c libdecor_frame_set_minimized() to bypass a bug in the Weston compositor before version 10.
|
|
Similarly, FLTK compiles file \c fl_libdecor-plugins.c which includes either \c libdecor-gtk.c or
|
|
\c libdecor-cairo.c to the effect that the desired plugin becomes part of libfltk.
|
|
|
|
To support function \c Fl_Widget_Surface::draw_decorated_window() that draws a mapped window and its
|
|
titlebar, FLTK needs to perform two operations: 1) identify what plugin is operating, and 2) call
|
|
a function that is specific of that plugin and that returns the pixels of the drawn titlebar.
|
|
|
|
FLTK performs operation 1) above using its function \c get_libdecor_plugin_description() of file
|
|
\c fl_libdecor-plugins.c that returns a human readable string describing the running plugin.
|
|
Each plugin puts its own string in member \c description of a record of type
|
|
<tt>struct libdecor_plugin_description</tt>. Although this type is public in header file
|
|
\c libdecor-plugin.h, accessing the symbol defined by the plugin to store a pointer to a value of this
|
|
type is complicated for a reason and solved by a method detailed in a comment before function
|
|
\c get_libdecor_plugin_description().
|
|
|
|
Operation 2) above is done by FLTK-defined function \c fl_libdecor_titlebar_buffer() from file
|
|
\c fl_libdecor-plugins.c. This function calls \c get_libdecor_plugin_description() seen above
|
|
to get the running plugin's descriptive string. That is <tt>"GTK3 plugin"</tt> with \c libdecor-gtk.
|
|
FLTK function \c gtk_titlebar_buffer() is then called, and returns a pointer to the start of a byte
|
|
buffer containing the titlebar graphics.
|
|
That is, again, not possible with the public \c libdecor API. Therefore,
|
|
FLTK copies to \c fl_libdecor-plugins.c the definitions of several types
|
|
given in \c libdecor-gtk.c or \c libdecor-cairo.c such as type <tt>struct border_component</tt>.
|
|
|
|
|
|
\section wayland-clipboard Copy/Paste/Drag-n-Drop
|
|
|
|
FLTK follows the procedure that is very well described in item "Wayland clipboard and drag &
|
|
drop" of the \ref wayland-doc. All corresponding source code is in file
|
|
\c src/drivers/Wayland/fl_wayland_clipboard_dnd.cxx.
|
|
|
|
This part of the \ref wayland-seat "Fl_Wayland_Screen_Driver::seat" record stores pointers to
|
|
Wayland objects used for clipboard and D-n-D operations:
|
|
\code
|
|
struct wl_data_device_manager *data_device_manager;
|
|
struct wl_data_device *data_device;
|
|
struct wl_data_source *data_source;
|
|
\endcode
|
|
|
|
FLTK can copy or paste plain UTF-8 text or image data to/from the clipboard. Images are copied to the
|
|
clipboard as \c image/bmp mime type. Images in \c image/bmp or \c image/png mime types from the
|
|
clipboard can be pasted to FLTK apps.
|
|
|
|
Files dropped are received one pathname per line, with no \c '\\n' after the last pathname.
|
|
|
|
|
|
\section wayland-egl EGL as support for OpenGL
|
|
|
|
Wayland uses <a href=https://www.khronos.org/api/egl>EGL™</a> to interface OpenGL with the underlying
|
|
native platform window system. OpenGL-using FLTK apps are therefore linked to \c libwayland-egl.so and
|
|
\c libEGL.so in addition to \c libGL.so and \c libGLU.so.
|
|
|
|
EGL completely hides the \c wl_buffer objects it uses to draw to GL windows.
|
|
The \ref wld_buffer structure and the 'buffer factory' described previously are not used for
|
|
\c Fl_Gl_Window's : the \c buffer member of an \c Fl_Gl_Window's \ref wld_window structure is
|
|
always NULL.
|
|
|
|
EGL is initialized calling member function \c Fl_Wayland_Gl_Window_Driver::init()
|
|
once, the first time the \c Fl_Wayland_Gl_Window_Driver c'tor runs. That is done
|
|
with calls to <tt>eglGetDisplay(), eglInitialize()</tt>, and \c eglBindAPI().
|
|
|
|
Member function \c Fl_Wayland_Gl_Window_Driver::find() calls \c eglChooseConfig()
|
|
to filter the set of GL configurations that match the \c Fl_Gl_Window's mode(),
|
|
and puts in the returned \c Fl_Gl_Choice object the first matching configuration.
|
|
The filtering gets done with bits \c EGL_WINDOW_BIT, to support the creation of window
|
|
surfaces, and \c EGL_OPENGL_BIT, to support the creation of OpenGL contexts.
|
|
|
|
EGL needs 2 more objects created for each \c Fl_Gl_Window. They have types
|
|
<tt>struct wl_egl_window</tt> and \c EGLSurface, and are created by member
|
|
function \c Fl_Wayland_Gl_Window_Driver::make_current_before() which runs at the
|
|
beginning of \c Fl_Gl_Window::make_current().
|
|
The first argument of the call to \c wl_egl_window_create() therein has type
|
|
<tt>struct wl_surface *</tt> and is what connects EGL with the targeted Wayland window.
|
|
|
|
EGL creates with \c eglCreateContext() an object of type \c EGLContext via member
|
|
function \c Fl_Wayland_Gl_Window_Driver::create_gl_context() called by
|
|
\c Fl_Gl_Window::make_current(). Types \c EGLContext and \c GLContext are 2 names
|
|
for the same object. The call to \c eglCreateContext() is made asking for a GL
|
|
context of version at least 2. This does not prevent from obtaining contexts of higher
|
|
versions, namely above 3.2, which are compatible with version 2 (the so-called
|
|
compatibility profile) under all tested Linux systems.
|
|
|
|
FLTK function \c Fl_Gl_Window::make_current() calls overridden function
|
|
\c Fl_Wayland_Gl_Window_Driver::set_gl_context() which calls EGL function
|
|
\c eglMakeCurrent() when the cached context changes.
|
|
|
|
FLTK calls function \c Fl_Wayland_Gl_Window_Driver::swap_buffers() each time it wants a
|
|
GL context to be sent to the display. This function contains some pure GL code to
|
|
emulate an overlay buffer to support Fl_Gl_Window objects overriding their
|
|
\c draw_overlay() member function. Then, it calls function \c eglSwapBuffers().
|
|
|
|
The overridden \c Fl_Wayland_Gl_Window_Driver::resize() function is implemented with
|
|
calls to \c wl_egl_window_get_attached_size() and \c wl_egl_window_resize().
|
|
|
|
Class \c Fl_Wayland_Gl_Plugin exists to allow \c libfltk to call functions from
|
|
\c libfltk_gl, \c libwayland-egl.so or \c libEGL.so and without having \c libfltk
|
|
force linking any FLTK app with these GL-related libraries.
|
|
For example, \c Fl_Wayland_Window_Driver::flush() needs to call
|
|
\c Fl_Gl_Window::valid(0).
|
|
|
|
<h3>Throttling GL window redraws</h3>
|
|
Although no documentation covering this subject was found, the EGL library internally
|
|
uses \c wl_callback objects to throttle GL window redraws, and FLTK needs not interfere with
|
|
these operations. Nevertheless FLTK creates and uses \c wl_callback objects for GL windows in 2 cases:
|
|
- when a decorated GL window is being interactively resized.
|
|
Function \c Fl_Wayland_Gl_Window_Driver::resize() creates a \c wl_callback object,
|
|
assigns it to xid->frame_cb and calls \c wl_callback_add_listener() before calling
|
|
\c wl_egl_window_resize(). This allows the mechanism described above that prevents surfaces from
|
|
being resized too frequently to operate with decorated \c Fl_GL_Window's too.
|
|
- when a GL subwindow is being refreshed by \c Fl_Wayland_Gl_Window_Driver::swap_buffers().
|
|
FLTK checks that \c xid->frame_cb is NULL and if so creates a \c wl_callback calling
|
|
\c wl_surface_frame() before calling \c eglSwapBuffers(). This is useful if the GL subwindow
|
|
becomes entirely out from the screen area. In that case, the Mutter compositor stops signaling
|
|
that the subwindow is ready for new commits which FLTK detects because \c xid->frame_cb remains
|
|
non-NULL. If the subwindow eventually re-appears partially on-screen, \c xid->frame_cb
|
|
becomes NULL and FLTK calls \c eglSwapBuffers() to redraw the GL scene.
|
|
|
|
|
|
\section wayland-type FLTK-defined, Wayland-specific types
|
|
|
|
\anchor wld_window
|
|
<h3>struct wld_window</h3>
|
|
Defined in \c Fl_Wayland_Window_Driver.H. One such record is created
|
|
for each shown()'n Fl_Window by \c Fl_Wayland_Window_Driver::makeWindow().
|
|
Function \c fl_wl_xid(Fl_Window*) returns a pointer to the <tt>struct wld_window</tt> of its argument.
|
|
\code
|
|
struct wld_window {
|
|
Fl_Window *fl_win;
|
|
struct wl_list outputs; // linked list of displays where part or whole of window maps
|
|
struct wl_surface *wl_surface; // the window's surface
|
|
struct wl_callback *frame_cb; // non-NULL until Wayland can process new surface commit
|
|
struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer; // see \ref wld_buffer
|
|
struct xdg_surface *xdg_surface;
|
|
enum Fl_Wayland_Window_Driver::kind kind; // DECORATED or POPUP or SUBWINDOW or UNFRAMED
|
|
union {
|
|
struct libdecor_frame *frame; // for DECORATED windows
|
|
struct wl_subsurface *subsurface; // for SUBWINDOW windows
|
|
struct xdg_popup *xdg_popup; // for POPUP windows
|
|
struct xdg_toplevel *xdg_toplevel; // for UNFRAMED windows
|
|
};
|
|
struct Fl_Wayland_Window_Driver::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; // used when negotiating window size with the compositor
|
|
int configured_height;
|
|
int floating_width; // helps restoring size after un-maximizing
|
|
int floating_height;
|
|
int state; // indicates whether window is fullscreen, maximized. Used otherwise for POPUPs
|
|
bool covered; // specially for Mutter and issue #878
|
|
}
|
|
\endcode
|
|
|
|
\anchor draw_buffer
|
|
<h3>struct Fl_Wayland_Graphics_Driver::draw_buffer</h3>
|
|
Defined in file \c Fl_Wayland_Graphics_Driver.H.
|
|
One such record is created when an Fl_Image_Surface object is created.
|
|
One such record is also embedded inside each
|
|
<tt>struct Fl_Wayland_Graphics_Driver::wld_buffer</tt> record (see \ref wld_buffer).
|
|
|
|
\code
|
|
struct Fl_Wayland_Graphics_Driver::draw_buffer {
|
|
unsigned char *buffer; // address of the beginning of the Cairo image surface's byte array
|
|
cairo_t *cairo_; // used when drawing to the Cairo image surface
|
|
size_t data_size; // of buffer and wl_buffer, in bytes
|
|
int stride; // bytes per line
|
|
int width; // in pixels
|
|
};
|
|
\endcode
|
|
FLTK gives offscreen buffers the platform-dependent type \c Fl_Offscreen which is
|
|
in fact member \c cairo_ of <tt>struct Fl_Wayland_Graphics_Driver::draw_buffer</tt>.
|
|
Thus, a variable with type \c Fl_Offscreen needs be cast to type \c cairo_t*.
|
|
Static member function <tt>struct draw_buffer *offscreen_buffer(Fl_Offscreen)</tt>
|
|
of class \c Fl_Wayland_Graphics_Driver returns the \c draw_buffer record corresponding
|
|
to an \c Fl_Offscreen value.
|
|
|
|
\anchor wld_buffer
|
|
<h3>struct Fl_Wayland_Graphics_Driver::wld_buffer</h3>
|
|
Defined in file \c Fl_Wayland_Graphics_Driver.H.
|
|
One such record is created by \c Fl_Wayland_Graphics_Driver::create_wld_buffer() when
|
|
an Fl_Window is show()'n or resized, when a custom cursor shape is created, or when
|
|
text is dragged.
|
|
\code
|
|
struct Fl_Wayland_Graphics_Driver::wld_buffer {
|
|
struct draw_buffer draw_buffer; // see draw_buffer
|
|
struct wl_list link; // links all buffers from the same wl_shm_pool
|
|
struct wl_buffer *wl_buffer; // the Wayland buffer
|
|
void *data; // address of the beginning of the Wayland buffer's byte array
|
|
struct wl_shm_pool *shm_pool; // pter to wl_shm_pool from which this wl_buffer comes
|
|
bool draw_buffer_needs_commit; // true when draw_buffer has been modified but not yet committed
|
|
bool in_use; // true while being committed
|
|
bool released; // true after buffer_release() was called
|
|
};
|
|
\endcode
|
|
|
|
\anchor wayland-output
|
|
<h3>struct Fl_Wayland_Screen_Driver::output</h3>
|
|
Defined in Fl_Wayland_Screen_Driver.H. One such record is created for each display of the
|
|
system by function \c registry_handle_global() when it receives a
|
|
\c "wl_output" interface. These records are kept in a linked list of them all, and
|
|
an identifier of this linked list is stored in member \c outputs of the unique
|
|
\c Fl_Wayland_Screen_Driver object FLTK uses. Thus,
|
|
\code
|
|
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
|
|
struct wl_list list_of_all_displays = scr_driver->outputs;
|
|
\endcode
|
|
gives access, the Wayland way, to the linked list of displays in the system.
|
|
\code
|
|
struct Fl_Wayland_Screen_Driver::output { // one record for each display
|
|
uint32_t id; // an identifier of the display
|
|
int x, y; // logical position of the top-left of display
|
|
int pixel_width; // in pixels
|
|
int pixel_height; // in pixels
|
|
int width; // in pixels, account for fractional scaling
|
|
int height; // in pixels, account for fractional scaling
|
|
float dpi; // at this point, always 96.
|
|
struct wl_output *wl_output; // the Wayland object for this display
|
|
int wld_scale; // Wayland scale factor
|
|
float gui_scale; // FLTK scale factor
|
|
bool done; // true means record members have been initialized
|
|
struct wl_list link; // links these records together
|
|
};
|
|
\endcode
|
|
It's possible to get the FLTK-defined record associated to a display from the
|
|
Wayland-associated object for the same display, say <tt>struct wl_output *wl_output</tt>,
|
|
by this call:
|
|
<tt>(struct Fl_Wayland_Screen_Driver::output *)wl_output_get_user_data(wl_output)</tt>.
|
|
|
|
\anchor wayland-seat
|
|
<h3>struct Fl_Wayland_Screen_Driver::seat</h3>
|
|
Defined in file \c Fl_Wayland_Screen_Driver.H. One record is created by
|
|
function \c registry_handle_global() when it receives a \c "wl_seat" or
|
|
\c wl_data_device_manager_interface.name interface. A pointer to this struct is stored in member
|
|
\c seat of the client's unique \c Fl_Wayland_Screen_Driver object.
|
|
\code
|
|
struct Fl_Wayland_Screen_Driver::seat {
|
|
struct wl_seat *wl_seat;
|
|
struct wl_pointer *wl_pointer;
|
|
struct wl_keyboard *wl_keyboard;
|
|
uint32_t keyboard_enter_serial;
|
|
struct wl_surface *keyboard_surface;
|
|
struct wl_list pointer_outputs;
|
|
struct wl_cursor_theme *cursor_theme;
|
|
struct wl_cursor *default_cursor;
|
|
struct wl_surface *cursor_surface;
|
|
struct wl_surface *pointer_focus;
|
|
int pointer_scale;
|
|
uint32_t serial;
|
|
uint32_t pointer_enter_serial;
|
|
struct wl_data_device_manager *data_device_manager;
|
|
struct wl_data_device *data_device;
|
|
struct wl_data_source *data_source;
|
|
struct xkb_state *xkb_state;
|
|
struct xkb_context *xkb_context;
|
|
struct xkb_keymap *xkb_keymap;
|
|
struct xkb_compose_state *xkb_compose_state;
|
|
char *name;
|
|
struct zwp_text_input_v3 *text_input;
|
|
struct gtk_shell1 *gtk_shell;
|
|
};
|
|
\endcode
|
|
|
|
|
|
\section wayland-doc Documentation resources
|
|
|
|
|
|
<table summary="Wayland Documentation" border="1">
|
|
<tr>
|
|
<td>
|
|
<a href=https://wayland-book.com/>The Wayland book</a>
|
|
</td>
|
|
<td>Extensive introduction to Wayland programming written by the author of the <i>sway</i>
|
|
compositor, unfortunately unachieved.
|
|
<td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<a href=https://wayland.app/protocols/>Wayland Explorer</a>
|
|
</td>
|
|
<td>Documentation of all Wayland protocols, both stable and unstable.
|
|
<td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<a href=https://wayland.freedesktop.org/docs/html/apa.html>Wayland Protocol Specification</a>
|
|
</td>
|
|
<td>Documentation for all functions of the Wayland core protocol.
|
|
<td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<a href=https://emersion.fr/blog/2020/wayland-clipboard-drag-and-drop/>Wayland clipboard and drag & drop</a>
|
|
</td>
|
|
<td>Detailed explanation of how clipboard and drag-and-drop work under Wayland.
|
|
<td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<a href=https://dcz_self.gitlab.io/posts/input_method/>Wayland and input methods</a>
|
|
</td>
|
|
<td>Blog article introducing to the issue of text input methods under Wayland.
|
|
<td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td>
|
|
<a href=https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/39>Input Method Hub</a>
|
|
</td>
|
|
<td>Entry page for input method support giving newcomers a first understanding of what input
|
|
methods are and how they are implemented in Wayland.
|
|
<td>
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
|
|
*/
|
|
|