984 lines
55 KiB
Plaintext
984 lines
55 KiB
Plaintext
/**
|
|
|
|
\page FLTK-devel Development of the FLTK library
|
|
|
|
- \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 socket between a client application and
|
|
another process called the Wayland compositor which creates, moves, resizes and draws windows
|
|
on the display. Diverse Wayland compositors exist. They can follow rather diverse logics.
|
|
For example, FreeBSD offers Sway which is a tiling compositor where the display is always
|
|
entirely filled with whatever windows are mapped at any given time. 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 bundles 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. 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 display is not done by the core protocol but by the
|
|
<tt>xdg shell</tt> protocol which is stable. Unstable protocols are named beginning with
|
|
letter 'z'. For example, the protocol FLTK uses to support CJK input methods is called
|
|
\c zwp_text_input_v3 and is, unfortunately, unstable.
|
|
|
|
Wayland makes intensive use of the 'listener' 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, and transmits relevant information to the client
|
|
app as parameters of these calls. Each listener is first associated to its corresponding
|
|
Wayland object by a call to a specific Wayland function of the form \c wl_XXX_add_listener().
|
|
|
|
Wayland differs noticeably 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 an already mapped window.
|
|
Subwindows can be positioned as usual relatively to their
|
|
parent window. FLTK uses that for the small, yellow windows that display
|
|
the new scale factor value when it's changed: these are created as short-lived subwindows
|
|
centered above \c Fl::first_window().
|
|
Wayland allows to create popup windows positioned relatively to a previously mapped other
|
|
window, with the sole restriction that any popup must intersect or at least touch that other
|
|
window.
|
|
This allows FLTK to create menus and tooltips, but it seriously complicates the algorithm
|
|
to pilot menus, because the previous algorithm conceived for other platforms assumes
|
|
the position of a window in the display to be known to the client app, which is wrong
|
|
under Wayland.
|
|
|
|
Wayland uses a trick of its own to handle lists of linked records. It defines the opaque 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. Records put in these
|
|
lists must 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 of 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 targetted 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 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, a member of the linked list of all displays in the system …
|
|
}
|
|
\endcode
|
|
|
|
Overall, and ignoring for now OpenGL usage, FLTK interacts with Wayland in 3 ways :
|
|
- Calling C functions of the \c libwayland-client.so,
|
|
\c libwayland-cursor.so and \c libxkbcommon.so shared libraries and of the bundled libdecor library.
|
|
The names of these functions begin with \c wl_, \c xkb_ or \c libdecor_.
|
|
- Being called by these libraries via the 'listener' mechanism.
|
|
- Listening, after a call to \c Fl::add_fd(), to data sent by the compositor to the client via
|
|
the socket.
|
|
|
|
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_ and
|
|
\c zwp_text_input_v3.
|
|
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 contain all the Wayland-specific code of the FLTK library.
|
|
This code is located at \c src/drivers/Wayland/ in the FLTK source tree.
|
|
Furthermore, class \c Fl_Unix_System_Driver is used by both the Wayland and the X11 FLTK platforms,
|
|
so that a specially important element of the FLTK library, the event loop, is nearly completely
|
|
identical in X11 and in Wayland.
|
|
|
|
The public C API to Wayland, xkb 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>
|
|
#include "../../../libdecor/src/libdecor.h"
|
|
#include "../../../libdecor/src/libdecor-plugin.h"
|
|
\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 <tt>xdg shell</tt>
|
|
protocol, these commands are run at build time to generate a .c file that will be compiled into libfltk
|
|
and a header file that FLTK code will include:
|
|
\code
|
|
set(PROTOCOLS /usr/share/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 <tt>xdg decoration unstable v1</tt> and
|
|
<tt>text input unstable v3</tt>.
|
|
|
|
|
|
\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 and all X11-specific code. This creates the constraint that Wayland and X11 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 dedicated 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.
|
|
|
|
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 -DOPTION_WAYLAND_ONLY=on</tt> or with
|
|
<tt>configure --disable-x11</tt>.
|
|
|
|
The rest of this chapter describes what happens when the Wayland leg has been chosen.
|
|
|
|
|
|
\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.
|
|
The name of the Wayland socket 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.
|
|
|
|
What socket is selected determines what compositor will be used by the client application.
|
|
|
|
Function \c Fl_Wayland_Screen_Driver::open_display_platform() establishes the connection to the
|
|
Wayland socket identified above calling \c wl_display_connect(NULL) which 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.
|
|
|
|
Then, function \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.
|
|
The prototype of this function 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. It's necessary to call each time function
|
|
\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 "wl_compositor", \c the value returned by wl_registry_bind() is
|
|
stored as member \c wl_compositor of the \c Fl_Wayland_Screen_Driver object.
|
|
\c registry_handle_global() also identifies whether the Mutter, Weston, or KDE compositor is connected
|
|
and stores this information in static member variable \c Fl_Wayland_Screen_Driver::compositor.
|
|
|
|
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.
|
|
The \c wl_display_dispatch() call is repeated 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 event loop of the Wayland backend
|
|
is nearly exactly the
|
|
same as that used by the X11 backend. The Wayland backend differs only in the callback function
|
|
called to handle data read from the Wayland connection socket, which is Wayland-specific.
|
|
|
|
\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". Buffers allow the client app to
|
|
draw to surfaces (see below). FLTK makes no use of local coordinate systems. FLTK creates a surface
|
|
with function \c wl_compositor_create_surface() each time an Fl_Window is show()'n.
|
|
Static member function <tt>Fl_Wayland_Screen_Driver::surface_to_window(struct wl_surface *)</tt>
|
|
gives the \c Fl_Window* corresponding to the surface given in argument.
|
|
Function \c wl_surface_add_listener() associates the surface with a listener which allows to
|
|
associate each surface with the display where it is mapped. FLTK recognizes 4 distinct
|
|
kinds of surfaces named DECORATED, UNFRAMED, POPUP and SUBWINDOW.
|
|
DECORATED are toplevel windows with a titlebar. UNFRAMED have no titlebar. POPUP correspond to menus
|
|
and tooltips, SUBWINDOW 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. 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; // used when kind == DECORATED
|
|
struct wl_subsurface *subsurface; // used when kind == SUBWINDOW
|
|
struct xdg_popup *xdg_popup; // used when kind == POPUP
|
|
struct xdg_toplevel *xdg_toplevel; // used when kind == UNFRAMED
|
|
};
|
|
\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(). Wayland calls it
|
|
twice when mapping a DECORATED surface. 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() and \c xdg_toplevel_configure().
|
|
They transmit effective window size information to FLTK. Also, these 'configure' functions 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.
|
|
|
|
\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 PostScript, or when the classic X11 platform uses Pango and draws PostScript.
|
|
This happens when classes \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 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 member functions
|
|
\c create_shm_buffer() and \c cairo_init() of \c Fl_Wayland_Graphics_Driver to create
|
|
- a Wayland buffer;
|
|
- a Cairo image surface.
|
|
|
|
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 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.
|
|
|
|
Section "Buffer factories" below details how FLTK creates \c wl_buffer objects.
|
|
|
|
FLTK associates to each surface a <tt>struct fl_wld_buffer</tt> (see \ref fl_wld_buffer) containing
|
|
a pointer to the byte array of the Cairo image surface (member \c draw_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 data_size), and other information. A pointer to this
|
|
<tt>struct fl_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 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. \c Fl_Wayland_Window_Driver::flush() also calls function
|
|
\c wl_surface_damage_buffer() with that information to inform the compositor of what parts
|
|
of the surface need its attention.
|
|
|
|
An important detail here is that FLTK uses Wayland's synchronization
|
|
mechanism to make sure the surface's \c wl_buffer is not changed until the surface is fully
|
|
mapped on the display. This 3-step mechanism works as follows:
|
|
- Fl_Wayland_Graphics_Driver::buffer_commit() first calls function \c wl_surface_frame() to
|
|
obtain a <tt>struct wl_callback</tt> object and stores it as member \c cb of the surface's
|
|
\ref fl_wld_buffer.
|
|
- Then it calls \c wl_callback_add_listener() to associate this object to the FLTK-defined,
|
|
callback function \c surface_frame_done() that Wayland calls when it's ready for another
|
|
mapping operation.
|
|
- Finally \c surface_frame_done() destroys the \c wl_callback object by function
|
|
\c wl_callback_destroy() and sets member \c cb to NULL.
|
|
|
|
This procedure ensures that FLTK never changes the surface's Wayland buffer
|
|
while it's being used by the compositor because \c Fl_Wayland_Window_Driver::flush()
|
|
checks that \c cb is NULL before calling \c Fl_Wayland_Graphics_Driver::buffer_commit().
|
|
If it's not NULL, FLTK calls function \c wl_callback_destroy() which instructs the compositor
|
|
to abort the mapping operation and to get ready for processing of a new byte buffer.
|
|
|
|
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->buffer->cb) &&
|
|
!wait_for_expose_value ) {
|
|
Fl_Wayland_Graphics_Driver::buffer_commit(window);
|
|
}
|
|
\endcode
|
|
Thus, \c buffer_commit() runs only when \c cb is NULL. If an app rapidly performs calls
|
|
to 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 cb is NULL
|
|
which means that the compositor is ready to start performing a mapping operation, and will only
|
|
modify \c draw_buffer when \c cb is not NULL, letting the compositor complete its ongoing
|
|
mapping task.
|
|
For example, FLTK's mandelbrot test 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 factories
|
|
|
|
Wayland names <em>buffer factory</em> a software procedure that constructs objects of type
|
|
<tt>struct wl_buffer</tt> for use by a client application.
|
|
FLTK creates a \c wl_buffer object each time an Fl_Window is mapped on a display or resized.
|
|
That's done by member function \c Fl_Wayland_Graphics_Driver::create_shm_buffer()
|
|
which follows this 3-step procedure to create a "buffer factory" for FLTK and construct
|
|
Wayland buffers from it:
|
|
- Libdecor function <tt>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 initially sets this file size to \c pool_size = 10 MB. This size will be increased when and
|
|
if necessary.
|
|
FLTK stores in variable \c pool_memory the address of the beginning of the mmap'ed memory structure.
|
|
- Wayland function \c wl_shm_create_pool() has this mmap'ed memory shared with the
|
|
Wayland compositor and returns an object of type <tt>struct wl_shm_pool</tt> which encapsulates
|
|
this memory. FLTK initializes
|
|
to 0 a variable called \c chunk_offset that represents the offset inside the mmap'ed memory available
|
|
for further \c wl_buffer objects.
|
|
- Wayland function \c wl_shm_pool_create_buffer() creates from the \c wl_shm_pool object a
|
|
\c wl_buffer object which encapsulates a section of a given size of the shared memory structure
|
|
beginning at offset \c chunk_offset in it. This function returns a pointer to the resulting
|
|
\c wl_buffer object. 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.
|
|
Variable \c chunk_offset is then increased by the length of this section.
|
|
|
|
A window's \c wl_buffer is re-used each time the window gets redrawn, and is destroyed by function
|
|
\c Fl_Wayland_Graphics_Driver::buffer_release() which calls \c wl_buffer_destroy() when
|
|
\c Fl_Window::hide() runs or the window is resized.
|
|
|
|
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 size in bytes of a memory buffer needed to store the window's graphics.
|
|
If <tt>chunk_offset + size > pool_size</tt> holds when function \c create_shm_buffer() attempts to
|
|
create a new \c wl_buffer object, \c chunk_offset is reset to 0,
|
|
function \c wl_shm_pool_destroy() is called to destroy
|
|
the current \c wl_shm_pool object, and a new \c wl_shm_pool object is created and used by FLTK's
|
|
"buffer factory". If <tt>size > pool_size</tt> holds at that step, the value of \c pool_size if
|
|
increased to <tt>2 * size</tt>. This mechanism allows to access new mmap'ed memory when
|
|
\c chunk_offset reaches the end of the previous mmap'ed section, and to enlarge the size of the
|
|
mmap'ed memory when necessary.
|
|
|
|
Wayland object <tt>struct wl_shm_pool</tt> guarantees that the corresponding mmap'ed memory will be
|
|
freed when all \c wl_buffer objects which encapsulate sections of this mmap'ed memory have been
|
|
destroyed.
|
|
|
|
Wayland uses also \c wl_buffer objects to support cursors, and
|
|
FLTK uses the "buffer factory" described here also when creating custom cursors (see below) with
|
|
function <tt>Fl_Wayland_Window_Driver::set_cursor(const Fl_RGB_Image *,…)</tt> because
|
|
\c create_shm_buffer() runs as well. 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 is an array of callback function
|
|
pointers among which one (\c output_mode) runs when the display is resized and another
|
|
(\c output_scale) when the Wayland scale factor (see below) is changed.
|
|
FLTK defines type <tt>struct Fl_Wayland_Screen_Driver::output</tt> (see \ref output)
|
|
to store display size and scaling information.
|
|
One such record is created for each display. FLTK uses 2 distinct scaling parameters for each display:
|
|
- <tt>int wld_scale;</tt>. This member variable of
|
|
<tt>struct Fl_Wayland_Screen_Driver::output</tt> typically equals 1 for standard, and 2 for
|
|
HighDPI displays. Its value is set by the Wayland compositor for each display with the effect
|
|
that 1 Wayland graphics unit represents a block of \c nxn pixels when the value is \c n.
|
|
Another effect is that a drawing buffer for a surface of size WxH units contains
|
|
<tt>W * n * H * n * 4</tt> bytes. This is enough to make FLTK apps HighDPI-aware because the
|
|
Wayland compositor automatically initializes parameter \c wld_scale to the value adequate for
|
|
each display's DPI. Under the gnome desktop, 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().
|
|
|
|
The display size information of <tt>struct Fl_Wayland_Screen_Driver::output</tt> accounts for
|
|
the value of its \c wld_scale member
|
|
variable: \c width and \c height are set to the number of pixels of the display / \c wld_scale.
|
|
|
|
Overall, an FLTK object, say an Fl_Window, of size \c WxH FLTK units occupies
|
|
<tt>W * wld_scale * gui_scale x H * wld_scale * gui_scale</tt> pixels on the display.
|
|
|
|
|
|
\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.
|
|
|
|
|
|
\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 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 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.
|
|
|
|
<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() 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 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 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.
|
|
|
|
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 Text input
|
|
|
|
The "Mouse handling" section above mentionned 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 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.
|
|
|
|
Function \c wl_keyboard_keymap() runs once and allows initialization of access to this keyboard.
|
|
Noticeably, member \c xkb_state of type <tt>struct xkb_state*</tt> of the current \ref seat record
|
|
is adequately initialized.
|
|
|
|
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.
|
|
|
|
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>Fl_Int_Vector 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).
|
|
|
|
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.
|
|
|
|
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 is at version 3 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 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.
|
|
|
|
Functions \c text_input_enter() and \c text_input_leave() are called when text input enters or leaves a
|
|
surface (which corresponds to an \c Fl_Window).
|
|
|
|
Functions \c text_input_preedit_string() and \c text_input_commit_string() are called when the text
|
|
input method asks the client app to 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 marked text
|
|
to distinguish it from regular text.
|
|
|
|
Functions \c text_input_delete_surrounding_text() and \c text_input_done() have
|
|
no effect at present, without this preventing input methods that have been tested with FLTK to work
|
|
satisfactorily.
|
|
|
|
It's necessary to inform text input methods of the current location of the insertion point in the
|
|
active surface. This information allows them to map their auxiliary windows next to the insertion
|
|
point, where they are expected to appear. 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 \c libdecor 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 load at run-time a plugin present in a shared library in the system and
|
|
expected to draw titlebars in a way that best matches the Desktop. As of early 2023, two plugins
|
|
are available:
|
|
- \c libdecor-gtk intended for the Gnome desktop;
|
|
- \c libdecor-cairo for other situations.
|
|
|
|
Because \c libdecor is not yet in Linux packages, or only in a preliminary state, FLTK bundles the
|
|
most recent source code of \c libdecor and its plugins. This code is included in libfltk.
|
|
FLTK uses \c libdecor-gtk when software package \c libgtk-3-dev is present in the
|
|
system, and \c libdecor-cairo otherwise.
|
|
|
|
\c Libdecor uses the Wayland protocol <tt>"xdg decoration unstable v1"</tt> hinted at before.
|
|
|
|
CMake \c OPTION_USE_SYSTEM_LIBDECOR has been defined to allow FLTK in the future, when \c libdecor and
|
|
\c libdecor-gtk will be part of Linux packages, to use these packages rather than the \c libdecor
|
|
code bundled in FLTK. When this option is ON, preprocessor variable \c USE_SYSTEM_LIBDECOR is 1,
|
|
and both \c libdecor and its plugin are loaded at run-time from shared libraries. This option is OFF
|
|
by default.
|
|
|
|
Whatever the value of \c OPTION_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, except if \c USE_SYSTEM_LIBDECOR
|
|
is 1, 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().
|
|
|
|
Function \c get_libdecor_plugin_description() also determines whether the compositor uses CSD or SSD
|
|
mode. This information is stored
|
|
in member \c decoration_mode of <tt>struct libdecor_frame_private</tt> which is not part of
|
|
the public libdecor API. For this reason, FLTK copies to \c fl_libdecor-plugins.c the definition of
|
|
this type present in \c libdecor.c.
|
|
|
|
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 \ref seat records 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. These librairies allow FLTK to
|
|
create and initialize an EGL display connection, create objects of type \c wl_egl_window,
|
|
\c EGLSurface, and \c GLContext. An object of type \c wl_egl_window is created by function
|
|
\c Fl_Wayland_Gl_Window_Driver::make_current_before() in reference to
|
|
an existing \c wl_surface object which connects this EGL-object with a given Wayland window.
|
|
|
|
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() after having called Wayland code to synchronize EGL use
|
|
with the rest of the Wayland compositor's activity.
|
|
This synchronization procedure is as explained in the
|
|
<a href=https://wayland.freedesktop.org/docs/html/apb.html#Client-classwl__display>
|
|
description of function wl_display_prepare_read_queue()</a>.
|
|
|
|
|
|
\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.
|
|
<pre>
|
|
struct wld_window {
|
|
Fl_Window *fl_win;
|
|
struct Fl_Wayland_Screen_Driver::output *output; // the display where win is mapped (see \ref output)
|
|
struct wl_surface *wl_surface; // the window's surface
|
|
struct fl_wld_buffer *buffer; // see \ref fl_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 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
|
|
}
|
|
</pre>
|
|
|
|
\anchor fl_wld_buffer
|
|
<h3>struct fl_wld_buffer</h3>
|
|
Defined in \c Fl_Wayland_Graphics_Driver.H. One such record is created when and by:
|
|
- an Fl_Window is show()'n or resized, by \c Fl_Wayland_Graphics_Driver::create_shm_buffer();
|
|
- an Fl_Image_Surface object is created, by the \c Fl_Wayland_Image_Surface_Driver c'tor;
|
|
- a custom cursor shape is created or text is dragged, by
|
|
\c Fl_Wayland_Graphics_Driver::create_shm_buffer().
|
|
|
|
<pre>
|
|
struct fl_wld_buffer {
|
|
struct wl_buffer *wl_buffer; // the Wayland buffer
|
|
void *data; // address of the beginning of the Wayland buffer's byte array
|
|
size_t data_size; // of wl_buffer and draw_buffer
|
|
int stride; // bytes per line
|
|
int width;
|
|
unsigned char *draw_buffer; // address of the beginning of the Cairo image surface's byte array
|
|
struct wl_callback *cb; // non-NULL while Wayland buffer is being committed
|
|
bool draw_buffer_needs_commit; // true when draw_buffer has been modified but not yet committed
|
|
cairo_t *cairo_; // used when drawing to the Cairo image surface
|
|
};
|
|
</pre>
|
|
|
|
\anchor output
|
|
<h3>struct Fl_Wayland_Screen_Driver::output</h3>
|
|
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.
|
|
<pre>
|
|
struct Fl_Wayland_Screen_Driver::output { // one record for each display
|
|
uint32_t id; // an identifier of the display
|
|
short width; // nber of horizontal pixels / wld_scale
|
|
short height; // nber of vertical pixels / wld_scale
|
|
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
|
|
struct wl_list link; // links these records together
|
|
};
|
|
</pre>
|
|
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 seat
|
|
<h3>struct seat</h3>
|
|
Defined in file \c Fl_Wayland_Screen_Driver.H. One such 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 record is stored in member
|
|
\c seat of the client's unique \c Fl_Wayland_Screen_Driver object.
|
|
<pre>
|
|
struct 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 link;
|
|
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;
|
|
};
|
|
</pre>
|
|
|
|
|
|
\section wayland-doc Documentation resources
|
|
|
|
|
|
<table summary="Wayland Documentation" width="100%" 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. A language-independent syntax is used which makes function names usable from C or C++ not always obvious. Some useful functions seem undocumented here for an unclear reason.
|
|
<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>
|
|
|
|
|
|
*/
|
|
|