Commit 57f44a63d99f
tarball29 files changed+2462-0
@@ -0,0 +1 @@
1
+load flake@@ -0,0 +1,2 @@
1
+/target2
+/result*@@ -0,0 +1,903 @@
1
+# This file is automatically @generated by Cargo.2
+# It is not intended for manual editing.3
+version = 44
+5
+[[package]]6
+name = "aho-corasick"7
+version = "1.1.5"8
+source = "registry+https://github.com/rust-lang/crates.io-index"9
+checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"10
+dependencies = [11
+ "memchr",12
+]13
+14
+[[package]]15
+name = "appendlist"16
+version = "1.4.0"17
+source = "registry+https://github.com/rust-lang/crates.io-index"18
+checksum = "e149dc73cd30538307e7ffa2acd3d2221148eaeed4871f246657b1c3eaa1cbd2"19
+20
+[[package]]21
+name = "bitflags"22
+version = "2.13.2"23
+source = "registry+https://github.com/rust-lang/crates.io-index"24
+checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"25
+26
+[[package]]27
+name = "calloop"28
+version = "0.14.5"29
+source = "registry+https://github.com/rust-lang/crates.io-index"30
+checksum = "43b32313e937c8721c9d17ee9bf60699a6402b4b7c2cb6ac1bee0ffcaa4b4e5e"31
+dependencies = [32
+ "bitflags",33
+ "polling",34
+ "rustix",35
+ "tracing",36
+]37
+38
+[[package]]39
+name = "cc"40
+version = "1.8.0"41
+source = "registry+https://github.com/rust-lang/crates.io-index"42
+checksum = "6651c9ed80effdc7db0ff72512157f901af5e3549e341e24b1dd4887d836d838"43
+dependencies = [44
+ "find-msvc-tools",45
+ "shlex",46
+]47
+48
+[[package]]49
+name = "cfg-if"50
+version = "1.0.5"51
+source = "registry+https://github.com/rust-lang/crates.io-index"52
+checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"53
+54
+[[package]]55
+name = "chacha20"56
+version = "0.10.2"57
+source = "registry+https://github.com/rust-lang/crates.io-index"58
+checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06"59
+dependencies = [60
+ "cfg-if",61
+ "cpufeatures",62
+ "rand_core",63
+]64
+65
+[[package]]66
+name = "concurrent-queue"67
+version = "2.5.0"68
+source = "registry+https://github.com/rust-lang/crates.io-index"69
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"70
+dependencies = [71
+ "crossbeam-utils",72
+]73
+74
+[[package]]75
+name = "cpufeatures"76
+version = "0.3.1"77
+source = "registry+https://github.com/rust-lang/crates.io-index"78
+checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566"79
+dependencies = [80
+ "libc",81
+]82
+83
+[[package]]84
+name = "crossbeam-utils"85
+version = "0.8.23"86
+source = "registry+https://github.com/rust-lang/crates.io-index"87
+checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6"88
+89
+[[package]]90
+name = "cursor-icon"91
+version = "1.2.0"92
+source = "registry+https://github.com/rust-lang/crates.io-index"93
+checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"94
+95
+[[package]]96
+name = "downcast-rs"97
+version = "1.2.1"98
+source = "registry+https://github.com/rust-lang/crates.io-index"99
+checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"100
+101
+[[package]]102
+name = "drm-fourcc"103
+version = "2.2.0"104
+source = "registry+https://github.com/rust-lang/crates.io-index"105
+checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4"106
+107
+[[package]]108
+name = "enumflags2"109
+version = "0.7.12"110
+source = "registry+https://github.com/rust-lang/crates.io-index"111
+checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"112
+dependencies = [113
+ "enumflags2_derive",114
+]115
+116
+[[package]]117
+name = "enumflags2_derive"118
+version = "0.7.12"119
+source = "registry+https://github.com/rust-lang/crates.io-index"120
+checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"121
+dependencies = [122
+ "proc-macro2",123
+ "quote",124
+ "syn 2.0.119",125
+]126
+127
+[[package]]128
+name = "equivalent"129
+version = "1.0.3"130
+source = "registry+https://github.com/rust-lang/crates.io-index"131
+checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826"132
+133
+[[package]]134
+name = "errno"135
+version = "0.3.14"136
+source = "registry+https://github.com/rust-lang/crates.io-index"137
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"138
+dependencies = [139
+ "libc",140
+ "windows-sys",141
+]142
+143
+[[package]]144
+name = "fastrand"145
+version = "2.5.0"146
+source = "registry+https://github.com/rust-lang/crates.io-index"147
+checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"148
+149
+[[package]]150
+name = "find-msvc-tools"151
+version = "0.1.14"152
+source = "registry+https://github.com/rust-lang/crates.io-index"153
+checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484"154
+155
+[[package]]156
+name = "generator"157
+version = "0.8.11"158
+source = "registry+https://github.com/rust-lang/crates.io-index"159
+checksum = "1be81b31c9ce4caf7f3178eeb90662bb1864d3ad363ec6bdada3201adff19c37"160
+dependencies = [161
+ "cc",162
+ "cfg-if",163
+ "libc",164
+ "log",165
+ "rustversion",166
+ "windows-link",167
+ "windows-result",168
+]169
+170
+[[package]]171
+name = "getrandom"172
+version = "0.4.3"173
+source = "registry+https://github.com/rust-lang/crates.io-index"174
+checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"175
+dependencies = [176
+ "cfg-if",177
+ "libc",178
+ "r-efi",179
+ "rand_core",180
+]181
+182
+[[package]]183
+name = "glam"184
+version = "0.33.12"185
+source = "registry+https://github.com/rust-lang/crates.io-index"186
+checksum = "37bfe73dc8ec21f54d181e5a60554327ec3f1c600c455a75ccbfc65d2106a6c4"187
+188
+[[package]]189
+name = "hashbrown"190
+version = "0.17.1"191
+source = "registry+https://github.com/rust-lang/crates.io-index"192
+checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"193
+194
+[[package]]195
+name = "hermit-abi"196
+version = "0.5.3"197
+source = "registry+https://github.com/rust-lang/crates.io-index"198
+checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284"199
+200
+[[package]]201
+name = "indexmap"202
+version = "2.14.2"203
+source = "registry+https://github.com/rust-lang/crates.io-index"204
+checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"205
+dependencies = [206
+ "equivalent",207
+ "hashbrown",208
+]209
+210
+[[package]]211
+name = "lazy_static"212
+version = "1.5.1"213
+source = "registry+https://github.com/rust-lang/crates.io-index"214
+checksum = "20870f649af7073d53e38067b2a84312175d56ea15217e1b15bc83506ec50afb"215
+216
+[[package]]217
+name = "libc"218
+version = "0.2.190"219
+source = "registry+https://github.com/rust-lang/crates.io-index"220
+checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78"221
+222
+[[package]]223
+name = "linux-raw-sys"224
+version = "0.12.1"225
+source = "registry+https://github.com/rust-lang/crates.io-index"226
+checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"227
+228
+[[package]]229
+name = "log"230
+version = "0.4.34"231
+source = "registry+https://github.com/rust-lang/crates.io-index"232
+checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"233
+234
+[[package]]235
+name = "loom"236
+version = "0.7.2"237
+source = "registry+https://github.com/rust-lang/crates.io-index"238
+checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"239
+dependencies = [240
+ "cfg-if",241
+ "generator",242
+ "scoped-tls",243
+ "tracing",244
+ "tracing-subscriber",245
+]246
+247
+[[package]]248
+name = "matchers"249
+version = "0.2.0"250
+source = "registry+https://github.com/rust-lang/crates.io-index"251
+checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"252
+dependencies = [253
+ "regex-automata",254
+]255
+256
+[[package]]257
+name = "memchr"258
+version = "2.8.3"259
+source = "registry+https://github.com/rust-lang/crates.io-index"260
+checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"261
+262
+[[package]]263
+name = "memmap2"264
+version = "0.9.11"265
+source = "registry+https://github.com/rust-lang/crates.io-index"266
+checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0"267
+dependencies = [268
+ "libc",269
+]270
+271
+[[package]]272
+name = "monolith"273
+version = "0.0.0"274
+dependencies = [275
+ "monolith-config",276
+ "monolith-ipc",277
+ "monolith-layout",278
+ "monolith-motion",279
+ "monolith-planner",280
+ "monolith-protocols",281
+ "monolith-render-vk",282
+ "monolith-testkit",283
+ "smithay",284
+ "tracy-client",285
+]286
+287
+[[package]]288
+name = "monolith-config"289
+version = "0.0.0"290
+291
+[[package]]292
+name = "monolith-ipc"293
+version = "0.0.0"294
+295
+[[package]]296
+name = "monolith-layout"297
+version = "0.0.0"298
+299
+[[package]]300
+name = "monolith-motion"301
+version = "0.0.0"302
+303
+[[package]]304
+name = "monolith-planner"305
+version = "0.0.0"306
+307
+[[package]]308
+name = "monolith-protocols"309
+version = "0.0.0"310
+dependencies = [311
+ "smithay",312
+]313
+314
+[[package]]315
+name = "monolith-render-vk"316
+version = "0.0.0"317
+dependencies = [318
+ "smithay",319
+]320
+321
+[[package]]322
+name = "monolith-testkit"323
+version = "0.0.0"324
+dependencies = [325
+ "smithay",326
+]327
+328
+[[package]]329
+name = "nu-ansi-term"330
+version = "0.50.3"331
+source = "registry+https://github.com/rust-lang/crates.io-index"332
+checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"333
+dependencies = [334
+ "windows-sys",335
+]336
+337
+[[package]]338
+name = "once_cell"339
+version = "1.21.4"340
+source = "registry+https://github.com/rust-lang/crates.io-index"341
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"342
+343
+[[package]]344
+name = "pin-project-lite"345
+version = "0.2.17"346
+source = "registry+https://github.com/rust-lang/crates.io-index"347
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"348
+349
+[[package]]350
+name = "pkg-config"351
+version = "0.3.34"352
+source = "registry+https://github.com/rust-lang/crates.io-index"353
+checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"354
+355
+[[package]]356
+name = "polling"357
+version = "3.11.0"358
+source = "registry+https://github.com/rust-lang/crates.io-index"359
+checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"360
+dependencies = [361
+ "cfg-if",362
+ "concurrent-queue",363
+ "hermit-abi",364
+ "pin-project-lite",365
+ "rustix",366
+ "windows-sys",367
+]368
+369
+[[package]]370
+name = "portable-atomic"371
+version = "1.15.0"372
+source = "registry+https://github.com/rust-lang/crates.io-index"373
+checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"374
+375
+[[package]]376
+name = "proc-macro2"377
+version = "1.0.107"378
+source = "registry+https://github.com/rust-lang/crates.io-index"379
+checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"380
+dependencies = [381
+ "unicode-ident",382
+]383
+384
+[[package]]385
+name = "profiling"386
+version = "1.0.18"387
+source = "registry+https://github.com/rust-lang/crates.io-index"388
+checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5"389
+dependencies = [390
+ "profiling-procmacros",391
+]392
+393
+[[package]]394
+name = "profiling-procmacros"395
+version = "1.0.18"396
+source = "registry+https://github.com/rust-lang/crates.io-index"397
+checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb"398
+dependencies = [399
+ "quote",400
+ "syn 2.0.119",401
+]402
+403
+[[package]]404
+name = "quick-xml"405
+version = "0.41.0"406
+source = "registry+https://github.com/rust-lang/crates.io-index"407
+checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1"408
+dependencies = [409
+ "memchr",410
+]411
+412
+[[package]]413
+name = "quote"414
+version = "1.0.47"415
+source = "registry+https://github.com/rust-lang/crates.io-index"416
+checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"417
+dependencies = [418
+ "proc-macro2",419
+]420
+421
+[[package]]422
+name = "r-efi"423
+version = "6.0.0"424
+source = "registry+https://github.com/rust-lang/crates.io-index"425
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"426
+427
+[[package]]428
+name = "rand"429
+version = "0.10.3"430
+source = "registry+https://github.com/rust-lang/crates.io-index"431
+checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af"432
+dependencies = [433
+ "chacha20",434
+ "getrandom",435
+ "rand_core",436
+]437
+438
+[[package]]439
+name = "rand_core"440
+version = "0.10.1"441
+source = "registry+https://github.com/rust-lang/crates.io-index"442
+checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"443
+444
+[[package]]445
+name = "regex-automata"446
+version = "0.4.18"447
+source = "registry+https://github.com/rust-lang/crates.io-index"448
+checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"449
+dependencies = [450
+ "aho-corasick",451
+ "memchr",452
+ "regex-syntax",453
+]454
+455
+[[package]]456
+name = "regex-syntax"457
+version = "0.8.11"458
+source = "registry+https://github.com/rust-lang/crates.io-index"459
+checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"460
+461
+[[package]]462
+name = "reis"463
+version = "0.7.1"464
+source = "registry+https://github.com/rust-lang/crates.io-index"465
+checksum = "4b967ec6489a42067a20724f11987bb96178ea796873cbe1afd25fb93ee6f85f"466
+dependencies = [467
+ "calloop",468
+ "enumflags2",469
+ "log",470
+ "rustix",471
+]472
+473
+[[package]]474
+name = "rustix"475
+version = "1.1.5"476
+source = "registry+https://github.com/rust-lang/crates.io-index"477
+checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d"478
+dependencies = [479
+ "bitflags",480
+ "errno",481
+ "libc",482
+ "linux-raw-sys",483
+ "windows-sys",484
+]485
+486
+[[package]]487
+name = "rustversion"488
+version = "1.0.23"489
+source = "registry+https://github.com/rust-lang/crates.io-index"490
+checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"491
+492
+[[package]]493
+name = "scoped-tls"494
+version = "1.0.1"495
+source = "registry+https://github.com/rust-lang/crates.io-index"496
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"497
+498
+[[package]]499
+name = "sharded-slab"500
+version = "0.1.7"501
+source = "registry+https://github.com/rust-lang/crates.io-index"502
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"503
+dependencies = [504
+ "lazy_static",505
+]506
+507
+[[package]]508
+name = "shlex"509
+version = "2.0.1"510
+source = "registry+https://github.com/rust-lang/crates.io-index"511
+checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"512
+513
+[[package]]514
+name = "siphasher"515
+version = "1.0.4"516
+source = "registry+https://github.com/rust-lang/crates.io-index"517
+checksum = "33f4fe9184a62d842c9ef383018f3306d8ba224fd9d836f56d7288308847c256"518
+519
+[[package]]520
+name = "smallvec"521
+version = "1.16.3"522
+source = "registry+https://github.com/rust-lang/crates.io-index"523
+checksum = "5b3dc8af474f516a851ff4bd12db780f948b9250ad37211e4eec0bccea54e01b"524
+525
+[[package]]526
+name = "smithay"527
+version = "0.7.0"528
+source = "git+https://github.com/Smithay/smithay?rev=023b78b1c5a05ca512767cacc07efca55fae8be4#023b78b1c5a05ca512767cacc07efca55fae8be4"529
+dependencies = [530
+ "appendlist",531
+ "bitflags",532
+ "calloop",533
+ "cursor-icon",534
+ "downcast-rs",535
+ "drm-fourcc",536
+ "errno",537
+ "glam",538
+ "indexmap",539
+ "libc",540
+ "portable-atomic",541
+ "profiling",542
+ "rand",543
+ "reis",544
+ "rustix",545
+ "siphasher",546
+ "smallvec",547
+ "tempfile",548
+ "thiserror",549
+ "tracing",550
+ "wayland-protocols",551
+ "wayland-protocols-misc",552
+ "wayland-protocols-wlr",553
+ "wayland-server",554
+ "xkbcommon",555
+]556
+557
+[[package]]558
+name = "syn"559
+version = "2.0.119"560
+source = "registry+https://github.com/rust-lang/crates.io-index"561
+checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"562
+dependencies = [563
+ "proc-macro2",564
+ "quote",565
+ "unicode-ident",566
+]567
+568
+[[package]]569
+name = "syn"570
+version = "3.0.9"571
+source = "registry+https://github.com/rust-lang/crates.io-index"572
+checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"573
+dependencies = [574
+ "proc-macro2",575
+ "quote",576
+ "unicode-ident",577
+]578
+579
+[[package]]580
+name = "tempfile"581
+version = "3.27.0"582
+source = "registry+https://github.com/rust-lang/crates.io-index"583
+checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"584
+dependencies = [585
+ "fastrand",586
+ "getrandom",587
+ "once_cell",588
+ "rustix",589
+ "windows-sys",590
+]591
+592
+[[package]]593
+name = "thiserror"594
+version = "2.0.21"595
+source = "registry+https://github.com/rust-lang/crates.io-index"596
+checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e"597
+dependencies = [598
+ "thiserror-impl",599
+]600
+601
+[[package]]602
+name = "thiserror-impl"603
+version = "2.0.21"604
+source = "registry+https://github.com/rust-lang/crates.io-index"605
+checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524"606
+dependencies = [607
+ "proc-macro2",608
+ "quote",609
+ "syn 3.0.9",610
+]611
+612
+[[package]]613
+name = "thread_local"614
+version = "1.1.10"615
+source = "registry+https://github.com/rust-lang/crates.io-index"616
+checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070"617
+dependencies = [618
+ "cfg-if",619
+]620
+621
+[[package]]622
+name = "tracing"623
+version = "0.1.44"624
+source = "registry+https://github.com/rust-lang/crates.io-index"625
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"626
+dependencies = [627
+ "log",628
+ "pin-project-lite",629
+ "tracing-attributes",630
+ "tracing-core",631
+]632
+633
+[[package]]634
+name = "tracing-attributes"635
+version = "0.1.31"636
+source = "registry+https://github.com/rust-lang/crates.io-index"637
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"638
+dependencies = [639
+ "proc-macro2",640
+ "quote",641
+ "syn 2.0.119",642
+]643
+644
+[[package]]645
+name = "tracing-core"646
+version = "0.1.36"647
+source = "registry+https://github.com/rust-lang/crates.io-index"648
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"649
+dependencies = [650
+ "once_cell",651
+ "valuable",652
+]653
+654
+[[package]]655
+name = "tracing-log"656
+version = "0.2.0"657
+source = "registry+https://github.com/rust-lang/crates.io-index"658
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"659
+dependencies = [660
+ "log",661
+ "once_cell",662
+ "tracing-core",663
+]664
+665
+[[package]]666
+name = "tracing-subscriber"667
+version = "0.3.23"668
+source = "registry+https://github.com/rust-lang/crates.io-index"669
+checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"670
+dependencies = [671
+ "matchers",672
+ "nu-ansi-term",673
+ "once_cell",674
+ "regex-automata",675
+ "sharded-slab",676
+ "smallvec",677
+ "thread_local",678
+ "tracing",679
+ "tracing-core",680
+ "tracing-log",681
+]682
+683
+[[package]]684
+name = "tracy-client"685
+version = "0.19.0"686
+source = "registry+https://github.com/rust-lang/crates.io-index"687
+checksum = "d93fd676d8946eaeaff4881d26d2240a8d106fcaccbd47cb9f1389e3561ba490"688
+dependencies = [689
+ "loom",690
+ "once_cell",691
+ "tracy-client-sys",692
+]693
+694
+[[package]]695
+name = "tracy-client-sys"696
+version = "0.29.0"697
+source = "registry+https://github.com/rust-lang/crates.io-index"698
+checksum = "ab27f167b093214c68413a2e0bcd318f0591af475597405ff68138ba0a433379"699
+dependencies = [700
+ "cc",701
+ "windows-targets",702
+]703
+704
+[[package]]705
+name = "unicode-ident"706
+version = "1.0.27"707
+source = "registry+https://github.com/rust-lang/crates.io-index"708
+checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"709
+710
+[[package]]711
+name = "valuable"712
+version = "0.1.1"713
+source = "registry+https://github.com/rust-lang/crates.io-index"714
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"715
+716
+[[package]]717
+name = "wayland-backend"718
+version = "0.3.17"719
+source = "registry+https://github.com/rust-lang/crates.io-index"720
+checksum = "38a91b4eaddff87b1cd1074985e3713da4af2c49742d1b356b2c01670a67a078"721
+dependencies = [722
+ "cc",723
+ "downcast-rs",724
+ "rustix",725
+ "smallvec",726
+ "wayland-sys",727
+]728
+729
+[[package]]730
+name = "wayland-protocols"731
+version = "0.32.13"732
+source = "registry+https://github.com/rust-lang/crates.io-index"733
+checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6"734
+dependencies = [735
+ "bitflags",736
+ "wayland-backend",737
+ "wayland-scanner",738
+ "wayland-server",739
+]740
+741
+[[package]]742
+name = "wayland-protocols-misc"743
+version = "0.3.12"744
+source = "registry+https://github.com/rust-lang/crates.io-index"745
+checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8"746
+dependencies = [747
+ "bitflags",748
+ "wayland-backend",749
+ "wayland-protocols",750
+ "wayland-scanner",751
+ "wayland-server",752
+]753
+754
+[[package]]755
+name = "wayland-protocols-wlr"756
+version = "0.3.12"757
+source = "registry+https://github.com/rust-lang/crates.io-index"758
+checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234"759
+dependencies = [760
+ "bitflags",761
+ "wayland-backend",762
+ "wayland-protocols",763
+ "wayland-scanner",764
+ "wayland-server",765
+]766
+767
+[[package]]768
+name = "wayland-scanner"769
+version = "0.31.11"770
+source = "registry+https://github.com/rust-lang/crates.io-index"771
+checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0"772
+dependencies = [773
+ "proc-macro2",774
+ "quick-xml",775
+ "quote",776
+]777
+778
+[[package]]779
+name = "wayland-server"780
+version = "0.31.14"781
+source = "registry+https://github.com/rust-lang/crates.io-index"782
+checksum = "0dde9c29be0f723a573977de51ee455bf3dfa03652730a74f9dd3b337e374d75"783
+dependencies = [784
+ "bitflags",785
+ "downcast-rs",786
+ "rustix",787
+ "wayland-backend",788
+ "wayland-scanner",789
+]790
+791
+[[package]]792
+name = "wayland-sys"793
+version = "0.31.11"794
+source = "registry+https://github.com/rust-lang/crates.io-index"795
+checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"796
+dependencies = [797
+ "pkg-config",798
+]799
+800
+[[package]]801
+name = "windows-link"802
+version = "0.2.1"803
+source = "registry+https://github.com/rust-lang/crates.io-index"804
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"805
+806
+[[package]]807
+name = "windows-result"808
+version = "0.4.1"809
+source = "registry+https://github.com/rust-lang/crates.io-index"810
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"811
+dependencies = [812
+ "windows-link",813
+]814
+815
+[[package]]816
+name = "windows-sys"817
+version = "0.61.2"818
+source = "registry+https://github.com/rust-lang/crates.io-index"819
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"820
+dependencies = [821
+ "windows-link",822
+]823
+824
+[[package]]825
+name = "windows-targets"826
+version = "0.52.6"827
+source = "registry+https://github.com/rust-lang/crates.io-index"828
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"829
+dependencies = [830
+ "windows_aarch64_gnullvm",831
+ "windows_aarch64_msvc",832
+ "windows_i686_gnu",833
+ "windows_i686_gnullvm",834
+ "windows_i686_msvc",835
+ "windows_x86_64_gnu",836
+ "windows_x86_64_gnullvm",837
+ "windows_x86_64_msvc",838
+]839
+840
+[[package]]841
+name = "windows_aarch64_gnullvm"842
+version = "0.52.6"843
+source = "registry+https://github.com/rust-lang/crates.io-index"844
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"845
+846
+[[package]]847
+name = "windows_aarch64_msvc"848
+version = "0.52.6"849
+source = "registry+https://github.com/rust-lang/crates.io-index"850
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"851
+852
+[[package]]853
+name = "windows_i686_gnu"854
+version = "0.52.6"855
+source = "registry+https://github.com/rust-lang/crates.io-index"856
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"857
+858
+[[package]]859
+name = "windows_i686_gnullvm"860
+version = "0.52.6"861
+source = "registry+https://github.com/rust-lang/crates.io-index"862
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"863
+864
+[[package]]865
+name = "windows_i686_msvc"866
+version = "0.52.6"867
+source = "registry+https://github.com/rust-lang/crates.io-index"868
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"869
+870
+[[package]]871
+name = "windows_x86_64_gnu"872
+version = "0.52.6"873
+source = "registry+https://github.com/rust-lang/crates.io-index"874
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"875
+876
+[[package]]877
+name = "windows_x86_64_gnullvm"878
+version = "0.52.6"879
+source = "registry+https://github.com/rust-lang/crates.io-index"880
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"881
+882
+[[package]]883
+name = "windows_x86_64_msvc"884
+version = "0.52.6"885
+source = "registry+https://github.com/rust-lang/crates.io-index"886
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"887
+888
+[[package]]889
+name = "xkbcommon"890
+version = "0.9.0"891
+source = "registry+https://github.com/rust-lang/crates.io-index"892
+checksum = "a7a974f48060a14e95705c01f24ad9c3345022f4d97441b8a36beb7ed5c4a02d"893
+dependencies = [894
+ "libc",895
+ "memmap2",896
+ "xkeysym",897
+]898
+899
+[[package]]900
+name = "xkeysym"901
+version = "0.2.1"902
+source = "registry+https://github.com/rust-lang/crates.io-index"903
+checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"@@ -0,0 +1,31 @@
1
+[workspace]2
+resolver = "3"3
+members = ["crates/*"]4
+5
+[workspace.package]6
+version = "0.0.0"7
+edition = "2024"8
+license = "EUPL-1.2"9
+publish = false10
+11
+[workspace.dependencies]12
+monolith-config = { path = "crates/config" }13
+monolith-ipc = { path = "crates/ipc" }14
+monolith-layout = { path = "crates/layout" }15
+monolith-motion = { path = "crates/motion" }16
+monolith-planner = { path = "crates/planner" }17
+monolith-protocols = { path = "crates/protocols" }18
+monolith-render-vk = { path = "crates/render-vk" }19
+monolith-testkit = { path = "crates/testkit" }20
+21
+smithay = { git = "https://github.com/Smithay/smithay", rev = "023b78b1c5a05ca512767cacc07efca55fae8be4", default-features = false, features = ["wayland_frontend"] }22
+tracy-client = { version = "0.19", default-features = false }23
+24
+[workspace.lints.rust]25
+unsafe_op_in_unsafe_fn = "deny"26
+27
+[workspace.lints.clippy]28
+all = { level = "warn", priority = -1 }29
+30
+[profile.dev.package."*"]31
+opt-level = 2@@ -0,0 +1,10 @@
1
+[package]2
+name = "monolith-config"3
+version.workspace = true4
+edition.workspace = true5
+license.workspace = true6
+publish.workspace = true7
+8
+[lints]9
+workspace = true10
+@@ -0,0 +1 @@
1
+@@ -0,0 +1,10 @@
1
+[package]2
+name = "monolith-ipc"3
+version.workspace = true4
+edition.workspace = true5
+license.workspace = true6
+publish.workspace = true7
+8
+[lints]9
+workspace = true10
+@@ -0,0 +1 @@
1
+@@ -0,0 +1,10 @@
1
+[package]2
+name = "monolith-layout"3
+version.workspace = true4
+edition.workspace = true5
+license.workspace = true6
+publish.workspace = true7
+8
+[lints]9
+workspace = true10
+@@ -0,0 +1 @@
1
+@@ -0,0 +1,26 @@
1
+[package]2
+name = "monolith"3
+version.workspace = true4
+edition.workspace = true5
+license.workspace = true6
+publish.workspace = true7
+8
+[lints]9
+workspace = true10
+11
+[features]12
+profile = ["dep:tracy-client", "tracy-client/enable"]13
+14
+[dependencies]15
+monolith-config.workspace = true16
+monolith-ipc.workspace = true17
+monolith-layout.workspace = true18
+monolith-motion.workspace = true19
+monolith-planner.workspace = true20
+monolith-protocols.workspace = true21
+monolith-render-vk.workspace = true22
+smithay.workspace = true23
+tracy-client = { workspace = true, optional = true }24
+25
+[dev-dependencies]26
+monolith-testkit.workspace = true@@ -0,0 +1 @@
1
+fn main() {}@@ -0,0 +1,10 @@
1
+[package]2
+name = "monolith-motion"3
+version.workspace = true4
+edition.workspace = true5
+license.workspace = true6
+publish.workspace = true7
+8
+[lints]9
+workspace = true10
+@@ -0,0 +1 @@
1
+@@ -0,0 +1,10 @@
1
+[package]2
+name = "monolith-planner"3
+version.workspace = true4
+edition.workspace = true5
+license.workspace = true6
+publish.workspace = true7
+8
+[lints]9
+workspace = true10
+@@ -0,0 +1 @@
1
+@@ -0,0 +1,12 @@
1
+[package]2
+name = "monolith-protocols"3
+version.workspace = true4
+edition.workspace = true5
+license.workspace = true6
+publish.workspace = true7
+8
+[lints]9
+workspace = true10
+11
+[dependencies]12
+smithay.workspace = true@@ -0,0 +1 @@
1
+@@ -0,0 +1,12 @@
1
+[package]2
+name = "monolith-render-vk"3
+version.workspace = true4
+edition.workspace = true5
+license.workspace = true6
+publish.workspace = true7
+8
+[lints]9
+workspace = true10
+11
+[dependencies]12
+smithay.workspace = true@@ -0,0 +1 @@
1
+@@ -0,0 +1,12 @@
1
+[package]2
+name = "monolith-testkit"3
+version.workspace = true4
+edition.workspace = true5
+license.workspace = true6
+publish.workspace = true7
+8
+[lints]9
+workspace = true10
+11
+[dependencies]12
+smithay.workspace = true@@ -0,0 +1 @@
1
+@@ -0,0 +1,88 @@
1
+# monolith design decisions2
+3
+Running log of settled decisions and open questions. Research backing lives in `research/`.4
+5
+## Settled6
+7
+### Project8
+- smithay based, Vulkan renderer, EUPL. jay/wry are reference only, nothing is copied.9
+- calloop event loop. jay's async runtime is not ported. Its phase ordering (event handling, layout, post-layout, present) is reproduced as fixed stages in the post-dispatch pipeline.10
+- No programmable layouts. TOML config, no scripting.11
+- Tree nodes and windows live in generational slotmaps referenced by typed keys, not `Rc` graphs.12
+13
+### Tags14
+- Each window carries exactly one tag. Several tags can be viewed at once.15
+- Tag names are global, view state is per output (model B). "View on all outputs" is a command built on top.16
+- A "homeless" tag is immutable and always viewed. New windows land there unless a rule or launch context says otherwise. Hiding a window means moving it to an unviewed tag.17
+- Windows are bound to an output. New homeless windows go to the output that was active when the spawn was requested.18
+- Launch context. Windows are placed on the tag and output that were active when they were launched, not where focus is when they map. Child windows and windows of child processes inherit the parent's tag and output. A compositor `spawn` command is acceptable as the launch path.19
+- Layout state is per tag-on-output.20
+- Unplug moves windows to an output viewing their tag, otherwise the tag is disabled there. Replug restores windows that left because of the unplug.21
+- Tag order is invocation order, remembered, with a reordering command.22
+- Sticky flag exists. Scratchpads are floating sticky sets of windows. Addressing a scratchpad cycles on, next window, ..., off, with dimensions kept. A sticky scratchpad stays on the output it was last summoned to. A dismiss-scratchpad command takes an optional target, defaulting to the last active.23
+- Tags are exposed through ext-workspace-v1, one group per output, one workspace per tag per output.24
+25
+### Layout26
+- Tree per tag-on-output, edited by the user, not derived from window count.27
+- Child splits run on the axis opposite their parent. Flipping orientation transposes the whole tree. Orientation is presentation only.28
+- Viewing several tags stacks each tag's tree as a row (or column when flipped), in invocation order. Each tag keeps its own split ratios. No positional L/C/R merging.29
+- An "expand" command at any tree level lets one window or subtree fill its slot. Autotiling on merge is a possible experiment.30
+- Floating windows allowed. No pure floating mode.31
+- A "deck" monocle (stacked windows with visible edges, selected window popped out) is a later container mode.32
+33
+### Animation34
+- Animate by default. No animation under tearing or for fullscreen scanout, or the texture freezes until done.35
+- Bezier curves with velocity-matched retargeting on interrupt. Springs may be supported too.36
+- Every animation, and every phase of a multiphase plan, takes exactly its configured `t`. Plan length is `phases * t`.37
+- Interruption restarts from the current visual position and must stay continuous under repeated interruption.38
+- wry `master` (8b7ad7e6) is a worked example of multiphase rules, not law. monolith needs a new planner that handles diagonal moves, cross-output moves and large tag-merge motion groups.39
+- Old textures are held during resize, oversize areas fill clamp-to-edge. Alpha blending and transition shaders (wipe, crossfade) are allowed for content swaps.40
+- Animation starts without waiting for clients. A short timeout is acceptable.41
+- Sample animations at predicted presentation time.42
+- Geometry is subpixel (floats), rounded to physical pixels only at render.43
+44
+### Rendering45
+- Individually rounded corners, borders, custom transition shaders (niri style). No user-facing transparency, blur or shadows. Normal alpha blending of client content is supported.46
+- Fullscreen drops rounding.47
+48
+### Decorations49
+- Server-side only, no titlebars in v1. Minimal i3-style titles possibly later.50
+- Advertise xdg-decoration and org_kde_kwin_server_decoration_manager defaulting to SERVER. Claim server-side and draw only borders.51
+- Send all four tiled states plus xdg-shell v7 constrained states (niri's "no CSD" signal).52
+53
+### IPC54
+- Wayland protocols for things bars consume (ext-workspace, foreign-toplevel).55
+- Control and scripting over a unix socket speaking versioned JSONL.56
+57
+### Hardware and performance58
+- NVIDIA first, must also work on Intel and AMD. An old Intel laptop is available for testing.59
+- Hybrid laptops out of v1, but per-output render GPU stays in the data model.60
+- VRR soon. HDR later, with room left in the color path.61
+- Targets. Compositor CPU per frame p99 under 0.5 ms. Render to scanout at most 1 vblank with deadline scheduling. Fullscreen direct scanout plus tearing adds zero compositor frames. No dropped animation frames at 360 Hz.62
+- lavapipe golden tests render to plain Vulkan images, not dmabuf.63
+64
+### Taken from jay/wry (as designs)65
+1. Deadline frame scheduler, running from flip timestamps on NVIDIA.66
+2. Off-thread shm upload with commit blocked until done.67
+3. Vulkan renderer internals, with syncobj handled via DRM ioctls plus binary sync_file semaphores.68
+4. Commit blocker taxonomy.69
+5. Two-timeline tree, transactions.70
+6. In-process integration test harness.71
+7. Small items (SCHED_RR with reset-on-fork, Tracy, KMS hygiene, VRR cursor throttling, lazy Xwayland).72
+73
+## Open74
+75
+- None. Crates and milestones are in `plan.md`.76
+77
+## Settled late78
+79
+- Interrupts replan, they never stack. No plan-length cap.80
+- Velocity matching is best-effort, applied only where the replan continues the current axis.81
+- Launch context resolves through a graceful ranking. Window rule, xdg-activation token, parent toplevel, tracked systemd scope or cgroup, PID ancestry, focus at map time. The target is where the user was when the launch command began. Windows placed on an unviewed tag mark it urgent.82
+- Nested sessions are a first-class, permanently supported backend.83
+- Input hit-tests against final layout geometry, never animated geometry.84
+- smithay is pinned by git rev. The repo uses jj.85
+86
+## Deferred87
+88
+- Tearing. At the scanout milestone, carry rebased smithay #1948, #1949 and #1325 as patches and push them upstream.@@ -0,0 +1,42 @@
1
+# monolith plan2
+3
+Crate layout and milestones. Decisions behind them are in `decisions.md`.4
+5
+## Crates6
+7
+The hard logic lives in crates with no smithay dependency, so it compiles fast and takes headless property tests.8
+9
+| crate | smithay | contents |10
+|---|---|---|11
+| `planner` | no | multiphase planner, geometry, invariants, property tests |12
+| `motion` | no | beziers with exact `t`, best-effort velocity handoff, timelines sampled at presentation time |13
+| `layout` | no | tags (model B), homeless, slotmap tree, row stacking, expand, floating, scratchpads, commands, target rects |14
+| `config` | no | TOML schema, parsing, reload diffing, window rules |15
+| `ipc` | no | versioned JSONL message types shared by compositor and CLI |16
+| `render-vk` | yes | Vulkan renderer implementing smithay's renderer traits |17
+| `protocols` | yes | protocols monolith writes itself (ext-workspace, later tearing-control, output management, foreign-toplevel management, gamma) |18
+| `testkit` | yes | headless backend, sctk test clients, golden images, tree invariants |19
+| `monolith` (bin) | yes | state, shell glue, backends, frame scheduler, stage pipeline, `msg` and `spawn` subcommands |20
+21
+`motion` and `planner` stay separate. The frame scheduler starts inside the binary.22
+23
+## Backends24
+25
+- **Nested** runs inside another Wayland compositor and is supported for the life of the project, not just during early development. It renders into dmabufs and attaches them to a host `wl_surface` through linux-dmabuf, so nested and DRM share one render-to-dmabuf path and both get presentation feedback. smithay's winit backend is EGL/GLES-only, so it is not used.26
+- **DRM** on bare metal from M5.27
+- **Headless** for `testkit`.28
+29
+## Milestones30
+31
+| # | milestone | notes |32
+|---|---|---|33
+| M0 | Scaffolding. jj repo, flake (crane, devshell, lavapipe), workspace skeleton, `nix flake check` gate, Tracy feature | |34
+| M1 | Planner spike. `planner` and `motion`, new planner for diagonal, cross-output and large merge groups, property tests over random layouts and interrupt sequences, plan visualiser | parallel with M2 |35
+| M2 | Nested compositor with the Vulkan renderer. Nested host backend, smithay renderer traits, shm and dmabuf import, xdg-shell, one output, one tag, lavapipe golden tests | proves the renderer |36
+| M3 | `layout` and tags. Model B, homeless, row stacking, expand, floating, scratchpads and sticky, ext-workspace, JSONL IPC and CLI, TOML config with reload, window rules | still nested |37
+| M4 | Animation integration. Planner wired in, presentation-time sampling, clamp-to-edge snapshots, spawn and close, transition-shader hook | |38
+| M5 | DRM backend. udev, libinput, session, smithay DRM compositor, deadline scheduler from flip timestamps, explicit sync, VRR with cursor throttling, hotplug and replug restore. Tested on NVIDIA and Intel | first bare metal |39
+| M6 | Daily-driver gate. layer-shell, session-lock, idle notify and inhibit, xdg-activation with launch context, lazy Xwayland, data-control, image-copy-capture and portals | switch from wry here |40
+| M7 | Performance. Off-thread shm upload, commit blockers, fullscreen direct scanout, SCHED_RR, global-priority queues, measured against the targets | |41
+| M8 | Gaming. smithay tearing patches, tearing-control, keyboard-shortcuts-inhibit, pointer constraints, cursor policy while tearing | |42
+| Later | Custom shaders, deck mode, i3-style titles, HDR and color, hybrid GPUs, output management, gamma | |@@ -0,0 +1,43 @@
1
+{2
+ "nodes": {3
+ "crane": {4
+ "locked": {5
+ "lastModified": 1791754810,6
+ "narHash": "sha256-cN0Mgk+sQtCJcGWs9H4M0CnLRAuC4I8aCwiBTcBJs6E=",7
+ "owner": "ipetkov",8
+ "repo": "crane",9
+ "rev": "bd63bf1d0a0f6dcacd7549c714a671ee63b0db6a",10
+ "type": "github"11
+ },12
+ "original": {13
+ "owner": "ipetkov",14
+ "repo": "crane",15
+ "type": "github"16
+ }17
+ },18
+ "nixpkgs": {19
+ "locked": {20
+ "lastModified": 1791966044,21
+ "narHash": "sha256-t1hT8UW97rFCWCJO7/pClvu0AhLN5V3uir2M0ic4w/Q=",22
+ "owner": "nixos",23
+ "repo": "nixpkgs",24
+ "rev": "319e4cebef10cbb636c6ea392137b52ad105336a",25
+ "type": "github"26
+ },27
+ "original": {28
+ "owner": "nixos",29
+ "ref": "nixpkgs-unstable",30
+ "repo": "nixpkgs",31
+ "type": "github"32
+ }33
+ },34
+ "root": {35
+ "inputs": {36
+ "crane": "crane",37
+ "nixpkgs": "nixpkgs"38
+ }39
+ }40
+ },41
+ "root": "root",42
+ "version": 743
+}@@ -0,0 +1,97 @@
1
+{2
+ inputs = {3
+ nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";4
+ crane.url = "github:ipetkov/crane";5
+ };6
+7
+ outputs =8
+ {9
+ self,10
+ nixpkgs,11
+ crane,12
+ }:13
+ let14
+ lib = nixpkgs.lib;15
+ systems = [16
+ "x86_64-linux"17
+ "aarch64-linux"18
+ ];19
+ forAllSystems = f: lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});20
+21
+ mkProject =22
+ pkgs:23
+ let24
+ craneLib = crane.mkLib pkgs;25
+ src = craneLib.cleanCargoSource ./.;26
+ lavapipeIcd = "${pkgs.mesa}/share/vulkan/icd.d/lvp_icd.${pkgs.stdenv.hostPlatform.parsed.cpu.name}.json";27
+ commonArgs = {28
+ inherit src;29
+ strictDeps = true;30
+ nativeBuildInputs = [ pkgs.pkg-config ];31
+ buildInputs = with pkgs; [32
+ libxkbcommon33
+ vulkan-loader34
+ ];35
+ };36
+ cargoArtifacts = craneLib.buildDepsOnly commonArgs;37
+ in38
+ {39
+ inherit craneLib lavapipeIcd commonArgs;40
+41
+ package = craneLib.buildPackage (42
+ commonArgs43
+ // {44
+ inherit cargoArtifacts;45
+ doCheck = false;46
+ }47
+ );48
+49
+ checks = {50
+ clippy = craneLib.cargoClippy (51
+ commonArgs52
+ // {53
+ inherit cargoArtifacts;54
+ cargoClippyExtraArgs = "--all-targets -- --deny warnings";55
+ }56
+ );57
+ fmt = craneLib.cargoFmt { inherit src; };58
+ test = craneLib.cargoTest (59
+ commonArgs60
+ // {61
+ inherit cargoArtifacts;62
+ VK_DRIVER_FILES = lavapipeIcd;63
+ }64
+ );65
+ };66
+ };67
+ in68
+ {69
+ packages = forAllSystems (pkgs: {70
+ default = (mkProject pkgs).package;71
+ });72
+73
+ checks = forAllSystems (pkgs: (mkProject pkgs).checks);74
+75
+ devShells = forAllSystems (76
+ pkgs:77
+ let78
+ project = mkProject pkgs;79
+ in80
+ {81
+ default = project.craneLib.devShell {82
+ inputsFrom = [ project.package ];83
+ packages = with pkgs; [84
+ rust-analyzer85
+ vulkan-tools86
+ vulkan-validation-layers87
+ tracy88
+ ];89
+ LD_LIBRARY_PATH = lib.makeLibraryPath [ pkgs.vulkan-loader ];90
+ LAVAPIPE_ICD = project.lavapipeIcd;91
+ };92
+ }93
+ );94
+95
+ formatter = forAllSystems (pkgs: pkgs.nixfmt);96
+ };97
+}@@ -0,0 +1,371 @@
1
+# Round 2 research reports2
+3
+Raw reports gathered while the tag/layout/animation design is being hardened. Paths prefixed `smithay/` and `niri/` refer to shallow clones at HEAD 2026-10-15 (smithay `023b78b`, niri `45739bc`).4
+5
+Status6
+7
+- [x] Smithay and niri internals8
+- [x] NVIDIA driver state9
+- [x] ext-workspace and decoration semantics10
+- [x] Protocol inventory11
+12
+---13
+14
+## 1. Smithay and niri internals15
+16
+### Smithay17
+18
+**1.1 Tearing.** No async flip or tearing control. No match for `PAGE_FLIP_ASYNC`, `ASYNC`, `tearing_control` or tearing-control-v1 anywhere under `smithay/src`. The only atomic flags used are `ALLOW_MODESET`, `TEST_ONLY`, `PAGE_FLIP_EVENT` and `NONBLOCK` (`smithay/src/backend/drm/surface/atomic.rs:830`, `:900`, `:943`). Page flips go through `DrmSurface::page_flip` (`smithay/src/backend/drm/surface/atomic.rs:868`). `DrmCompositor` submit picks commit versus page_flip (`smithay/src/backend/drm/compositor/mod.rs:2541`). The narrowest patch point is `page_flip` plus the `FrameState` commit helpers (`smithay/src/backend/drm/compositor/mod.rs:697`), with a new `FrameFlags` bit plumbed from `render_frame` (`:1686`) into the try_assign paths. A custom commit path would also need drm-rs `AtomicCommitFlags` to gain an ASYNC bit first.19
+20
+**1.2 Deferred submit in `Frame::finish`.** Allowed by design. `Frame` is defined at `smithay/src/backend/renderer/mod.rs:270`, with `wait` at `:349` and `finish` returning `SyncPoint` at `:362`. `OutputDamageTracker::damage_output_internal` (`smithay/src/backend/renderer/damage/mod.rs:475`) only calls `geometry`, `src`, `opaque_regions` and `damage_since`, so there is no GPU readback between draws. `render_output_internal` draws elements, finishes once and returns the sync in `RenderOutputResult`. `DrmCompositor::render_frame` binds the swapchain dmabuf and calls `render_output` (`smithay/src/backend/drm/compositor/mod.rs:2241`), then stores the result sync into the plane config (`:2316`, `:2337`). `GlesFrame::finish_internal` (`smithay/src/backend/renderer/gles/mod.rs:2552`) exports an EGL fence when available (`:2588`), else `glFinish` and signaled (`:2597`). `SyncPoint` is an optional fence with `wait`, `is_exportable` and `export` (`smithay/src/backend/renderer/sync/mod.rs:81`, `:96`, `:104`). `DrmCompositor` attaches the exported fd as in-fence and reports `needs_sync` when fencing is unsupported or not exportable (`smithay/src/backend/drm/compositor/mod.rs:230`, `:774`). niri does the consumer CPU wait at `niri/src/backend/tty.rs:1965`. A Vulkan renderer can record into an op list and submit in `finish`, as long as `wait` is honored and `finish` returns a real fence when work is pending.21
+22
+**1.3 xdg configure/ack.** `ToplevelSurface::send_configure` (`smithay/src/wayland/shell/xdg/mod.rs:1489`) allocates a `SERIAL_COUNTER` serial, pushes `ToplevelConfigure` to `pending_configures` and sends it. `send_pending_configure` (`:1472`) only sends on `has_pending_changes`. `with_pending_state` (`:1698`) seeds `server_pending` from `current_server_state`. Ack handling (`smithay/src/wayland/shell/xdg/handlers/surface.rs:293`) calls `ack_configure` (`smithay/src/wayland/shell/xdg/mod.rs:221`), which sets `last_acked` and retains newer configures. The toplevel pre-commit hook (`:1585`) copies `last_acked` into cached pending state when a buffer is present (`:1617`). The committed serial is readable via `with_cached_state` or `with_committed_state` (`:1736`, `:1746`), and niri reads it that way (`niri/src/handlers/compositor.rs:322`). A transaction hold works through `add_pre_commit_hook` (`smithay/src/wayland/compositor/mod.rs:454`, invoked at `smithay/src/wayland/compositor/handlers.rs:276`) plus `add_blocker` (`smithay/src/wayland/compositor/mod.rs:554`). Blocker states are at `smithay/src/wayland/compositor/transaction.rs:61`, and `TransactionQueue::take_ready` (`:293`) enforces readiness and per-surface ordering. `blocker_cleared` (`smithay/src/wayland/compositor/mod.rs:641`) wakes the queue.23
+24
+**1.4 Plane assignment.** Tries primary, then cursor, then overlay (`smithay/src/backend/drm/compositor/mod.rs:2851`). Primary scanout is tried only for the single last element when no underlay overlaps and either the clear is black/transparent or the element covers the output and is opaque (`:1995`). Overlay requires `ALLOW_OVERLAY_PLANE_SCANOUT` (`:3728`), element kind `ScanoutCandidate` or `Cursor` (`:3733`), a free plane, compatible zpos and properties, and an atomic `TEST_ONLY` pass (`:3809`). Cursor assignment (`:3022`) checks kind, single assignment, sticky plane reuse, size hints and re-render on move, and falls back to the primary composite when too big. `RenderFrameResult` exposes `primary_element`, `overlay_elements` and `cursor_element` (`smithay/src/backend/drm/compositor/frame_result.rs:40`). Overlay assignment prefers sticky planes by z-index (`:3791`), which can block a later higher element if ordering shifts.25
+26
+**1.5 Vblank timing.** No `CRTC_SEQUENCE` or vblank timestamp ioctl. Smithay only consumes page flip events carrying sequence and monotonic time, as `DrmEventMetadata` (`smithay/src/backend/drm/device/mod.rs:493`). Callers must invoke `frame_submitted` after the event (`smithay/src/backend/drm/compositor/mod.rs:2614`, `smithay/src/backend/drm/output.rs:676`). Prediction is the compositor's job. Anvil estimates from last presentation plus frame duration (`smithay/anvil/src/udev.rs:1262`).27
+28
+### niri29
+30
+**1.6 Transactions.** A refcounted `Inner` with `AtomicBool completed` plus `Weak` `TransactionBlocker` (`niri/src/utils/transaction.rs:48`, `:159`). Timeout is 300 ms (`:18`) with a calloop deadline timer (`:86`). Drop of the last `Transaction` completes it and removes the timer (`:136`). Per-window state is `transaction_for_next_configure` plus a `pending_transactions` vec of (serial, Transaction) (`niri/src/window/mapped.rs:125`). `send` (`:1104`) pairs serial to transaction, and `take_pending_transaction` (`:460`) completes all serials up to the committed one so fast resizes collapse to the latest. The comment at `:466` acknowledges that a window waiting on a skipped intermediate size can look inconsistent. The close path creates a `Transaction`, passes the blocker into `start_close_animation`, and arms the timer only when not last (`niri/src/handlers/compositor.rs:272`). The `Blocker` impl maps live-and-incomplete to `Pending` (`niri/src/utils/transaction.rs:159`).31
+32
+**1.7 Custom shaders.** Config keys are `animations.window_resize`, `window_close` and `window_open` `custom_shader`, consumed at `niri/src/niri.rs:1800`, `:1810`, `:1820`. Compilation wraps the user body with prelude, epilogue and rounding alpha (`niri/src/render_helpers/shaders/mod.rs:222`, `:270`, `:313`). Resize uniforms (`:235`) are `niri_progress`, `niri_clamped_progress`, geometry matrices, corner radius, clip flag, `niri_tex_prev` and `niri_tex_next`. Open and close expose `input_to_geo`, `geo_size`, `geo_to_tex`, `progress`, `clamped_progress`, `random_seed` and `niri_tex` (`:281`, `:324`). On compile error the setter warns and keeps the prior program (`:250`). Resize falls back to the built-in shader when no custom one is set (`:199`), while open and close skip the custom path.33
+34
+**1.8 Resize rendering.** Mid-resize renders a crossfade between snapshots. `Tile` stores a `ResizeAnimation` with snapshot plus anim and fullscreen/expanded progress (`niri/src/layout/tile.rs:143`). A new size triggers `take_animation_snapshot` and a new `ResizeAnimation` (`:272`). Render builds `ResizeRenderElement` from `texture_prev` and `texture_next` with progress (`niri/src/render_helpers/resize.rs:24`, `niri/src/layout/tile.rs:1127`). Snapshots are lazy encompassing textures (`niri/src/render_helpers/snapshot.rs:54`). If the client never commits, the 300 ms deadline completes the blocker anyway, and later commits collapse via `take_pending_transaction`.35
+36
+**1.9 Animation model.** Kinds are `Easing`, `Spring` and `Deceleration` (`niri/src/animation/mod.rs:32`), curves `Linear`, `EaseOutQuad`, `EaseOutCubic`, `EaseOutExpo` and `CubicBezier` (`:44`). `SpringParams` derive damping from damping ratio and stiffness (`niri/src/animation/spring.rs:19`), with analytic under-, critically and overdamped solutions (`:141`) and durations (`:48`, `:110`). Retarget via `restarted` (`niri/src/animation/mod.rs:111`) reuses the prior config, but the spring branch copies `self.initial_velocity` rather than the passed velocity (`:128`), so spring retargets do not carry fresh velocity while easing does after rate scaling. Likely a bug. Time comes from a lazy adjustable `Clock` (`niri/src/animation/clock.rs:12`) with rate and complete-instantly flags. Sampling is `value_at(now)` (`niri/src/animation/mod.rs:249`). niri advances animations at current time (`niri/src/niri.rs:816`), then freezes the clock at predicted presentation time before rendering (`:4869`), so rendered values sample predicted presentation time.37
+38
+**1.10 Frame scheduling.** niri renders right after vblank or after an estimated-vblank timer. `on_vblank` (`niri/src/backend/tty.rs:1630`) feeds `frame_clock.presented` (`:1804`), then queues a redraw when needed or animations remain, else sends frame callbacks (`:1806`). Empty frames arm `queue_estimated_vblank_timer` for the target presentation time (`:2032`, implementation `:3056`). `FrameClock::next_presentation_time` (`niri/src/frame_clock.rs:56`) rounds up from the last presentation and returns now when VRR skipped more than one interval (`:92`). VRR toggling and on-demand VRR are at `niri/src/backend/tty.rs:1354` and `:2295`. Cursor-only updates are skipped during VRR via `SKIP_CURSOR_ONLY_UPDATES` (`:1951`, smithay logic at `smithay/src/backend/drm/compositor/mod.rs:2379`). Early rogue vblanks arriving in under half a refresh are throttled (`niri/src/utils/vblank_throttle.rs:33`). No deadline scheduling.39
+40
+**1.11 Direct scanout and tearing.** Scanout requires a fullscreen `ScanoutCandidate` with matching dmabuf feedback modifiers, enabled through `ALLOW_PRIMARY_PLANE_SCANOUT_ANY` by default (`niri/src/backend/tty.rs:1934`), cursor plane only, overlays off unless the debug flag `enable_overlay_planes` is set (`:1941`). Feedback tranches restrict to the Linear modifier for multi-GPU (`:2952`). No tearing path exists. No ASYNC commit and no tearing-control protocol.41
+42
+---43
+44
+## 4. Protocol inventory45
+46
+wry paths are under `src/ifs/` in `/home/bolt/code/wry`. smithay helpers are modules under `smithay/src/wayland/`. niri globals are created around `niri/src/niri.rs:2500` plus custom implementations in `niri/src/protocols/`. Hand-verified that niri has no syncobj code and wry has no keyboard-shortcuts-inhibit.47
+48
+| protocol | wry | smithay helper | niri | purpose |49
+| --- | --- | --- | --- | --- |50
+| wl_compositor, subcompositor, shm, wl_output, wl_seat, wl_data_device | y | y (`compositor`, `shm`, `output`, `seat`, `selection`) | y | core |51
+| xdg-shell | y `xdg_wm_base.rs` | y `shell::xdg` | y | app windows |52
+| xdg-decoration, kde-decoration, xdg-dialog | y | y `shell::xdg`, `shell::kde` | y | decoration mode, modal dialogs |53
+| wlr-layer-shell | y | y `shell::wlr_layer` | y | panels, wallpapers, lockers |54
+| ext-session-lock | y | y `session_lock` | y | screen locker |55
+| linux-dmabuf | y | y `dmabuf` | y | GPU buffers |56
+| linux-drm-syncobj | y | y `drm_syncobj` | n | explicit sync |57
+| drm-lease | y | y `drm_lease` | y (tty) | VR headsets |58
+| ext-image-copy-capture plus capture sources | y | y `image_copy_capture`, `image_capture_source` | y | modern screen capture |59
+| wlr-screencopy | y | n | y custom | legacy screen capture |60
+| wp-tearing-control | y | n | n | tearing for games |61
+| wp-fractional-scale | y | y | y | fractional scaling |62
+| viewporter | y | y | y | crop and scale |63
+| single-pixel-buffer | y | y | y (test only) | solid color buffers |64
+| presentation-time | y | y | y | presentation timestamps |65
+| content-type | y | y | n | game/video/photo hint |66
+| alpha-modifier | y | y | n | per-surface alpha |67
+| wp-fifo | y | y | n | FIFO present barrier |68
+| wp-commit-timing | y | y | n | timed commits |69
+| wp-color-management, wp-color-representation | y | n | n | HDR and color spaces |70
+| wp-cursor-shape | y | y | y | named cursors |71
+| pointer-constraints | y | y | y | pointer lock for games |72
+| relative-pointer | y | y | y | raw motion for games |73
+| pointer-gestures | y | y | y | touchpad gestures |74
+| wp-pointer-warp | y | y | n | pointer warp |75
+| tablet | y | y `tablet_manager` | y | stylus |76
+| text-input, input-method | y | y | y | IME |77
+| virtual-keyboard | y | y | y | synthetic keys |78
+| wlr virtual-pointer | y | y | y | synthetic pointer |79
+| idle-inhibit | y | y | y | block idle |80
+| ext-idle-notify | y | y | y | idle notifications |81
+| wp-security-context | y | y | y | sandbox identity |82
+| keyboard-shortcuts-inhibit | n | y | y | games/VMs grab hotkeys |83
+| xdg-activation | y | y | y | focus tokens |84
+| ext-foreign-toplevel-list | y | y | y custom | window lists |85
+| wlr-foreign-toplevel-management | y | n | y custom | control other windows |86
+| ext-workspace | y | n | y custom | workspaces for bars |87
+| wlr-output-management | y | n | y custom | runtime output config |88
+| wlr-gamma-control | y | n | y custom | night light |89
+| wlr and ext data-control | y | y `selection` | y | clipboard managers |90
+| primary-selection | y | y `selection` | y | middle-click paste |91
+| ext-background-effect | n | y | y | blur behind surfaces |92
+| xdg-foreign | n | y | y | cross-client parenting |93
+| xdg-system-bell | n | y | n | bell |94
+| xdg-toplevel-icon | y | y | n | window icons |95
+| xdg-toplevel-tag | y | y | n | stable window tag string |96
+| xdg-toplevel-drag | y | n | n | tab drag-out |97
+| ext-transient-seat | y | n | n | remote desktop seats |98
+| xwayland-shell | y | y (xwayland feature) | n | Xwayland association |99
+| xwayland-keyboard-grab | n | y (xwayland feature) | n | Xwayland grabs |100
+| xdg-output | y | y | y | output geometry |101
+| wl-fixes, wl-drm | y | y `fixes` | n (wl-drm) | registry fixes, legacy drm |102
+| xdg-session-management | y | n | n | session restore |103
+| jay-private (compositor control, tree query, tray, sync-file, screencast, xx-geometry) | y | n | n | jay IPC |104
+| hyprland-focus-grab | y | n | n | focus grab for popups |105
+| mutter-x11-interop | n | n | y custom | GNOME Xwayland interop |106
+107
+No smithay helper, so monolith writes these itself if wanted. wp-tearing-control, wp-color-management and wp-color-representation, ext-transient-seat, xdg-toplevel-drag, wlr-foreign-toplevel-management, ext-workspace, wlr-output-management, wlr-screencopy, wlr-gamma-control, xdg-session-management.108
+109
+---110
+111
+## 3. ext-workspace and decoration semantics112
+113
+Spec at `/tmp/monolith-research/ext-workspace-v1.xml`. Source revisions used by the reader include gtk main `ce6b23c`, gtk-3-24 `0f3aa45`, libadwaita main `0531b4c`, niri `45739bc`. Two early unsourced sub-reports (claiming GTK4 supports xdg-decoration and river 0.4 keeps tags) were discarded as wrong after checking source.114
+115
+### 3.1 ext-workspace-v1116
+117
+**Several active per group.** Allowed. `active` is a per-workspace state bit (xml:313), an unset bit means inactive (xml:330), `activate` "may or may not deactivate all other workspaces in the same group" (xml:391-393), and `commit` exists so deactivate-plus-activate is atomic (xml:85-86).118
+119
+**Group membership.** A workspace "may only ever be assigned to a single group at a single point in time" (xml:186-187, :248). Workspaces start unassigned (xml:72). Groups carry a set of outputs via `output_enter`/`output_leave` (xml:130-135), and the spec allows per-output sets or "all outputs (or any other arbitrary grouping)" sharing (xml:39-41). A zero-output group is not forbidden.120
+121
+**Capabilities.** Group has `create_workspace` with no guarantees (xml:144, :217-218). Workspace has `activate`, `deactivate`, `remove` (only for ungrouped workspaces, xml:371-372) and `assign(group)` (xml:337-340). Unsupported requests are silently ignored (xml:155-161, :351-357). Per-workspace `id` (stable across sessions), `name`, N-dimensional `coordinates`, states `urgent` and `hidden`.122
+123
+**Mapping tags.**124
+- Per-output tags map to one group per output, one workspace per tag, every viewed tag `active`, `coordinates=[n]` for order, `hidden` for empty tags if wanted.125
+- Global tags map to one group whose outputs are all outputs. Switching modes at runtime regroups inside one `done` batch (xml:98-102).126
+127
+**What it cannot express.** A primary tag among the active ones (only active, urgent, hidden exist). Focused output. Which window has which tag (no toplevel linkage). Per-tag client counts. Window-to-output binding. Whether `activate` is exclusive or toggling. Whether the mode is global or per-output.128
+129
+**Client handling of several active workspaces.**130
+- Waybar handles it, per-button `.active` class (`src/modules/ext/workspace_manager.cpp:387-410, :436` @4e207a0), has a deactivate action (`:475-476`), and also ships native river/dwl/mango tag modules.131
+- quickshell handles it, and its docs explicitly mention tag-based WMs (`src/windowmanager/windowset.hpp:38-53` @4f508be).132
+- astal handles it, "any combination of these is allowed" (`lib/workspace/src/workspace.vala:3-8` @cbcd9f4).133
+- ironbar main has no ext-workspace backend and assumes exactly one focused monitor (`src/modules/workspaces/mod.rs:503-508` @149c0d0). PR #1465 adds multi-focus support.134
+- sfwbar renders per-item focus fine but keeps a single global `focus` pointer (`src/workspace.c:11, 248-249` @a3594e5). Issue #389 open.135
+- eww has no ext-workspace support.136
+137
+**Protocols with tag semantics.**138
+- river-status-unstable-v1 (river 0.3.x). Per-output `focused_tags` u32 mask, `view_tags` masks without window identity, `urgent_tags`. Seat sends `focused_output` and `focused_view`. Control is stringly typed `run_command`.139
+- river 0.4 and later (0.5.0 released 2026-10-10). river-status and river-control are gone. river-window-management-v1 has no tags, the external WM owns them.140
+- dwl-ipc-unstable-v2. Per-output `tag(index, state{active,urgent}, clients, focused)` events, `active` output flag, `layout`, title, appid, `frame` batching. Requests `set_tags(mask, toggle)` and `set_client_tags(and, xor)` on the focused client.141
+- cosmic-workspace-unstable-v2. Extends ext-workspace with rename, tiling state, move_before/after, pin. Still no window mapping or counts.142
+- Hyprland socket2 text events (`workspacev2`, `focusedmonv2`, `activespecialv2`, `movewindowv2`). One workspace per window, one regular workspace per monitor plus a special overlay (inferred).143
+144
+**Reader recommendation.** Model a custom protocol on dwl-ipc v2, keyed on ext-foreign-toplevel handles with `set_window_tag(toplevel, tag)`, a manager event for per-output vs global mode, a seat-level focused-output event, per-tag `(state, clients, focused)` events and `frame` batching. Keep ext-workspace-v1 as the compatibility layer for generic bars.145
+146
+### 3.2 Client-side decorations147
+148
+**GTK3, GTK4, libadwaita.**149
+- Neither GTK3 nor GTK4 implements xdg-decoration. Both bind only `org_kde_kwin_server_decoration_manager` (gtk4 `gdk/wayland/gdkdisplay-wayland.c:667`, gtk3 `:528`).150
+- `gdk_wayland_display_prefers_ssd()` is true only when that KDE global defaults to SERVER (gtk4 `gdkdisplay-wayland.c:1270-1282`). `gtk_window_should_use_csd()` returns `!prefers_ssd` on Wayland (gtk4 `gtk/gtkwindow.c:3976-3996`, gtk3 `gtkwindow.c:6152-6177`). `GTK_CSD` is ignored on Wayland.151
+- `gtk_window_set_titlebar` forces CSD unconditionally (gtk4 `gtkwindow.c:3195`). `AdwWindow`/`AdwApplicationWindow` always install a titlebar gizmo (libadwaita `src/adw-window.c:174-176, 325-328`). So every libadwaita and headerbar app keeps its headerbar no matter what.152
+153
+**Tiled and constrained states.**154
+- GTK4 maps tiled_* to `.tiled-*` classes (`gtkwindow.c:4674-4687`) and parses xdg-shell v7 `constrained_*` into per-edge resizability (`gdktoplevel-wayland.c:762-773`, landed 4.19.1 via MR !8445). Without v7, non-tiled edges count as resizable (`:565-586`).155
+- libadwaita on any `.tiled*` sets window radius 0 and drops the outline, but keeps a 1px `$border_color` line plus a transparent 20px shadow (`src/stylesheet/widgets/_window.scss:49-63`). Only maximized/fullscreen remove everything (`:66-71`). Whether that 20px becomes input area is unverified.156
+- GTK3 binds at most xdg_wm_base v6. Adwaita squares corners on `.tiled*`.157
+- Qt records tiled edges only (qtbase `qwaylandxdgshell.cpp:169-180` @a4b1c40). Chromium tiled edges only (`ui/ozone/platform/wayland/host/xdg_toplevel.cc:256-264`).158
+159
+**Other toolkits.**160
+- Qt6 requests server-side unless frameless (`qwaylandxdgshell.cpp:197-211`), draws CSD only if refused.161
+- Chromium/Electron default to CSD. SSD is a user opt-in that appears only when the xdg-decoration manager exists (crbug 40817442). Electron Wayland CSD default since v28 (electron#39582).162
+- Firefox forces CLIENT on Wayland unless `MOZ_GTK_TITLEBAR_DECORATION` is set (`widget/gtk/nsWindow.cpp:7224-7244`), honours prefers_ssd when tabs are not in the titlebar (`:855-882`).163
+- SDL2/SDL3 prefer xdg-decoration SERVER and fall back to libdecor only when the manager is absent (SDL3 `SDL_waylandwindow.c:2451-2456`, SDL#8173).164
+- GLFW 3.4 prefers libdecor (`init.c:67`), else requests SERVER.165
+- winit requests Server unless `prefer_csd` (winit-wayland `frame.rs:176-195`).166
+- Java JBR WLToolkit defaults to GTK-drawn CSD, SSD with `-Dsun.awt.wl.WindowDecorationStyle=server` (`WLDecoratedPeer.java:50-68`).167
+168
+**Other compositors.**169
+- niri gates visibility of decoration globals on `prefer-no-csd` (`src/niri.rs:2917`) and sends all four tiled_* states as the "no CSD" signal (`src/utils/mod.rs:421-475`), with a `tiled-state` window rule override.170
+- sway forces SERVER for tiled views and honours client mode only when floating (`xdg_decoration.c:64-91` @1652c54). KDE manager defaults to SERVER (`server.c:386-394`).171
+- river 0.4 forwards the client hint to the WM, CSD by default (`river-window-management-v1.xml:552-600`), no KDE protocol.172
+173
+**Reader takeaway.** Advertise both xdg-decoration and KDE server-decoration defaulting to SERVER, always send all four tiled_* plus v7 constrained states. GTK/libadwaita windows become square and shadowless, headerbars and libadwaita's 1px line remain.174
+175
+---176
+177
+## 2. NVIDIA driver state178
+179
+Primary sources where available. nvidia-drm read from open-gpu-kernel-modules `main` (`version.mk` 615.78.08), lavapipe from Mesa `main`. URLs inline.180
+181
+### 2.1 Current drivers182
+183
+- Latest is 615.78.08, New Feature Branch, 2026-10-07 ([linuxiac](https://linuxiac.com/nvidia-615-78-08-linux-driver-brings-vulkan-latency-wayland-fixes/)).184
+- Production branch R595, latest 595.104.02 ([9to5Linux](https://9to5linux.com/nvidia-595-104-02-linux-graphics-driver-is-out-now-with-better-wayland-support)). R595 made `nvidia-drm modeset=1` the default.185
+- R610 (beta 610.43.02, May 2026) added the per-plane DRM color pipeline, FP16 EGL on Wayland, multi-planar YCbCr modifiers, mmap of dGPU dmabufs ([Phoronix](https://www.phoronix.com/news/NVIDIA-610.43.02-Linux-Driver)).186
+187
+### 2.2 NVIDIA workarounds in local trees188
+189
+- wry `src/state.rs:883` skips nvidia DRM devices when picking the initial render device unless nothing else works (likely a hybrid-laptop iGPU preference, uncommented).190
+- wry `src/backends/metal/video.rs:2010-2016` detects nvidia by driver name. `src/backends/metal/present.rs:549, :582` never set `IN_FENCE_FD` on nvidia, any version.191
+- wry `src/backends/metal/video/metal_cm/metal_cm_plane.rs:347` treats nvidia colorop 1D LUTs as low precision unless `WRY_MCM_NVIDIA_USE_LUTS` (`env.rs:232`). `metal_cm.rs:52` notes LUT size 1024.192
+- smithay `src/backend/drm/compositor/mod.rs:1183-1206, :1366-1389` disables `IN_FENCE_FD` below 560.35.03, version read from `/sys/module/nvidia_drm/version` (`:4424`), for [ogkm#622](https://github.com/NVIDIA/open-gpu-kernel-modules/issues/622).193
+- smithay `src/backend/egl/display.rs:932` ignores `EGL_BAD_PARAMETER` from `eglQueryDmaBufModifiersEXT` (nvidia since 520).194
+- smithay `src/backend/winit/mod.rs:317` resize-before-make_current ordering note.195
+- smithay `src/wayland/drm_lease/mod.rs:372` tolerates `EINVAL` on drop-master.196
+- niri `src/backend/tty.rs:73` orders `Abgr2101010` first because nvidia dGPU to iGPU copies only handle X/ABGR.197
+198
+### 2.3 Explicit sync199
+200
+- nvidia-drm sets `DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE` (`nvidia-drm-drv.c:1938`). linux-drm-syncobj-v1 client support since 555.58 (2024-06-27), needs kernel 6.8+.201
+- NVIDIA does not participate in dmabuf implicit sync. Undocumented what `DMA_BUF_IOCTL_EXPORT_SYNC_FILE` returns on its buffers. Assume empty/signalled and require syncobj from clients.202
+- Importing a timeline syncobj fd as an `OPAQUE_FD` timeline semaphore fails on NVIDIA by design ("would never", [gamescope#1662](https://github.com/ValveSoftware/gamescope/issues/1662)). The portable path is syncobj ioctls on the DRM fd plus binary `SYNC_FD` semaphore import/export, checking the importable feature bit at runtime ([forum](https://forums.developer.nvidia.com/t/vulkan-importing-external-linux-sync-file-descriptors-is-unsupported/268125)). This is what smithay does. `VK_EXT_external_semaphore_drm_syncobj` ([Vulkan-Docs#2692](https://github.com/KhronosGroup/Vulkan-Docs/pull/2692)) appears unmerged.203
+204
+### 2.4 Vulkan extensions on NVIDIA205
+206
+| extension | state |207
+|---|---|208
+| `VK_EXT_image_drm_format_modifier` | since 515.48.07, YCbCr fixes in 580.94.18 and 610 |209
+| `VK_EXT_external_memory_dma_buf` | since 515.48.07, needs modeset=1 |210
+| `VK_KHR_external_semaphore_fd` SYNC_FD | present, check feature bits |211
+| timeline semaphore as syncobj | not supported, by design |212
+| global priority | EXT since 455, KHR since 470. gamescope with CAP_SYS_NICE failed `vkCreateDevice` on 515 ([gamescope#521](https://github.com/ValveSoftware/gamescope/issues/521)). Query and fall back. |213
+| `VK_EXT_descriptor_buffer` | since 515.49.25 |214
+| `VK_EXT_descriptor_heap` | 595.58.03 stable |215
+| `VK_EXT_queue_family_foreign` | since 470.56.05 |216
+| BDA, dynamic rendering | core 1.2/1.3 |217
+218
+### 2.5 KMS219
+220
+- **Async flips.** `async_page_flip = true` (`nvidia-drm-drv.c:364`), atomic `PAGE_FLIP_ASYNC` maps to tearing (`nvidia-drm-crtc.c:1683-1795`). Kernel core only lets async commits change FB_ID, so expect fallbacks to sync.221
+- **VRR.** `VRR_ENABLED` honoured (`crtc.c:2740`). The driver forces VRR-capable displays into VRR mode by default, `conceal_vrr_caps` hides the capability ([README](https://download.nvidia.com/XFree86/Linux-x86_64/615.78.08/README/kms.html)).222
+- **Vblank events.** `nvidia_drm vblank` module parameter defaults to 0, so `enable_vblank` returns `-ENODEV` (`nvidia-drm-os-interface.c:47`, `crtc.c:2587`). `WAIT_VBLANK` and `CRTC_QUEUE_SEQUENCE` are off by default. Page-flip events still work, so timing must come from flip timestamps.223
+- **IN_FENCE_FD** since 560.28.03 beta, multi-monitor freeze fixed in 560.35.03 (ogkm#622).224
+- **OUT_FENCE_PTR.** Standard property unconfirmed. Vendor `NV_DRM_OUT_FENCE_PTR` signals when the buffer is flipped away, not on display (`drv.c:467`).225
+- **Overlay planes** exist per hardware layer (`crtc.c:3426-3437`), count per GPU unknown, direct-scanout usability undocumented.226
+- **Cursor plane** sized from `maxCursorSizeInPixels` (`drv.c:351`).227
+- **COLOR_PIPELINE** on by default with R610+ and kernel 6.19+. Pipelines can contain non-bypassable colorops, which blanks the screen on HDR enable for compositors that don't handle them. Escape hatch `color_pipeline=0` ([README](https://download.nvidia.com/XFree86/Linux-x86_64/615.78.08/README/wayland-issues.html)).228
+- **HDR_OUTPUT_METADATA** on connector (`nvidia-drm-connector.c:56, :569`), plus vendor `NV_HDR_STATIC_METADATA`.229
+- `DEGAMMA_LUT`, `CTM`, `GAMMA_LUT` supported. `COLOR_ENCODING`, `COLOR_RANGE` not.230
+231
+### 2.6 GBM and scanout232
+233
+- GBM since 495.44 via `nvidia-drm_gbm.so`, needs Mesa libgbm 21.2+.234
+- Native buffers use block-linear modifiers. Without a modifier NVIDIA assumes pitch-linear. Intersect plane `IN_FORMATS` with renderer modifiers and use dmabuf feedback scanout tranches.235
+- Same-GPU direct scanout on the primary plane works (smithay, KWin). No source on scanout from sysmem or iGPU buffers, assume a copy.236
+237
+### 2.7 Hybrid laptops238
+239
+- Internal panel usually on the iGPU, HDMI often on the dGPU ([zamundaaa 2026-07](https://zamundaaa.github.io/wayland/2026/07/31/fixing-multi-gpu.html)).240
+- Compositors usually render on the iGPU and copy to nvidia outputs. Known slow, high CPU, ~50 fps clamping, regressions as recent as 590 ([KDE 452219](https://bugs.kde.org/show_bug.cgi?id=452219)).241
+- linux-dmabuf v6 per-output target device exists, NVIDIA's side unreleased as of July 2026. No automatic mux switching, no public API to power down VRAM.242
+- Requirements are a per-output render GPU, cross-GPU copies with format quirks, and not keeping the dGPU awake.243
+244
+### 2.8 Known pitfalls 2025-2026245
+246
+- VRR plus cursor-only commits swing refresh and flicker OLEDs ([niri#1214](https://github.com/niri-wm/niri/issues/1214)). Throttle cursor to the game's frame rate under fullscreen VRR.247
+- "Pageflip timed out" persists on 595.58.03 with Blackwell ([forum](https://forums.developer.nvidia.com/t/kwin-wayland-drm-pageflip-timed-out-this-is-a-bug-in-the-nvidia-drm-kernel-driver/331077?page=2)). Freeze on monitor wake regressed in 570.124.04.248
+- Suspend VRAM preservation via kernel notifiers is default in 615.78.08. Mixing with `PreserveVideoMemoryAllocations` and systemd units breaks resume. Treat lost device state after resume as recoverable.249
+- NVIDIA heap doesn't return freed buffers, niri climbs toward 1 GiB without `GLVidHeapReuseRatio=0` ([niri wiki](https://github.com/niri-wm/niri/wiki/Nvidia)). GL-specific, Vulkan unknown.250
+- simpledrm alongside nvidia-drm needs `fbdev=1`, now default.251
+- Xwayland and Electron need explicit sync (`WaylandLinuxDrmSyncobj` for Electron).252
+253
+### 2.9 lavapipe for CI254
+255
+- dmabuf since Mesa 24.1. `EXT_external_memory_dma_buf` and `EXT_image_drm_format_modifier` need libdrm at build time and a winsys fd or writable `/dev/udmabuf` at runtime (`lp_screen.c:200-207`, `lvp_device.c:1541-1556`). Without udmabuf only import works and the modifier extension is hidden.256
+- Only `DRM_FORMAT_MOD_LINEAR` is offered (`lvp_formats.c:264, :278, :297`). Tiled modifiers, multi-modifier negotiation and KMS are untestable.257
+- Advertises descriptor_buffer, descriptor_heap, queue_family_foreign, timeline semaphores, global priority up to REALTIME (`lvp_device.c:173-302, :1763`).258
+- Accepts a pipe as a sync_file where ANV rejects it, so fence bugs can pass CI. Treat it as a rendering-correctness test only.259
+260
+### 2.10 Uncertainties261
+262
+Overlay counts per GPU, standard `OUT_FENCE_PTR`, which version added the `vblank` parameter, CAP_SYS_NICE and global priority today, NVIDIA's dmabuf implicit fences, direct scanout from sysmem. The README `kms.html` is stale against source in several places, source was trusted.263
+264
+---265
+266
+## 5. Smithay tearing status267
+268
+GitHub data pulled unauthenticated (rate limit hit partway), line numbers against the local smithay clone `023b78b`.269
+270
+### 5.1 Upstream PRs and issues271
+272
+- [#1325](https://github.com/Smithay/smithay/pull/1325) `wp_tearing_control_v1` helper (PolyMeilex). Open since 2024-02, mergeable. Drakulix "Wayland side LGTM". YaLTeR wants compositor-decided policy. Unmerged only because the DRM side never landed.273
+- [#1335](https://github.com/Smithay/smithay/pull/1335) initial async flip (PolyMeilex). Draft, conflicting, last activity 2026-02-28. cmeissl on 2026-02-16 "imo it does not make sense without the atomic backend".274
+- [#1948](https://github.com/Smithay/smithay/pull/1948) async page flip v2 (cmeissl). Open, conflicting, no maintainer activity since March 2026. Compositor passes desired mode into `render_frame`, overlays disabled while async, async only when the primary plane alone changed compatibly, `async_flip_failed` triggers sync retry. Testers report it working on AMD, niri on AMD, NVIDIA Turing (nvidia-open) and RTX 4060 (610.43, kernel 6.18). Design intent is to keep the decision compositor policy.275
+- [#1949](https://github.com/Smithay/smithay/pull/1949) async formats (cmeissl, draft). On top of #1948, adds `IN_FORMATS_ASYNC` per plane and an async dmabuf feedback tranche, needed for Intel.276
+- [#2099](https://github.com/Smithay/smithay/pull/2099) closed 2026-07-17, Drakulix redirected to #1325 and #1948.277
+- Tracker [#781](https://github.com/Smithay/smithay/issues/781) lists tearing-control as not implemented. No CHANGELOG entries.278
+279
+### 5.2 drm-rs280
+281
+`AtomicCommitFlags::PAGE_FLIP_ASYNC` exists in 0.14.0, 0.14.1 and `develop` (`src/control/mod.rs:1524`). `DriverCapability::AtomicASyncPageFlip` and `PageFlipFlags::ASYNC` exist too. `atomic_commit` passes bits straight to the ioctl. No drm-rs patch needed. The gap is smithay's `AtomicDrmSurface::page_flip` hardcoding `PAGE_FLIP_EVENT | NONBLOCK` (`smithay/src/backend/drm/surface/atomic.rs:898-903`).282
+283
+### 5.3 Other compositors284
+285
+- cosmic-comp pins upstream smithay `cf43a02`, no tearing. [#248](https://github.com/pop-os/cosmic-comp/issues/248) open, "not a very high priority". [PR #2588](https://github.com/pop-os/cosmic-comp/pull/2588) closed by Drakulix, who said it belongs in smithay and that composited tearing still syncs on the cross-GPU copy, gaining little latency.286
+- niri [#844](https://github.com/niri-wm/niri/issues/844) open. YaLTeR "We're mainly waiting for Smithay/smithay#1335", and in [discussion #2450](https://github.com/niri-wm/niri/discussions/2450) "There's nobody driving the Smithay PR... just someone to finish the Smithay PR." No timeline.287
+- Forks carrying #1948 plus #1949. [urayde/niri](https://github.com/urayde/niri) (AUR `niri-tearing-git`, pins `urayde/smithay@6df9a643`, users report busy-looping frame callbacks while tearing), niri-spicy (`losnoco/smithay` `spicy-master`), [alexparlett/smithay#4](https://github.com/alexparlett/smithay/pull/4) rebase from 2026-10-10. Pinnacle, MagmaWM, StrataWM, fht-compositor have nothing.288
+289
+### 5.4 Kernel constraints (torvalds master)290
+291
+- 6.8 added atomic async flips and `DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP`. 6.11 allowed changing `IN_FENCE_FD` and `FB_DAMAGE_CLIPS`. 6.15 let drivers accept non-primary planes via `atomic_async_check`. 6.18 fixed a 6.15-6.17 regression for unchanged values on non-primary planes.292
+- `drm_atomic_set_property` (`drm_atomic_uapi.c:1190-1297`). Connector and CRTC props only with unchanged values. Plane props unchanged except `FB_ID`, `IN_FENCE_FD`, `FB_DAMAGE_CLIPS`, on primary always and on others if the driver allows. smithay writes `VRR_ENABLED` each flip (`atomic.rs:1198`), fine since unchanged.293
+- amdgpu fast updates only, overlays from about 6.15, cursor rejected, async disables PSR, `IN_FORMATS_ASYNC` in 7.2.294
+- i915/xe require unchanged stride, modifier, format, rects and plane set. The first async request is silently done as sync. `IN_FORMATS_ASYNC` in 6.16.295
+- nouveau primary only. nvidia-drm maps async to tearing (515-615), primary only, format handling unknown.296
+- Async flips with NONBLOCK still deliver a flip event, at latch time not vblank. On failure userspace must retry sync. Async plus VRR unverified on all drivers.297
+298
+### 5.5 Options299
+300
+- **Wait.** Nothing is moving. #1948 has had no maintainer action for about seven months.301
+- **Contribute upstream, carry #1948 meanwhile (reader's recommendation).** Rebase onto HEAD, which conflicts with `commit_frame` and `handle_flip(&PreparedFrame)` (`smithay/src/backend/drm/compositor/mod.rs:2484-2600`). Touch points `FrameState::commit`/`page_flip` (`:732`, `:747`), `submit` (`:2541`), `handle_flip` (`:2570`), overlay and cursor assignment (`:3709`, `:3022`), the `render_frame` diff base (`:1784`), `DrmSurface::page_flip` (`surface/mod.rs:438`), `AtomicDrmSurface::page_flip` (`atomic.rs:868`), capability probing in `device/mod.rs`. Then #1949 and #1325. About 400 lines across 12 files on a fast-moving file.302
+- **Side-channel commit, not viable.** `current_frame`/`pending_frame`/`queued_frame` (`:1069-1072`) go stale and `render_frame` diffs against them (`:1784`). `DrmDevice` reports every flip as an anonymous `DrmEvent::VBlank(crtc)` (`device/mod.rs:544-554`), so `frame_submitted` (`:2618`) would retire the wrong frame and could re-render into a buffer still on screen. `used_planes` (`atomic.rs:171`) and swapchain accounting drift. Concurrent commits likely EBUSY (unverified).303
+- **Composited tearing** (async-flip the compositor's own buffer) works on primary-only drivers, gains little where a cross-GPU copy is involved, reasonable fallback on single GPU when the game can't be scanned out.304
+305
+---306
+307
+## 6. wry `master` animation system (the reference design)308
+309
+Snapshot of wry `master` (`8b7ad7e6`, 2026-08-11) extracted to `/tmp/monolith-research/wry-master`. Paths relative to that root. This supersedes `wry-reports.md` section 5 for animation semantics, which described the later `jay-port` branch.310
+311
+### 6.1 Data model312
+313
+Animations interpolate integer `Rect`s per tree node, nothing else. `AnimationState` (`src/animation.rs:28`) holds `windows` (`WindowAnimation`), `phased` (`PhasedWindowAnimation`) and `exits` (`ExitAnimation`) plus a tick handle. `WindowAnimation` stores `from, to, start_nsec, duration_nsec, curve, last_damage, retained` (`:480`). `PhasedWindowAnimation` stores `segments`, `start_nsec`, per-phase `duration_nsec`, `curve`, `last_damage`, `final_rect`, `route_edges`, `retained` (`:506`). `ExitAnimation` adds a retained toplevel, `frame_inset`, `source_body_size`, `active`, `layer` (`:666`). Render queries `visual_rect(node, layout, now)` (`src/renderer.rs:218, :481, :1090`). Containers offset children through `presentation_child_body` (`src/renderer.rs:471`). Floats animate outer frame and body as two nodes (`src/tree/float.rs:401`).314
+315
+### 6.2 Multiphase planning316
+317
+A phase is a synchronized step list where every step is a pure horizontal or vertical move or scale (`crates/layout-animation/src/lib.rs:1650`). Diagonal or mixed-axis motion of a single rect is rejected. A plan is `Vec<MultiphasePhase>` (`:362`).318
+319
+`plan_forward` (`:696`) tries single-action, then `SpaceThenOrthogonalGrowth` per axis, then `HierarchyOrderedScales`, then `OrientationChange` per axis, then `SwapLanes` per axis. `plan_no_overlap_explained` (`:522`) rejects empty bounds, empty windows, duplicate nodes, initial or final overlap (`:651`), and undersized targets via `sane_min_size` of `bounds/8 min 1` (`:674`, `:1777`). It tries forward, then the reversed request with the plan reversed (`:1682`, `:1699`). Strategy enum `NoOp, SingleAction, MixedSinglePhase, HierarchyOrderedScales, OrientationChange, SwapLanes, SpaceThenOrthogonalGrowth, ReversedForwardPlan` (`:317`).320
+321
+Batching partitions windows into motion groups by intersecting `from.union(to)` expanded by clearance (`:618`, `:1750`). Clearance is `border` or `gap + 2*border` (`src/state/animations.rs:274`). Groups with any `Plain`-style node run plain (`:259`), otherwise phased with plain fallback on diagnostic failure (`:280`).322
+323
+`validate_plan_continuous_diagnostic` (`crates/layout-animation/src/lib.rs:1444`) checks each step starts at the current rect, no duplicate or unknown node per phase, pairwise linear motion overlap-free across the whole phase via rational time-interval intersection (`:1540`), and every node ends at its target. `validate_phase_paths` does the same for retarget paths (`:572`). Swap lanes snap sub-pixel remainders within `SWAP_AXIS_SNAP_PX = 1` (`:169`).324
+325
+### 6.3 Interruption326
+327
+Plain `set_target` restarts from the current visual rect (`src/animation.rs:190`). Same target keeps running, `from == new` cancels (`:206`). No velocity stored or used, sampling is `lerp_rect(from, to, curve(t))` (`:751`).328
+329
+Batched multiphase interruption builds requests from `visual_rect(node, old, now)` (`src/state/animations.rs:246`) and tries, in order, existing phased retarget, bridged retarget, fresh plan (`:280`).330
+- Existing retarget routes each node through its stored `route_edges` graph to the new target with BFS `rect_graph_route` and validates the combined paths (`src/animation.rs:554, :599`, `src/state/animations.rs:320`).331
+- Bridged retarget routes from the current visual position back to the stale `candidate.old`, pads shorter bridges with holds, then appends a fresh plan from settled rects (`src/state/animations.rs:355, :64`).332
+- Duplicate candidates within one layout pass coalesce to first `old` and last `new` with chained hierarchy (`:32`).333
+334
+Repeated interruption re-roots every plan at displayed rects while `route_edges` accumulate history, so paths can reverse along prior lanes (tests `phased_route_remembers_original_path_after_retarget` at `src/animation.rs:964`, `bridged_retarget_handles_second_rotation_interrupt` at `src/state/animations.rs:698`).335
+336
+### 6.4 Curves and timing337
+338
+`Linear` or pregenerated piecewise cubic bezier (`crates/layout-animation/src/lib.rs:8`). Presets linear, ease, ease-in, ease-out, ease-in-out, unknown falls back to ease-out (`:30`). Custom beziers need finite values and `x1, x2` in `0..=1` (`:40`). Sampling flattens to at most 33 points and lerps (`:71`, `:86`).339
+340
+`duration_ms` is total for plain and exit, per phase for phased, so phased total is `duration * phases` (`src/animation.rs:523`). Zero duration or empty rect disables (`:200`, `:255`). Time is `eng.now()` (`src/state.rs:598`), sampled at queue, render and latch time, no predicted presentation time (`src/animation.rs:731`, `src/renderer.rs:209`).341
+342
+Ticks are driven by output latch events. `ensure_animation_tick` attaches a latch listener to every output and damages full output extents (`src/state/animations.rs:623`). `after_latch` re-damages active unions and detaches when idle (`src/animation.rs:725`).343
+344
+### 6.5 Client buffers345
+346
+No resize transaction or blocking. `tl_change_extents` queues the animation from `prev` to `rect` and configures the client immediately (`src/tree/toplevel.rs:193`). The renderer stretches the live surface into the current visual body with a scissor equal to it, `stretch` set whenever content size differs (`src/renderer.rs:774, :836, :1111`). A new buffer mid-animation is simply stretched into the next sampled rect. Moves never snapshot, `retained` is `None` for plain, phased and spawn-in (`src/animation.rs:230, :280, :296`). Only close captures a `RetainedToplevel` (`:87`).347
+348
+### 6.6 Spawn, close, other349
+350
+Spawn-in grows from an empty rect at the target centre (`src/animation.rs:739`, test `:1057`). Spawn-out shrinks from the current visual rect to centre, rendering the retained texture plus border and background (`:317`, `src/renderer.rs:530`). Tiled resize/move use multiphase when plannable, else plain. Floating map uses spawn-in (`src/tree/float.rs:164`), floating move/resize queue plain animations for outer and body (`:401`), tile-to-float is linear (`src/tree/toplevel.rs:1252`). Mono collapse and child removal set `animate_next_layout` or wrap in `with_layout_animations` (`src/tree/container.rs:1301, :1977`).351
+352
+### 6.7 Scheduling and damage353
+354
+Start damages `old.union(new)` plus border (`src/state/animations.rs:208`). Multiphase start damages the union of all phase endpoints (`:446`). Each latch tick runs `damage_active` over `last ∪ current ∪ target` per animation, drops finished ones, returns liveness (`src/animation.rs:419`). Layout batching wraps `perform_layout` in `begin_layout_animation_batch` / `finish_layout_animation_batch` inside the `container_layout` task (`src/tree/container/tasks.rs:14`).355
+356
+### 6.8 Config357
+358
+`[animations]` with `enabled` (default false), `duration-ms` (160), `style` `plain` or `multiphase` (default multiphase), `curve` preset or `[x1,y1,x2,y2]` (default ease-out) (`crates/toml-spec/spec/spec.generated.md:1081`, `crates/jay-config-schema/src/animations.rs:1`). Runtime IPC mirrors these, duration clamped to 10000 (`src/config/handler/options.rs:61`, `src/config/handler/dispatch.rs:395`). Disabling clears state and damages root (`src/state/animations.rs:564`). Scoped overrides `with_layout_animations`, `with_linear_layout_animations`, `suppress_animations_for_next_layout` (`:597`, `:606`, `src/state.rs:253`).359
+360
+### 6.9 Tests361
+362
+About 40 planner tests in `crates/layout-animation` (swaps, rotations, lane direction and clearance, uneven splits, orientation changes, hierarchy-ordered scales, generated sibling swaps and redistributions, mixed single phase vs diagonal rejection, reverse equivalence, diagnostics, continuous validation). `src/animation.rs:772` onward covers lerp, bezier, spawn/exit, per-phase duration, route memory, interruption and retargeting. `src/state/animations.rs:643` onward covers plain-style groups, bridged interrupts and candidate coalescing.363
+364
+### 6.10 Limitations visible in code365
+366
+- Only single-axis move or scale steps are expressible. Diagonal motion falls back to overlapping plain interpolation.367
+- No-overlap holds only within a clearance-expanded motion group. Cross-output or cross-scale moves skip animation (`src/state/animations.rs:177`).368
+- Durations are per phase, so complex plans exceed `duration-ms`.369
+- Interruption is position-based, no velocity continuity.370
+- Retained snapshots only for exits. Live resize visibly stretches the client buffer.371
+- 1px swap snap and `bounds/8` minimum shrink are deliberate imperceptibility heuristics.@@ -0,0 +1,118 @@
1
+# What monolith should take from jay/wry2
+3
+Companion to `wry-reports.md`, which holds the raw findings and citations.4
+5
+## The async runtime6
+7
+Don't port it. Port the phase discipline.8
+9
+Why it doesn't pay off inside a smithay compositor.10
+11
+- Most of jay's I/O is client sockets. In smithay those belong to wayland-backend, which reads and writes through its own readiness loop in `dispatch_clients`/`flush_clients`. Moving to io_uring would leave only DRM events, udev, libinput and timers on the ring, a handful of completions per frame.12
+- Even inside jay the raw speed win is small. A frame costs on the order of tens to low hundreds of syscalls, well under 1% of a 6.9 ms frame at 144 Hz. Input-to-photon latency is decided by when you render, not by how cheaply you wake up.13
+- Tasks can't hold `&mut State`, so jay runs on `Rc<State>` with `Cell`/`CloneCell`/`CopyHashMap` everywhere. Smithay's handler traits hand you `&mut D`. Running both models means fighting every delegate.14
+- There is no fallback when io_uring is unavailable (`kernel.io_uring_disabled`, hardened kernels, some sandboxes). Jay exits.15
+- Every smithay helper that is a calloop `EventSource` (`DrmDeviceNotifier`, `LibinputInputBackend`, `XWayland`, `DrmSyncPointSource`, `DmabufSource`, the socket listener) would need rewrapping.16
+17
+What it does well, and the calloop equivalent.18
+19
+| jay feature | real benefit | in monolith |20
+|---|---|---|21
+| Four ordered phases fed by dirty queues | N events collapse into one layout and one render per pass, with deterministic ordering | Fixed stages in the post-dispatch closure (layout, derived state, animation tick, render, flush), each with a dirty set and a scheduled flag |22
+| `now()` frozen per pass | Every animation computed in a pass sees one timestamp | Store an `Instant` in `State` at the top of the post-dispatch closure |23
+| Async fns for sequential flows (present loop, xwm, dbus, pipewire) | Readable state machines | Explicit enum for the present loop, calloop's executor for portal and screencast flows, and x11rb/zbus/pipewire-rs for the protocols |24
+| Cancel-on-drop tasks | Structured lifetimes | `async_task::Task` also cancels on drop |25
+| Linked timeouts on client `sendmsg` | A stuck client can't stall the compositor | wayland-backend flushes nonblocking already |26
+27
+## Worth taking, ranked28
+29
+### 1. Deadline frame scheduler30
+31
+`src/backends/metal/present.rs`, flip handling in `src/backends/metal/video.rs`.32
+33
+Biggest latency payoff, and it slots straight into smithay because `DrmCompositor` leaves timing to the caller. Sleep until `next_vblank - pre_margin - post_margin`, then latch, render and commit.34
+35
+- `pre_margin` is a decaying per-second max of measured wakeup-to-latch CPU time.36
+- `post_margin` grows 500 µs per second that saw a missed flip and decays toward the device minimum otherwise.37
+- Vblank timestamps come from queued `CRTC_SEQUENCE` events, with emulation when the driver returns `EOPNOTSUPP`.38
+39
+This is an algorithm port, a few hundred lines. With a Vulkan renderer, GPU time needs to be covered too, either through `post_margin` misses as jay does or with timestamp queries.40
+41
+### 2. Async shm upload pipeline42
+43
+`src/cpu_worker.rs`, `src/gfx_apis/vulkan/transfer.rs`, `src/clientmem.rs`.44
+45
+Smithay's GLES renderer uploads shm synchronously inside `import_shm_buffer` on the main thread. Jay keeps the commit blocked while a worker thread memcpys only damaged rows into a staging buffer, and the GPU finishes the copy on the transfer queue. Sealed memfds (`F_SEAL_SHRINK` plus a large enough size) let the worker read client memory without the thread-local SIGBUS dance. foot, software-rendered toolkits and Xwayland without glamor all take this path. Build it into monolith's `ImportMemWl` as a smithay `Blocker`.46
+47
+### 3. Vulkan renderer internals as a reference48
+49
+Smithay has no Vulkan renderer, so monolith writes one either way. Jay's ~10k lines are coupled to `cmm`, `theme`, `State`, the ring and the cpu worker, so lift modules and ideas rather than the crate.50
+51
+- fp16 linear blend buffer with a resolve pass. Blending is correct and HDR-ready, and color transforms happen in one place.52
+- Op list sorted and batched by pipeline key, one `queue_submit2` per frame.53
+- Vertex data via buffer device address in push constants, instanced draws.54
+- Specialization constants for EOTF, matrix and alpha mode instead of runtime branches.55
+- Descriptor heap, then descriptor buffer, then push descriptor tiers.56
+- Timeline semaphores mapped to DRM syncobj points, sync_file import/export for implicit sync.57
+- Global-priority graphics and transfer queues (`src/gfx_apis/vulkan/device.rs:448-528`).58
+- Offline shaderc compilation with hash tracking.59
+60
+Implement smithay's `Renderer`, `Frame`, `Bind<Dmabuf>`, `Offscreen`, `ImportDma`, `ImportMemWl` and `ExportMem` so that `DrmCompositor`, `OutputDamageTracker` and the element system work unmodified. `Frame` calls append to the op list and `finish` records and submits. Jay stops at rounded rects, so blur and shadows are new work. They would be extra op kinds or custom `RenderElement`s.61
+62
+### 4. Commit blocker taxonomy63
+64
+Smithay already has per-client transaction queues and `Blocker`. Jay's contribution is the list of things a commit waits on, all treated uniformly.65
+66
+- implicit dmabuf fences67
+- syncobj acquire points68
+- shm upload completion69
+- prime copy completion70
+- fifo-v1 barrier71
+- commit-timing-v1 deadline, released by the scheduler's before-latch hook72
+- layout transaction serial73
+74
+Each becomes a smithay `Blocker` or pre-commit hook. The commit-timing integration with the deadline scheduler is the subtle one.75
+76
+### 5. Two-timeline tree, transactions, multiphase animations77
+78
+`SplitView`/`TreeLink`, `LiveTL`/`RenderTL`, `src/transactions.rs` and `src/tree_serial_groups.rs` are upstream jay. `src/presentation/` is yours.79
+80
+`LiveTL` is what clients have been configured to and `RenderTL` is what is on screen. `RenderTL` advances only when every affected client has acked and committed, or after a 50 ms timeout. Animations are transaction effects that diff the pre and post trees at publication time. Tags are a natural fit. A tag switch changes the visibility and size of many windows at once, which is exactly the case transactions exist for.81
+82
+- `multiphase.rs` is near-pure geometry with 1.7k lines of tests. Lift it almost verbatim.83
+- Retained snapshots from `layout_animation.rs`, where the old texture is scaled to the animated rect while the client catches up, port as a concept onto smithay elements.84
+- Reimplement transactions and configure serial groups against smithay's xdg-shell configure/ack and `Blocker`s.85
+86
+### 6. Integration test harness87
+88
+`src/it/`.89
+90
+An in-process compositor on a headless backend, real-socket clients, zeroed timeouts, tree-invariant verification after every idle point, and golden-pixel QOI comparisons. Use wayland-client or smithay-client-toolkit in place of jay's `wl_usr`. An animation-heavy compositor needs this from the start.91
+92
+### 7. Small items93
+94
+- `SCHED_RR` with `SCHED_RESET_ON_FORK`, with `CAP_SYS_NICE` parked on a helper thread (`src/utils/nice.rs`, `src/pr_caps.rs`).95
+- Tracy zones named per task.96
+- KMS hygiene. Reset unknown properties to defaults at init, retry device init with features disabled, `TEST_ONLY` before modesets, roll back on failure.97
+- VRR cursor throttling (`src/output_schedule.rs`), so cursor motion doesn't pin VRR at max refresh.98
+- Lazy Xwayland startup on the first X connection.99
+100
+## Skip101
+102
+- Bespoke wire protocol, codegen and `wl_usr`. Smithay owns this layer.103
+- Homegrown xcon, dbus and pipewire stacks. Use x11rb via smithay's `X11Wm`, zbus and pipewire-rs.104
+- Forker process. `std::process::Command` uses `posix_spawn` and is safe from a threaded process. Set env, rlimits and sched policy on the `Command`.105
+- The `Rc` plus cell utility collection, which only exists because of jay's executor.106
+- Jay's plane assignment, which never uses overlays. `DrmCompositor` already assigns overlays.107
+- `metal_cm` KMS color pipelines for now. It is large and driver-quirky. Keep the renderer's color math shaped so it can slot in later.108
+- SQLite session manager, unless session restore becomes a goal.109
+110
+## Gaps smithay leaves111
+112
+- No tearing/async page flip control in `DrmCompositor`. Needs an upstream patch or a custom commit path.113
+- No KMS color pipeline, CTM/gamma or HDR metadata handling.114
+- No tearing-control-v1 or color-management-v1 protocol helpers.115
+116
+## License117
+118
+jay and wry are `GPL-3.0-only`. Copying code from them makes monolith GPL-3.0. Smithay is MIT, which is compatible with that. `src/presentation/` is your own new code and can be relicensed freely. Jay-authored code (scheduler, shaders, transfer path) can only be reimplemented from the design if monolith is meant to be permissive.@@ -0,0 +1,645 @@
1
+# wry backend research reports2
+3
+Raw reader reports on wry (jay fork) internals, gathered for monolith planning.4
+Paths are relative to `/home/bolt/code/wry` at `6df16858`. Wry forked from jay at `f73b4efe` (2026-08-16).5
+6
+Status7
+8
+- [x] Executor core (`src/async_engine*`)9
+- [x] io_uring, timers, cpu_worker, task usage10
+- [x] DRM/KMS metal backend and frame scheduling11
+- [x] Wire protocol and client handling12
+- [x] Vulkan renderer13
+- [x] Tree, transactions, multiphase animations14
+- [x] Process model, config, tests, utilities15
+- [x] Smithay extension points (comparison baseline)16
+17
+---18
+19
+## 0. Executor core20
+21
+`src/async_engine.rs`, `src/async_engine/ae_task.rs`, `src/async_engine/ae_yield.rs`, about 500 lines total.22
+23
+- Single threaded, `Rc` based, nothing is `Send`. Wakers use a custom `RawWakerVTable` with a non-atomic refcount (`NumCell`), so a waker crossing threads would be UB. The cpu_worker bridges threads with eventfds instead.24
+- One `Box<Task>` allocation per spawn. A wake pushes a two-word `Runnable` onto a `VecDeque`. No atomics or locks anywhere on the wake path.25
+- Task state is a bitset (`RUNNING`, `RUN_AGAIN`, `COMPLETED`, `EMPTIED`, `CANCELLED`). A wake while running sets `RUN_AGAIN` instead of double-queueing.26
+- Dropping the returned `SpawnedFuture` cancels the task, so lifetimes are structured by ownership. Awaiting it yields the task's output.27
+- Four ordered phases, `EventHandling`, `Layout`, `PostLayout`, `Present`. `dispatch()` drains phase 0 to a fixpoint, then phase 1, and so on. Work woken into an earlier phase by a later one waits for the next pass. After all phases the iteration counter bumps and `yield_now()` waiters wake.28
+- `now()` / `now_rt()` are cached per pass, so everything in one pass sees the same timestamp.29
+- `spawn` defaults to `EventHandling`. `spawn2` pins a phase.30
+31
+How the phases are used (`src/compositor.rs:598-745`). About 38 long-lived global tasks follow a dirty-queue pattern. Example `container_layout` (`src/tree/container.rs:2706`) loops on `state.pending_container_layout.pop().await` and checks a `layout_scheduled` flag. The task lives in `Phase::Layout`, so any number of requests that dirty a container during event handling collapse into one layout pass. The same pattern drives render positions, title textures, output render data, popup positioning, tree serial groups, configurables (all `PostLayout`), and screencast and present loops (`Present`). Client send tasks are `PostLayout` (`src/client/tasks.rs:27`), so events generated during a pass are flushed once after layout.32
+33
+---34
+35
+## 1. io_uring, timers, cpu_worker, task usage36
+37
+### 1.1 io_uring layer (`src/io_uring.rs`, `src/io_uring/ops/`)38
+39
+Opcodes wrapped, one module each (`src/io_uring/ops.rs:4-19`). `READ`/`WRITE` (`ops/read_write.rs:18-30`), `READ`/`WRITE` no-cancel with explicit offset (`ops/read_write_no_cancel.rs:21-58`), `WRITE` vec (`ops/read_write_vec.rs:29-86`), `WRITEV` (`ops/writev_external.rs:25-47`), `RECVMSG` (`ops/recvmsg.rs:18-83`), `SENDMSG` (`ops/sendmsg.rs:22-85`), `POLL_ADD` (`ops/poll.rs:17-42`, external callback variant `ops/poll_external.rs:35-74`), `TIMEOUT` (`ops/timeout.rs:28-44`, external `ops/timeout_external.rs:49-73`), `LINK_TIMEOUT` (`ops/timeout_link.rs:18-29`), `ACCEPT` (`ops/accept.rs:15-37`), `CONNECT` (`ops/connect.rs:19-39`), `ASYNC_CANCEL` (`ops/async_cancel.rs:16-24`), `FUTEX_WAIT`/`FUTEX_WAKE` (`ops/futex.rs:33-75`), `READ_MULTISHOT` (`ops/read_multishot.rs:49-77`). A provided buffer-ring helper exists (`buffer_ring.rs:79-173`) but outside tests is only wired to multishot reads.40
+41
+Setup has no `SQPOLL` and no registered fds/buffers on the hot path. `IoUring::new` tries `SUBMIT_ALL|COOP_TASKRUN|SINGLE_ISSUER|DEFER_TASKRUN` then drops one flag at a time on `ENOSYS` (`src/io_uring.rs:131-154`), and hard-requires `NODROP` (`src/io_uring.rs:155-157`). Rings are `mmap`ed directly, queue depth 32 on both main and worker rings (`src/compositor.rs:276`, `src/cpu_worker.rs:367`).42
+43
+Submit. `encode` (`src/io_uring.rs:488-525`) drains `to_encode` into the SQ while space remains, reserves two slots for `has_timeout` ops, and sets `IOSQE_IO_LINK` on the parent so the following `LINK_TIMEOUT` fires as a link (`src/io_uring.rs:516-518`, `ops/timeout_link.rs:40-46`). Used by `write` and `sendmsg` with absolute `Time` (`ops/read_write.rs:32-61`, `ops/sendmsg.rs:74-83`).44
+45
+Enter/wait. `run` (`src/io_uring.rs:400-453`) is the only blocking point. An empty queue submits `0` and waits for `1` with `GETEVENTS`. Pending submits with empty `to_encode` submit `to_submit` and wait for `1`. Non-empty `to_encode` submits `u32::MAX` with no wait and loops to encode more (`src/io_uring.rs:417-424`). `yields` force `min_complete=0` (`src/io_uring.rs:425-427`). `EAGAIN|EBUSY|EINTR` are ignored, other `enter` errors are fatal (`src/io_uring.rs:438-443`). A short submit retries with a pure wait (`src/io_uring.rs:444-451`). On teardown `kill` encodes, submits `u32::MAX`, then waits until `tasks` empties (`src/io_uring.rs:594-617`).46
+47
+Wakeup. Almost every awaitable op takes `pending_results.acquire()` (`src/io_uring/pending_result.rs:21-37`), stores a clone in the task, schedules a pooled task struct, and awaits it (e.g. `ops/read_write.rs:40-60`). `dispatch_completions` matches `user_data` to the task, with `CQE_F_MORE` routed to `complete_partial` for multishot and terminal CQEs routed to `task.complete` (`src/io_uring.rs:455-486`). `PendingResult::complete` stores `res` and wakes the `Waker` (`src/io_uring/pending_result.rs:66-74`). `poll` translates negative `res` to `OsError` (`src/io_uring/pending_result.rs:105-122`). Non-`Future` completions use `PollCallback`, `TimeoutCallback`, `WritevData::done`, `ReadMultishotCallback` (`ops/poll_external.rs:15-17`, `ops/timeout_external.rs:16-18`, `ops/writev_external.rs:20-24`, `ops/read_multishot.rs:28-31`).48
+49
+Cancel. Dropping an op future cancels it, because the future owns a `Cancellable` whose `Drop` calls `cancel_task` (`src/io_uring.rs:527-544`, `src/io_uring.rs:640-644`). Not-yet-encoded ops complete locally with synthetic `ECANCELED` (`src/io_uring.rs:542-554`). In-kernel ops get an `ASYNC_CANCEL` by `user_data` (`ops/async_cancel.rs:16-24`, `ops/async_cancel.rs:41-44`). `is_cancel` tasks are exempt from `kill` cancellation (`src/io_uring.rs:596-604`). `PendingPoll`, `PendingTimeout`, `PendingReadMultishot` `Drop` impls do the same (`ops/poll_external.rs:25-33`, `ops/timeout_external.rs:26-34`, `ops/read_multishot.rs:39-47`). `read/write_no_cancel` instead forbids userspace cancel with an `on_drop` panic guard, for worker-thread shm I/O (`ops/read_write_no_cancel.rs:92-96`).50
+51
+No epoll fallback. `CreateUring`/`NoDrop` become `CompositorError::IoUringError` and exit (`src/io_uring.rs:96-102`, `src/compositor.rs:227-241`, `src/compositor.rs:275-276`). Grep finds no epoll/calloop fallback.52
+53
+### 1.2 Main loop, one iteration (`src/io_uring.rs:400-453`)54
+55
+Bump `iteration`, wake `yields` wakers, then loop `eng.dispatch()` plus `dispatch_completions()` until no more CQEs were consumed. Then `encode()` queued SQEs, accumulating `to_submit`, then one `io_uring_enter` with the wait policy above. Startup order is engine, ring 32, signal handler, wheel, run-toplevel, cpu worker, eventfd cache, then `ring.run()` (`src/compositor.rs:275-286`, `src/compositor.rs:530-535`). The `compositor` future only runs backend setup. Blocking lives in the ring (`src/compositor.rs:538-588`).56
+57
+Unusual. Ready futures are drained to quiescence before entering the kernel, and `Debouncer` plus `yields` let hot paths skip the blocking wait (`src/io_uring/debounce.rs:15-34`, `src/io_uring.rs:425-427`).58
+59
+### 1.3 Timers60
+61
+`wheel.rs` is a single-`timerfd`, heap-multiplexed wheel (`src/wheel.rs:102-114`). `Wheel::new` creates a `CLOCK_MONOTONIC` `timerfd` and spawns `dispatch` (`src/wheel.rs:117-137`). `timeout(ms)` rounds to ms, re-arms with `TFD_TIMER_ABSTIME` only if earlier than the armed expiry, pushes `{expiration,id}` on a `BinaryHeap`, and registers the waiter (`src/wheel.rs:156-194`, `src/time.rs:34-46`). `dispatch_once` does `ring.read(timerfd)` then completes every due entry and re-arms to the next live entry, skipping cancelled ids (`src/wheel.rs:218-254`). Waiters are manual `Future`s storing a `Waker` (`src/wheel.rs:65-100`). `Drop` unregisters and recycles (`src/wheel.rs:78-87`). Consumers include client shutdown 5s (`src/client/tasks.rs:35`) and cursor tick (`src/tasks/hardware_cursor.rs:17-22`).62
+63
+Separately, `TimerFd::expired` is `ring.read(timerfd)` plus `timerfd_settime` programming (`src/utils/timer.rs:42-67`), used by idle, 40Hz latch, damage, config, virtual-output (`src/tasks/idle.rs:14-20`, `src/tasks/const_clock.rs:19-35`, `src/virtual_output.rs:731`). Raw `ring.timeout(nsec)` with `IORING_TIMEOUT_ABS` is used for present/vblank, transactions, sqlite, session (`src/io_uring/ops/timeout.rs:27-44`, `src/virtual_output.rs:475`, `src/output_schedule.rs:172-174`, `src/transactions.rs:453`).64
+65
+### 1.4 cpu_worker (`src/cpu_worker.rs`, `src/cpu_worker/jobs/`)66
+67
+One background thread named `cpu worker` with its own `AsyncEngine` plus ring depth 32 (`src/cpu_worker.rs:275-294`, `src/cpu_worker.rs:366-386`). `CpuJob::work` returns the thread-side `CpuWork`, and `completed` runs back on the main thread (`src/cpu_worker.rs:33-50`).68
+69
+Main submits with `submit`, leaking the job box and queueing `Job::New{id, *mut dyn CpuWork}` on an `AsyncQueue`, returning a `PendingJob` (`src/cpu_worker.rs:323-339`). An `equeue_jobs` task moves it under lock to the shared `VecDeque` and writes the `have_new_jobs` eventfd (`src/cpu_worker.rs:249-261`). The worker `ring.read`s that eventfd and runs `CpuWork::run`. `None` completes synchronously, `Some(AsyncCpuWork)` is spawned on the worker engine until `WorkCompletion::complete` (`src/cpu_worker.rs:425-473`, `src/cpu_worker.rs:68-77`). Completion pushes the id to the shared completed queue, signals a condvar for sync waiters, and writes `have_completed_jobs` (`src/cpu_worker.rs:475-487`). The main `wait_for_completions` task `ring.read`s it and invokes `job.completed()` (`src/cpu_worker.rs:216-247`). Dropping a pending job warns, sends `Job::Cancel`, and blocks dispatching until gone. `detach` marks it abandoned (`src/cpu_worker.rs:151-204`).70
+71
+Call sites, all via `cpu.submit`.72
+73
+- `CloseMemWork`, munmap off-thread, detached (`src/clientmem.rs:211-215`)74
+- Text `RenderJob` (`src/text.rs:528`)75
+- Vulkan allocator `FreeJob` and `AllocJob` (`src/gfx_apis/vulkan/allocator.rs:185`, `src/gfx_apis/vulkan/allocator.rs:303`)76
+- shm transfers, `ImgCopyWork` for pointer-backed and `ReadWriteWork` for fd-backed mem (`src/gfx_apis/vulkan/transfer.rs:505`, `src/gfx_apis/vulkan/transfer.rs:539`, `src/cpu_worker/jobs/img_copy.rs:32-59`, `src/cpu_worker/jobs/read_write.rs:98-148`)77
+78
+### 1.5 eventfd_cache (`src/eventfd_cache.rs`)79
+80
+Avoids `eventfd()` churn and forgotten counter drains. Pools fds, `signaled` bools, and `Readable` callback objects (`src/eventfd_cache.rs:39-45`), with a `recycle` task spawned at creation (`src/eventfd_cache.rs:64-75`). `acquire` reuses or creates (`src/eventfd_cache.rs:77-89`). Async wait is `ring.readable` (`src/eventfd_cache.rs:97-104`). External wait reuses a pooled `Readable` through `readable_external` (`src/eventfd_cache.rs:120-137`). `Drop` of a still-signaled fd pushes it to `recycle`, which `ring.read`s each fd empty before returning it to the pool (`src/eventfd_cache.rs:156-224`). `return_signaled` recycles the bool only when the cache holds the last ref (`src/eventfd_cache.rs:208-215`). Held by `State` and passed to the copy-device registry (`src/compositor.rs:466-469`).81
+82
+### 1.6 Long-lived tasks in `src/tasks/`83
+84
+- `handle_backend_events`, demuxes the backend queue into connector/input/drm handlers (`src/tasks.rs:22-25`, `src/tasks/backend.rs:12-28`)85
+- `handle_slow_clients` / `handle_slow_ei_clients`, backpressure kills via `check_queue_size` (`src/tasks.rs:27-35`, `src/tasks/slow_clients.rs:8-28`)86
+- `handle_const_40hz_latch`, dummy-output latch clock (`src/tasks.rs:37-44`)87
+- Per-connector `ConnectorHandler`, output lifecycle (`src/tasks/connector.rs:92-98`, `src/tasks/connector.rs:113-141`)88
+- Per-DRM-device handler (`src/tasks/drmdev.rs:74`)89
+- Per-input-device handler (`src/tasks/input_device.rs:48-58`)90
+- `idle`, `TimerFd`-driven DPMS/idle state machine (`src/tasks/idle.rs:13-32`, `src/compositor.rs:651`)91
+92
+### 1.7 Spawn survey93
+94
+`eng.spawn`/`spawn2` has 175 hits in 64 files.95
+96
+- `start_global_event_handlers` alone spawns about 38 named globals covering backend events, slow clients, cursor tick, layout, render positions/titles/textures, output render, managers, idle, popups, screencast, damage, matchers, seats, sqlite, sessions, transactions (`src/compositor.rs:598-745`).97
+- Per client, 3 tasks per Wayland client plus 2-3 per EI client (`src/client/tasks.rs:20-28`, `src/ei/ei_client/ei_tasks.rs:21-27`, `src/client.rs:258-260`).98
+- Acceptors/listeners are async accept loops, Wayland secure and insecure, IPC, tagged, security-context, EI (`src/acceptor.rs:144-153`, `src/acceptor.rs:168-191`, `src/ipc.rs:114`, `src/tagged_acceptor.rs:84`, `src/security_context_acceptor.rs:90-95`, `src/ei/ei_acceptor.rs:124`).99
+- Protocol I/O is async everywhere. dbus auth/in/out, pipewire in/out, forker in/out, xcon in/out, tool clients (`src/dbus/auth.rs:45-50`, `src/pipewire/pw_con.rs:356-359`, `src/forker.rs:123-135`, `src/xcon.rs:469-522`).100
+- Xwayland is `xwayland::manage` plus `XWM` plus per-transfer shm copy tasks (`src/state.rs:1636`, `src/xwayland.rs:219`, `src/xwayland/xwm.rs:1788-1901`).101
+- Rendering/output is phased `spawn2` layout/present work plus per-output schedule, vblank, virtual-output, backend event/present tasks (`src/compositor.rs:611-680`, `src/tasks/connector.rs:186-189`, `src/virtual_output.rs:1023-1024`, `src/backends/x.rs:359-364`, `src/backends/metal/video.rs:1221`).102
+- Vulkan transfer/vblank await tasks are also spawned (`src/gfx_apis/vulkan/transfer.rs:582-627`).103
+104
+Rough split of non-test spawns. Client/EI/IPC/dbus/pipewire about 35, render/layout/output/present about 45, backend/connector/DRM/input/seat about 15, xwayland/X11 about 8, global compositor/timer/sqlite/config about 25.105
+106
+### 1.8 Client sockets107
+108
+One supervisor plus receive, send, terminate tasks per client (`src/client/tasks.rs:18-44`). Receive parses with `WlBufFdIn` over `ring.recvmsg`, including SCM_RIGHTS fds (`src/client/tasks.rs:59-93`, `src/utils/buffd/wl_buf_in.rs:52-126`). Send buffers into `OutBufferSwapchain`. `commit` moves a non-empty `cur` to `pending`, and the send task waits on `flush_request`, swaps `pending` out, and `BufFdOut::flush`es each buffer with an absolute 5s `sendmsg` linked timeout (`src/client/tasks.rs:112-147`, `src/utils/buffd/buf_out.rs:57-91`, `src/utils/buffd/buf_out.rs:102-131`, `src/io_uring/ops/sendmsg.rs:32-85`). `ECANCELED` becomes `Timeout`, `EPIPE`/`ECONNRESET` become `Closed` (`src/utils/buffd/buf_out.rs:121-130`). Slow clients are reaped when `pending` exceeds 10 (`src/utils/buffd/buf_out.rs:48-59`, `src/client.rs:520`). Direct `recvmsg`/`sendmsg` with no userspace epoll shim. Generic bidirectional connections use the same pair through `BufIo` (`src/utils/bufio.rs:101-114`, `src/utils/bufio.rs:143-179`).109
+110
+Notable vs calloop. Everything above plus pooled task structs, `PendingResult` slab recycling raw pointers, link-timeouts instead of timer races, and a second full runtime on the worker thread.111
+112
+---113
+114
+## 2. DRM/KMS metal backend and frame scheduling115
+116
+### 2.1 KMS model117
+118
+Atomic only. Device init enables `DRM_CLIENT_CAP_ATOMIC` with value 2 and then probes resources, planes, CRTCs, encoders (`src/backends/metal/video.rs:2030`, `src/backends/metal/video.rs:2032`, `src/backends/metal/video.rs:2040`). It also tries `DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE` and records support (`src/backends/metal/video.rs:2035`).119
+120
+Desired state is `BackendConnectorState` in `backend.rs` (`src/backend.rs:785`). Committed DRM state is split into `DrmPlaneState`, `DrmCrtcState`, `DrmConnectorState` plus property structs in transaction (`src/backends/metal/transaction.rs:71`). `create_transaction` clones live `drm_state` for every non-leased plane, CRTC and connector and stores requested connector state separately (`src/backends/metal/transaction.rs:221`).121
+122
+`calculate_drm_state` assigns CRTCs, primary and cursor planes, checks formats and modifiers, allocates scanout buffers when needed, and picks modes, VRR, tearing, colorspace, EOTF and HDR blobs (`src/backends/metal/transaction.rs:314`). `calculate_change` diffs new property structs against live objects with `differs` and `prepare_conditional` and only emits changed objects (`src/backends/metal/transaction.rs:902`, `src/backends/metal/transaction.rs:928`, `src/backends/metal/transaction.rs:949`). It also resets unknown or vendor default properties when asked (`src/backends/metal/transaction.rs:874`).123
+124
+`TEST_ONLY` is used in the prepare path. `MetalDeviceTransaction::prepare` calls `calculate_drm_state` then `calculate_change(true,false)` (`src/backends/metal/transaction.rs:1216`). When test is true it calls `Change::test` with `DRM_MODE_ATOMIC_ALLOW_MODESET`, which adds `DRM_MODE_ATOMIC_TEST_ONLY` in `video/drm.rs` (`src/backends/metal/transaction.rs:982`, `src/video/drm.rs:1097`). Failure surfaces as `AtomicTestFailed` (`src/backend/transaction.rs:124`).125
+126
+Normal apply first commits with flags 0 then retries once with `ALLOW_MODESET` after logging (`src/backends/metal/transaction.rs:993`). After a successful kernel commit it swaps new state into live `drm_state`, connector, CRTC and plane objects with `mem::swap` and bumps the connector version (`src/backends/metal/transaction.rs:1092`, `src/backends/metal/transaction.rs:1100`). Rollback keeps the pre-apply state and reapplies it with `calculate_change(false,false)` (`src/backends/metal/transaction.rs:1194`). The generic wrapper also rolls back on drop (`src/backend/transaction.rs:248`). Device init has an extra recovery loop that retries init while disabling direct scanout, non-XRGB formats and non-default modes (`src/backends/metal/video.rs:2553`, `src/backends/metal/video.rs:2589`).127
+128
+### 2.2 Planes129
+130
+Types are `Overlay`, `Primary` and `Cursor` (`src/backends/metal/video.rs:1023`). The transaction carries `primary` plus `cursor` per CRTC (`src/backends/metal/transaction.rs:283`). Overlay planes are explicitly discarded during assignment, so compositing uses at most primary plus cursor (`src/backends/metal/transaction.rs:360`).131
+132
+Cursor uses fixed `ARGB8888` (`src/backends/metal/transaction.rs:281`) and separate double-buffered cursor scanout buffers (`src/backends/metal/video.rs:628`). Cursor enable, move and swap produce `CursorProgrammingType::Enable` or `Disable` (`src/backends/metal/present.rs:127`).133
+134
+Direct scanout puts a client dmabuf on the primary plane through `add_fb` instead of the compositor render buffer. It is attempted only when enabled and when `prepare_direct_scanout` succeeds (`src/backends/metal/present.rs:901`). That path rejects cross-device buffers unless the scanout device is the render device, rejects SHM, rejects buffers with pending lazy work, checks plane format and modifier support, and caches failures by a key of buffer ID, plane, CRTC, source and destination color descriptions, intent, gamma, cursor presence and pipeline mode (`src/backends/metal/present.rs:746`, `src/backends/metal/present.rs:778`, `src/backends/metal/present.rs:845`). A failed atomic commit with direct scanout retries once as a full composite and then marks that key impossible (`src/backends/metal/present.rs:337`).135
+136
+### 2.3 Frame scheduling137
+138
+Each connector spawns its own `present_loop` in phase `Present` (`src/backends/metal/video.rs:1220`). Page flip events arrive on a per-device task that awaits `DrmMaster::event`, which does an io_uring read on the DRM fd and parses `DRM_EVENT_FLIP_COMPLETE` and `DRM_EVENT_CRTC_SEQUENCE` (`src/backends/metal/video.rs:2268`, `src/video/drm.rs:590`, `src/video/drm.rs:607`).139
+140
+The loop waits for `present_trigger` then requires both `buffers_idle` and `crtc_idle` (`src/backends/metal/present.rs:189`). For synced flips it computes `next_vblank - pre_commit_margin - post_commit_margin` and sleeps with `ring.timeout` until that deadline (`src/backends/metal/present.rs:203`). If already past the deadline it increments the expected sequence so a missed vblank can be detected (`src/backends/metal/present.rs:210`). Tearing flips skip that wait (`src/backends/metal/present.rs:203`).141
+142
+It then calls `before_latch` with either now for tearing or `next_vblank_nsec` for synced output (`src/backends/metal/present.rs:218`). It aborts if the connector version changed while yielding (`src/backends/metal/present.rs:226`). Then `present_once` latches damage, renders or selects direct scanout, waits render then scanout fences, programs the atomic commit, and on success marks CRTC busy and buffers busy (`src/backends/metal/present.rs:250`, `src/backends/metal/present.rs:314`, `src/backends/metal/present.rs:387`).143
+144
+`pre_commit_margin` starts at 16ms and `post_commit_margin` starts at 1.5ms (`src/backends/metal/present.rs:145`). The pre margin tracks the per-second maximum CPU time from wakeup through latch with a geometric decay of 0.5 (`src/backends/metal/present.rs:238`, `src/backends/metal/video.rs:1208`). The post margin grows by 500us per second containing missed flips and decays toward the device minimum otherwise (`src/backends/metal/present.rs:147`, `src/backends/metal/video.rs:2425`). Expected versus actual CRTC sequence decides misses (`src/backends/metal/video.rs:2378`).145
+146
+Flip completion clears `pending_flip`, promotes `next_framebuffer` to `active_framebuffer`, sets `buffers_idle`, emits presentation feedback with `HW_COMPLETION` plus `VSYNC` and `ZERO_COPY` when applicable, then sets `crtc_idle` and reschedules if damage remains (`src/backends/metal/video.rs:2368`, `src/backends/metal/video.rs:2387`, `src/backends/metal/video.rs:2406`). It also maintains `next_vblank_nsec` from flip timestamps plus refresh interval and continuously queues `CRTC_SEQUENCE` for vblank timing unless emulation is active (`src/backends/metal/video.rs:2347`, `src/backends/metal/video.rs:2418`, `src/backends/metal/video.rs:867`). If queueing sequences returns `EOPNOTSUPP` it switches that output to vblank emulation (`src/backends/metal/video.rs:881`).147
+148
+`output_schedule.rs` is separate from that pacing loop. It only throttles cursor updates in VRR mode. `latched` records present time and clears awaiting-latch flags (`src/output_schedule.rs:100`). `run_once` sleeps until `last_present + cursor_delta` when VRR plus a cursor change is pending, then damages either the hardware cursor or the connector (`src/output_schedule.rs:157`, `src/output_schedule.rs:187`). Tree latch plumbing calls `schedule.latched` from `OutputNode::latched` (`src/tree/output.rs:355`).149
+150
+### 2.4 VRR and tearing151
+152
+State carries `vrr` and `tearing` booleans (`src/backend.rs:792`). The transaction rejects VRR on connectors without `vrr_capable` and rejects tearing without atomic async flip support (`src/backends/metal/transaction.rs:766`, `src/backends/metal/transaction.rs:772`). `vrr_capable` comes from the `vrr_capable` property (`src/backends/metal/video.rs:1423`) and async support comes from `DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP` (`src/video/drm.rs:404`).153
+154
+Per-frame tearing is `persistent tearing AND supports_async_commit` (`src/backends/metal/present.rs:442`). The atomic commit first tries `NONBLOCK | EVENT | ASYNC`, and on failure falls back to synced `NONBLOCK | EVENT` while also attaching `IN_FENCE_FD` and requesting `OUT_FENCE_PTR` except on Nvidia (`src/backends/metal/present.rs:572`, `src/backends/metal/present.rs:581`). Any plane geometry change or cursor plane change forces the synced path (`src/backends/metal/present.rs:493`, `src/backends/metal/present.rs:516`).155
+156
+### 2.5 Color management157
+158
+`metal_cm` selects a KMS programming for each frame from source and destination `ColorDescription` values plus render intent, gamma LUT, cursor presence and pipeline selection (`src/backends/metal/video/metal_cm.rs:100`). It caches programmings, matcher results and blobs (`src/backends/metal/video/metal_cm.rs:60`). A programming can set plane `COLOR_PIPELINE`, plane colorop bypass, type, interpolation, data and multiplier, and CRTC gamma LUT (`src/backends/metal/video/metal_cm.rs:127`, `src/backends/metal/video/metal_cm.rs:138`, `src/backends/metal/video/metal_cm.rs:153`). Matching logic decides whether matrix, source EOTF, destination EOTF or client gamma are needed (`src/backends/metal/video/metal_cm/metal_cm_matcher.rs:124`). The minimum LUT size considered useful is 1024 (`src/backends/metal/video/metal_cm.rs:54`). On AMD with a cursor plane it disables plane pipelines as a driver workaround (`src/backends/metal/video/metal_cm.rs:185`).159
+160
+`metal_cm_paths/generated.rs` holds generated static tables of `PlaneOpKind`, matrix, LUT and multiply variants used by the matcher (`src/backends/metal/video/metal_cm/metal_cm_paths/generated.rs:12`). The handwritten wrapper defines `Path`, plane and CRTC op accessors and criteria filters (`src/backends/metal/video/metal_cm/metal_cm_paths.rs:15`).161
+162
+Legacy properties are also handled. CRTC defaults include `CTM`, `DEGAMMA_LUT`, `OUT_FENCE_PTR` and AMD regamma (`src/backends/metal/video.rs:1565`). Plane defaults include AMD blend, shaper, 3D LUT, HDR multiplier, alpha, blend mode, rotation and `COLOR_PIPELINE Bypass` for non-primary planes (`src/backends/metal/video.rs:1680`). Plane pipeline use is opt-in through `use_plane_color_pipelines`, default false (`src/backends/metal/video.rs:2123`, `src/backend.rs:672`).163
+164
+HDR uses connector `Colorspace` and `HDR_OUTPUT_METADATA` plus EDID-derived PQ and BT2020 support, primaries and luminance (`src/backends/metal/video.rs:1351`, `src/backends/metal/video.rs:1452`, `src/backends/metal/video.rs:1500`). The transaction creates the HDR metadata blob from EOTF, primaries and luminance (`src/backends/metal/transaction.rs:802`, `src/video/drm.rs:919`). The renderer supplies `cd`, linear and blend descriptions per frame, and `metal_cm` maps framebuffer description to output description (`src/backends/metal/present.rs:277`, `src/backends/metal/present.rs:889`).165
+166
+### 2.6 Allocator and multi-GPU167
+168
+Allocation is GBM based, not dumb buffers. It creates render and scanout `GbmBo` objects, imports them as gfx textures and framebuffers, and registers display framebuffers with `add_fb2` (`src/backends/metal/allocator.rs:878`, `src/backends/metal/allocator.rs:914`, `src/video/drm.rs:441`). GEM handles are cached (`src/video/drm.rs:484`). Modifier lists come from the plane `IN_FORMATS` blob, with fallback to the legacy plane format list (`src/backends/metal/video.rs:1616`, `src/video/drm.rs:531`).169
+170
+Same-GPU scanout uses one BO for render and scanout (`src/backends/metal/allocator.rs:454`). Cross-GPU tries configurable prime methods in order `direct-pull`, `direct-push`, `udmabuf`, `direct-sampling` and `indirect-pull`, controlled by `WRY_PRIME_METHODS` (`src/backends/metal/allocator.rs:464`, `src/backends/metal/allocator.rs:615`, `src/backends/metal/allocator.rs:1528`). Each method intersects render write and read modifiers, plane modifiers and copy device source and destination modifiers, with copy paths forced to linear when available (`src/backends/metal/allocator.rs:1224`, `src/backends/metal/allocator.rs:1595`). Buffer kinds are direct sampling versus three copy device topologies plus direct push, which is noted as unsafe on AMD (`src/backends/metal/allocator.rs:85`, `src/backends/metal/allocator.rs:621`). Per-frame `copy_to_dev` performs the needed GPU or copy device transfer (`src/backends/metal/allocator.rs:154`).171
+172
+### 2.7 Hotplug, session, lease173
+174
+Hotplug watches `drm` and `input` through udev (`src/backends/metal.rs:307`). A task waits for the monitor fd with io_uring readable readiness and dispatches add versus change (`src/backends/metal/monitor.rs:42`). DRM change diffs kernel connectors against known connectors, emits `Disconnected` plus `Removed`, revokes affected leases, refreshes properties and display data, creates new connector objects and reinitializes the device (`src/backends/metal/video.rs:1844`).175
+176
+Logind handling takes session control, takes each device fd, and installs pause and resume handlers (`src/backends/metal.rs:295`, `src/logind.rs:76`, `src/backends/metal/monitor.rs:402`). Pause marks the DRM device paused, hides non-desktop outputs and parks leases in `leases_to_break` (`src/backends/metal/monitor.rs:166`). Resume clears paused state, retries lease revocation, refreshes properties and reschedules presents (`src/backends/metal/monitor.rs:97`, `src/backends/metal/video.rs:2248`). Losing DRM master surfaces as `EACCES` on commit and is intentionally ignored per frame (`src/backends/metal/present.rs:379`).177
+178
+Leases are supported for non-desktop outputs. Creation requires connected non-desktop connectors, allocates an unused CRTC and primary plane per connector, calls the kernel lease ioctl and emits `Unavailable` (`src/backends/metal/video.rs:264`, `src/video/drm.rs:638`). Dropping the lease object revokes it or parks it for later revocation and makes the connector available again (`src/backends/metal/video.rs:550`, `src/backends/metal/video.rs:514`).179
+180
+### 2.8 Unusual choices181
+182
+- Overlays are enumerated but never assigned, unlike many compositors (`src/backends/metal/transaction.rs:360`).183
+- Direct scanout participates in color pipeline search and has its own impossible-cache (`src/backends/metal/present.rs:778`).184
+- Frame timing uses two adaptive margins rather than a fixed deadline, with the pre margin from measured CPU time and the post margin from missed sequence counts (`src/backends/metal/present.rs:238`, `src/backends/metal/video.rs:2425`).185
+- The modeset path blocks on buffer clear and copy syncs inside transaction calculation and can synchronously poll a CRTC out fence before blanking (`src/backends/metal/transaction.rs:693`, `src/backends/metal/transaction.rs:843`).186
+- Init and hotplug paths reset unknown KMS properties to driver defaults, which helps against firmware or desktop leftover state (`src/backends/metal/transaction.rs:886`, `src/backends/metal/video.rs:2589`).187
+188
+---189
+190
+## 3. Wire protocol and client handling191
+192
+### 3.1 Protocol description format193
+194
+`wire/` holds a custom DSL, one file per interface. `wire/wl_surface.txt:1-8` shows `request destroy`, typed fields, `(destructor)`, `(since = 2)`, `(new)`. No XML is stored in tree. XML appears only at conversion boundaries.195
+196
+The DSL parser lives in `build/wire/parser.rs:209-224` for `Type` with `Id, U32, I32, U64, U64Rev, Str, OptStr, BStr, Fixed, Fd, Bool, Array, Pod`, plus `build/wire/parser.rs:236-246` for `Message` and `build/wire/parser.rs:350-403` for message parsing.197
+198
+`xml-to-wire/src/main.rs:9-14` plus `xml-to-wire/src/builder.rs:27-42` converts Wayland XML to `wire/*.txt`. Special handling maps `x_hi` plus `x_lo` pairs to `u64` or `u64_rev` (`xml-to-wire/src/builder.rs:83-106`) and maps untyped arrays or `dev_t` blobs to `array` or `pod` (`xml-to-wire/src/builder.rs:132-160`). `wire-to-xml/src/main.rs:41-60` does the reverse for debugging or interop.199
+200
+The build script (`build/build.rs:53-67`) runs `wire::main` and emits to `OUT_DIR`. `build/wire.rs:497-539` scans `wire/`, parses every file, and writes `wire/mod.rs` plus one generated `wire/<iface>.rs` per interface. It also emits `id!` plus `Interface` constants (`build/wire.rs:455-494`).201
+202
+A generated request type is a plain struct with `self_id` plus fields (`build/wire.rs:57-76`). Its `RequestParser` impl is emitted in `build/wire.rs:78-202`. Fixed-size messages destructure `*parser.data()` directly (`build/wire.rs:112-170`). Variable-size messages call `parser.uint`, `str`, `bstr`, `fd`, `binary` and then `parser.eof` (`build/wire.rs:171-198`).203
+204
+A generated event writer implements `EventFormatter` (`build/wire.rs:203-302`). Fixed events use one `fmt.data` word array (`build/wire.rs:214-250`). Variable events use `fmt.header` plus per-field writers (`build/wire.rs:252-291`). Request and event handler traits are emitted together (`build/wire.rs:311-431`), with server traits using `Object` plus `Client` and client-side traits using `UsrObject` plus `UsrCon` (`build/wire.rs:327-348`).205
+206
+Parsing is largely zero-copy. `MsgParser` borrows the `&[u32]` body plus fd queue (`src/utils/buffd/parser.rs:33-47`). `str`, `bstr`, `array`, `binary_array` return borrowed `&str`, `&BStr`, `&[u8]`, `&[T]` (`src/utils/buffd/parser.rs:96-146`, `src/utils/buffd/parser.rs:159-176`).207
+208
+File descriptors travel as ancillary data. `WlBufFdIn` collects them with `recvmsg` (`src/utils/buffd/wl_buf_in.rs:110-126`). `MsgParser::fd` pops from that queue (`src/utils/buffd/parser.rs:122-127`). `MsgFormatter::fd` queues them and `write_len` records their message offset (`src/utils/buffd/formatter.rs:93-96`, `src/utils/buffd/formatter.rs:135-149`). Sending splits at fd boundaries (`src/utils/buffd/buf_out.rs:102-120`). This split-queue design is unusual relative to libwayland.209
+210
+### 3.2 Object model211
+212
+Each `Client` owns an `Objects` store (`src/client.rs:355`, `src/client/objects.rs:87-135`). There is one generic `registry` map plus many typed `CopyHashMap` maps for surfaces, buffers, seats and others (`src/client/objects.rs:88-134`).213
+214
+ID allocation splits the space at `MIN_SERVER_ID 0xff000000` (`src/client/objects.rs:137`). Client-created IDs must be nonzero and below that bound (`src/client/objects.rs:266-282`). Server IDs are allocated from a bitset freelist (`src/client/objects.rs:235-250`, `src/client/objects.rs:314-325`).215
+216
+Dispatch starts in the receive task. It reads `WlMessage obj_id message body fds` (`src/client/tasks.rs:64-72`), looks up the object (`src/client/tasks.rs:73-80`), then calls `handle_request` (`src/client/tasks.rs:81-90`). Every object forwards through `object_base!` to generated `handle_request_impl` (`src/macros.rs:41-66`). The generated dispatcher matches the opcode and emits a `version >= since` guard (`build/wire.rs:387-411`). Unknown opcodes become `InvalidMethod` (`build/wire.rs:413`, `src/client/error.rs:18-19`).217
+218
+Versioning is also enforced at bind time. `WlRegistry bind` checks interface name and `version <= global.version` (`src/ifs/wl_registry.rs:64-89`). Globals expose fixed versions, for example compositor version 7 (`src/ifs/wl_compositor.rs:82-86`).219
+220
+Protocol errors use `wl_display/error`.221
+222
+- Unknown objects trigger `send_invalid_object` plus shutdown (`src/client/tasks.rs:73-78`, `src/ifs/wl_display.rs:79-82`).223
+- Unknown requests trigger `send_invalid_request` plus shutdown (`src/client.rs:394-416`, `src/ifs/wl_display.rs:68-77`).224
+- Handler failures trigger `send_implementation_error` plus shutdown (`src/client/tasks.rs:94-108`, `src/ifs/wl_display.rs:84-86`).225
+- Explicit `protocol_error` does the same (`src/client.rs:489-494`).226
+- Method wrapping preserves interface, id, method and source error (`src/client/error.rs:35-42`).227
+228
+Lifetime uses `Rc` with explicit cycle breaking. `Object` has a default empty `break_loops` (`src/object.rs:25-27`). Client teardown calls `Objects::destroy`, which drains the generic registry through `break_loops` and clears typed maps (`src/client/objects.rs:186-233`). `ClientHolder Drop` drives that plus shutdown, activation token and timeline cleanup (`src/client.rs:320-337`). `WlSurface break_loops` drops children, dnd, cursors, transactions, feedback, viewporter and timelines (`src/ifs/wl_surface.rs:2104-2127`). Server ID removal can emit `delete_id` when symmetric deletion is active (`src/client/objects.rs:284-308`). Typed insertion and lookup go through `dedicated_add_obj!` (`src/macros.rs:341-361`), while simple objects and globals use `simple_add_obj!` and `simple_add_global!` (`src/macros.rs:363-373`). Explicit teardown instead of `Weak` everywhere is unusual relative to wayland-rs.229
+230
+### 3.3 Client I/O231
+232
+Input uses `WlBufFdIn` with an 8 KiB buffer and 4 KiB max message (`src/utils/buffd/wl_buf_in.rs:15-29`). It validates word alignment and `8 <= len <= 4096` (`src/utils/buffd/wl_buf_in.rs:69-77`), compacts unconsumed bytes (`src/utils/buffd/wl_buf_in.rs:100-108`), and caps queued fds at 32 (`src/utils/buffd/wl_buf_in.rs:122-124`). Error variants cover oversize, undersize, unaligned, missing and excess fds (`src/utils/buffd.rs:24-44`) with limits `BUF_SIZE 4096` and `MAX_IN_FD 32` (`src/utils/buffd.rs:46-47`).233
+234
+Output uses `OutBufferSwapchain` with current, pending and free buffers (`src/utils/buffd/buf_out.rs:50-69`). Each output buffer is 8 KiB and considered full past 4 KiB (`src/utils/buffd/buf_out.rs:14`, `src/utils/buffd/buf_out.rs:42-46`). `Client::event` formats into the current buffer, commits on full, then signals the sender (`src/client.rs:496-514`). The sender swaps pending buffers and flushes with a 5 second timeout (`src/client/tasks.rs:112-132`). Used buffers are recycled through `free` (`src/client/tasks.rs:128-131`, `src/utils/buffd/buf_out.rs:62-68`).235
+236
+Backpressure is bounded but not lossy for compliant clients. More than 10 pending buffers counts as over limit (`src/utils/buffd/buf_out.rs:57-60`). Over-limit clients enter the global slow queue once (`src/client.rs:505-511`). A background handler rechecks after a yield and kills still-slow clients (`src/client.rs:520-530`, `src/tasks/slow_clients.rs:9-14`). Event flushing is otherwise triggered, not synchronous after every request, as seen by the commented-out flush (`src/client/tasks.rs:91`) and trigger use (`src/client.rs:513`).237
+238
+### 3.4 `clientmem.rs`239
+240
+SHM pools are mapped with `mmap64`, length rounded up to page size, with an empty mapping for zero length (`src/clientmem.rs:104-124`). Read-only pools use `PROT_READ`, writable pools add `PROT_WRITE`, normally `MAP_SHARED` (`src/clientmem.rs:78-123`). Private mappings are a separate constructor (`src/clientmem.rs:68-76`).241
+242
+Truncated client memory is handled with `SIGBUS`. Startup installs a `SA_SIGINFO` plus `SA_NODEFER` handler (`src/clientmem.rs:273-284`). Safe access pushes a thread-local record, runs the closure, then reports `Sigbus` if marked failed (`src/clientmem.rs:170-189`). The handler checks whether the fault address lies inside a tracked mapping (`src/clientmem.rs:241-254`), replaces that range with anonymous memory via `MAP_FIXED` (`src/clientmem.rs:255-262`), marks failure and returns (`src/clientmem.rs:266-267`), otherwise restores default and raises `SIGBUS` (`src/clientmem.rs:233-239`, `src/clientmem.rs:269`).243
+244
+Sealed memfds skip the handler path. If `F_SEAL_SHRINK` plus a large enough `st_size` is observed, the mapping is marked `sigbus_impossible` (`src/clientmem.rs:89-96`). It may first extend the file with `ftruncate` (`src/clientmem.rs:105-109`). Direct pointer access is allowed only in that case, otherwise callers get an fd plus offset (`src/clientmem.rs:319-324`). Destruction unmaps inline or offloads close plus unmap to the CPU worker (`src/clientmem.rs:207-222`).245
+246
+### 3.5 `leaks.rs`247
+248
+`track!` registers each new `Rc` against its client (`src/leaks.rs:3-7`). Without `rc_tracking` the tracker is a zero-sized phantom (`src/leaks.rs:9-39`). With `rc_tracking` it assigns tracker IDs, installs a tracing global allocator, records allocation backtraces, and on demand groups unreleased objects by client with containing allocation and stack evidence (`src/leaks.rs:59-178`, `src/leaks.rs:193-240`, `src/leaks.rs:268-299`). This is a debug facility, not a production GC.249
+250
+### 3.6 Acceptors251
+252
+The primary acceptor creates two Unix stream sockets, insecure `wayland-N` and secure `wayland-N.wry`, plus a lock file, scanning `1..1000` (`src/acceptor.rs:66-132`). It unlinks stale paths, takes a nonblocking `flock`, binds, listens with backlog 4096 and stores the lock fd (`src/acceptor.rs:81-142`). It then spawns separate secure and insecure accept loops (`src/acceptor.rs:144-156`). Each accepted fd becomes a `Client` (`src/acceptor.rs:168-191`).253
+254
+Tagged acceptors create extra named sockets on demand for a string tag, starting at `wayland-1000` and skipping locked names (`src/tagged_acceptor.rs:63-101`, `src/tagged_acceptor.rs:156-197`). Their metadata carries the tag and non-sandboxed status (`src/tagged_acceptor.rs:72-79`). Their accept loop is otherwise the same (`src/tagged_acceptor.rs:115-135`).255
+256
+Security context acceptors do not bind. They accept from a client-supplied listen fd and watch a close fd (`src/security_context_acceptor.rs:22-31`, `src/security_context_acceptor.rs:112-137`). New clients are marked sandboxed with caller-supplied sandbox engine, app id, instance id and bounding caps (`src/security_context_acceptor.rs:59-80`, `src/security_context_acceptor.rs:122-129`). Dual secure plus insecure sockets for one compositor is jay/wry specific.257
+258
+### 3.7 `wl_usr`259
+260
+`wl_usr` is a Wayland client stack used by the compositor itself. `UsrCon::new` connects over a Unix socket with io_uring (`src/wl_usr.rs:99-129`). `from_socket` reserves IDs 0 and 1, installs `UsrWlDisplay` object 1, and spawns incoming plus outgoing tasks (`src/wl_usr.rs:131-187`). Outgoing requests reuse the same swapchain and formatter design as server-to-client events (`src/wl_usr.rs:260-279`, `src/wl_usr.rs:281-316`). Incoming events dispatch by object id to `handle_event`, ignoring unknown server IDs but erroring on unknown client IDs (`src/wl_usr.rs:339-357`).261
+262
+Client-side objects live in `src/wl_usr/usr_ifs/`. For example `UsrWlCompositor::create_surface` sends the request and tracks the new object (`src/wl_usr/usr_ifs/usr_wl_compositor.rs:17-30`). The generated client trait uses `UsrObject`, `UsrCon` and `UsrConError` (`build/wire.rs:339-347`). The portal uses this to connect to nested displays named `wayland-*.wry` (`src/portal/ptl_display.rs:298-343`), then binds compositor, fractional scale, viewporter, dmabuf and other globals (`src/portal/ptl_display.rs:346-373`).263
+264
+### 3.8 Performance notes265
+266
+- Fixed messages avoid per-field calls and copy words directly. Variable strings, byte strings and arrays borrow from the input buffer.267
+- Small inline storage appears widely, including `SmallVec` for rects, syncs, copies and seats and `ArrayVec` for descriptor layouts, barriers and singleton broadcasts (`src/rect.rs:33`, `src/video/dmabuf.rs:327`, `src/ifs/wl_surface/commit_timeline.rs:553-561`, `src/globals.rs:410`, `src/utils/buffd/buf_in.rs:73`).268
+- Object maps use custom `CopyHashMap` over hashbrown with unsafe interior access (`src/utils/copyhashmap.rs:21-80`).269
+- Output buffers and `Buf` storage are recycled rather than reallocated in the swapchain and sender paths.270
+- Slow-client detection avoids blocking the compositor on a non-draining peer.271
+272
+## 4. Vulkan renderer273
+274
+The reader returned this one as a single dense block with fewer line citations. It is restructured here. Claims marked (verified) were spot-checked by hand. The "bridge" description was corrected, since the code gates it on `for_rendering` (`src/gfx_apis/vulkan/image.rs:535`).275
+276
+### 4.1 Abstraction277
+278
+The compositor is renderer-agnostic. `GfxApiOp` (`src/gfx_api.rs:115`) has `FillRect` (`:117`), `CopyTexture` (`:118`), `RoundedFillRect` (`:119`) and `RoundedCopyTexture` (`:120`). The `FillRect` struct (`src/gfx_api.rs:286`) and `CopyTexture` (`src/gfx_api.rs:304`) carry `acquire_sync` (`:309`) plus `release_sync` and `lazy` (`:318`). The rounded variants (`:323`, `:344`) mirror that shape.279
+280
+- `LazyTexture` (`src/gfx_api.rs:373`) lets a backend defer work, with `perform_lazy_work` feeding a `Vec<FdSync>`.281
+- `AcquireSync` (`src/gfx_api.rs:426`) has `None`, `Implicit`, `FdSync` and `Unnecessary`, with `from_fd_sync` (`:434`). `ReleaseSync` (`src/gfx_api.rs:452`) is the release side.282
+- `GfxFramebuffer` (`src/gfx_api.rs:503`) exposes `render_with_region_impl` (`:506`) and wrappers through `:889`.283
+- `GfxTexture` (`src/gfx_api.rs:931`) and `GfxContext` (`src/gfx_api.rs:1030`) complete the surface, with `supports_color_management` (`:1108`) and `supports_alpha_modes` (`:1112`).284
+- Direct scanout filtering lives near `src/gfx_api.rs:1529`, rejecting `FillRect` (`:1536`) and `RoundedCopyTexture` (`:1540`).285
+286
+A second backend exists. `create_gfx_context` (`src/gfx_apis.rs:17`) tries APIs (`:26`) as OpenGL then Vulkan, looping software false then true (`:29`), with dispatch at `:70`. The GL backend (`src/gfx_apis/gl.rs` plus `src/gfx_apis/gl/`, about 3.3k lines) is a full parallel renderer using EGL images, GL textures, and its own `TexCopyType` and `TexSourceType`.287
+288
+### 4.2 Frame recording289
+290
+A frame is an op list turned into one Vulkan submit. `RendererBase` in compositor code emits `FillRect` and `CopyTexture` ops, and `VulkanRenderer` executes them. `convert_ops` (`src/gfx_apis/vulkan/renderer.rs:1007`, verified) sorts by color, z order and index (`:1030`), then groups by pipeline key. `TexPipelineKey` (`src/gfx_apis/vulkan/renderer.rs:416`, verified) includes copy type, source type, alpha mode, EOTF pair, color matrix presence, grayscale and rounded. A separate `OutPipelineKey` (`:434`) covers the resolve pass.291
+292
+Recording centers on `record_draws` (`src/gfx_apis/vulkan/renderer.rs:1729`), `begin_rendering` (`:1620`), `submit` (`:2393`) and `execute` (`:2475`).293
+294
+- `begin_rendering` uses Vulkan 1.3 dynamic rendering with `cmd_begin_rendering` (`:1685`).295
+- Full-area clears use load op `CLEAR` (`:1667`), otherwise partial clears go through `cmd_clear_attachments` (`:1694`). The blend buffer pass uses `DONT_CARE` (`:1668`).296
+- Viewport and scissor cover the full target (`:1702`).297
+298
+Vertex data uses device-addressable buffers. `tex.common.glsl` (`src/gfx_apis/vulkan/shaders/tex.common.glsl:8`) declares a `buffer_reference` `Vertices` with push-constant `Data` carrying vertices and `mul` (`:12`). This avoids vertex bindings and lets one instanced draw consume many rects from a data buffer.299
+300
+Specialization uses seven constants in `frag_spec_const.glsl` (`src/gfx_apis/vulkan/shaders/frag_spec_const.glsl:4`): `src_has_alpha`, `has_alpha_multiplier`, `eotf`, `inv_eotf`, `has_matrix`, `alpha_mode` and `grayscale`. Pipeline creation consumes those as specialization constants.301
+302
+Descriptors have three tiers. Device probing (`src/gfx_apis/vulkan/device.rs:433`) prefers `descriptor_heap` plus `maintenance5`, falls back to `descriptor_buffer` (`:441`), and finally legacy `push_descriptor`. Enablement (`:481`) adds heap and maintenance5 or buffer names. Features (`:490`) enable timeline semaphores, `synchronization2`, `dynamic_rendering` (`:492`), `buffer_device_address` (`:500`), plus heap or buffer features (`:538`). Color bindings are fixed in `tex_set.glsl` (`src/gfx_apis/vulkan/shaders/tex_set.glsl:4`) with `texture2D tex` plus `ColorManagementData` matrix, `EotfArgs` and `InvEotfArgs`.303
+304
+Wait setup (`src/gfx_apis/vulkan/renderer.rs:2254`) imports dmabuf sync files and explicit `FdSync` into `TOP_OF_PIPE` waits. Submit uses `queue_submit2` (`:2409`) and stores `release_sync` (`:2417`). Pending frames (`:2439`) retain command buffers, images, semaphores and heap entries and spawn `await_release` on the async engine (`:2463`).305
+306
+### 4.3 Upload and import307
+308
+- SHM textures are created `OPTIMAL`, `SAMPLED` plus `TRANSFER_DST`, and filled from staging buffers with `BufferImageCopy2` limited to damage. `transfer.rs` computes copies with queue-family ownership barriers and granularity masks and expands for 4-byte alignment.309
+- The async path uses `CpuWorker` jobs `IoTransferJob` and `CopyTransferJob` (`src/gfx_apis/vulkan/transfer.rs:633`, `:642`, verified) with ring readable waits for gfx queue release. The sync path submits a buffer-image copy on the gfx queue directly.310
+- Dmabuf import in `image.rs` uses explicit DRM format modifier creation with per-plane fd import and `bind_image_memory2`, and checks disjoint planes and single-file reuse.311
+- When a dmabuf's modifier is not renderable, rendering goes into an intermediate "bridge" image and is copied out (`src/gfx_apis/vulkan/image.rs:97`, `:113`, `:535`, verified).312
+- Shm reuse checks dimensions and format before reallocating. Internal framebuffers, read-write images and blend buffers are cached by size.313
+- The blend buffer is `ABGR16161616F` (`src/gfx_apis/vulkan/format.rs:100`, verified). It holds linear working space, then an out pass resolves to the framebuffer.314
+- Staging shells and dmabuf host buffers reduce mapping churn.315
+316
+### 4.4 Synchronization317
+318
+`FdSync` (`src/gfx_api.rs:1384`) is `SyncFile` or `Syncobj`, with async wait (`:1392`), blocking wait (`:1408`), poll (`:1434`) and external ring integration (`:1474`).319
+320
+- The acquire path (`src/gfx_apis/vulkan/renderer.rs:2254`) dups explicit sync files (`:2303`) and exports dmabuf sync files with `DMA_BUF_SYNC_READ` for textures (`:2313`) and `DMA_BUF_SYNC_WRITE` for the framebuffer (`:2322`), plus an extra `FdSync` list (`:2329`).321
+- The release path (`:2343`) attaches the submit fence or timeline point to buffer reservations (`:2357`) or imports into dmabufs (`:2361`), with a fallback warning relying on implicit sync.322
+- Async SHM tracking sets `last_gfx_use` (`:2381`).323
+- Device sync creation (`:2401`) returns a fence or timeline semaphore, and submit signals it.324
+- Timeline opaque fd export is probed (`src/gfx_apis/vulkan/device.rs:472`) and mapped to DRM syncobj points with a two-point validation handshake in `timeline_semaphore.rs`. Binary semaphores import `SYNC_FD` in `semaphore.rs`.325
+- Pending frame release uses the async engine and io_uring readable waits.326
+327
+### 4.5 Memory328
+329
+Memory uses gpu-alloc, not a custom allocator and not VMA. `Cargo.toml:55` pins `ash` as `jay-ash` 0.12.0, `:56` `gpu-alloc` 0.6.0 and `:57` `gpu-alloc-types` 0.3.0 (verified). `allocator.rs` wraps `AshMemoryDevice` in `VulkanAllocator` and `VulkanThreadedAllocator` with CpuWorker async alloc and free, and zeroed pages via `Config::i_am_prototyping`. Separate small caches exist for shader buffers, uniform buffers and descriptor buffers, plus size-keyed blend buffer and image caches.330
+331
+### 4.6 Color management and HDR332
+333
+Color work happens in shaders, driven by CPU-side color descriptions.334
+335
+`tex.frag` steps in order.336
+337
+1. Sample with `textureLod` (`src/gfx_apis/vulkan/shaders/tex.frag:18`).338
+2. Unpremultiply electrical-premultiplied inputs (`:21`).339
+3. Apply EOTF (`:24`) and handle optical unpremultiply.340
+4. Apply matrix (`:29`).341
+5. Optional grayscale with Rec 709 luma (`:31`).342
+6. Inverse EOTF (`:35`).343
+7. Repremultiply (`:37`).344
+8. Alpha multiplier (`:41`).345
+346
+`out.frag` (`src/gfx_apis/vulkan/shaders/out.frag:16`) fetches from the blend buffer and repeats EOTF, matrix and inverse EOTF (`:17`).347
+348
+`eotfs.glsl` defines `TF_LINEAR` through `TF_COMPOUND_POWER_2_4` (`src/gfx_apis/vulkan/shaders/eotfs.glsl:7`), with PQ (`:38`), BT1886 with four args (`:20`), ST240, log, ST428, pow and compound variants, dispatched by `apply_eotf` (`:121`) and `apply_inv_eotf` (`:139`). Alpha modes (`src/gfx_apis/vulkan/shaders/alpha_modes.glsl:4`) are premultiplied electrical, premultiplied optical and straight.349
+350
+### 4.7 copy_device.rs351
+352
+A separate minimal Vulkan copy engine for multi-GPU movement, used by neither screencast nor the main compositor render path. `PhysicalCopyDevice` and `CopyDevice` classify `TransferType` and direction as Blit, Intra, Download or Upload. Entry points are `create_buffer` (`src/copy_device.rs:1037`, `:1342`), `create_fixed_copy` (`:1233`) and `create_copy` (`:1242`). `State` holds `render_ctx_prime_copy_device` (`src/state.rs:277`), assigned from the DRM device (`src/state.rs:2803`). Metal allocator call sites copy render to device BOs (`src/backends/metal/allocator.rs:1149`, `:1212`, `:1235`, `:1395`), including udmabuf staging (`:1507`). Prime modifier feedback reads `render_ctx_prime_modifiers` (`src/dmabuf_feedback.rs:141`).353
+354
+### 4.8 Decorations355
+356
+Effects stop at rounded rectangles and borders. The rounding SDF (`src/gfx_apis/vulkan/shaders/rounding.glsl:1`) returns zero outside bounds, selects corner radius and center per quadrant, and smoothsteps distance with half-pixel width (`:24`). Shader families include `fill`, `rounded_fill`, `tex` and `rounded_tex`, plus legacy variants under `src/gfx_apis/vulkan/shaders/legacy`. No blur and no shadow passes exist. `RendererBase` emits tab bars, borders and highlights as `FillRect` and `RoundedFillRect` ops.357
+358
+### 4.9 Coupling and requirements359
+360
+Coupling is broad outside the Vulkan directory. `vulkan.rs`, `renderer.rs`, `image.rs`, `device.rs`, `transfer.rs`, `shm_image.rs` and `pipeline.rs` import crate `backend`, `cmm`, `gfx_api`, `theme`, `utils`, `video`, `allocator`, `state`, async engine, ring and CPU worker types. The GL context (`src/gfx_apis/gl.rs:21`) shows the same pattern. A port would carry color management, dmabuf, syncobj, timeline and worker abstractions or stub them.361
+362
+- Vulkan 1.3. `VULKAN_API_VERSION` (`src/vulkan_core.rs:356`) equals `API_VERSION_1_3` (`:15`). The loader uses `libvulkan.so.1` (`:67`, `:73`).363
+- Required instance extensions (`src/vulkan_core.rs:268`) are `debug_utils` only.364
+- Required device extensions (`src/gfx_apis/vulkan/device.rs:728`) include `external_memory_fd`, `external_semaphore_fd`, `external_fence_fd`, `external_memory_dma_buf`, `queue_family_foreign`, `image_drm_format_modifier` and `push_descriptor`. Optional paths add `descriptor_heap` plus `maintenance5`, or `descriptor_buffer`.365
+- Shaders compile offline. `compile` (`compile-shaders/compile/src/main.rs:18`) maps frag and vert to shaderc kinds (`:33`) and writes SPIR-V (`:44`), with an include callback (`:27`) and hash tracking via `TREES` (`:9`).366
+367
+### 4.10 Unusual choices368
+369
+- Linear fp16 blend buffer with a separate resolve pass.370
+- Device-address vertex buffers fed through push constants.371
+- Specialization-constant-driven color pipelines.372
+- Damage-limited staging copies, optionally on the cpu_worker thread.373
+- Threaded gpu-alloc.374
+- Vulkan timeline semaphore to DRM syncobj point mapping.375
+376
+## 5. Tree, transactions, multiphase animations (jay-port branch, NOT the reference, see round2-reports.md section 6)377
+378
+### 5.1 Commit timeline379
+380
+Each client owns `CommitTimelines` (`src/client.rs:229`) and each `WlSurface` owns a `CommitTimeline` (`src/ifs/wl_surface.rs:778`). Shared state is in `src/ifs/wl_surface/commit_timeline.rs:62-70`, with per-surface `own_timeline`, `effective_timeline` plus waiters for fifo, commit time, restore, serial and unmap (`src/ifs/wl_surface/commit_timeline.rs:77-87`).381
+382
+`wl_surface.commit` validates release protocols then hands `PendingState` to the timeline (`src/ifs/wl_surface.rs:1286-1316`). Sync subsurface commits do not reach the timeline. They merge into the parent pending and abort (`src/ifs/wl_surface/wl_subsurface.rs:414-427`). Sync state is `sync_requested or sync_ancestor` (`src/ifs/wl_surface/wl_subsurface.rs:255-256`). Desync flushes cached child state through the timeline (`src/ifs/wl_surface/wl_subsurface.rs:287-293`).383
+384
+Immediate apply happens only with no dependencies, an empty queue and no fifo barrier (`src/ifs/wl_surface/commit_timeline.rs:275-282`). Otherwise an `EntryKind::Commit` is queued. Client depth is capped at 256 with a `Depth` error (`src/ifs/wl_surface/commit_timeline.rs:58-60`, `src/ifs/wl_surface/commit_timeline.rs:283-285`).385
+386
+The collector gathers acquire points, shm uploads, dmabuf presence, prime need, acquire files, commit time and restore flags (`src/ifs/wl_surface/commit_timeline.rs:243-263`, `src/ifs/wl_surface/commit_timeline.rs:963-1005`).387
+388
+- Explicit acquire points come from `set_acquire_point` (`src/ifs/wl_surface/wp_linux_drm_syncobj_surface_v1.rs:59-63`).389
+- Implicit dmabuf fences are added as plane fds when neither syncobj sync nor sync-file acquire is present (`src/ifs/wl_surface/commit_timeline.rs:977-984`).390
+- Sync-file acquire is added in `src/ifs/wl_surface/commit_timeline.rs:986-988`.391
+- Fifo flags come from `set_barrier` and `wait_barrier` (`src/ifs/wl_surface/wp_fifo_v1.rs:43-50`).392
+- Commit time comes from `set_timestamp` (`src/ifs/wl_surface/wp_commit_timer_v1.rs:45-61`).393
+394
+Waits are registered without blocking the main thread. Syncobj uses `wfs.wait` (`src/ifs/wl_surface/commit_timeline.rs:321-331`). Implicit fds use `ring.readable_external` (`src/ifs/wl_surface/commit_timeline.rs:340-350`). Shm uploads, prime copies, commit time, restore, serial and unmap mark `needs_flush` (`src/ifs/wl_surface/commit_timeline.rs:333-364`). The queue head is flushed when previously empty (`src/ifs/wl_surface/commit_timeline.rs:366-368`).395
+396
+Queues are FIFO (`src/ifs/wl_surface/commit_timeline.rs:569-579`). The head applies only when all dependencies clear (`src/ifs/wl_surface/commit_timeline.rs:582-653`). Other clients have separate `Inner` queues, so one blocked surface does not stop another client except through explicit cross-timeline links. Parent and subsurface timelines are unified through `set_effective_timeline` (`src/ifs/wl_surface/commit_timeline.rs:1008-1034`) using `Wait` and `Signal` entries (`src/ifs/wl_surface/commit_timeline.rs:529-534`). Later wakeups go through per-client `process_flush_requests` (`src/ifs/wl_surface/commit_timeline.rs:1041-1053`).397
+398
+Completion callbacks decrement counters and retry the list. This covers syncobj (`src/ifs/wl_surface/commit_timeline.rs:430-442`), shm upload (`src/ifs/wl_surface/commit_timeline.rs:453-468`), prime copy (`src/ifs/wl_surface/commit_timeline.rs:470-485`), dmabuf poll (`src/ifs/wl_surface/commit_timeline.rs:487-501`) and commit timer timeout (`src/ifs/wl_surface/commit_timeline.rs:504-521`).399
+400
+Fifo barrier handling parks `Queued` commits as `Registered` and stores `fifo_waiter` (`src/ifs/wl_surface/commit_timeline.rs:600-612`). Barrier release pushes the waiter (`src/ifs/wl_surface/commit_timeline.rs:376-381`). Latch clears it for normal presentation but defers to vblank under tearing (`src/ifs/wl_surface.rs:2561-2584`), with vblank cleanup in `src/ifs/wl_surface.rs:2543-2552`. Applied commits set the barrier (`src/ifs/wl_surface.rs:1664-1688`).401
+402
+Commit timer registration subtracts render plus flip margins from the requested time (`src/ifs/wl_surface/commit_timeline.rs:702-730`). It uses `ring.timeout_external` and attaches a `before_latch_listener`, then `before_latch` releases the commit when presentation time has passed (`src/ifs/wl_surface/commit_timeline.rs:405-427`, `src/ifs/wl_surface.rs:2555-2559`).403
+404
+Destroy versus loop breaking is split (`src/ifs/wl_surface/commit_timeline.rs:210-235`), with dependency state discarded in `src/ifs/wl_surface/commit_timeline.rs:197-208` and destroyed timelines drained through a `Gc` entry (`src/ifs/wl_surface/commit_timeline.rs:660-663`).405
+406
+### 5.2 Layout transactions407
+408
+Tree mutation uses `Transactionable` and `TransactionData` (`src/transactions.rs:20-38`, `src/transactions.rs:65-71`). Any op schedules the object and triggers serial groups (`src/transactions.rs:195-209`).409
+410
+`Transactions::commit` snapshots scheduled objects, surfaces and effects, creates or recycles a `Transaction` plus `Blocker`, queues surface blockers and assigns member ops (`src/transactions.rs:216-282`). Surfaces receive `UnblockCommitsUntil` (`src/transactions.rs:230-237`). Apply runs `before_apply`, member ops, `after_apply`, then recycles the transaction (`src/transactions.rs:284-299`).411
+412
+`Blocker` drop schedules apply exactly once by version (`src/transactions.rs:345-359`). Timeout applies the same version after a 50ms default (`src/transactions.rs:78-80`, `src/transactions.rs:441-467`). The apply loop is separate (`src/transactions.rs:469-475`).413
+414
+Per-surface blocking is in `src/transactions.rs:116-127`. `unblock_transactions_until` drops blockers at or before the committed serial (`src/transactions.rs:374-400`). `commit_is_blocked` compares `unblocked_commit` against the needed serial (`src/transactions.rs:417-419`). Disabled means tardy or zero enable count (`src/transactions.rs:421-423`). Missing acks mark `tardy`, clear blockers and force the timeout path (`src/transactions.rs:402-415`).415
+416
+The commit timeline and layout transaction meet in `maybe_apply` (`src/ifs/wl_surface/commit_timeline.rs:632-638`). The surface first records the serial, then parks on `serial_waiter` if still blocked. Publication calls `serial_unblocked` through `WlSurfaceTransactionOp::UnblockCommitsUntil` (`src/ifs/wl_surface.rs:2696-2702`), with waiter release in `src/ifs/wl_surface/commit_timeline.rs:393-397`.417
+418
+Each serial group tick allocates one `TreeSerial`, commits transactions, then runs configure groups (`src/tree_serial_groups.rs:18-30`). Configures are grouped by serial (`src/configurable.rs:241-280`). `schedule_configure` also adds the surface to transactions (`src/configurable.rs:91-105`). Readiness counting releases whole groups (`src/configurable.rs:170-189`, `src/configurable.rs:336-410`). Flush sends the serial and records `requested_serial` (`src/configurable.rs:218-239`). The client ack must carry at least that serial before `ready` (`src/ifs/wl_surface.rs:1307-1312`, `src/ifs/wl_surface.rs:1370-1374`). The t0061 test asserts `LiveTL` moves first while `RenderTL` stays old until commit (`src/it/tests/t0061_transactional_layout_animation.rs:35-47`).419
+420
+### 5.3 Tree421
+422
+Node behavior is `NodeBase` (`src/tree.rs:445-945`) with dyn wrapper `Node` (`src/tree.rs:947-968`). Visitors are in `src/tree/walker.rs:18-86`. Timelines are `LiveTL` and `RenderTL` (`src/tree.rs:976-981`), with `SplitView` maps (`src/tree.rs:984`). Links carry per-timeline validity (`src/tree.rs:985-1008`).423
+424
+Layers are ordered `Display` through `OverlayStacked` (`src/tree.rs:343-365`), with linked variants in `src/tree.rs:367-385`. Concrete types include `DisplayNode` (`src/tree/display.rs:47`), `OutputNode` (`src/tree/output.rs:131`), `WorkspaceNode` (`src/tree/workspace.rs:76-98`), containers, floats, toplevels and placeholders. Parents implement `ContainingNode` (`src/tree/containing.rs:7-59`). Toplevel behavior splits into `ToplevelNodeBase` (`src/tree/toplevel.rs:366`) and `ToplevelNode` (`src/tree/toplevel.rs:81-101`).425
+426
+Rendering walks the same hierarchy through `node_render` and `node_visit_children`. Examples are output (`src/tree/output.rs:2477`), container (`src/tree/container.rs:2953`), workspace (`src/tree/workspace.rs:559`), float (`src/tree/float.rs:972`), display (`src/tree/display.rs:285`) and placeholder (`src/tree/placeholder.rs:229`). The renderer starts from outputs and workspaces, then stacked layers and exit frames (`src/renderer.rs:250-303`).427
+428
+### 5.4 Multiphase animations429
+430
+A phase is a `MultiphasePhase` with one action plus steps (`src/presentation/multiphase.rs:204-209`). Each step is classified as horizontal or vertical move or scale (`src/presentation/multiphase.rs:1486-1509`). Diagonal or size-plus-position changes cannot be one phase and force multiphase plans.431
+432
+The planning entry is `plan_no_overlap` (`src/presentation/multiphase.rs:354-356`), with forward then reversed attempts (`src/presentation/multiphase.rs:364-412`). Strategies include `SingleAction`, `SpaceThenOrthogonalGrowth`, `HierarchyOrderedScales`, `OrientationChange`, `SwapLanes` and reversed plans (`src/presentation/multiphase.rs:159-168`), tried in that order (`src/presentation/multiphase.rs:535-616`). Independent motion groups split first (`src/presentation/multiphase.rs:460-488`).433
+434
+Validation checks chaining, duplicates, action counts, unknown nodes, stale starts, intra-phase overlap and final rects (`src/presentation/multiphase.rs:1280-1368`). Overlap uses linear edge deltas with rational time intervals (`src/presentation/multiphase.rs:1370-1484`).435
+436
+Runtime animations store multiple `from to` segments (`src/presentation/layout_animation.rs:160-168`). Total time is duration times phase count (`src/presentation/layout_animation.rs:1292-1296`). Position interpolates within the current phase with the configured curve (`src/presentation/layout_animation.rs:1309-1319`). Interrupts retarget from the current `rect_at` (`src/presentation/layout_animation.rs:846-892`, `src/presentation/layout_animation.rs:894-952`, `src/presentation/layout_animation.rs:1112-1129`). Path validation for reversed interrupts is in `src/presentation/multiphase.rs:414-458`. Appearing windows can wait behind existing phases (`src/presentation/layout_animation.rs:976-1018`).437
+438
+Ticks are per-output `LatchListener`s (`src/presentation/layout_animation.rs:257-260`, `src/presentation/layout_animation.rs:1945-1960`). Each latch damages active rects and stops when idle (`src/presentation/layout_animation.rs:1218-1248`). Ticks attach to `output.latch_event` (`src/presentation/layout_animation.rs:1250-1269`) and detach in `src/presentation/layout_animation.rs:1271-1289`. The test asserts latch drives damage and that output disconnect or reconnect changes tick count (`src/it/tests/t0061_transactional_layout_animation.rs:69-90`).439
+440
+Animations are transaction effects. The trait observes pre and post trees without mutating live state (`src/transactions.rs:45-58`). Scheduling adds effects (`src/presentation/layout_animation.rs:561-738`). Single moves capture the before rect plus current visual (`src/presentation/layout_animation.rs:1472-1514`). Batches capture whole container snapshots before and after (`src/presentation/layout_animation.rs:1596-1664`), with coalescing per container (`src/presentation/layout_animation.rs:1666-1671`). Direct manipulation first cancels animations then suppresses new ones for that transaction (`src/presentation/layout_animation.rs:527-559`, `src/presentation/layout_animation.rs:1516-1522`). The test asserts no animation before publication and a multiphase spawn after (`src/it/tests/t0061_transactional_layout_animation.rs:92-127`), plus no animation for direct manipulation (`src/it/tests/t0061_transactional_layout_animation.rs:129-146`).441
+442
+Old pixels use retained captures. `RetainedToplevel`, `RetainedSurface` and `RetainedContent` hold textures or colors plus subsurface offsets (`src/presentation/layout_animation.rs:107-153`). Capture clones live buffers and subsurface structure (`src/presentation/layout_animation.rs:1355-1469`). Exits expose timed frames (`src/presentation/layout_animation.rs:766-778`). Spawns expose retained frames while resizing (`src/presentation/layout_animation.rs:780-794`). Close while blocked produces no exit until timeout publication (`src/it/tests/t0061_transactional_layout_animation.rs:151-174`). Closing mid-animation hands the presented rect to the exit (`src/it/tests/t0061_transactional_layout_animation.rs:176-201`).443
+444
+### 5.5 layout_animation computation and render plug-in445
+446
+It computes curve samples, spawn endpoints, roll edges, batch plans, per-node visual rects, damage and retained frames. Curves include linear and flattened cubic bezier (`src/presentation/layout_animation.rs:281-323`, `src/presentation/layout_animation.rs:326-411`). Spawn endpoints cover grow, fly and roll (`src/presentation/layout_animation.rs:413-450`), with edge helpers in `src/presentation/layout_animation.rs:452-519`. Batch planning filters to moving windows, partitions groups, then plans or falls back to direct interpolation (`src/presentation/layout_animation.rs:954-1110`).447
+448
+Render integration reads `visual_rect` for fullscreen, container children and mono bodies (`src/renderer.rs:311-330`, `src/renderer.rs:470-505`, `src/renderer.rs:571-589`). Exit frames render above tiled or floating stacks (`src/renderer.rs:256-274`, `src/renderer.rs:737-765`). Spawns render retained content scaled from source to target size (`src/renderer.rs:767-820`, `src/renderer.rs:822-860`). Client damage follows the presented window through `visual_damage` (`src/ifs/wl_surface.rs:1735-1752`, `src/presentation/layout_animation.rs:747-764`).449
+450
+### 5.6 Tree verify451
+452
+`verify_tree_invariants` checks both timelines (`src/tree/verify.rs:14-22`) and runs after idle in integration tests (`src/it/testrun.rs:198-201`). It checks output identity and dummy status, active workspace membership, workspace-to-output links, toplevel parent and workspace pointers, float workspace and display stack membership, fullscreen uniqueness and placeholder linkage, scratchpad mapping and container snapshots (`src/tree/verify.rs:72-108` and following).453
+454
+Container checks include nonempty children, cached counts, child map identity, finite nonnegative factors, factor sum tolerance, focus history and backlinks, mono child placement, no pending layout and non-mono overlap (`src/tree/verify.rs:671-853`).455
+456
+### 5.7 Unusual choices457
+458
+- Per-client commit queues plus per-surface timelines with explicit cross-timeline signals.459
+- Shm upload, prime copy, dmabuf poll, fifo, commit time, restore, serial and unmap are treated uniformly as queue blockers.460
+- The tardy path that discards blockers and relies on timeout is pragmatic.461
+- A collision-free geometric planner with exact rational overlap checks drives window animation.462
+- Retained textures render scaled from old source size to animated target size while clients resize.463
+464
+## 6. Process model, config, tests, utilities465
+466
+### 6.1 Startup467
+468
+- `main()` only calls `cli::main()` (`src/main.rs:138-140`). CLI parsing decides run vs control commands (`src/cli.rs:312-356`).469
+- Non-`run` commands drop all process capabilities immediately (`src/cli.rs:320-322`).470
+- `Cmd::Run` calls `start_compositor` (`src/cli.rs:327`). It resets signal handlers (`src/compositor.rs:162-164`), makes the process a child subreaper or re-execs under one (`src/compositor.rs:164`, `src/utils/clone3.rs:87-115`), snapshots capabilities (`src/compositor.rs:165`), and keeps only `CAP_SYS_NICE` for the compositor (`src/pr_caps.rs:83-115`).471
+- If `CAP_SYS_NICE` survived, it elevates the scheduler and moves the privileged capability onto a dedicated helper thread (`src/compositor.rs:166-172`, `src/pr_caps.rs:122-147`). The reaper child also drops caps (`src/utils/clone3.rs:116`).472
+- The forker is created next (`src/compositor.rs:173-174`), before logging, io_uring, threads, portal, or graphics. Later startup creates threads and io_uring state that must not be forked.473
+- Startup then spawns the portal helper, sleeper, profiler, and logger (`src/compositor.rs:174-184`), then builds the full `State` in `start_compositor2` (`src/compositor.rs:253-536`).474
+- `start_compositor2` installs the Wayland acceptor, IPC acceptor, forker environment, portal task, and compositor task, then runs io_uring (`src/compositor.rs:495-535`).475
+- The forker child sanitizes its environment by forcing `XDG_SESSION_TYPE=wayland` and removing `DISPLAY`/`WAYLAND_DISPLAY` (`src/forker.rs:343-348`), renames itself (`src/forker.rs:349`), takes `SIGKILL` on parent death (`src/forker.rs:350`, `src/forker.rs:584-592`), ignores `SIGCHLD` (`src/forker.rs:351-353`), and collapses its fds so only the control socket survives (`src/forker.rs:569-582`).476
+- `start_compositor3` selects a backend, publishes sockets, loads config, creates the default seat, starts global handlers and Xwayland, then runs the backend (`src/compositor.rs:538-588`).477
+- Backend order defaults to X11 then Metal (`src/compositor.rs:755-758`). Config is loaded after graphics/backend selection but before seats and Xwayland (`src/compositor.rs:565-581`).478
+- `RLIMIT_NOFILE` is raised to the hard limit by default (`src/compositor.rs:797-814`).479
+480
+### 6.2 Forker481
+482
+- `ForkerProxy::create` makes a control socketpair and calls `double_fork` (`src/forker.rs:99-119`). The parent gets a pidfd and the child enters `Forker::handle` (`src/forker.rs:114-118`).483
+- `double_fork` uses pidfd-carrying socketpairs so both stages reliably learn child pidfds (`src/utils/clone3.rs:24-48`, `src/utils/clone3.rs:50-85`).484
+- The compositor never calls `fork`/`exec` directly for clients. It queues `ServerMessage::Spawn` plus separately passed fds (`src/forker.rs:206-225`, `src/forker.rs:276-290`). The forker re-forks, remaps fds with collision handling, applies env deltas, and execs (`src/forker.rs:449-546`, `src/forker.rs:594-616`).485
+- It is forked early because only an early, single-threaded, pre-graphics process can safely `fork` again later. It also owns the child environment via `SetEnv` (`src/forker.rs:139-151`, `src/forker.rs:423-431`) and reports exec failures and pidfds back (`src/forker.rs:473-503`, `src/forker.rs:548-556`).486
+- Xwayland is one special spawn with fixed fds stderr=2, displayfd=3, listenfd=4, wm=5, wayland=6 (`src/forker.rs:170-194`).487
+488
+### 6.3 Config489
+490
+- Current config is in-process and typed. `ConfigProxy::default` wires the compositor handler directly to `wry_toml_config::configure` through `wry_config::_private::client::init` (`src/config.rs:255-259`).491
+- `init` takes a `Weak<dyn ConfigServerEndpoint>`, an `InitMessage`, and a configure function pointer (`wry-config/src/_private/client.rs:242-246`).492
+- Traffic consists of cloned Rust enums such as `ServerMessage`, `ClientMessage`, and `Response` (`wry-config/src/_private/ipc.rs:84-150`, `src/config/handler.rs:101-117`). `ConfigProxyHandler::send` calls `client.handle(msg.clone())` with no serialization (`src/config/handler.rs:324-328`).493
+- No `dlopen`/`libloading`/`config.so` loading remains in `src/config/handler.rs`. The public docs still describe a `cdylib` plus `config!` workflow (`wry-config/src/lib.rs:19-33`), but `wry-config/src/macros.rs:1-28` now only defines `try_get!`/`get!`, so that documentation looks stale.494
+- The high-level `wry-config` API exposes seats, windows, workspaces, video/connectors, input, keyboard, theme, timers, tasks, exec, logging, and lifecycle calls (`wry-config/src/lib.rs:61-79`, `wry-config/src/lib.rs:107-122`).495
+- `reload()` calls into the ambient config client (`wry-config/src/lib.rs:117-122`), becomes `ClientMessage::Reload` (`src/config/handler.rs:3714`), invokes `state.reload_config()` (`src/config/handler.rs:523-525`), and reissues `configure(true)` (`src/state.rs:2497-2502`).496
+- Every configure sends feature negotiation then `Configure { reload }` (`src/config.rs:240-253`).497
+- The TOML implementation preserves a `PersistentState` across reloads and clears ephemeral actions/rules/triggers/counters on unload (`toml-config/src/lib.rs:2118-2145`).498
+- Unload clears timers, pollables, and client/window matchers (`src/config/handler.rs:307-322`), while `ConfigProxy::destroy` drops the client endpoint (`src/config.rs:48-52`) and `Clear` is available as an explicit message (`src/config.rs:128-130`).499
+- There is a versioned `VERSION = 1` and feature list (`wry-config/src/_private.rs:21-29`, `wry-config/src/_private/ipc.rs:69-82`).500
+501
+### 6.4 Xwayland502
+503
+- Xwayland is started lazily. The manager allocates and listens on an X socket, exports `DISPLAY`, then waits for a readable connection attempt before launching Xwayland (`src/xwayland.rs:106-133`).504
+- Launch uses fixed args including `-terminate`, `-rootless`, `-displayfd 3`, `-listenfd 4`, `-wm 5`, plus optional `-enable-ei-portal` after probing `Xwayland -help` (`src/xwayland.rs:231-282`).505
+- The compositor creates a privileged Wayland client for the Xwayland process itself (`src/xwayland.rs:188-203`), waits for the displayfd signal (`src/xwayland.rs:205`), constructs the WM asynchronously (`src/xwayland.rs:214-219`), and supervises its pidfd (`src/xwayland.rs:220-225`).506
+- Window management lives in `src/xwayland/xwm.rs:1-60` and speaks X11 through the local `xcon` parser/formatter and generated `wire_xcon` bindings (`src/xcon/parser.rs`, `src/xcon/formatter.rs`, `src/xcon/consts.rs`, `src/xwayland/xwm.rs:54-60`).507
+- Compositor-level Xwayland supervision is one async task installed once (`src/state.rs:1630-1638`), and stopping only clears the task when the server is not running (`src/state.rs:1640-1645`).508
+509
+### 6.5 Integration tests510
+511
+- Tests are in-process and feature-gated (`src/compositor.rs:205-218`, `src/cli.rs:145-151`, `src/cli.rs:350-351`).512
+- Each test boots a fresh compositor with `TestBackend` via `start_compositor_for_test` (`src/it.rs:124-186`, `src/compositor.rs:747-754`).513
+- `TestBackend` provides an 800x600 virtual connector, keyboard, mouse, and test graphics context (`src/it/test_backend.rs:97-120`).514
+- Virtual clients connect over a real Unix socket to the test acceptor and speak Wayland through `TestTransport` (`src/it/testrun.rs:53-107`, `src/it/test_transport.rs:35-80`).515
+- Config is replaced by a thread-local `TestConfig` implementing the same typed config endpoint (`src/it/test_config.rs:34-113`, `src/config.rs:261-264`).516
+- Test mode zeroes configure/transaction timeouts (`src/compositor.rs:575-578`). Each test runs as a `Phase::Present` future with a watchdog timeout (`src/it.rs:156-172`).517
+- Tests run in parallel across worker threads, one isolated runtime dir per process (`src/it.rs:46-52`, `src/it.rs:77-103`).518
+- There are about 73 `src/it/tests/t*.rs` cases, plus protocol-object helpers under `src/it/test_ifs/` and utilities under `src/it/test_utils/`.519
+- Assertions cover tree geometry and focus (`src/it/tests/t0002_window.rs:9-28`), protocol behavior, session-manager state, and golden pixels. The scaled-render test renders through the real screenshot path and byte-compares QOI output (`src/it/tests/t0071_scaled_render.rs:22-60`).520
+521
+### 6.6 `sm`522
+523
+- `sm` is a SQLite-backed Wayland session manager (`src/sm.rs:1-56`, `src/compositor.rs:287-292`).524
+- It models sessions, named toplevel sessions, owners, and asynchronous acquire/rename/update/disown jobs (`src/sm.rs:66-144`, `src/sm.rs:209-228`, `src/sm.rs:359-384`).525
+- Toplevel state tracks output, workspace, floating rectangle, and fullscreen (`src/sm.rs:178-186`, `src/sm.rs:484-539`).526
+- Persistence work is scheduled as SQLite jobs and flushed by a background task (`src/sm.rs:464-475`, `src/sm.rs:681-705`).527
+528
+### 6.7 Keyboard and input529
+530
+- `kbvm` is an external crates.io dependency, version 0.1.7 with `compose` (`Cargo.toml:64`).531
+- It supplies the XKB context, RMLVO/keymap compilation, state machine, and lookup tables (`src/kbvm.rs:42-72`, `src/kbvm.rs:105-164`, `src/kbvm.rs:289-323`).532
+- Keymaps are sealed memfds, with a separate Xwayland-compatible rendering (`src/kbvm.rs:210-231`).533
+- `libinput` is used through unsafe FFI, with no evdev reimplementation. The code binds `libinput_path_create_context`, dispatch, event, fd, logging, and ref functions (`src/libinput.rs:15-26`), supplies restricted open/close callbacks (`src/libinput.rs:39-63`), forwards libinput logs (`src/libinput.rs:176-195`), and unrefs on drop (`src/libinput.rs:168-174`).534
+- Local `evdev` code is only a small `EVIOCGBIT` key-capability helper (`src/evdev.rs:1-34`).535
+536
+### 6.8 Homegrown utilities537
+538
+- `CloneCell` (`src/utils/clonecell.rs:9-55`). `UnsafeCell` cell for cheaply cloneable `Rc`/handle types in single-threaded code.539
+- `CopyHashMap` (`src/utils/copyhashmap.rs:21-56`). Interior-mutable hashbrown map with access counting and lockable hasher behavior.540
+- `NumCell` (`src/utils/numcell.rs:9-30`). `Cell` numeric wrapper with fetch-add/sub and bit operations.541
+- `AsyncQueue` (`src/utils/queue.rs:16-54`). Single-consumer async FIFO with waker support.542
+- `SyncQueue` (`src/utils/syncqueue.rs:10-37`). Non-async interior-mutable deque.543
+- `Stack`/`AsyncStack` (`src/utils/stack.rs:15-48`, `src/utils/stack.rs:72-99`). Interior-mutable `Vec` stacks, optionally awaitable for nonemptiness.544
+- `SmallMap` (`src/utils/smallmap.rs:11-58`). Small inline map with interior mutability.545
+- `LinkedList` (`src/utils/linkedlist.rs:13-60`). Intrusive-style doubly linked list with manual refcounts.546
+- `VecSet` (`src/utils/vecset.rs:4-41`). Small vector-backed set used for pressed keys and similar sets.547
+- `RefCounted` (`src/utils/refcounted.rs:8-40`). Manual refcount table with version bumping.548
+- `AsyncEvent` (`src/utils/asyncevent.rs:11-42`). Counting one-shot async trigger.549
+- `rc_eq`/`RcEq` (`src/utils/rc_eq.rs:5-45`). Pointer-identity equality for `Rc`.550
+- `ErrorFmt` (`src/utils/errorfmt.rs:5-22`). Formats chained `Error::source` output.551
+- `oserror` (`src/utils/oserror.rs:9-60`). Raw errno wrapper plus generated constructors/messages.552
+- `clone3` helpers (`src/utils/clone3.rs:24-85`). pidfd-aware fork/double-fork, reaper setup, and child supervision.553
+- The workspace also contains its own TOML parser, PHF maps, and wire-format codegen crates (`Cargo.toml:16`, `toml-config/src/toml/`, `toml-config/src/phf.rs`).554
+555
+### 6.9 Tracy556
+557
+- Profiling is behind the `tracy` Cargo feature (`Cargo.toml:105`).558
+- Without it, zone/frame macros compile to no-ops (`src/tracy.rs:1-16`, `src/tracy/tracy_noop.rs:21-51`).559
+- With it, zone names are cached in a global map and emitted through `tracy-client-sys` begin/end calls (`src/tracy/tracy_impl.rs:30-80`).560
+- The profiler is enabled during normal startup (`src/compositor.rs:176`).561
+562
+### 6.10 Unusual choices563
+564
+- Nearly everything is single-threaded `Rc` plus `UnsafeCell` interior mutability, with threads isolated to CPU work, SQLite, `CAP_SYS_NICE`, and test runners.565
+- Spawning is centralized in a pre-thread double-forked helper that passes fds over a socket and remaps colliding targets.566
+- Config, tests, and production share the same typed config traits rather than separate serialization boundaries.567
+- The repo vendors protocol-adjacent machinery including X11 wire parsing, Wayland buffer handling, TOML parsing, PHF maps, and XML/wire codegen.568
+- SQLite is a core runtime dependency for session state, not merely diagnostics.569
+570
+## 7. Smithay extension points571
+572
+Paths in this section are relative to shallow clones of smithay (`023b78b`, 2026-10-15) and calloop (0.14.5), prefixed `smithay/` and `calloop/`.573
+574
+### 7.1 Event loop575
+576
+calloop 0.14.5 uses the `polling` 3.0.0 crate rather than direct epoll calls (`calloop/Cargo.toml:26`). `Poll` holds an `Arc<Poller>` (`calloop/src/sys.rs:174`) built with `Poller::new()` (`calloop/src/sys.rs:207`). Docs describe it as OS-provided polling with epoll on Linux (`calloop/src/lib.rs:99`). No `io_uring` string appears in calloop or smithay.577
+578
+The futures executor is optional behind the `executor` feature (`calloop/Cargo.toml:41`). It is implemented as `Executor<T>` plus `Scheduler<T>` (`calloop/src/sources/futures.rs:45`), built on `async-task` runnables woken through a `PingSource`. The module docs state futures need their own wakeup means and point at `LoopHandle::adapt_io` (`calloop/src/sources/futures.rs:17`).579
+580
+smithay does not hard-require calloop for `wayland-server::Display`. Anvil drives clients by wrapping the display in a generic fd source with `Generic::new(display, Interest::READ, Mode::Level)` then calling `dispatch_clients` and `flush_clients` (`smithay/anvil/src/state.rs:682`, `smithay/anvil/src/udev.rs:544`). The listening socket helper is itself an `EventSource` (`smithay/src/wayland/socket.rs:95`), and other helpers merely provide `EventSource` impls.581
+582
+- `DmabufSource` (`smithay/src/backend/allocator/dmabuf.rs:631`)583
+- `DrmDeviceNotifier` (`smithay/src/backend/drm/device/mod.rs:525`)584
+- `UdevBackend` (`smithay/src/backend/udev.rs:130`)585
+- `LibinputInputBackend` (`smithay/src/backend/libinput/mod.rs:707`)586
+- `X11Backend` (`smithay/src/backend/x11/mod.rs:561`)587
+- `WinitEventLoop` (`smithay/src/backend/winit/mod.rs:789`)588
+- `DrmSyncPointSource` (`smithay/src/wayland/drm_syncobj/sync_point.rs:286`)589
+- `SecurityContextListenerSource` (`smithay/src/wayland/security_context/listener_source.rs:36`)590
+- `XWayland` (`smithay/src/xwayland/xserver.rs:279`)591
+- `EiInput` (`smithay/src/backend/libei/mod.rs:163`)592
+593
+A compositor could poll the display fd from another loop and call `dispatch_clients` plus `flush_clients` itself, at the cost of those ready-made sources and the `LoopHandle` insertion helpers used across backends and xwayland.594
+595
+### 7.2 Wayland server596
+597
+smithay uses `wayland-server` 0.31.13 and `wayland-backend` 0.3.15 as optional deps enabled by `wayland_frontend` (`smithay/Cargo.toml:64`, `smithay/Cargo.toml:66`, `smithay/Cargo.toml:115`). The default is the pure Rust backend. The libwayland system backend is only selected by `use_system_lib` through `wayland-backend/server_system` plus `wayland-sys` (`smithay/Cargo.toml:113`). That feature also gates `gbm?/import-wayland` and the `ImportEgl` path.598
+599
+Dispatch uses `wayland-server` `Dispatch` and `GlobalDispatch`, but smithay modules expose the simpler `Dispatch2` and `GlobalDispatch2` (`smithay/src/wayland/dispatch2.rs:6`, `smithay/src/wayland/dispatch2.rs:25`), with a `delegate_dispatch2` macro (`smithay/src/wayland/dispatch2.rs:45`) that blanket-implements the upstream traits. Each protocol module documents `*State` plus `*Handler` plus `delegate_dispatch2`, for example compositor (`smithay/src/wayland/compositor/mod.rs:63`).600
+601
+### 7.3 Renderer abstraction602
+603
+Core traits live in `smithay/src/backend/renderer/mod.rs`.604
+605
+- `Bind` (`:216`) has `bind` and `supported_formats`.606
+- `Texture` (`:233`) has `size`, `width`, `height` and `format`.607
+- `Frame` (`:270`) has `context_id`, `clear`, `draw_solid`, `render_texture_at`, `render_texture_from_to`, `transformation`, `output_size`, `wait` and `finish`.608
+- `Renderer` plus `RendererSuper` (`:385`, `:402`) have `context_id`, `downscale_filter`, `upscale_filter`, `set_debug_flags`, `debug_flags`, `render`, `wait`, `cleanup_texture_cache` and `invalidate_caches`.609
+- `Offscreen::create_buffer` (`:467`).610
+- `ImportMem` (`:511`) with `import_memory`, `update_memory` and `mem_formats`. `ImportMemWl` (`:479`) with `import_shm_buffer` and `shm_formats`.611
+- `ImportDma` (`:643`) with `dmabuf_formats`, `has_dmabuf_format` and `import_dmabuf`. `ImportDmaWl::import_dma_buffer` (`:618`). `ImportAll::import_buffer` (`:677`).612
+- `ExportMem` (`:747`) with `copy_framebuffer`, `copy_texture`, `can_read_texture` and `map_texture`.613
+- `Blit::blit` (`:808`) and `BlitFrame` (`:836`) with `blit_to` and `blit_from`.614
+615
+There is no Vulkan renderer in tree. `src/backend/vulkan` only has instance and physical device helpers. Its module docs state it does not provide logical devices, rendering or memory allocation (`smithay/src/backend/vulkan/mod.rs:1`). Vulkan allocation is separate in `src/backend/allocator/vulkan`.616
+617
+Multigpu is generic over `GraphicsApi` and `ApiDevice` (`smithay/src/backend/renderer/multigpu/mod.rs:728`, `:749`). A device exposes `renderer`, `renderer_mut`, `allocator` returning a `Dmabuf` allocator, `node`, plus cross-device import hints. Target selection needs `Bind<Dmabuf>` (`:328`), cross rendering needs target-side `ImportDma` (`:394`), and `early_import` needs `ImportMemWl` plus `ImportDmaWl` plus `ExportMem` (`:493`).618
+619
+`OutputDamageTracker` (`smithay/src/backend/renderer/damage/mod.rs:193`) exposes `new`, `from_output`, `from_mode_source`, `mode`, `render_output`, `render_output_with_states` and `damage_output` (`:250`, `:279`, `:300`, `:317`, `:326`, `:382`, `:435`). Rendering requires `E: RenderElement<R>` and `R: Renderer`.620
+621
+`Element` (`smithay/src/backend/renderer/element/mod.rs:518`) requires `id`, `current_commit`, `src`, `geometry` plus defaulted `location`, `transform`, `damage_since`, `opaque_regions`, `alpha`, `kind` and `is_framebuffer_effect`. `RenderElement` (`:572`) requires `draw` and optionally `underlying_storage` plus `capture_framebuffer`.622
+623
+### 7.4 DRM624
+625
+`DrmCompositor` docs (`smithay/src/backend/drm/compositor/mod.rs:1`) describe walking elements front to back, assigning overlays, then trying the top fitting element for cursor, and compositing the rest on primary with the supplied renderer. Overlays are atomic-only, with legacy limited to primary, plus `UnderlyingStorage` export and atomic test commits. Scanout permission is controlled by `FrameFlags` (`:1021`) with `ALLOW_PRIMARY_PLANE_SCANOUT`, `ALLOW_PRIMARY_PLANE_SCANOUT_ANY`, `ALLOW_OVERLAY_PLANE_SCANOUT`, `ALLOW_CURSOR_PLANE_SCANOUT`, `SKIP_CURSOR_ONLY_UPDATES`, `ALLOW_SCANOUT` and `DEFAULT`.626
+627
+Frame scheduling is caller-driven. `render_frame` (`:1686`) needs `R: Renderer + Bind<Dmabuf>` and prepares planes and damage. `queue_frame` (`:2441`) queues and submits when idle. `commit_frame` (`:2487`) commits without vblank generation. `frame_submitted` (`:2618`) retires the pending frame and submits a queued one. Empty frames must be rescheduled by the caller, typically with a retrace timer.628
+629
+VRR exists on surfaces as `vrr_supported`, `vrr_enabled` and `use_vrr` backed by `VRR_ENABLED` (`smithay/src/backend/drm/surface/atomic.rs:558`, `smithay/src/backend/drm/surface/gbm.rs:502`), with `VrrSupport` values including `RequiresModeset`. No tearing or async flip control, no gamma or CTM or color pipeline properties, and no render-as-late-as-possible deadline API were found in `src/backend/drm/compositor`.630
+631
+### 7.5 Surface commit handling632
+633
+Commits use double-buffered cached state plus transactions and blockers. `Blocker` and `BlockerState` with `Pending`, `Released` and `Cancelled` are in `smithay/src/wayland/compositor/transaction.rs:53`. A `Barrier` helper is at `:70`. Transactions collect surfaces plus blockers (`:111`) and support `add_blocker` (`:164`), `merge_into` (`:174`), `finalize` (`:207`), global readiness in `Transaction::state` (`:236`), application in `apply` (`:261`), and per-client ordering in `TransactionQueue` (`:280`). The header comment (`:1`) describes synchronized subsurfaces merging into the parent pending transaction and future explicit sync blockers.634
+635
+Public hooks are `add_pre_commit_hook`, `remove_pre_commit_hook` and `add_blocker` (`smithay/src/wayland/compositor/mod.rs:454`, `:547`), backed by `PrivateSurfaceData` (`smithay/src/wayland/compositor/tree.rs:188`). `wl_surface.commit` invokes pre-commit hooks then commits (`smithay/src/wayland/compositor/handlers.rs:266`).636
+637
+Dmabuf readiness uses `Dmabuf::generate_blocker` returning `DmabufBlocker` plus `DmabufSource` (`smithay/src/backend/allocator/dmabuf.rs:267`), with the `Blocker` impl at `:524`. Explicit sync uses acquire and release points stored in `DrmSyncobjSurfaceCachedState` (`smithay/src/wayland/drm_syncobj/mod.rs:96`), validated as pairs (`:242`), installed by pre-commit hook (`:330`), and waited on through `DrmSyncPoint::generate_blocker` plus `DrmSyncPointBlocker` (`smithay/src/wayland/drm_syncobj/sync_point.rs:240`, `:324`). Multiple surfaces can join one transaction through `PendingTransaction::merge_into` and synchronized subsurface grouping.638
+639
+### 7.6 Presentation and timing protocols640
+641
+`src/wayland` contains `presentation` (presentation-time), `fifo` (fifo-v1), `commit_timing` (commit-timing-v1), `drm_syncobj` (linux-drm-syncobj-v1), `content_type` (content-type-v1) and `alpha_modifier` (alpha-modifier-v1) (`smithay/src/wayland/mod.rs:50`). No `tearing_control` or `color_management` modules exist, so tearing-control-v1 and color-management-v1 have no dedicated smithay helpers in this checkout.642
+643
+### 7.7 Allocator644
+645
+Common docs and traits are in `smithay/src/backend/allocator/mod.rs:1`, with submodules `dmabuf`, `dumb`, `format`, `gbm`, `udmabuf`, `vulkan` and `swapchain` (`:18`), and the `Allocator` plus `Buffer` traits (`:44`, `:60`). Concrete types are `GbmAllocator` (`smithay/src/backend/allocator/gbm.rs:147`), `DumbBuffer` (`smithay/src/backend/allocator/dumb.rs:17`), `VulkanAllocator` (`smithay/src/backend/allocator/vulkan/mod.rs:123`), `VulkanImage` (`:382`), `Swapchain` (`smithay/src/backend/allocator/swapchain.rs:46`) and `Slot` (`:74`), plus `Dmabuf` in `dmabuf.rs`. The Vulkan allocator targets up to Vulkan 1.3 and requires `VK_EXT_image_drm_format_modifier`, `VK_EXT_external_memory_dmabuf` and `VK_KHR_external_memory_fd`, with optional `VK_EXT_4444_formats` (`smithay/src/backend/allocator/vulkan/mod.rs:1`).