1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
|
/**
\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 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.
<pre>
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;
};
</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>
*/
|