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
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
|
/**
\page FLTK-devel Development of the FLTK library
- \subpage cmp
- \subpage wayland-devel
- \subpage bundled-libs
- \subpage development
*/
/**
\page wayland-devel The Wayland backend for its developer
This chapter describes how the Wayland backend of FLTK works from a developer's viewpoint.
\section wayland-intro Introduction to Wayland
Wayland usage involves communication via a Unix domain socket between a client
application and another process called the Wayland compositor which creates,
moves and resizes windows and displays their graphical content.
Diverse Wayland compositors exist which follow diverse strategies to size, decorate
and position windows on the screens. Compositors follow either the
client-side decoration (CSD) rule where client apps draw window titlebars, or the
server-side decoration (SSD) rule where the compositor draws titlebars. FLTK supports both
CSD and SSD compositors. It uses a library called \c libdecor charged of determining whether
a CSD or a SSD compositor is active, and of drawing titlebars in the first case.
Wayland is divided in various protocols that a given compositor may or may not support,
although they all support the \c core protocol. Each protocol adds functionality
not available in the core protocol and determines a well defined API the client must follow
to gain such functionality.
<a href=https://wayland.app/protocols/>Wayland Explorer</a> lists
all protocols and details their APIs.
Wayland compositors evolve in time by supporting more protocols or newly defined protocols.
The core protocol allows a client app
to discover what protocols the connected compositor supports. Protocols can be stable,
which means they have a defined API that will not change but can be expanded, or unstable.
For example, mapping a window on a screen is not done by the core protocol but by the
<tt>xdg shell</tt> protocol which is stable. The names of symbols used by unstable
protocols always begin with letter 'z'. For example, FLTK uses unstable protocol
<a href=https://wayland.app/protocols/text-input-unstable-v3>Text input</a>
to support CJK input methods; its symbol names begin with \c zwp_text_input_v3.
Wayland makes intensive use of the <em>listener</em> mechanism. A listener is a small array
of pointers to FLTK-defined callback functions associated to a Wayland-defined object;
Wayland calls these functions when defined events occur (more at \ref wayland-listeners
below).
Wayland differs noticeably from X11 in that rendering is left to clients: Wayland provides
no drawing API. Instead, Wayland provides objects of type <tt>struct wl_buffer</tt> which
encapsulate a memory array of pixel values shared between the client and the compositor.
The client app is expected to draw to that memory buffer with whatever means it chooses,
and to instruct the compositor to map those pixels to the screen when the buffer is
ready for display. The Wayland platform of FLTK draws with the Cairo library to \c Fl_Window's
and \c Fl_Image_Surface's, and with OpenGL to \c Fl_Gl_Window's.
Wayland differs also from X11 in that the position of a window in the display is
completely hidden to the client app. This prevents function \c Fl_Window::position() from having
any effect on a top-level window. Wayland also prevents a client app from knowing whether
a window is minimized: \c Fl_Window::show() has no effect on a minimized window.
Subwindows can be positioned as usual relatively to their parent window.
Wayland allows to create popup windows positioned relatively to a previously mapped other
window. This allows FLTK to position adequately menu and tooltip windows
(see \ref menu-windows).
FLTK uses also popups for the small, yellow windows that display
the new scale factor value when it's changed: these are created as short-lived popups
centered above \c Fl::first_window().
Wayland offers a means to handle lists of linked records. It defines type
<tt>struct wl_list</tt> and a few macros (\c wl_list_init(), \c wl_list_for_each(), \c wl_list_insert(),
\c wl_list_for_each_safe(), \c wl_list_remove()) to manage linked lists. Memory records put in these
lists must have the same type and contain a member variable of type <tt>struct wl_list</tt> used
to link records together
and often named 'link'. Access to such a list is possible memorizing a value of type
<tt>struct wl_list</tt> computed by macro \c wl_list_init().
Macro <tt>wl_list_for_each(arg1, arg2, arg3)</tt> allows to run through all list elements with:
- \c arg1 is a pointer variable to the type of elements of the linked list;
- \c arg2 is the address of a variable of type <tt>struct wl_list</tt> identifying the targeted list;
- \c arg3 is the name of the member variable of these elements used to link them together.
For example, \c wl_list_for_each() can be used as follows to scan the linked list
of all displays of the system (see \ref wayland-output "Fl_Wayland_Screen_Driver::output"):
\code
Fl_Wayland_Screen_Driver::output *output;
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
wl_list_for_each(output, &(scr_driver->outputs), link) {
// … work with output, an item of the linked list of all displays in the system …
}
\endcode
Overall, and ignoring for now OpenGL usage, FLTK interacts with Wayland as follows :
- When opening the display: FLTK calls \c Fl::add_fd() in \c FL_READ mode to associate
a callback function called \c wayland_socket_callback to the socket connecting the client
and the compositor.
- Client to compositor: FLTK calls C functions of the \c libwayland-client.so,
\c libwayland-cursor.so, \c libxkbcommon.so and \c libdecor.so shared libraries.
The names of these functions begin with \c wl_, \c xkb_ or \c libdecor_.
Functions from the first 2 of these libraries instruct the compositor to perform
various operations sending it data via the socket.
- Compositor to client: the callback function \c wayland_socket_callback runs when there are
data to read in the socket; it calls \c wl_display_dispatch() which interprets the read data
and calls corresponding listeners.
The core protocol defines also a number of mostly opaque structures whose names begin with \c wl_.
The names of symbols and types defined by the other protocols FLTK uses begin with \c xdg_,
\c zwp_text_input_v3, \c zxdg_toplevel_decoration_, \c gtk_shell1_ and \c gtk_surface1_.
FLTK defines a few structures holding Wayland-related data.
The names of FLTK-defined structures don't begin with \c wl_. For example,
<tt>struct wld_window</tt> (see \ref wld_window) is used to store all Wayland-specific data associated
to a mapped Fl_Window.
\section wayland-build Building libfltk as a Wayland client
Classes \c Fl_Wayland_Window_Driver, \c Fl_Wayland_Screen_Driver, \c Fl_Wayland_Graphics_Driver,
\c Fl_Wayland_Copy_Surface_Driver, \c Fl_Wayland_Image_Surface_Driver and
\c Fl_Wayland_Gl_Window_Driver and file \c fl_wayland_platform_init.cxx
contain all the Wayland-specific code of the FLTK library.
This code is located at \c src/drivers/Wayland/ in the FLTK source tree.
A single C++ source file generally contains all the code of a given class.
The code for class \c Fl_Wayland_Screen_Driver, however, is split in two source files,
\c Fl_Wayland_Screen_Driver.cxx and \c fl_wayland_clipboard_dnd.cxx that contains
all code related to copy, paste and drag-and-drop.
Furthermore, class \c Fl_Unix_System_Driver is used by both the Wayland and the X11
FLTK platforms. File FL/fl_config.h defines preprocessor variables
\c FLTK_USE_WAYLAND and \c FLTK_USE_CAIRO.
The public C API to Wayland, xkb, EGL and libdecor libraries are obtained with
\code
#include <wayland-client.h>
#include <wayland-cursor.h>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-compose.h>
#include <linux/input.h> // for BTN_LEFT, BTN_RIGHT, BTN_MIDDLE
#include "../../../libdecor/build/fl_libdecor.h"
#if HAVE_GL
# include <wayland-egl.h>
# include <EGL/egl.h>
#endif // HAVE_GL
\endcode
as necessary.
File \c README.Wayland.txt details what software packages are needed on Debian-based, Fedora
and FreeBSD systems for FLTK to use Wayland. Wayland protocols are packaged as XML files
accompanied by a utility program, \c wayland-scanner, able to generate a header file and a
necessary glue C source file from a given XML file. For example, for FLTK to use the
<a href=https://wayland.app/protocols/xdg-shell>XDG shell</a> protocol, these commands are
run at build time to generate, using file \c xdg-shell.xml, a .c file (\c xdg-shell-protocol.c)
that will be compiled into
\c libfltk and a header file (\c xdg-shell-client-protocol.h) that the FLTK code will include:
\code
PROTOCOLS=`pkg-config --variable=pkgdatadir wayland-protocols`
wayland-scanner private-code $PROTOCOLS/stable/xdg-shell/xdg-shell.xml xdg-shell-protocol.c
wayland-scanner client-header $PROTOCOLS/stable/xdg-shell/xdg-shell.xml xdg-shell-client-protocol.h
\endcode
Similar operations are performed for FLTK to use protocols
<a href=https://wayland.app/protocols/xdg-dialog-v1>XDG dialog windows</a>,
<a href=https://wayland.app/protocols/xdg-decoration-unstable-v1>XDG decoration</a>,
<a href=https://wayland.app/protocols/text-input-unstable-v3>Text input</a>
and <a href=https://wayland.app/protocols/gtk-shell>GTK Shell</a>.
\section wayland-x11-hybrid The hybrid Wayland/X11 platform
The Wayland platform of FLTK is normally a two-legged hybrid able to use either Wayland or X11
and to choose between these possibilities at run-time, without any change to the client
application. The Wayland/X11 hybrid is essentially a version of the FLTK library containing both all
Wayland-specific <u>and</u> all X11-specific code. That's reflected in file
FL/fl_config.h which defines both \c FLTK_USE_WAYLAND and \c FLTK_USE_X11.
This creates the constraint that FLTK's Wayland-specific and X11-specific source code cannot
use the same type name for different purposes or the same symbol name.
That is why function <tt>fl_xid(const Fl_Window*)</tt> is deprecated in FLTK 1.4 and replaced by
\c fl_wl_xid() for Wayland and \c fl_x11_xid() for X11. Also, global variable
<tt>Window fl_window</tt> is not used by the Wayland platform which instead uses
<tt>static struct wld_window *Fl_Wayland_Window_Driver:: wld_window</tt>.
The FLTK library contains also a short source file,
\c fl_wayland_platform_init.cxx, that determines, at startup time, whether
the app will run as a Wayland or as an X11 client. Function \c attempt_wayland() therein performs
this choice as follows :
- if the app defines a global bool variable called \c fl_disable_wayland and this variable is true,
the X11 leg is chosen;
- if environment variable FLTK_BACKEND is defined to string "wayland", the Wayland leg is chosen;
- if environment variable FLTK_BACKEND is defined to string "x11", the X11 leg is chosen;
- otherwise, a connection to a Wayland compositor is attempted; if it's successful, the Wayland
leg is chosen; if it's not, the X11 leg is chosen.
The first condition listed above is meant to facilitate transition to FLTK 1.4 of source code
written for FLTK 1.3 and containing X11-specific code : it's enough to put
\code
FL_EXPORT bool fl_disable_wayland = true;
\endcode
anywhere in the source code, for the app to run with 1.4, using the x11 leg of the hybrid platform,
without any other change in the source code nor to the application's environment.
Function \c attempt_wayland() must be called before the very first platform-dependent operation FLTK
performs so that operation is done the Wayland or the X11 way, as appropriate. That's why
4 locations of the FLTK source code call \c attempt_wayland():
<tt>Fl_Graphics_Driver::newMainGraphicsDriver(), Fl_Screen_Driver::newScreenDriver(),
Fl_Window_Driver::newWindowDriver(Fl_Window*), and
Fl_Image_Surface_Driver::newImageSurfaceDriver().</tt>
In special situations, such as with embedded systems equipped with the Wayland software but lacking
the X11 library, it's possible to build the FLTK library such as it contains only the Wayland backend.
This is achieved building FLTK with <tt>cmake -DFLTK_BACKEND_X11=OFF</tt>.
In that case, FL/fl_config.h does not define \c FLTK_USE_X11.
The rest of this chapter describes what happens when the Wayland leg has been chosen.
\section wayland-listeners Listeners
A Wayland 'listener' is a small array of pointers to FLTK-defined callback functions
associated to a Wayland-defined object.
A call to a specific Wayland function with a name of the form \c wl_XXX_add_listener
associates the Wayland object and its listener, usually right after creation of this object.
After defined events have occurred, the Wayland compositor sends appropriate commands
to the client through the socket; the event loop detects the availability of data in the
socket and calls function \c wayland_socket_callback(); this function calls the appropriate
member of the listener and transmits relevant information to the client app as parameters of this call.
For example, this code:
\code
static void surface_enter(……) { …… } // called when a surface enters a display
static void surface_leave(……) { …… } // called when a surface leaves a display
static struct wl_surface_listener surface_listener = {
surface_enter,
surface_leave,
};
some_pointer_type pter_to_data;
struct wl_surface *my_wl_surface;
my_wl_surface = wl_compositor_create_surface(scr_driver->wl_compositor);
wl_surface_add_listener(my_wl_surface, &surface_listener, pter_to_data);
\endcode
creates a Wayland object of type <tt>struct wl_surface</tt> (roughly, a window), and associates it with
a 2-member listener called \c surface_listener. After this, Wayland is expected to call
the 2 listener members, \c surface_enter or \c surface_leave, each time \c my_wl_surface will
enter or leave, respectively, a display. The arguments of these calls, not detailed here,
allow the member functions to identify which surface enters or leaves which display.
The \c wl_surface_add_listener() call above also associates \c pter_to_data to
\c my_wl_surface as <em>user data</em>. The \c wl_surface object's
"user data" can be obtained later calling function \c wl_surface_get_user_data().
Wayland function \c wl_proxy_get_listener() returns a pointer
to a Wayland object's listener provided that object is transmitted cast to type
<tt>struct wl_proxy *</tt>. This gives a handy way to distinguish FLTK-created Wayland
objects from objects of other origin: the listener of an FLTK-created object is a known
FLTK listener. For example, function \c Fl_Wayland_Window_Driver::surface_to_window()
uses this possibility calling <tt>wl_proxy_get_listener( (struct wl_proxy *)wl_surface )</tt>
for any object of type <tt>struct wl_surface</tt>: if that object was created as in the
example above, this call returns a pointer to FLTK's \c surface_listener static variable.
\section wayland-connection Opening a Wayland connection
Establishing a Wayland connection requires environment variable \c XDG_RUNTIME_DIR to be
defined and to point to a directory containing a socket connected to a Wayland
compositor. This variable is usually set by the login procedure of Wayland-friendly desktops.
Which socket-file to use within that directory is determined as follows:
- the client may call <tt>Fl::display(const char *display_name)</tt> before
\c fl_open_display() runs or use the \c -display command line argument and transmit there the
socket name;
- environment variable \c WAYLAND_DISPLAY can be defined to the socket name;
- otherwise, \c "wayland-0" is used.
Which socket is selected determines the compositor used by the client application: that at the other
end of the socket.
Establishing the connection begins with a call to <tt>wl_display_connect(const char *display_name)
</tt>. That call is done inside function \c attempt_wayland() mentioned before with a NULL argument,
or when a non default Wayland display name is specified as explained above. That call returns a
<tt>struct wl_display</tt> pointer or NULL in case of failure. Such NULL return is the hint
that allows the FLTK display opening procedure of the Wayland/X11 hybrid to recognize when Wayland
access is not possible and to fallback to X11. If the call is successful, its non-NULL return
is assigned to class variable \c Fl_Wayland_Screen_Driver::wl_display.
The rest of the work is done in function \c Fl_Wayland_Screen_Driver::open_display_platform().
A call to \c wl_registry_add_listener() associates a 2-member listener, whose 1st member,
\c registry_handle_global(), will be called by Wayland a number of times to indicate each time
a protocol supported by the compositor or a system feature such as displays and keyboards.
FLTK runs this code to receive calls to \c registry_handle_global():
\code
static void sync_done(void *data, struct wl_callback *cb, uint32_t time) {
*(struct wl_callback **)data = NULL;
wl_callback_destroy(cb);
}
static const struct wl_callback_listener sync_listener = {
sync_done
};
struct wl_callback *registry_cb = wl_display_sync(wl_display);
wl_callback_add_listener(registry_cb, &sync_listener, ®istry_cb);
while (registry_cb) wl_display_dispatch(wl_display);
\endcode
A pointer to an object of type <tt>struct wl_callback</tt> created by function
\c wl_display_sync() is assigned to variable \c registry_cb.
Then a 1-member listener is attached to this object. Wayland will run this listener's
member function, \c sync_done(), after all calls to \c registry_handle_global() have
occurred. Function \c sync_done() sets to null variable \c registry_cb and destroys
the \c wl_callback.
Finally, function \c wl_display_dispatch() is called as long as variable \c registry_cb is
not null. Thus, when \c sync_done() runs, FLTK has received all due calls to
\c registry_handle_global().
The prototype of function \c registry_handle_global is:
\code
static void registry_handle_global(void *user_data, struct wl_registry *wl_registry,
uint32_t id, const char *interface, uint32_t version)
\endcode
Each time Wayland calls \c registry_handle_global(), \c interface and \c version give the name
and version of a component or feature of the Wayland system.
Here is the list of the \c interface value for all protocols and system features relevant for FLTK:
<table>
<tr><th>Interface </th><th>How FLTK uses this interface</th> </tr>
<tr><td>wl_compositor </td><td> create wl_surface objects </td> </tr>
<tr><td>wl_subcompositor </td><td>create subwindows </td> </tr>
<tr><td>wl_shm </td><td> create shared memory pools and buffers</td> </tr>
<tr><td>wl_seat </td><td> create the unique "seat"</td> </tr>
<tr><td>wl_data_device </td><td> support of copy/paste/drag-n-drop</td> </tr>
<tr><td>wl_output </td><td>received once for each display </td> </tr>
<tr><td>xdg_wm_base </td><td>create mapped windows </td> </tr>
<tr><td>xdg_wm_dialog_v1 </td><td>create dialogs related to other windows</td> </tr>
<tr><td>gtk_shell1 </td><td>indicates Mutter is in use + titlebar gestures</td> </tr>
<tr><td>weston_desktop_shell </td><td> indicates Weston is in use</td></tr>
<tr><td>org_kde_plasma_shell </td><td> indicates KDE/Plasma is in use</td></tr>
<tr><td>zwp_text_input_manager_v3 </td><td>interface with Text Input Methods </td></tr>
<tr><td>zxdg_decoration_manager_v1 </td><td>select between CSD and SSD modes </td></tr>
</table>
Wayland compositors typically support several other protocols (e.g., \c zxdg_output_manager_v1)
that FLTK does not use.
Each time \c registry_handle_global runs with an \c interface from the table above, FLTK calls
\c wl_registry_bind() which returns a pointer to a Wayland structure that will be the client's access
point to the corresponding Wayland protocol or system feature. This pointer is stored in a dedicated
member variable of the unique \c Fl_Wayland_Screen_Driver object of an FLTK app, or of another object
accessible from this object.
For example, when \c interface equals \c "wl_compositor", the value returned by
\c wl_registry_bind() is stored as member \c wl_compositor of the
\c Fl_Wayland_Screen_Driver object.
Function \c registry_handle_global() also identifies whether the Mutter, Weston, KWin or Wayfire compositor
is connected and stores this information in static member variable \c Fl_Wayland_Screen_Driver::compositor.
Other compositors (e.g., \c sway, \c labwc) are not specifically identified by FLTK and
store value \c unspecified in member variable \c compositor.
Wayland calls \c registry_handle_global() with its parameter \c interface equals to
\c "wl_output" once for each screen connected to the system. Each time, a pointer to a Wayland object
of type <tt>struct wl_output</tt> is obtained by a call to \c wl_registry_bind() and an FLTK
record of type \ref wayland-output "Fl_Wayland_Screen_Driver::output" is created.
This FLTK record is added to the end of the linked list of known screens that starts
at member \c outputs of the \c Fl_Wayland_Screen_Driver object.
A 4-member listener is associated to the <tt>struct wl_output</tt> by function
\c wl_output_add_listener(). The 3rd member of this 4-function listener, \c output_done(),
runs after all initialization steps of the screen have completed and turns to \c true
member \c done of the record of type \c Fl_Wayland_Screen_Driver::output.
Function \c sync_done() mentioned above therefore also calls \c wl_display_dispatch()
until the \c done member of all \c Fl_Wayland_Screen_Driver::output records are \c true.
Overall, after return from function \c sync_done(), FLTK has been made aware of all
optional protocols and features of its connected Wayland compositor, and has initialized
all screens of the system.
Finally, function \c wl_display_get_fd() is called to obtain the file descriptor of the Wayland socket
and a call to Fl::add_fd() makes FLTK listen to this descriptor in \c FL_READ mode and associates
function \c wayland_socket_callback() from file \c Fl_Wayland_Screen_Driver.cxx with it.
This function calls \c wl_display_dispatch() which reads and interprets data available from the
file descriptor, and calls corresponding listeners. Function \c wayland_socket_callback() repeats
the \c wl_display_dispatch() call as long as data are available for reading.
The event loop is run by function \c Fl_Unix_System_Driver::wait() which is used by both
the Wayland and X11 FLTK backends. Among various tasks, this function waits for data arriving
on the file descriptors FLTK is listening. Overall, the Wayland backend's event loop differs
from that of the X11 backend only in the callback function handling data read from the Wayland
connection socket and in overridden functions \c Fl_Wayland_Screen_Driver::poll_or_select_with_delay()
and \c Fl_Wayland_Screen_Driver::poll_or_select().
\section wayland-surface Wayland windows and surfaces
Wayland defines objects called surfaces of type <tt>struct wl_surface</tt>. A Wayland surface
"has a rectangular area which may be displayed on zero or more displays, present buffers,
receive user input, and define a local coordinate system". In short,
surface is the name Wayland uses for a window.
Buffers allow the client app to define the graphical content of surfaces (see \ref wayland-buffer).
FLTK creates a surface each time an Fl_Window is show()'n calling function
\c wl_compositor_create_surface().
Static member function <tt>Fl_Wayland_Window_Driver::surface_to_window(struct wl_surface *)</tt>
gives the \c Fl_Window* corresponding to the surface given in argument.
FLTK recognizes 4 mutually exclusive kinds of surfaces :
- DECORATED are toplevel windows with a titlebar;
- UNFRAMED are toplevel windows without titlebar;
- POPUP correspond to menus and tooltips;
- SUBWINDOW correspond to an Fl_Window embedded in another Fl_Window.
Function \c Fl_Wayland_Window_Driver::makeWindow() creates all these surfaces, creates for each
a record of type <tt>struct wld_window</tt> (see \ref wld_window), and stores the window kind in
member variable \c kind of this record. Member variable \c xid of the window's \c Fl_X record stores
the adress of this record.
Except for SUBWINDOW's, each surface needs a Wayland object of type <tt>struct xdg_surface</tt>
used to make it become a mapped window and stored in member \c xdg_surface of the window's
\ref wld_window record. For DECORATED windows, this object is created inside libdecor
and transmitted to FLTK by function \c libdecor_frame_get_xdg_surface(). For UNFRAMED
and POPUP windows, it's created by function \c xdg_wm_base_get_xdg_surface().
Finally, each surface is also associated to one more Wayland object whose type
varies with the window's kind. These explain this part of the \ref wld_window record:
\code
union {
struct libdecor_frame *frame; // created for DECORATED by libdecor_decorate()
struct wl_subsurface *subsurface; // created for SUBWINDOW by wl_subcompositor_get_subsurface()
struct xdg_popup *xdg_popup; // created for POPUP by xdg_surface_get_popup()
struct xdg_toplevel *xdg_toplevel; // created for UNFRAMED by xdg_surface_get_toplevel()
};
\endcode
Except for SUBWINDOW's, each surface is associated to a 'configure' function that Wayland
calls one or more times when the window is going to be mapped on the display.
The 'configure' function of DECORATED surfaces is \c handle_configure() which is the 1st
member of a 4-member listener named \c libdecor_frame_iface associated to a decorated window
when it's created calling \c libdecor_decorate(). Finally, a call to \c libdecor_frame_map()
triggers the process of mapping the newly created DECORATED surface on a display.
Wayland calls \c handle_configure() twice during this process.
The first \c handle_configure() run allows to set the window's \c xdg_surface object
which is returned by function \c libdecor_frame_get_xdg_surface().
FLTK distinguishes the first from the second run of \c handle_configure() by looking at
the \c xdg_surface member variable that's NULL at the beginning of the 1st run and not NULL later.
Wayland calls \c handle_configure() also during operations such as resizing, minimizing (see below).
With the help of a few calls to libdecor functions, FLTK obtains in this function
all needed information about the size and state of the mapped window.
The 'configure' functions of UNFRAMED and POPUP surfaces are \c xdg_surface_configure(),
\c xdg_toplevel_configure() and \c popup_configure(). The mapping process of these surfaces
is triggered by a call to \c wl_surface_commit().
These 'configure' functions transmit effective window size
information to FLTK. Also, they are where the window's
\c Fl_Window_Driver::wait_for_expose_value member variable is set to 0 to indicate that the
window has been mapped to display. \b Caution: there are some small
differences between how and when the various Wayland compositors call \c handle_configure().
When a decorated window changes size, whatever the cause of it, Wayland calls
\c handle_configure() which sets member variable \c Fl_Wayland_Window_Driver::in_handle_configure to true
and calls the window's virtual \c resize() function which ultimately runs
\c Fl_Wayland_Window_Driver::resize() which calls Fl_Group::resize() to perform FLTK's resize
operations and \c Fl_Wayland_Graphics_Driver::buffer_release()
to delete the existing window buffer that's not adequate for the new window size.
At the end of the run of \c handle_configure(), \c in_handle_configure is set back to false.
When the window size change is caused by the app itself calling the window's \c resize() function,
\c Fl_Wayland_Window_Driver::in_handle_configure is false. This allows
\c Fl_Wayland_Window_Driver::resize()
to detect that Wayland needs be informed of the desired size change, which gets done by a call
to \c libdecor_frame_commit(). Wayland later calls \c handle_configure() and events described
above unfold.
Wayland generally does not provide a way to control where the compositor should map a window
in the system displays. Nevertheless, for multi-display systems, Wayland allows to control
on what display should the compositor map a fullscreen window. That is done inside function
\c handle_configure() which calls \c libdecor_frame_set_fullscreen() for DECORATED windows
and inside function \c xdg_toplevel_configure() which calls \c xdg_toplevel_set_fullscreen()
for UNFRAMED. The <tt>struct wl_output</tt> pointer for the targeted display is transmitted
as 2nd argument of these calls.
\section menu-windows Menu windows and other popups
Menu windows, tiny menu title windows, and tooltip windows are implemented using Wayland's
popup mechanism which allows to position a popup window relatively to a previously mapped
window, itself a popup or another kind of window, with the restriction that any popup
must overlap or at least touch that other window.
Member function \c Fl_Wayland_Window_Driver::makeWindow calls member function
\c Fl_Wayland_Window_Driver::process_menu_or_tooltip to create all popups.
This function gets called after FLTK has computed using a given algorithm the desired \c (x,y)
position of the popup window's top-left corner, using coordinates centered on the top-left
corner of the toplevel window from which the popup originates.
This algorithm is able to prevent popups from
being positioned beyond the screen borders under the assumption that the position of a
toplevel window inside a screen is known. While this assumption holds for other
platforms, it does not for the Wayland platform. The FLTK code for the Wayland platform
therefore modifies the algorithm that FLTK uses to compute the position of menu windows.
The key information used by this algorithm is obtained by virtual member function
\c Fl_Window_Driver::menu_window_area which computes the coordinates of the rectangle where
menu windows are allowed to be positioned. Under other platforms, this function just returns
the origin and size of the work area of the screen in use.
In contrast, the Wayland platform handles two situations differently :
- For menu windows that are not taller than the display in use, the
Wayland-overridden member function \c Fl_Wayland_Window_Driver::menu_window_area returns
large negative origin and large width and height values. This lets the standard FLTK
algorithm position the menu relatively to its window of origin without concern about screen
limits, and relies on Wayland's constraint mechanism described below to prevent the menu from
going beyond these limits, without FLTK having to know where they are.
- Menu windows taller than the screen where they are mapped need special handling
described in detail in a comment above the source code of function
\c Fl_Wayland_Window_Driver::process_menu_or_tooltip.
Function \c Fl_Wayland_Window_Driver::process_menu_or_tooltip
first computes \c origin_win, pointer to the \c Fl_Window relatively to which the popup is to
be positioned. Window \c origin_win is the parent menu window when the popup is a sub-menu;
it's the tiny windowtitle when the popup is a menu with a title; otherwise, it's the window
containing the point of origin of the popup.
An object of type <tt>struct xdg_positioner</tt> created by function
\c xdg_wm_base_create_positioner() is used to express the rules that will determine the
popup position relatively to \c origin_win as follows:
- Function \c xdg_positioner_set_anchor_rect() determines a rectangle in \c origin_win
relatively to which the popup is to be positioned. When the popup to be created is a menu
window spawned by an Fl_Menu_Bar, that rectangle is the full area of the menu title window.
Otherwise, that rectangle is an adequately located point.
- Function \c xdg_positioner_set_size() sets the popup size.
- The <tt>xdg_positioner_set_anchor(positioner, XDG_POSITIONER_ANCHOR_BOTTOM_LEFT);</tt>
and <tt>xdg_positioner_set_gravity(positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);</tt>
calls position the popup so that its top-left corner is initially below and at right of
the bottom-left corner of the \c origin_win 's anchor rectangle.
- The call to \c xdg_positioner_set_offset() further changes the popup vertical position.
- The call to \c xdg_positioner_set_constraint_adjustment() uses constraint flags
\c XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X and
\c XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y which mean that the compositor will move
the popup horizontally and vertically if its initial position would make it expand beyond
the edges of the screen. Furthermore, flag XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y is
added when the popup is a menu window spawned by an Fl_Menu_Bar; this has the popup flipped
above the Fl_Menu_Bar if there's not enough screen room below it for the popup.
- Finally, a call to function \c xdg_surface_get_popup() creates the popup accounting for
position rules listed above. The positioner is then deleted
by \c xdg_positioner_destroy(), a listener is associated to the popup surface with
\c xdg_popup_add_listener(), and a call to \c wl_surface_commit() triggers the mapping
of the popup on the display.
Overall, the expected coordinates of the top-left corner of the popup relatively to
\c origin_win are <tt>popup_x, popup_y</tt>. They are memorized in a record of FLTK-defined
type <tt>struct win_positioner</tt> that's associated to the popup listener.
When the compositor maps the popup, function
\c popup_configure, the first element of the popup listener, runs and receives as arguments
the coordinates of the popup top left and its size. These values account for the positioning
constraints of the popup which may have moved it to avoid screen borders. This function
can therefore detect whether constraints applied have modified the effective popup location
in comparison to the expected coordinates which are available as member variables of the
<tt>struct win_positioner</tt> record mentioned above. That's key to the handling by FLTK
of tall menu windows.
Groups of popups containing a menutitle, the associated menuwindow, and optionally
a submenu window and that don't belong to an Fl_Menu_Bar are mapped in a different order:
the menuwindow is mapped first, and the menutitle is mapped second above it as a child popup.
Function \c Fl_Window_Driver::is_floating_title() detects when such a menutitle is created,
static member variable \c previous_floatingtitle is assigned the value of this menutitle, and
the menutitle is mapped only after the menuwindow has been mapped, as a child of it.
This positions better the popup group in the display relatively to where the popup
was created.
\section wayland-graphics-driver Fl_Wayland_Graphics_Driver and Fl_Cairo_Graphics_Driver
The Wayland platform of FLTK uses an \c Fl_Wayland_Graphics_Driver object for all its on-screen
drawing operations.
This object is created by function \c Fl_Graphics_Driver::newMainGraphicsDriver() called by
\c Fl_Display_Device::display_device() when the library opens the display.
New \c Fl_Wayland_Graphics_Driver objects are also created for each \c Fl_Image_Surface and
each \c Fl_Copy_Surface used, and deleted when these objects are deleted.
Class \c Fl_Wayland_Graphics_Driver derives from class \c Fl_Cairo_Graphics_Driver which
implements all the FLTK drawing API for a Cairo surface.
Function \c Fl_Wayland_Graphics_Driver::cairo_init()
creates the Cairo surface used by each \c Fl_Wayland_Graphics_Driver object by calling \c
cairo_image_surface_create_for_data() for the window's or offscreen's \c draw_buffer (see below).
Class \c Fl_Cairo_Graphics_Driver is also used
by the X11 leg of the hybrid Wayland-X11 platform because this leg draws to the display with
an \c Fl_X11_Cairo_Graphics_Driver object which derives from class
\c Fl_Cairo_Graphics_Driver. Finally, \c Fl_Cairo_Graphics_Driver is also used, in the form of
an object from its derived class \c Fl_PostScript_Graphics_Driver, when the hybrid Wayland-X11
platform draws PDF or PostScript, or when the classic X11 platform uses Pango and draws
PDF or PostScript. This happens when classes \c Fl_PDF_File_Surface, \c Fl_PostScript_File_Device
and \c Fl_Printer are used.
\section wayland-buffer Wayland buffers
Wayland uses buffers, objects of type <tt>struct wl_buffer</tt>, to draw to surfaces. In principle,
one or more buffers can be associated to a surface, and functions \c wl_surface_attach() and
\c wl_surface_commit() are called to first attach one such buffer to the surface and then inform the
compositor to map this buffer's graphics content on the display. Wayland buffers can use various
memory layouts. FLTK uses WL_SHM_FORMAT_ARGB8888, which is the same layout as what Cairo calls
CAIRO_FORMAT_ARGB32.
FLTK calls function \c Fl_Wayland_Window_Driver::make_current() before drawing to any Fl_Window.
Member \c buffer of this Fl_Window's <tt>struct wld_window</tt> (see \ref wld_window) is NULL when the
window has just been created or resized. In that case, FLTK calls
\c Fl_Wayland_Graphics_Driver::create_wld_buffer() which
returns a pointer to a <tt>struct wld_buffer</tt> containing
- a Wayland buffer, member \c wl_buffer;
- a Cairo image surface, created by a call to \c Fl_Wayland_Graphics_Driver::cairo_init().
Each of these two objects encapsulates a byte array of the same size and the same memory layout
destined to contain the Fl_Window's graphics. The Cairo image surface object is where FLTK draws.
The Wayland buffer is what Wayland maps on the display. FLTK copies the Cairo surface's byte array
to the Wayland buffer's byte array before beginning the mapping operation.
If \c width and \c height are a window's dimensions in pixels,
\code
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
int size = stride * height;
\endcode
give \c size, the common size of both byte arrays.
The effective creation of the \c wl_buffer object is delayed until function
\c Fl_Wayland_Graphics_Driver::buffer_commit() gets called.
Section \ref wayland-buffer-factory below details how FLTK creates \c wl_buffer objects.
The <tt>struct Fl_Wayland_Graphics_Driver::wld_buffer</tt> (see \ref wld_buffer) contains
a pointer to the byte array of the Cairo image surface (member \c draw_buffer.buffer), information about the
Wayland buffer (members \c wl_buffer and \c data), the common size of the Cairo surface's and
Wayland buffer's byte arrays (member \c draw_buffer.data_size), and other information. A pointer to this
<tt>struct Fl_Wayland_Graphics_Driver::wld_buffer</tt> is memorized as member \c buffer of the Fl_Window's \ref wld_window.
All drawing operations to the Fl_Window then modify the content of the Cairo image surface.
Function \c Fl_Wayland_Window_Driver::flush() is in charge of sending FLTK
graphics data to the display. That is done by calling function \c
Fl_Wayland_Graphics_Driver::buffer_commit() which creates the <tt>struct wl_buffer</tt>
object calling \c create_shm_buffer() if that was not done before,
copies the byte array of the Cairo surface to
the Wayland buffer's starting memory address, and calls functions \c wl_surface_attach()
and \c wl_surface_commit(). Before calling Fl_Window::flush(),
FLTK has computed a damaged region. If that region is not null,
\c Fl_Wayland_Graphics_Driver::buffer_commit() copies only the damaged parts of the Cairo
surface to the Wayland buffer and calls function \c wl_surface_damage_buffer() for these
parts to inform the compositor of what parts of the surface need its attention.
<h3>Wayland buffer deletion</h3>
Each \ref wld_buffer record contains boolean member \c in_use which is set to \c true
just before the buffer gets committed, and boolean member \c released which
is set to \c true when FLTK no longer needs the buffer and calls
\c Fl_Wayland_Graphics_Driver::buffer_release().
FLTK's buffer-creating function, \c Fl_Wayland_Graphics_Driver::create_shm_buffer(),
attaches a 1-member listener to each buffer which Wayland calls after a commit
operation to indicate the client is allowed to re-use the buffer.
This listener's member function, \c buffer_release_listener(),
turns to false member \c in_use of the buffer's \ref wld_buffer record.
Since the two events 'FLTK no longer needs the buffer' and
'the client is allowed to re-use the buffer' can arrive in
any order, FLTK deletes the <tt>struct wl_buffer</tt> object by running
\c do_buffer_release() only after both events happened, that is, when \c in_use is
\c false and \c released is \c true. That's why function \c do_buffer_release()
is called by both functions \c Fl_Wayland_Graphics_Driver::buffer_release()
and \c buffer_release_listener().
\section throttling Throttling window redraws
FLTK uses Wayland's synchronization
mechanism to make sure any committed \c wl_buffer is not changed while the
compositor is using it and to refrain from calling \c wl_surface_commit()
more frequently than the system can process it.
Firstly, as seen above, Wayland calls function \c buffer_release_listener() when the client is
free to reuse or destroy a given \c wl_buffer. FLTK won't change or destroy a committed
\c wl_buffer before that call.
Second, this 2-step mechanism prevents Wayland clients from committing new buffer states
too frequently:
- \c Fl_Wayland_Graphics_Driver::buffer_commit() first calls function \c wl_surface_frame() to
obtain a pointer to a <tt>struct wl_callback</tt> object and stores it as member
\c frame_cb of the surface's \ref wld_window.
Then it calls \c wl_callback_add_listener() to associate this object to the
FLTK-defined, callback function \c surface_frame_done().
It next calls \c wl_surface_commit().
Together, these 3 calls instruct Wayland to start mapping the buffer content to the
display and to call \c surface_frame_done() later, when it will have become ready
for another mapping operation.
- Later, \c surface_frame_done() runs and destroys the \c wl_callback object by
function \c wl_callback_destroy() and sets member \c frame_cb to NULL.
Member variable \c draw_buffer_needs_commit of the \ref wld_buffer is also
important in this mechanism : it informs FLTK that the graphics buffer has
changed and needs being committed. This variable is turned \c true every time a
graphics operation changes the buffer content and turned \c false when the
buffer gets committed.
This procedure ensures that FLTK never calls \c wl_surface_commit()
before the compositor becomes ready for a new commit because
\c Fl_Wayland_Window_Driver::flush() calls
\c Fl_Wayland_Graphics_Driver::buffer_commit() only if \c frame_cb is NULL.
If it's not NULL, the exact content of function \c surface_frame_done() :
\code
static void surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) {
struct wld_window *window = (struct wld_window *)data;
wl_callback_destroy(cb);
window->frame_cb = NULL;
if (window->buffer && window->buffer->draw_buffer_needs_commit) {
Fl_Wayland_Graphics_Driver::buffer_commit(window);
}
}
\endcode
has the effect that when the mapping operation eventually completes, Wayland runs
\c surface_frame_done(), which, if the buffer's \c draw_buffer_needs_commit member is true,
calls \c Fl_Wayland_Graphics_Driver::buffer_commit() anew. The net result is that the screen
shows the most recent surface content.
This synchronization mechanism is also used when performing an interactive window resize
operation. During such operation, the compositor informs the client an interactive
resize is being performed and sends window resize commands at high rate (~60 Hz) to the
client via the socket. Libdecor turns on flag \c LIBDECOR_WINDOW_STATE_RESIZING
to inform the client, and runs function \c handle_configure() for each received resize
command. Before calling Fl_Group::resize() and later Fl_Window::draw(),
\c handle_configure() tests whether \c window->frame_cb is NULL. When it's not
because a previous resize operation is being performed, the current resize command is
skipped. At the end of the interactive resize, flag \c LIBDECOR_WINDOW_STATE_RESIZING
is off and Wayland sends a final resize command which is not skipped. Overall, this
ensures the client program resizes its window as frequently as it can without
falling behind resize commands sent by the compositor.
To account for a bug in Mutter (issue #878), the \c window->frame_cb object is
not created when a toplevel window is being resized and is entirely covered by
one subwindow.
<h3>Progressive window drawing</h3>
FLTK supports progressive drawing when an app calls function Fl_Window::make_current()
at any time and then calls the FLTK drawing API. This is made possible
in function \c Fl_Wayland_Window_Driver::make_current() with
\code
// to support progressive drawing
if ( (!Fl_Wayland_Window_Driver::in_flush_) && window->buffer && (!window->frame_cb)
&& (!wait_for_expose_value) ) {
Fl_Wayland_Graphics_Driver::buffer_commit(window);
}
\endcode
Thus, \c buffer_commit() runs only when \c frame_cb is NULL. If an app rapidly performs calls
to \c Fl_Window::make_current() and to drawing functions, FLTK will copy \c draw_buffer to
the Wayland buffer and instruct Wayland to map it to the display when \c frame_cb is NULL
which means that the compositor is ready to start performing a mapping operation.
This occurs when the progressive drawing operation begins. Later, \c frame_cb is generally found
non NULL when \c Fl_Wayland_Window_Driver::make_current() runs because the compositor
is busy processing the previous Wayland buffer. When the compositor has completed
this processing, the client app runs \c surface_frame_done()
which, provided member variable \c draw_buffer_needs_commit is true, calls
\c Fl_Wayland_Graphics_Driver::buffer_commit(). This makes the compositor map the
Wayland buffer in its new, more advanced, state.
An example of progressive drawing is given by FLTK's mandelbrot test app.
When maximized, this app can be seen to progressively fill its window from
top to bottom by blocks of lines, each block appearing when the compositor is ready to map
a new buffer. When the compositor is not ready, the app does not block but continues
computing and drawing in memory but not on display more lines of the desired Mandelbrot
graph.
\section wayland-buffer-factory Buffer factory
Wayland names <em>buffer factory</em> a software procedure that constructs objects of type
<tt>struct wl_buffer</tt>, that is, Wayland buffers, for use by a client application.
FLTK creates a \c wl_buffer object each time a non-GL Fl_Window is mapped on a display or resized.
FLTK triggers the creation of a Wayland buffer as follows:
<pre>Fl::flush() calls, if the window is damaged:
\e Fl_Wayland_Window_Driver::flush() calls, if the window's \c frame_cb is NULL:
\e Fl_Wayland_Graphics_Driver::buffer_commit() calls, if the window's \c buffer->wl_buffer is NULL:
\e Fl_Wayland_Graphics_Driver::create_shm_buffer() creates a Wayland buffer.</pre>
Member function \c Fl_Wayland_Graphics_Driver::create_shm_buffer()
follows this 3-step procedure to create one or more <em>shared memory pools</em> and to
construct Wayland buffers from them:
- Libdecor function <tt>libdecor_os_create_anonymous_file(off_t size)</tt> creates an adequate file
and mmap's it. This file lives in RAM because it is created by function \c memfd_create().
FLTK sets this file size to 10 MB unless the size of the buffer to be created
is larger; in that case the anonymous file is sized to twice the buffer size.
- Wayland function \c wl_shm_create_pool() shares this mmap'ed memory with the
Wayland compositor and returns an object of type <tt>struct wl_shm_pool</tt>, a shared memory pool,
which encapsulates this memory. A record of type
<tt>struct Fl_Wayland_Graphics_Driver::wld_shm_pool_data</tt> is created and associated to
the newly created \c wl_shm_pool by \c wl_shm_pool_set_user_data(). This record stores
the starting address (\c pool_memory) and size (\c pool_size) of the pool's encapsulated
memory. The record also contains member \c buffers of type <tt>struct wl_list</tt> which
stores the access point to the linked list of \c wl_buffer objects that will be created from
the \c wl_shm_pool.
- A variable named \c chunk_offset represents the offset within the pool's shared
memory available for the buffer being constructed. It equals 0 when the pool has just been
created and is updated as detailed below each time a buffer is created
from the pool. A record of type <tt>struct Fl_Wayland_Graphics_Driver::wld_buffer</tt> is created. This record will
contain (member \c wl_buffer) the address of a \c wl_buffer object that's created by function
\c wl_shm_pool_create_buffer(). This \c wl_buffer object encapsulates a section of a given
size of the pool's shared memory beginning at offset \c chunk_offset in it.
Quantity <tt>pool_memory + chunk_offset</tt> is therefore the address of the
beginning of the mmap'ed memory section encapsulated by this \c wl_buffer.
Member \c shm_pool of the newly constructed \c Fl_Wayland_Graphics_Driver::wld_buffer object is set to the address of
the current \c wl_shm_pool object. This record is added to the head of the linked list of
current pool's buffers by a call to \c wl_list_insert().
At that point, a <tt>struct Fl_Wayland_Graphics_Driver::wld_buffer</tt> record is part of the linked list of all
such records corresponding to \c wl_buffer objects created from the same \c wl_shm_pool
object, and member \c shm_pool of this record gives the address of this \c wl_shm_pool.
When a new <tt>struct Fl_Wayland_Graphics_Driver::wld_buffer</tt> record is to be created,
\code
struct wld_shm_pool_data *pool_data =
(struct wld_shm_pool_data *)wl_shm_pool_get_user_data(pool);
struct Fl_Wayland_Graphics_Driver::wld_buffer *record = wl_container_of(pool_data->buffers.next, record, link);
int chunk_offset = ((char*)record->data - pool_data->pool_memory) + record->data_size;
\endcode
gives the offset within the current pool's mmap'ed memory available for a new \c wl_buffer.
Macro \c wl_container_of() gives the address of a record belonging to a linked list of
records of the same type.
A window's \c wl_buffer is re-filled by graphics data and committed each time
the window gets redrawn, and is set to be destroyed by function
\c Fl_Wayland_Graphics_Driver::buffer_release() when \c Fl_Window::hide() runs or
the window is resized. When the \c wl_buffer is no longer in use, function
\c do_buffer_release() gets called as explained above. It destroys the
\c wl_buffer with \c wl_buffer_destroy(), and removes the corresponding
\c Fl_Wayland_Graphics_Driver::wld_buffer record from the linked list of buffers from the same \c wl_shm_pool.
Since new \c Fl_Wayland_Graphics_Driver::wld_buffer records are added at the head of the linked list, and since
the record at the head of this list is used to compute the offset within the pool's mmap'ed
memory available for a new \c wl_buffer, destruction of the last created \c wl_buffer
allows to re-use the destroyed buffer's pool's memory for a new \c wl_buffer.
When function \c do_buffer_release() finds the list of buffers from a given pool empty,
two situations can occur. 1) This pool is the current pool. Its mmap'ed memory will be
re-used from offset 0 to create future \c wl_buffer objects. 2) This pool is not
current. It gets destroyed with \c wl_shm_pool_destroy(), the pool's mmap'ed memory
is munmap'ed, and the pool's associated <tt>struct wld_shm_pool_data</tt> is freed.
In situation 1) above, the next \c wl_buffer to be created can need more memory than
the current pool's memory size. If so, the current pool gets destroyed and replaced
by a new, larger pool.
If the sum of \c chunk_offset plus the buffer size is larger than the current pool's size
when function \c create_shm_buffer() is called, \c chunk_offset is reset
to 0, and a new \c wl_shm_pool object is created and used by FLTK's "buffer factory".
This mechanism allows to access new mmap'ed memory when \c chunk_offset reaches the end of
the previous mmap'ed section.
Wayland uses also \c wl_buffer objects to support cursors. FLTK uses the "buffer factory"
described here when creating custom cursors (see \ref custom-cursor) with
function <tt>Fl_Wayland_Window_Driver::set_cursor(const Fl_RGB_Image *,…)</tt> which
calls \c create_shm_buffer() via \c set_cursor_4args(), \c custom_offscreen()
and \c create_wld_buffer().
In contrast, standard shaped-cursors (e.g., FL_CURSOR_INSERT)
use their own "buffer factory" inside Wayland functions such as
\c wl_cursor_theme_get_cursor().
Therefore, the fact that the \c wl_buffer objects behind standard cursors are never destroyed
doesn't prevent disused <tt>struct wl_shm_pool</tt> objects from being freed because those
buffers come from a distinct "buffer factory".
The "buffer factory" described here is also used by function \c offscreen_from_text() when
displaying dragged text in a DnD operation.
\section wayland-display Displays and HighDPI support
Wayland uses the concept of <em>seat</em> of type <tt>struct wl_seat</tt> which encompasses displays,
a keyboard, a mouse, and a trackpad. Although Wayland may be in principle able to deal with several
seats, FLTK's Wayland platform is conceived for one seat only. That seat may contain one or more
displays, which Wayland calls <em>outputs</em>, of type <tt>struct wl_output</tt>.
As written above, function \c registry_handle_global() discovers the available seat at start-up time.
This function also associates a listener to each display connected to the system
by calling function \c wl_output_add_listener(). This listener's member functions run
at program startup when Wayland discovers its displays (see \ref wayland-connection).
Member \c output_mode runs also when the display is resized and member
\c output_scale also when the Wayland scale factor (see below) is changed.
FLTK defines type \c struct \ref wayland-output "Fl_Wayland_Screen_Driver::output"
to store display size and scaling information.
One such record is created for each display. These records are put in a
<tt>struct wl_list</tt> accessible from member \c outputs of the single
\c Fl_Wayland_Screen_Driver object.
FLTK uses 2 distinct scaling parameters for each display:
- <tt>int wld_scale;</tt>. This member variable of the
\c struct \ref wayland-output "Fl_Wayland_Screen_Driver::output" record
typically equals 1 for standard, and 2 for HighDPI displays.
The effect of value \c n of variable \c wld_scale is
that 1 Wayland graphics unit represents a block of \c nxn pixels.
Another effect is that a drawing buffer for a surface of size WxH units
contains <tt>W * n * H * n * 4</tt> bytes.
Member function \c output_scale() mentioned above sets this value for
each system's display at startup time. Member function \c
Fl_Wayland_Graphics_Driver::buffer_commit() informs the Wayland compositor
of the value of \c wld_scale calling \c wl_surface_set_buffer_scale()
which is enough to make FLTK apps HighDPI-aware.
Under the gnome and KDE desktops, this parameter is visible in the "Settings" app,
"Displays" section, "Scale" parameter which is 200% on HighDPI displays.
- <tt>float gui_scale;</tt>. This other member variable is where FLTK's own GUI scaling mechanism
with ctrl/+/-/0/ keystrokes and with environment variable FLTK_SCALING_FACTOR operates:
when FLTK is scaled at 150%, \c gui_scale is assigned value 1.5. Function
<tt>Fl_Wayland_Screen_Driver::scale(int n, float f)</tt> assigns value \c f to the \c gui_scale
member variable of display # \c n. This variable is used by function
\c Fl_Wayland_Window_Driver::make_current() when it calls \c Fl_Wayland_Graphics_Driver::set_buffer()
that scales the graphics driver by this factor with \c cairo_scale().
Overall, an FLTK object, say an Fl_Window, of size \c WxH FLTK units occupies
<tt>int(W * gui_scale) * wld_scale x int(H * gui_scale) * wld_scale</tt> pixels
on the display.
When an \c Fl_Window is to be show()'n, \c Fl_Wayland_Window_Driver::makeWindow() creates
a <tt>struct wl_surface</tt> with \c wl_compositor_create_surface() and associates it
calling \c wl_surface_add_listener() with a 2-member listener called \c surface_listener
encharged of managing as follows the list of displays where this \c wl_surface will map.
The \c Fl_Window possesses an initially empty linked list of displays accessible at
member \c outputs of the window's \ref wld_window record.
When the \c Fl_Window, or more exactly its associated <tt>struct wl_surface</tt> is mapped
on a display, member \c surface_enter() of \c surface_listener runs.
This function adds the display where the surface belongs to <u>the end</u> of the linked
list of displays for this surface.
When a surface is dragged or enlarged across the edge of a display
in a multi-display system and expands on a second display, \c surface_enter() runs again,
and this surface's list of displays contains 2 items.
When a surface leaves a display, member \c surface_leave() of \c surface_listener runs.
It removes that display from the surface's list of displays.
Each time <u>the first</u> item of a surface's list of displays
changes, function \c change_scale() is called and applies that display's \c gui_scale
value to that surface calling \c Fl_Window_Driver::screen_num(int). When a window
is unmapped by function \c Fl_Wayland_Window_Driver::hide(), the surface's list of displays
is emptied.
<h3>Fractional scaling</h3>
The KWin and gnome compositors allow to use <em>fractional scaling</em>
that can take values between 100% and 400% that are not a multiple of 100%.
Wayland implements this rendering all <tt>wl_surface</tt>'s as if the scaling had
the next value above that is a multiple of 100% (e.g., 175% --> 200%), and downsizing them
to the desired fractional scale value at the compositing stage.
Seen from FLTK, everything runs with <tt>wld_scale</tt> having an integer value (1, 2, 3 or 4).
Recent gnome versions natively support fractional scaling. Older ones require to use these commands
to make them accept/refuse fractional scaling:
\code
gsettings set org.gnome.mutter experimental-features "['scale-monitor-framebuffer']"
gsettings reset org.gnome.mutter experimental-features
\endcode
\section wayland-mouse Mouse and trackpad handling
FLTK receives information about mouse and pointer events via a 'listener' made up of 5
pointers to functions which Wayland calls when events listed in table below occur.
These functions receive from Wayland enough information in their parameters to generate
corresponding FLTK events, that is, calls to <tt>Fl::handle(int event_type, Fl_Window *)</tt>.
<table summary="Mouse and pointer handling" border="1">
<tr><th>listener function</th><th>called by Wayland when</th><th>resulting FLTK events</th></tr>
<tr><td>\c pointer_enter</td><td>pointer enters a window</td><td>FL_ENTER</td></tr>
<tr><td>\c pointer_leave</td><td>pointer leaves a window</td><td>FL_LEAVE</td></tr>
<tr><td>\c pointer_motion</td><td>pointer moves inside a window</td><td>FL_MOVE</td></tr>
<tr><td>\c pointer_button</td><td>state of mouse buttons changes</td><td>FL_PUSH, FL_RELEASE</td></tr>
<tr><td>\c pointer_axis</td><td>trackpad is moved vertically or horizontally</td>
<td>FL_MOUSEWHEEL</td></tr>
</table>
\c pointer_listener is installed by a call to function \c wl_pointer_add_listener()
made by function \c seat_capabilities() which is itself another 'listener' made up of 2
function pointers
\code
static struct wl_seat_listener seat_listener = {
seat_capabilities,
seat_name
};
\endcode
installed by a call to function \c wl_seat_add_listener() made by function
\c registry_handle_global() when it receives a \c "wl_seat" interface.
<h3>Handling middle mouse button clicks on window titlebars</h3>
The gnome desktop, via its \c gnome-tweaks application, allows to determine what
happens when a middle mouse button click occurs on a window titlebar. To obey this
setting, FLTK implements part of the
<a href=https://wayland.app/protocols/gtk-shell>GTK Shell</a> protocol as follows.
Mutter, gnome's Wayland compositor, declares its support of the <tt>GTK Shell</tt>
protocol calling \c registry_handle_global() with its \c interface argument equal to
\c "gtk_shell1". FLTK initializes then member variable \c seat->gtk_shell of type
<tt>struct gtk_shell1*</tt>.
Member functions of \c pointer_listener mentioned above run for all mouse events
on all \c wl_surface objects. The table above describes what these functions do for
mouse events on FLTK-created \c wl_surface objects. But they also run for the
libdecor-created \c wl_surface objects corresponding to window titlebars.
Thus, member function \c pointer_enter() runs when the mouse enters a titlebar.
It calls \c Fl_Wayland_Screen_Driver::event_coords_from_surface() which calls
\c Fl_Wayland_Window_Driver::surface_to_window() which, as mentioned above, can
distinguish FLTK-created from non FLTK-created \c wl_surface objects.
This allows \c pointer_enter() to identify the entered surface as a titlebar
and to assign static global variable \c gtk_shell_surface
with the titlebar's \c wl_surface when the mouse enters a titlebar.
Similarly, member function \c pointer_leave() sets \c gtk_shell_surface to NULL
when the mouse leaves this titlebar. When there's a click on a titlebar,
member function \c pointer_button() runs this code
\code
if (gtk_shell_surface && state == WL_POINTER_BUTTON_STATE_PRESSED && button == BTN_MIDDLE) {
struct gtk_surface1 *gtk_surface = gtk_shell1_get_gtk_surface(seat->gtk_shell, gtk_shell_surface);
gtk_surface1_titlebar_gesture(gtk_surface, serial, seat->wl_seat, GTK_SURFACE1_GESTURE_MIDDLE_CLICK);
gtk_surface1_release(gtk_surface);
return;
}
\endcode
which ensures that what \c gnome-tweaks has assigned to middle-click events is executed.
At this point in time, FLTK obeys what \c libdecor decides for right-click (display the window
menu) and double-click (maximize the window) events on titlebars which may diverge
from \c gnome-tweaks settings.
\section wayland-cursor Wayland cursors
Wayland defines types <tt>struct wl_cursor</tt> and <tt>struct wl_cursor_theme</tt> to hold
cursor-related data.
FLTK uses function \c init_cursors() from file \c Fl_Wayland_Screen_Driver.cxx to obtain the
'cursor theme' name using function \c libdecor_get_cursor_settings() of library \c libdecor.
Function \c wl_cursor_theme_load() then returns a pointer to an object of type
<tt>struct wl_cursor_theme</tt> stored in member variable \c cursor_theme of the
\ref wayland-seat "Fl_Wayland_Screen_Driver::seat" record.
Function \c init_cursors() is itself called by a 'listener' called \c seat_capabilities()
installed when function \c registry_handle_global() receives a \c "wl_seat" interface, at program
startup. It is also called when the value of the Wayland scaling factor changes:
\c output_done() calls \c try_update_cursor() calls \c init_cursors(). Function \c output_done()
belongs to a 'listener' installed when function \c registry_handle_global() receives a
\c "wl_output" interface.
Each time \c Fl_Window::cursor(Fl_Cursor) runs, FLTK calls
\c Fl_Wayland_Window_Driver::set_cursor(Fl_Cursor) which calls \c wl_cursor_theme_get_cursor()
to set the current cursor shape to one of the standard shapes from the \c Fl_Cursor enumeration.
This Wayland function selects a cursor shape based on the current \c wl_cursor_theme object
and a cursor name and returns a pointer to a <tt>struct wl_cursor</tt>.
Under the gnome desktop, cursor names are the files of directory \c /usr/share/icons/XXXX/cursors/
where \c XXXX is the 'gnome cursor theme' (default= Adwaita). For example, what FLTK calls
\c FL_CURSOR_INSERT corresponds to file \c xterm therein. The full correspondance between
\c Fl_Cursor values and names of files therein is found in function
\c Fl_Wayland_Window_Driver::set_cursor(Fl_Cursor). FLTK stores in member variable
\c default_cursor of the \ref wayland-seat "Fl_Wayland_Screen_Driver::seat" record a pointer
to the currently used \c wl_cursor object,
and the current \c Fl_Cursor value in member \c standard_cursor_ of the
\c Fl_Wayland_Window_Driver object.
Finally, function do_set_cursor() of file \c Fl_Wayland_Screen_Driver.cxx makes the system pointer use
the current \c wl_cursor object to draw its shape on screen. That's done with a call to
\c wl_pointer_set_cursor() and a few other functions.
\anchor custom-cursor
<h3>Custom cursor shapes</h3>
To support custom cursors, FLTK presently uses a <u>non-public type</u>,
<tt>struct cursor_image</tt>, defined in file \c Fl_Wayland_Window_Driver.cxx as follows:
\code
struct cursor_image {
struct wl_cursor_image image;
struct wl_cursor_theme *theme;
struct wl_buffer *buffer;
int offset;
};
\endcode
This definition has been copied to the FLTK source code from file
<a href=https://gitlab.freedesktop.org/wayland/wayland/-/blob/main/cursor/wayland-cursor.c>
wayland-cursor.c</a> of the Wayland project source code
because it's not accessible via Wayland header files.
It shows that a pointer to a \c cursor_image object can also be viewed as a pointer to the
embedded <tt>struct wl_cursor_image</tt> object, this one being part of the public Wayland API.
It also shows that a <tt>struct cursor_image</tt> object has an associated
<tt>struct wl_buffer</tt> object used to contain the cursor's graphics.
Function <tt>Fl_Wayland_Window_Driver::set_cursor(const Fl_RGB_Image *rgb, int hotx, int hoty)</tt>
gives FLTK support of custom cursor shapes. It calls \c Fl_Wayland_Window_Driver::set_cursor_4args()
that creates a \c cursor_image object, allocates the
corresponding \c wl_buffer by a call to
\c Fl_Wayland_Graphics_Driver::create_shm_buffer() via \c custom_offscreen()
and \c create_wld_buffer() and draws
the cursor shape into that buffer using the offscreen-drawing method of FLTK.
The public type <tt>struct wl_cursor</tt> is essentially an array of \c wl_cursor_image objects
and a name:
\code
struct wl_cursor {
unsigned int image_count;
struct wl_cursor_image **images;
char *name;
};
\endcode
Function \c Fl_Wayland_Window_Driver::set_cursor_4args() also creates a <tt>struct wl_cursor</tt>
object containing a single \c wl_cursor_image, which is in fact the \c cursor_image.
Finally, a <tt>struct Fl_Wayland_Window_Driver::custom_cursor</tt> (see \ref wld_window) is allocated and used to memorize
the <tt>struct wl_cursor</tt> and the cursor's image and hotspot.
A pointer to this <tt>struct Fl_Wayland_Window_Driver::custom_cursor</tt> object is stored in member \c custom_cursor of the
window's \ref wld_window.
Function \c Fl_Wayland_Window_Driver::set_cursor_4args() is also called when a window with a custom
cursor is moved between distinct displays or when a display is rescaled to adapt the cursor size
to the new display's scale factor.
Static member function \c Fl_Wayland_Window_Driver::delete_cursor() is used to delete any custom cursor
shape. This occurs when a window associated to a custom cursor is un-mapped and when such a window
gets associated to a standard cursor or to a new custom cursor.
\section wayland-text Keyboard support
The "Mouse handling" section above mentioned function \c seat_capabilities() that Wayland calls when
the app discovers its "seat". Presence of flag \c WL_SEAT_CAPABILITY_KEYBOARD in argument
\c capabilities of this function indicates that a keyboard is available. In that case, a call
to \c wl_seat_get_keyboard() returns a pointer stored in member \c wl_keyboard of the
\ref wayland-seat "Fl_Wayland_Screen_Driver::seat" object,
and a call to \c wl_keyboard_add_listener() installs a 6-member listener of type
<tt>struct wl_keyboard_listener</tt>. These 6 FLTK-defined, callback functions are used as follows.
1) Function \c wl_keyboard_keymap() runs when the app starts and also if the keyboard layout
is changed during run-time. It allows initialization of access to this keyboard.
Noticeably, member \c xkb_state of type <tt>struct xkb_state*</tt> of the current
\ref wayland-seat "Fl_Wayland_Screen_Driver::seat" record is adequately initialized.
2-3) Functions \c wl_keyboard_enter() and \c wl_keyboard_leave(), called when focus enters and
leaves a surface, send \c FL_FOCUS and \c FL_UNFOCUS events to the \c Fl_Window object corresponding
to this surface.
4) Function \c wl_keyboard_key() runs each time a keyboard key is pressed or released. Its argument \c key,
to which 8 must be added, provides the keycode via function \c xkb_state_key_get_one_sym() and then the
corresponding text via function \c xkb_state_key_get_utf8() which is put in \c Fl::e_text.
Then, a few calls to functions whose name begin with \c xkb_compose_ are necessary to support
dead and compose keys. Finally a call to \c Fl::handle() sends an \c FL_KEYDOWN or \c FL_KEYUP event to
the appropriate \c Fl_Window. Also, function \c wl_keyboard_key() uses global variable
<tt>std::vector<int> key_vector</tt> to record all currently pressed keys. This is the base of the
implementation of \c Fl_Wayland_Screen_Driver::event_key(int).
5) Function \c wl_keyboard_modifiers() runs when a modifier key (e.g., shift, control) is pressed or
released. Calls to functions \c xkb_state_update_mask() and \c xkb_state_mod_name_is_active() allow FLTK
to set \c Fl::e_state adequately.
6) Function \c wl_keyboard_repeat_info() does not run, for now, because this would require version 4 of
the <tt>wl_keyboard</tt> object which has earlier versions in all tested Wayland compositors.
\section wayland-text-input Support of text input methods
When the connected Wayland compositor supports text input methods, function
\c registry_handle_global() gets called with its \c interface argument equal to
\c zwp_text_input_manager_v3_interface.name. The following call to \c wl_registry_bind() returns a
pointer to type <tt>struct zwp_text_input_manager_v3</tt> that is stored as member
\c text_input_base of the \c Fl_Wayland_Screen_Driver object.
Later, when function \c seat_capabilities() runs, \c text_input_base is found not NULL, which triggers
a call to function \c zwp_text_input_manager_v3_get_text_input() returning a value of type
<tt>struct zwp_text_input_v3 *</tt> and stored as member \c text_input of the
\ref wayland-seat "Fl_Wayland_Screen_Driver::seat" object.
Next, a call to \c zwp_text_input_v3_add_listener() associates this \c text_input with a 6-member
listener of type <tt>struct zwp_text_input_v3_listener</tt>. These 6 FLTK-defined, callback functions
are used as follows.
1-2) Functions \c text_input_enter() and \c text_input_leave() run when text input enters or leaves a
surface.
3-4) Functions \c text_input_preedit_string() and \c text_input_commit_string() are called when the
text input method prepares the client app to later insert 'marked' text or regular text, respectively.
Complex text input often begins by inserting temporary text which is said to be 'marked' before
replacing it with the text that will stay in the document. FLTK underlines or selects marked text
to distinguish it from regular text.
5) Function \c text_input_done() runs when it's time to send either regular or marked text
to the client app. This is done by function \c send_text_to_fltk() which uses static variables
\c current_pre_edit, \c pending_pre_edit and \c pending_commit to determine the sent text.
6) Function \c text_input_delete_surrounding_text() has no effect at present, without this preventing
input methods that have been tested with FLTK from working satisfactorily.
It's necessary to inform the running text input method of the current location of the insertion
point in the active surface. This information allows the input method to map its auxiliary window
close to the insertion point. The flow of information on this topic is as follows:
- The two FLTK widgets supporting text input, Fl_Input_ and Fl_Text_Display, transmit to FLTK the window
coordinates of the bottom of the current insertion point and the line height each time they change
calling function \c fl_set_spot().
- fl_set_spot() calls the platform override of virtual member function \c Fl_Screen_Driver::set_spot().
Under Wayland, this just calls
\c Fl_Wayland_Screen_Driver::insertion_point_location(int x, int y, int height) which calls
\c zwp_text_input_v3_set_cursor_rectangle() to inform the text input method about the surface
position and size of the insertion point and also memorizes this information in static member
variables of class \c Fl_Wayland_Screen_Driver.
- Callback function \c text_input_enter() calls
\c Fl_Wayland_Screen_Driver::insertion_point_location(int *x, int *y, int *height) which gives it
the stored position information, and then calls \c zwp_text_input_v3_set_cursor_rectangle() to inform the
text input method about the position of the insertion point.
\section wayland-libdecor Interface with libdecor
FLTK uses a library called
<a href= https://gitlab.freedesktop.org/libdecor/libdecor/-/blob/master/README.md>
libdecor</a> to determine whether the Wayland compositor uses CSD or SSD mode,
and also to draw window titlebars when in CSD mode (see \ref bundled-libdecor).
\c Libdecor is conceived to be present in a shared library linked to the Wayland
client application which itself, and if the running Wayland compositor uses CSD mode,
loads another shared library intended to draw titlebars in a way that best matches the
Desktop. As of late 2023, libdecor contains two titlebar-drawing plugins:
- \c libdecor-gtk intended for the Gnome desktop;
- \c libdecor-cairo for other situations.
On recent Linux distributions, FLTK uses the system \c libdecor-0.so shared library
available via packages \c libdecor-0-dev and \c libdecor-0-plugin-1-gtk.
On earlier Linux versions, or if CMake option \c FLTK_USE_SYSTEM_LIBDECOR is set
to OFF, FLTK bundles the most recent source code of \c libdecor and its plugins.
The libdecor code bundled inside FLTK is compiled and put in libfltk.
FLTK uses \c libdecor-gtk when software package \c libgtk-3-dev
is present in the build system, and \c libdecor-cairo otherwise.
FLTK prefixes all symbols of its bundled libdecor with "fl_". This allows an FLTK client app
to link to other libraries which may use the system version of libdecor.
\c Libdecor uses the Wayland protocol
<a href=https://wayland.app/protocols/xdg-decoration-unstable-v1>XDG decoration</a>
to request being decorated by a supporting compositor.
If the running compositor supports SSD, \c libdecor doesn't draw window titlebars because
the compositor does it. That is what happens with the \c KWin and \c Sway compositors.
However, if environment variable \c LIBDECOR_FORCE_CSD is defined to value \c 1 when an
FLTK app runs, \c libdecor instructs an SSD-able compositor to refrain from decorating its
windows and decorates windows itself.
Whatever the value of \c FLTK_USE_SYSTEM_LIBDECOR, FLTK and \c libdecor use environment variable
\c LIBDECOR_PLUGIN_DIR as follows: if this variable is defined and points to the name of a directory,
this directory is searched for a potential \c libdecor plugin in the form of a shared library;
if one is found, FLTK and \c libdecor load it and use it.
The \c libdecor source code bundled in FLTK is identical to that of the \c libdecor repository.
Nevertheless, FLTK uses this code with some minor changes. For example, when \c FLTK_USE_SYSTEM_LIBDECOR
is 0, FLTK needs to modify function \c libdecor_new() charged of loading the plugin, to make it use
the plugin code that is included in libfltk if none is found as a dynamic library. This is done as
follows in file \c libdecor/build/fl_libdecor.c:
\code
#define libdecor_new libdecor_new_orig
#include "../src/libdecor.c"
#undef libdecor_new
void libdecor_new() { // FLTK rewrite of this function
……
}
\endcode
FLTK compiles file \c fl_libdecor.c which includes \c libdecor.c to the effect that all of
the \c libdecor code becomes part of libfltk except that function \c libdecor_new() is substituted by
its FLTK rewrite, without file \c libdecor.c being modified at all. This trick is also used to modify
function \c libdecor_frame_set_minimized() to bypass a bug in the Weston compositor before version 10.
Similarly, FLTK compiles file \c fl_libdecor-plugins.c which includes either \c libdecor-gtk.c or
\c libdecor-cairo.c to the effect that the desired plugin becomes part of libfltk.
To support function \c Fl_Widget_Surface::draw_decorated_window() that draws a mapped window and its
titlebar, FLTK needs to perform two operations: 1) identify what plugin is operating, and 2) call
a function that is specific of that plugin and that returns the pixels of the drawn titlebar.
FLTK performs operation 1) above using its function \c get_libdecor_plugin_description() of file
\c fl_libdecor-plugins.c that returns a human readable string describing the running plugin.
Each plugin puts its own string in member \c description of a record of type
<tt>struct libdecor_plugin_description</tt>. Although this type is public in header file
\c libdecor-plugin.h, accessing the symbol defined by the plugin to store a pointer to a value of this
type is complicated for a reason and solved by a method detailed in a comment before function
\c get_libdecor_plugin_description().
Operation 2) above is done by FLTK-defined function \c fl_libdecor_titlebar_buffer() from file
\c fl_libdecor-plugins.c. This function calls \c get_libdecor_plugin_description() seen above
to get the running plugin's descriptive string. That is <tt>"GTK3 plugin"</tt> with \c libdecor-gtk.
FLTK function \c gtk_titlebar_buffer() is then called, and returns a pointer to the start of a byte
buffer containing the titlebar graphics.
That is, again, not possible with the public \c libdecor API. Therefore,
FLTK copies to \c fl_libdecor-plugins.c the definitions of several types
given in \c libdecor-gtk.c or \c libdecor-cairo.c such as type <tt>struct border_component</tt>.
\section wayland-clipboard Copy/Paste/Drag-n-Drop
FLTK follows the procedure that is very well described in item "Wayland clipboard and drag &
drop" of the \ref wayland-doc. All corresponding source code is in file
\c src/drivers/Wayland/fl_wayland_clipboard_dnd.cxx.
This part of the \ref wayland-seat "Fl_Wayland_Screen_Driver::seat" record stores pointers to
Wayland objects used for clipboard and D-n-D operations:
\code
struct wl_data_device_manager *data_device_manager;
struct wl_data_device *data_device;
struct wl_data_source *data_source;
\endcode
FLTK can copy or paste plain UTF-8 text or image data to/from the clipboard. Images are copied to the
clipboard as \c image/bmp mime type. Images in \c image/bmp or \c image/png mime types from the
clipboard can be pasted to FLTK apps.
Files dropped are received one pathname per line, with no \c '\\n' after the last pathname.
\section wayland-egl EGL as support for OpenGL
Wayland uses <a href=https://www.khronos.org/api/egl>EGL™</a> to interface OpenGL with the underlying
native platform window system. OpenGL-using FLTK apps are therefore linked to \c libwayland-egl.so and
\c libEGL.so in addition to \c libGL.so and \c libGLU.so.
EGL completely hides the \c wl_buffer objects it uses to draw to GL windows.
The \ref wld_buffer structure and the 'buffer factory' described previously are not used for
\c Fl_Gl_Window's : the \c buffer member of an \c Fl_Gl_Window's \ref wld_window structure is
always NULL.
EGL is initialized calling member function \c Fl_Wayland_Gl_Window_Driver::init()
once, the first time the \c Fl_Wayland_Gl_Window_Driver c'tor runs. That is done
with calls to <tt>eglGetDisplay(), eglInitialize()</tt>, and \c eglBindAPI().
Member function \c Fl_Wayland_Gl_Window_Driver::find() calls \c eglChooseConfig()
to filter the set of GL configurations that match the \c Fl_Gl_Window's mode(),
and puts in the returned \c Fl_Gl_Choice object the first matching configuration.
The filtering gets done with bits \c EGL_WINDOW_BIT, to support the creation of window
surfaces, and \c EGL_OPENGL_BIT, to support the creation of OpenGL contexts.
EGL needs 2 more objects created for each \c Fl_Gl_Window. They have types
<tt>struct wl_egl_window</tt> and \c EGLSurface, and are created by member
function \c Fl_Wayland_Gl_Window_Driver::make_current_before() which runs at the
beginning of \c Fl_Gl_Window::make_current().
The first argument of the call to \c wl_egl_window_create() therein has type
<tt>struct wl_surface *</tt> and is what connects EGL with the targeted Wayland window.
EGL creates with \c eglCreateContext() an object of type \c EGLContext via member
function \c Fl_Wayland_Gl_Window_Driver::create_gl_context() called by
\c Fl_Gl_Window::make_current(). Types \c EGLContext and \c GLContext are 2 names
for the same object. The call to \c eglCreateContext() is made asking for a GL
context of version at least 2. This does not prevent from obtaining contexts of higher
versions, namely above 3.2, which are compatible with version 2 (the so-called
compatibility profile) under all tested Linux systems.
FLTK function \c Fl_Gl_Window::make_current() calls overridden function
\c Fl_Wayland_Gl_Window_Driver::set_gl_context() which calls EGL function
\c eglMakeCurrent() when the cached context changes.
FLTK calls function \c Fl_Wayland_Gl_Window_Driver::swap_buffers() each time it wants a
GL context to be sent to the display. This function contains some pure GL code to
emulate an overlay buffer to support Fl_Gl_Window objects overriding their
\c draw_overlay() member function. Then, it calls function \c eglSwapBuffers().
The overridden \c Fl_Wayland_Gl_Window_Driver::resize() function is implemented with
calls to \c wl_egl_window_get_attached_size() and \c wl_egl_window_resize().
Class \c Fl_Wayland_Gl_Plugin exists to allow \c libfltk to call functions from
\c libfltk_gl, \c libwayland-egl.so or \c libEGL.so and without having \c libfltk
force linking any FLTK app with these GL-related libraries.
For example, \c Fl_Wayland_Window_Driver::flush() needs to call
\c Fl_Gl_Window::valid(0).
<h3>Throttling GL window redraws</h3>
Although no documentation covering this subject was found, the EGL library internally
uses \c wl_callback objects to throttle GL window redraws, and FLTK needs not interfere with
these operations. Nevertheless FLTK creates and uses \c wl_callback objects for GL windows in 2 cases:
- when a decorated GL window is being interactively resized.
Function \c Fl_Wayland_Gl_Window_Driver::resize() creates a \c wl_callback object,
assigns it to xid->frame_cb and calls \c wl_callback_add_listener() before calling
\c wl_egl_window_resize(). This allows the mechanism described above that prevents surfaces from
being resized too frequently to operate with decorated \c Fl_GL_Window's too.
- when a GL subwindow is being refreshed by \c Fl_Wayland_Gl_Window_Driver::swap_buffers().
FLTK checks that \c xid->frame_cb is NULL and if so creates a \c wl_callback calling
\c wl_surface_frame() before calling \c eglSwapBuffers(). This is useful if the GL subwindow
becomes entirely out from the screen area. In that case, the Mutter compositor stops signaling
that the subwindow is ready for new commits which FLTK detects because \c xid->frame_cb remains
non-NULL. If the subwindow eventually re-appears partially on-screen, \c xid->frame_cb
becomes NULL and FLTK calls \c eglSwapBuffers() to redraw the GL scene.
\section wayland-type FLTK-defined, Wayland-specific types
\anchor wld_window
<h3>struct wld_window</h3>
Defined in \c Fl_Wayland_Window_Driver.H. One such record is created
for each shown()'n Fl_Window by \c Fl_Wayland_Window_Driver::makeWindow().
Function \c fl_wl_xid(Fl_Window*) returns a pointer to the <tt>struct wld_window</tt> of its argument.
\code
struct wld_window {
Fl_Window *fl_win;
struct wl_list outputs; // linked list of displays where part or whole of window maps
struct wl_surface *wl_surface; // the window's surface
struct wl_callback *frame_cb; // non-NULL until Wayland can process new surface commit
struct Fl_Wayland_Graphics_Driver::wld_buffer *buffer; // see \ref wld_buffer
struct xdg_surface *xdg_surface;
enum Fl_Wayland_Window_Driver::kind kind; // DECORATED or POPUP or SUBWINDOW or UNFRAMED
union {
struct libdecor_frame *frame; // for DECORATED windows
struct wl_subsurface *subsurface; // for SUBWINDOW windows
struct xdg_popup *xdg_popup; // for POPUP windows
struct xdg_toplevel *xdg_toplevel; // for UNFRAMED windows
};
struct Fl_Wayland_Window_Driver::custom_cursor {
struct wl_cursor *wl_cursor;
const Fl_RGB_Image *rgb;
int hotx, hoty;
} *custom_cursor; // non-null when using custom cursor
int configured_width; // used when negotiating window size with the compositor
int configured_height;
int floating_width; // helps restoring size after un-maximizing
int floating_height;
int state; // indicates whether window is fullscreen, maximized. Used otherwise for POPUPs
bool covered; // specially for Mutter and issue #878
}
\endcode
\anchor draw_buffer
<h3>struct Fl_Wayland_Graphics_Driver::draw_buffer</h3>
Defined in file \c Fl_Wayland_Graphics_Driver.H.
One such record is created when an Fl_Image_Surface object is created.
One such record is also embedded inside each
<tt>struct Fl_Wayland_Graphics_Driver::wld_buffer</tt> record (see \ref wld_buffer).
\code
struct Fl_Wayland_Graphics_Driver::draw_buffer {
unsigned char *buffer; // address of the beginning of the Cairo image surface's byte array
cairo_t *cairo_; // used when drawing to the Cairo image surface
size_t data_size; // of buffer and wl_buffer, in bytes
int stride; // bytes per line
int width; // in pixels
};
\endcode
FLTK gives offscreen buffers the platform-dependent type \c Fl_Offscreen which is
in fact member \c cairo_ of <tt>struct Fl_Wayland_Graphics_Driver::draw_buffer</tt>.
Thus, a variable with type \c Fl_Offscreen needs be cast to type \c cairo_t*.
Static member function <tt>struct draw_buffer *offscreen_buffer(Fl_Offscreen)</tt>
of class \c Fl_Wayland_Graphics_Driver returns the \c draw_buffer record corresponding
to an \c Fl_Offscreen value.
\anchor wld_buffer
<h3>struct Fl_Wayland_Graphics_Driver::wld_buffer</h3>
Defined in file \c Fl_Wayland_Graphics_Driver.H.
One such record is created by \c Fl_Wayland_Graphics_Driver::create_wld_buffer() when
an Fl_Window is show()'n or resized, when a custom cursor shape is created, or when
text is dragged.
\code
struct Fl_Wayland_Graphics_Driver::wld_buffer {
struct draw_buffer draw_buffer; // see draw_buffer
struct wl_list link; // links all buffers from the same wl_shm_pool
struct wl_buffer *wl_buffer; // the Wayland buffer
void *data; // address of the beginning of the Wayland buffer's byte array
struct wl_shm_pool *shm_pool; // pter to wl_shm_pool from which this wl_buffer comes
bool draw_buffer_needs_commit; // true when draw_buffer has been modified but not yet committed
bool in_use; // true while being committed
bool released; // true after buffer_release() was called
};
\endcode
\anchor wayland-output
<h3>struct Fl_Wayland_Screen_Driver::output</h3>
Defined in Fl_Wayland_Screen_Driver.H. One such record is created for each display of the
system by function \c registry_handle_global() when it receives a
\c "wl_output" interface. These records are kept in a linked list of them all, and
an identifier of this linked list is stored in member \c outputs of the unique
\c Fl_Wayland_Screen_Driver object FLTK uses. Thus,
\code
Fl_Wayland_Screen_Driver *scr_driver = (Fl_Wayland_Screen_Driver*)Fl::screen_driver();
struct wl_list list_of_all_displays = scr_driver->outputs;
\endcode
gives access, the Wayland way, to the linked list of displays in the system.
\code
struct Fl_Wayland_Screen_Driver::output { // one record for each display
uint32_t id; // an identifier of the display
int x, y; // logical position of the top-left of display
int pixel_width; // in pixels
int pixel_height; // in pixels
int width; // in pixels, account for fractional scaling
int height; // in pixels, account for fractional scaling
float dpi; // at this point, always 96.
struct wl_output *wl_output; // the Wayland object for this display
int wld_scale; // Wayland scale factor
float gui_scale; // FLTK scale factor
bool done; // true means record members have been initialized
struct wl_list link; // links these records together
};
\endcode
It's possible to get the FLTK-defined record associated to a display from the
Wayland-associated object for the same display, say <tt>struct wl_output *wl_output</tt>,
by this call:
<tt>(struct Fl_Wayland_Screen_Driver::output *)wl_output_get_user_data(wl_output)</tt>.
\anchor wayland-seat
<h3>struct Fl_Wayland_Screen_Driver::seat</h3>
Defined in file \c Fl_Wayland_Screen_Driver.H. One record is created by
function \c registry_handle_global() when it receives a \c "wl_seat" or
\c wl_data_device_manager_interface.name interface. A pointer to this struct is stored in member
\c seat of the client's unique \c Fl_Wayland_Screen_Driver object.
\code
struct Fl_Wayland_Screen_Driver::seat {
struct wl_seat *wl_seat;
struct wl_pointer *wl_pointer;
struct wl_keyboard *wl_keyboard;
uint32_t keyboard_enter_serial;
struct wl_surface *keyboard_surface;
struct wl_list pointer_outputs;
struct wl_cursor_theme *cursor_theme;
struct wl_cursor *default_cursor;
struct wl_surface *cursor_surface;
struct wl_surface *pointer_focus;
int pointer_scale;
uint32_t serial;
uint32_t pointer_enter_serial;
struct wl_data_device_manager *data_device_manager;
struct wl_data_device *data_device;
struct wl_data_source *data_source;
struct xkb_state *xkb_state;
struct xkb_context *xkb_context;
struct xkb_keymap *xkb_keymap;
struct xkb_compose_state *xkb_compose_state;
char *name;
struct zwp_text_input_v3 *text_input;
struct gtk_shell1 *gtk_shell;
};
\endcode
\section wayland-doc Documentation resources
<table summary="Wayland Documentation" border="1">
<tr>
<td>
<a href=https://wayland-book.com/>The Wayland book</a>
</td>
<td>Extensive introduction to Wayland programming written by the author of the <i>sway</i>
compositor, unfortunately unachieved.
<td>
</tr>
<tr>
<td>
<a href=https://wayland.app/protocols/>Wayland Explorer</a>
</td>
<td>Documentation of all Wayland protocols, both stable and unstable.
<td>
</tr>
<tr>
<td>
<a href=https://wayland.freedesktop.org/docs/html/apa.html>Wayland Protocol Specification</a>
</td>
<td>Documentation for all functions of the Wayland core protocol.
<td>
</tr>
<tr>
<td>
<a href=https://emersion.fr/blog/2020/wayland-clipboard-drag-and-drop/>Wayland clipboard and drag & drop</a>
</td>
<td>Detailed explanation of how clipboard and drag-and-drop work under Wayland.
<td>
</tr>
<tr>
<td>
<a href=https://dcz_self.gitlab.io/posts/input_method/>Wayland and input methods</a>
</td>
<td>Blog article introducing to the issue of text input methods under Wayland.
<td>
</tr>
<tr>
<td>
<a href=https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/39>Input Method Hub</a>
</td>
<td>Entry page for input method support giving newcomers a first understanding of what input
methods are and how they are implemented in Wayland.
<td>
</tr>
</table>
*/
|