Commit 0c1685f1d412
tarball11 files changed+1065-73
@@ -82,6 +82,26 @@ dependencies = [
8282
]8383
8484
[[package]]85
+name = "bytemuck"86
+version = "1.25.2"87
+source = "registry+https://github.com/rust-lang/crates.io-index"88
+checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"89
+dependencies = [90
+ "bytemuck_derive",91
+]92
+93
+[[package]]94
+name = "bytemuck_derive"95
+version = "1.12.1"96
+source = "registry+https://github.com/rust-lang/crates.io-index"97
+checksum = "6a1f896587b6f2c069c73d2f0913e2d590c3990285cd2f0b6aa02b786b4c679c"98
+dependencies = [99
+ "proc-macro2",100
+ "quote",101
+ "syn 3.0.9",102
+]103
+104
+[[package]]85105
name = "bytes"86106
version = "1.12.1"87107
source = "registry+https://github.com/rust-lang/crates.io-index"@@ -95,7 +115,7 @@ checksum = "43b32313e937c8721c9d17ee9bf60699a6402b4b7c2cb6ac1bee0ffcaa4b4e5e"
95115
dependencies = [96116
"bitflags",97117
"polling",98
- "rustix",118
+ "rustix 1.1.5",99119
"tracing",100120
]101121
@@ -106,7 +126,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
106126
checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa"107127
dependencies = [108128
"calloop",109
- "rustix",129
+ "rustix 1.1.5",110130
"wayland-backend",111131
"wayland-client",112132
]@@ -187,12 +207,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
187207
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"188208
189209
[[package]]210
+name = "drm"211
+version = "0.14.1"212
+source = "registry+https://github.com/rust-lang/crates.io-index"213
+checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f"214
+dependencies = [215
+ "bitflags",216
+ "bytemuck",217
+ "drm-ffi",218
+ "drm-fourcc",219
+ "libc",220
+ "rustix 0.38.44",221
+]222
+223
+[[package]]224
+name = "drm-ffi"225
+version = "0.9.1"226
+source = "registry+https://github.com/rust-lang/crates.io-index"227
+checksum = "51a91c9b32ac4e8105dec255e849e0d66e27d7c34d184364fb93e469db08f690"228
+dependencies = [229
+ "drm-sys",230
+ "rustix 1.1.5",231
+]232
+233
+[[package]]190234
name = "drm-fourcc"191235
version = "2.2.0"192236
source = "registry+https://github.com/rust-lang/crates.io-index"193237
checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4"194238
195239
[[package]]240
+name = "drm-sys"241
+version = "0.8.1"242
+source = "registry+https://github.com/rust-lang/crates.io-index"243
+checksum = "ecc8e1361066d91f5ffccff060a3c3be9c3ecde15be2959c1937595f7a82a9f8"244
+dependencies = [245
+ "libc",246
+ "linux-raw-sys 0.9.4",247
+]248
+249
+[[package]]196250
name = "enumflags2"197251
version = "0.7.12"198252
source = "registry+https://github.com/rust-lang/crates.io-index"@@ -225,7 +279,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
225279
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"226280
dependencies = [227281
"libc",228
- "windows-sys",282
+ "windows-sys 0.61.2",229283
]230284
231285
[[package]]@@ -247,6 +301,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
247301
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"248302
249303
[[package]]304
+name = "gbm"305
+version = "0.18.0"306
+source = "registry+https://github.com/rust-lang/crates.io-index"307
+checksum = "ce852e998d3ca5e4a97014fb31c940dc5ef344ec7d364984525fd11e8a547e6a"308
+dependencies = [309
+ "bitflags",310
+ "drm",311
+ "drm-fourcc",312
+ "gbm-sys",313
+ "libc",314
+]315
+316
+[[package]]317
+name = "gbm-sys"318
+version = "0.4.0"319
+source = "registry+https://github.com/rust-lang/crates.io-index"320
+checksum = "c13a5f2acc785d8fb6bf6b7ab6bfb0ef5dad4f4d97e8e70bb8e470722312f76f"321
+dependencies = [322
+ "libc",323
+]324
+325
+[[package]]250326
name = "generator"251327
version = "0.8.11"252328
source = "registry+https://github.com/rust-lang/crates.io-index"@@ -299,6 +375,12 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
299375
300376
[[package]]301377
name = "hermit-abi"378
+version = "0.3.9"379
+source = "registry+https://github.com/rust-lang/crates.io-index"380
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"381
+382
+[[package]]383
+name = "hermit-abi"302384
version = "0.5.3"303385
source = "registry+https://github.com/rust-lang/crates.io-index"304386
checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284"@@ -314,6 +396,35 @@ dependencies = [
314396
]315397
316398
[[package]]399
+name = "input"400
+version = "0.10.0"401
+source = "registry+https://github.com/rust-lang/crates.io-index"402
+checksum = "f9793345a65d71317763a33066b5d8351f8760dde8d4930fe9e39b5f14a7959d"403
+dependencies = [404
+ "bitflags",405
+ "input-sys",406
+ "libc",407
+ "udev",408
+]409
+410
+[[package]]411
+name = "input-sys"412
+version = "1.19.0"413
+source = "registry+https://github.com/rust-lang/crates.io-index"414
+checksum = "36eee07d8e02bd95bf52b2e642cf13d33701b94c6e4b04fbf1d1fb07e9cb19e7"415
+416
+[[package]]417
+name = "io-lifetimes"418
+version = "1.0.11"419
+source = "registry+https://github.com/rust-lang/crates.io-index"420
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"421
+dependencies = [422
+ "hermit-abi 0.3.9",423
+ "libc",424
+ "windows-sys 0.48.0",425
+]426
+427
+[[package]]317428
name = "itoa"318429
version = "1.0.18"319430
source = "registry+https://github.com/rust-lang/crates.io-index"@@ -342,6 +453,48 @@ dependencies = [
342453
]343454
344455
[[package]]456
+name = "libseat"457
+version = "0.2.4"458
+source = "registry+https://github.com/rust-lang/crates.io-index"459
+checksum = "6656c69b6e0366ffb83faa232f3e4fda5fc91161722d140f2c95ce2e90b1ef31"460
+dependencies = [461
+ "errno",462
+ "libseat-sys",463
+ "log",464
+]465
+466
+[[package]]467
+name = "libseat-sys"468
+version = "0.2.0"469
+source = "registry+https://github.com/rust-lang/crates.io-index"470
+checksum = "e8a484fa48be5f62a8d3ffed767779d0ab808cfead91de98ef4362d469097dfb"471
+dependencies = [472
+ "pkg-config",473
+]474
+475
+[[package]]476
+name = "libudev-sys"477
+version = "0.1.4"478
+source = "registry+https://github.com/rust-lang/crates.io-index"479
+checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324"480
+dependencies = [481
+ "libc",482
+ "pkg-config",483
+]484
+485
+[[package]]486
+name = "linux-raw-sys"487
+version = "0.4.15"488
+source = "registry+https://github.com/rust-lang/crates.io-index"489
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"490
+491
+[[package]]492
+name = "linux-raw-sys"493
+version = "0.9.4"494
+source = "registry+https://github.com/rust-lang/crates.io-index"495
+checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"496
+497
+[[package]]345498
name = "linux-raw-sys"346499
version = "0.12.1"347500
source = "registry+https://github.com/rust-lang/crates.io-index"@@ -406,11 +559,13 @@ dependencies = [
406559
"serde_json",407560
"slotmap",408561
"smithay",562
+ "smithay-drm-extras",409563
"tracing",410564
"tracing-subscriber",411565
"tracy-client",412566
"wayland-client",413567
"wayland-protocols",568
+ "xcursor",414569
]415570
416571
[[package]]@@ -469,7 +624,7 @@ dependencies = [
469624
"ash",470625
"glam",471626
"gpu-allocator",472
- "rustix",627
+ "rustix 1.1.5",473628
"smithay",474629
"thiserror 2.0.21",475630
]@@ -487,7 +642,7 @@ version = "0.50.3"
487642
source = "registry+https://github.com/rust-lang/crates.io-index"488643
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"489644
dependencies = [490
- "windows-sys",645
+ "windows-sys 0.61.2",491646
]492647
493648
[[package]]@@ -525,10 +680,10 @@ checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
525680
dependencies = [526681
"cfg-if",527682
"concurrent-queue",528
- "hermit-abi",683
+ "hermit-abi 0.5.3",529684
"pin-project-lite",530
- "rustix",531
- "windows-sys",685
+ "rustix 1.1.5",686
+ "windows-sys 0.61.2",532687
]533688
534689
[[package]]@@ -723,7 +878,20 @@ dependencies = [
723878
"calloop",724879
"enumflags2",725880
"log",726
- "rustix",881
+ "rustix 1.1.5",882
+]883
+884
+[[package]]885
+name = "rustix"886
+version = "0.38.44"887
+source = "registry+https://github.com/rust-lang/crates.io-index"888
+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"889
+dependencies = [890
+ "bitflags",891
+ "errno",892
+ "libc",893
+ "linux-raw-sys 0.4.15",894
+ "windows-sys 0.59.0",727895
]728896
729897
[[package]]@@ -735,8 +903,8 @@ dependencies = [
735903
"bitflags",736904
"errno",737905
"libc",738
- "linux-raw-sys",739
- "windows-sys",906
+ "linux-raw-sys 0.12.1",907
+ "windows-sys 0.61.2",740908
]741909
742910
[[package]]@@ -859,23 +1027,32 @@ dependencies = [
8591027
"appendlist",8601028
"bitflags",8611029
"calloop",1030
+ "cc",8621031
"cursor-icon",8631032
"downcast-rs",1033
+ "drm",1034
+ "drm-ffi",8641035
"drm-fourcc",1036
+ "drm-sys",8651037
"errno",1038
+ "gbm",8661039
"glam",8671040
"indexmap",1041
+ "input",8681042
"libc",1043
+ "libseat",1044
+ "pkg-config",8691045
"portable-atomic",8701046
"profiling",8711047
"rand",8721048
"reis",873
- "rustix",1049
+ "rustix 1.1.5",8741050
"siphasher",8751051
"smallvec",8761052
"tempfile",8771053
"thiserror 2.0.21",8781054
"tracing",1055
+ "udev",8791056
"wayland-protocols",8801057
"wayland-protocols-misc",8811058
"wayland-protocols-wlr",@@ -884,6 +1061,14 @@ dependencies = [
8841061
]8851062
8861063
[[package]]1064
+name = "smithay-drm-extras"1065
+version = "0.1.0"1066
+source = "git+https://github.com/Smithay/smithay?rev=023b78b1c5a05ca512767cacc07efca55fae8be4#023b78b1c5a05ca512767cacc07efca55fae8be4"1067
+dependencies = [1068
+ "drm",1069
+]1070
+1071
+[[package]]8871072
name = "syn"8881073
version = "2.0.119"8891074
source = "registry+https://github.com/rust-lang/crates.io-index"@@ -914,8 +1099,8 @@ dependencies = [
9141099
"fastrand",9151100
"getrandom",9161101
"once_cell",917
- "rustix",918
- "windows-sys",1102
+ "rustix 1.1.5",1103
+ "windows-sys 0.61.2",9191104
]9201105
9211106
[[package]]@@ -1098,7 +1283,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
10981283
checksum = "ab27f167b093214c68413a2e0bcd318f0591af475597405ff68138ba0a433379"10991284
dependencies = [11001285
"cc",1101
- "windows-targets",1286
+ "windows-targets 0.52.6",1287
+]1288
+1289
+[[package]]1290
+name = "udev"1291
+version = "0.9.3"1292
+source = "registry+https://github.com/rust-lang/crates.io-index"1293
+checksum = "af4e37e9ea4401fc841ff54b9ddfc9be1079b1e89434c1a6a865dd68980f7e9f"1294
+dependencies = [1295
+ "io-lifetimes",1296
+ "libc",1297
+ "libudev-sys",1298
+ "pkg-config",11021299
]11031300
11041301
[[package]]@@ -1152,7 +1349,7 @@ checksum = "38a91b4eaddff87b1cd1074985e3713da4af2c49742d1b356b2c01670a67a078"
11521349
dependencies = [11531350
"cc",11541351
"downcast-rs",1155
- "rustix",1352
+ "rustix 1.1.5",11561353
"smallvec",11571354
"wayland-sys",11581355
]@@ -1164,7 +1361,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
11641361
checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073"11651362
dependencies = [11661363
"bitflags",1167
- "rustix",1364
+ "rustix 1.1.5",11681365
"wayland-backend",11691366
"wayland-scanner",11701367
]@@ -1227,7 +1424,7 @@ checksum = "0dde9c29be0f723a573977de51ee455bf3dfa03652730a74f9dd3b337e374d75"
12271424
dependencies = [12281425
"bitflags",12291426
"downcast-rs",1230
- "rustix",1427
+ "rustix 1.1.5",12311428
"wayland-backend",12321429
"wayland-scanner",12331430
]@@ -1258,6 +1455,24 @@ dependencies = [
12581455
12591456
[[package]]12601457
name = "windows-sys"1458
+version = "0.48.0"1459
+source = "registry+https://github.com/rust-lang/crates.io-index"1460
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"1461
+dependencies = [1462
+ "windows-targets 0.48.5",1463
+]1464
+1465
+[[package]]1466
+name = "windows-sys"1467
+version = "0.59.0"1468
+source = "registry+https://github.com/rust-lang/crates.io-index"1469
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"1470
+dependencies = [1471
+ "windows-targets 0.52.6",1472
+]1473
+1474
+[[package]]1475
+name = "windows-sys"12611476
version = "0.61.2"12621477
source = "registry+https://github.com/rust-lang/crates.io-index"12631478
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"@@ -1267,34 +1482,67 @@ dependencies = [
12671482
12681483
[[package]]12691484
name = "windows-targets"1485
+version = "0.48.5"1486
+source = "registry+https://github.com/rust-lang/crates.io-index"1487
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"1488
+dependencies = [1489
+ "windows_aarch64_gnullvm 0.48.5",1490
+ "windows_aarch64_msvc 0.48.5",1491
+ "windows_i686_gnu 0.48.5",1492
+ "windows_i686_msvc 0.48.5",1493
+ "windows_x86_64_gnu 0.48.5",1494
+ "windows_x86_64_gnullvm 0.48.5",1495
+ "windows_x86_64_msvc 0.48.5",1496
+]1497
+1498
+[[package]]1499
+name = "windows-targets"12701500
version = "0.52.6"12711501
source = "registry+https://github.com/rust-lang/crates.io-index"12721502
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"12731503
dependencies = [1274
- "windows_aarch64_gnullvm",1275
- "windows_aarch64_msvc",1276
- "windows_i686_gnu",1504
+ "windows_aarch64_gnullvm 0.52.6",1505
+ "windows_aarch64_msvc 0.52.6",1506
+ "windows_i686_gnu 0.52.6",12771507
"windows_i686_gnullvm",1278
- "windows_i686_msvc",1279
- "windows_x86_64_gnu",1280
- "windows_x86_64_gnullvm",1281
- "windows_x86_64_msvc",1508
+ "windows_i686_msvc 0.52.6",1509
+ "windows_x86_64_gnu 0.52.6",1510
+ "windows_x86_64_gnullvm 0.52.6",1511
+ "windows_x86_64_msvc 0.52.6",12821512
]12831513
12841514
[[package]]12851515
name = "windows_aarch64_gnullvm"1516
+version = "0.48.5"1517
+source = "registry+https://github.com/rust-lang/crates.io-index"1518
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"1519
+1520
+[[package]]1521
+name = "windows_aarch64_gnullvm"12861522
version = "0.52.6"12871523
source = "registry+https://github.com/rust-lang/crates.io-index"12881524
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"12891525
12901526
[[package]]12911527
name = "windows_aarch64_msvc"1528
+version = "0.48.5"1529
+source = "registry+https://github.com/rust-lang/crates.io-index"1530
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"1531
+1532
+[[package]]1533
+name = "windows_aarch64_msvc"12921534
version = "0.52.6"12931535
source = "registry+https://github.com/rust-lang/crates.io-index"12941536
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"12951537
12961538
[[package]]12971539
name = "windows_i686_gnu"1540
+version = "0.48.5"1541
+source = "registry+https://github.com/rust-lang/crates.io-index"1542
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"1543
+1544
+[[package]]1545
+name = "windows_i686_gnu"12981546
version = "0.52.6"12991547
source = "registry+https://github.com/rust-lang/crates.io-index"13001548
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"@@ -1307,24 +1555,48 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
13071555
13081556
[[package]]13091557
name = "windows_i686_msvc"1558
+version = "0.48.5"1559
+source = "registry+https://github.com/rust-lang/crates.io-index"1560
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"1561
+1562
+[[package]]1563
+name = "windows_i686_msvc"13101564
version = "0.52.6"13111565
source = "registry+https://github.com/rust-lang/crates.io-index"13121566
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"13131567
13141568
[[package]]13151569
name = "windows_x86_64_gnu"1570
+version = "0.48.5"1571
+source = "registry+https://github.com/rust-lang/crates.io-index"1572
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"1573
+1574
+[[package]]1575
+name = "windows_x86_64_gnu"13161576
version = "0.52.6"13171577
source = "registry+https://github.com/rust-lang/crates.io-index"13181578
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"13191579
13201580
[[package]]13211581
name = "windows_x86_64_gnullvm"1582
+version = "0.48.5"1583
+source = "registry+https://github.com/rust-lang/crates.io-index"1584
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"1585
+1586
+[[package]]1587
+name = "windows_x86_64_gnullvm"13221588
version = "0.52.6"13231589
source = "registry+https://github.com/rust-lang/crates.io-index"13241590
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"13251591
13261592
[[package]]13271593
name = "windows_x86_64_msvc"1594
+version = "0.48.5"1595
+source = "registry+https://github.com/rust-lang/crates.io-index"1596
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"1597
+1598
+[[package]]1599
+name = "windows_x86_64_msvc"13281600
version = "0.52.6"13291601
source = "registry+https://github.com/rust-lang/crates.io-index"13301602
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"@@ -1339,6 +1611,12 @@ dependencies = [
13391611
]13401612
13411613
[[package]]1614
+name = "xcursor"1615
+version = "0.3.11"1616
+source = "registry+https://github.com/rust-lang/crates.io-index"1617
+checksum = "163b33ed8786455e2fa5d72f554057ce3f3182425434f756cd39c99839d88e23"1618
+1619
+[[package]]13421620
name = "xkbcommon"13431621
version = "0.9.0"13441622
source = "registry+https://github.com/rust-lang/crates.io-index"@@ -23,7 +23,16 @@ monolith-render-vk.workspace = true
2323
pound = "0.5"2424
serde_json.workspace = true2525
slotmap.workspace = true26
-smithay = { workspace = true, features = ["desktop"] }26
+smithay = { workspace = true, features = [27
+ "desktop",28
+ "backend_drm",29
+ "backend_gbm",30
+ "backend_libinput",31
+ "backend_udev",32
+ "backend_session_libseat",33
+] }34
+smithay-drm-extras = { git = "https://github.com/Smithay/smithay", rev = "023b78b1c5a05ca512767cacc07efca55fae8be4", default-features = false }35
+xcursor = "0.3"2736
tracing = "0.1"2837
tracing-subscriber = { version = "0.3", features = ["env-filter"] }2938
tracy-client = { workspace = true, optional = true }@@ -0,0 +1,110 @@
1
+//! The pointer image on bare metal, where nothing else draws one. A client2
+//! cursor surface wins, otherwise the xcursor theme's default arrow.3
+4
+use crate::scene::Draw;5
+use monolith_render_vk::VulkanRenderer;6
+use smithay::backend::allocator::Fourcc;7
+use smithay::backend::renderer::element::memory::{8
+ MemoryRenderBuffer, MemoryRenderBufferRenderElement,9
+};10
+use smithay::backend::renderer::element::solid::SolidColorRenderElement;11
+use smithay::backend::renderer::element::surface::render_elements_from_surface_tree;12
+use smithay::backend::renderer::element::{Id, Kind};13
+use smithay::backend::renderer::utils::CommitCounter;14
+use smithay::input::pointer::{CursorImageAttributes, CursorImageStatus};15
+use smithay::utils::{Logical, Physical, Point, Rectangle, Transform};16
+use smithay::wayland::compositor::with_states;17
+use std::sync::Mutex;18
+19
+pub struct Cursor {20
+ image: Option<(MemoryRenderBuffer, Point<i32, Logical>)>,21
+}22
+23
+impl Cursor {24
+ pub fn load() -> Self {25
+ let theme = std::env::var("XCURSOR_THEME").unwrap_or_else(|_| "default".into());26
+ let size = std::env::var("XCURSOR_SIZE")27
+ .ok()28
+ .and_then(|s| s.parse().ok())29
+ .unwrap_or(24u32);30
+ let image = load_image(&theme, size);31
+ if image.is_none() {32
+ tracing::warn!(theme, "no xcursor theme found, drawing a plain cursor");33
+ }34
+ Self { image }35
+ }36
+37
+ /// Elements for the pointer at `pos`, in output-local coordinates.38
+ pub fn elements(39
+ &self,40
+ renderer: &mut VulkanRenderer,41
+ status: &CursorImageStatus,42
+ pos: Point<f64, Logical>,43
+ ) -> Vec<Draw> {44
+ let at = |hotspot: Point<i32, Logical>| -> Point<i32, Physical> {45
+ let p = pos - hotspot.to_f64();46
+ (p.x.round() as i32, p.y.round() as i32).into()47
+ };48
+ match status {49
+ CursorImageStatus::Hidden => Vec::new(),50
+ CursorImageStatus::Surface(surface) => {51
+ let hotspot = with_states(surface, |s| {52
+ s.data_map53
+ .get::<Mutex<CursorImageAttributes>>()54
+ .and_then(|a| a.lock().ok().map(|a| a.hotspot))55
+ .unwrap_or_default()56
+ });57
+ render_elements_from_surface_tree(58
+ renderer,59
+ surface,60
+ at(hotspot),61
+ 1.0,62
+ 1.0,63
+ Kind::Cursor,64
+ )65
+ .into_iter()66
+ .map(Draw::Surface)67
+ .collect()68
+ }69
+ CursorImageStatus::Named(_) => match &self.image {70
+ Some((buffer, hotspot)) => MemoryRenderBufferRenderElement::from_buffer(71
+ renderer,72
+ at(*hotspot).to_f64(),73
+ buffer,74
+ None,75
+ None,76
+ None,77
+ Kind::Cursor,78
+ )79
+ .map(|e| vec![Draw::Memory(e)])80
+ .unwrap_or_default(),81
+ None => vec![Draw::Border(SolidColorRenderElement::new(82
+ Id::new(),83
+ Rectangle::new(at(Point::default()), (8, 8).into()),84
+ CommitCounter::default(),85
+ [1.0, 1.0, 1.0, 1.0],86
+ Kind::Cursor,87
+ ))],88
+ },89
+ }90
+ }91
+}92
+93
+fn load_image(theme: &str, size: u32) -> Option<(MemoryRenderBuffer, Point<i32, Logical>)> {94
+ let theme = xcursor::CursorTheme::load(theme);95
+ let path = theme96
+ .load_icon("default")97
+ .or_else(|| theme.load_icon("left_ptr"))?;98
+ let bytes = std::fs::read(path).ok()?;99
+ let images = xcursor::parser::parse_xcursor(&bytes)?;100
+ let image = images.iter().min_by_key(|i| i.size.abs_diff(size))?;101
+ let buffer = MemoryRenderBuffer::from_slice(102
+ &image.pixels_rgba,103
+ Fourcc::Argb8888,104
+ (image.width as i32, image.height as i32),105
+ 1,106
+ Transform::Normal,107
+ None,108
+ );109
+ Some((buffer, (image.xhot as i32, image.yhot as i32).into()))110
+}@@ -0,0 +1,511 @@
1
+//! Bare metal. A libseat session, udev for the primary GPU and its2
+//! connectors, libinput for input, and one smithay DRM output per connector,3
+//! each rendered when its own vblank lands.4
+5
+use crate::scene::Draw;6
+use crate::state::{State, now_ns};7
+use monolith_layout::{OutputId, Rect};8
+use smithay::backend::allocator::Fourcc;9
+use smithay::backend::allocator::gbm::{GbmAllocator, GbmBufferFlags, GbmDevice};10
+use smithay::backend::drm::compositor::FrameFlags;11
+use smithay::backend::drm::exporter::gbm::GbmFramebufferExporter;12
+use smithay::backend::drm::output::{DrmOutput, DrmOutputManager, DrmOutputRenderElements};13
+use smithay::backend::drm::{14
+ DrmDevice, DrmDeviceFd, DrmEvent, DrmEventMetadata, DrmEventTime, DrmNode, NodeType,15
+};16
+use smithay::backend::input::{17
+ AbsolutePositionEvent, Axis, AxisSource, Event, InputEvent, KeyboardKeyEvent, PointerAxisEvent,18
+ PointerButtonEvent, PointerMotionEvent,19
+};20
+use smithay::backend::libinput::{LibinputInputBackend, LibinputSessionInterface};21
+use smithay::backend::session::libseat::LibSeatSession;22
+use smithay::backend::session::{Event as SessionEvent, Session};23
+use smithay::backend::udev::{UdevBackend, UdevEvent, all_gpus, primary_gpu};24
+use smithay::input::pointer::AxisFrame;25
+use smithay::output::{Mode, Output, PhysicalProperties, Scale, Subpixel};26
+use smithay::reexports::calloop::timer::{TimeoutAction, Timer};27
+use smithay::reexports::calloop::{EventLoop, LoopHandle};28
+use smithay::reexports::drm::Device as _;29
+use smithay::reexports::drm::control::{ModeTypeFlags, connector, crtc};30
+use smithay::reexports::input::Libinput;31
+use smithay::reexports::rustix::fs::OFlags;32
+use smithay::utils::{DeviceFd, Point, Transform};33
+use smithay_drm_extras::drm_scanner::{DrmScanEvent, DrmScanner};34
+use std::collections::HashMap;35
+use std::time::Duration;36
+37
+const CLEAR: [f32; 4] = [0.07, 0.075, 0.09, 1.0];38
+39
+type Allocator = GbmAllocator<DrmDeviceFd>;40
+type Exporter = GbmFramebufferExporter<DrmDeviceFd>;41
+type Manager = DrmOutputManager<Allocator, Exporter, (), DrmDeviceFd>;42
+type Out = DrmOutput<Allocator, Exporter, (), DrmDeviceFd>;43
+44
+pub struct Drm {45
+ pub session: LibSeatSession,46
+ libinput: Libinput,47
+ handle: LoopHandle<'static, State>,48
+ node: DrmNode,49
+ manager: Manager,50
+ scanner: DrmScanner,51
+ nvidia: bool,52
+ surfaces: HashMap<crtc::Handle, Surface>,53
+}54
+55
+struct Surface {56
+ output: Output,57
+ id: OutputId,58
+ drm: Out,59
+ /// A frame is on its way to the screen and its vblank has not landed.60
+ queued: bool,61
+ redraw: bool,62
+ last_vblank_ns: Option<u64>,63
+ refresh_ns: u64,64
+}65
+66
+impl Surface {67
+ /// The vblank after `now` that a frame rendered now will reach.68
+ fn predict(&self, now: u64) -> u64 {69
+ match self.last_vblank_ns {70
+ Some(last) if last <= now && self.refresh_ns > 0 => {71
+ last + ((now - last) / self.refresh_ns + 1) * self.refresh_ns72
+ }73
+ _ => now,74
+ }75
+ }76
+}77
+78
+pub fn init(79
+ event_loop: &mut EventLoop<'static, State>,80
+ state: &mut State,81
+) -> Result<(), Box<dyn std::error::Error>> {82
+ let (mut session, notifier) = LibSeatSession::new()?;83
+ let seat = session.seat();84
+ let path = match primary_gpu(&seat)? {85
+ Some(p) => p,86
+ None => all_gpus(&seat)?.into_iter().next().ok_or("no GPU found")?,87
+ };88
+ let node = DrmNode::from_path(&path)?;89
+ tracing::info!(path = %path.display(), "primary GPU");90
+ let fd = session.open(91
+ &path,92
+ OFlags::RDWR | OFlags::CLOEXEC | OFlags::NOCTTY | OFlags::NONBLOCK,93
+ )?;94
+ let fd = DrmDeviceFd::new(DeviceFd::from(fd));95
+ let (device, drm_events) = DrmDevice::new(fd.clone(), true)?;96
+ let nvidia = device97
+ .get_driver()98
+ .map(|d| d.name().to_string_lossy().to_lowercase().contains("nvidia"))99
+ .unwrap_or(false);100
+ let gbm = GbmDevice::new(fd)?;101
+ let render_node = node102
+ .node_with_type(NodeType::Render)103
+ .and_then(Result::ok)104
+ .unwrap_or(node);105
+ let allocator = GbmAllocator::new(106
+ gbm.clone(),107
+ GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT,108
+ );109
+ let exporter = GbmFramebufferExporter::new(gbm.clone(), render_node.into());110
+ let render_formats: Vec<_> = state.renderer.render_formats().iter().copied().collect();111
+ let manager = DrmOutputManager::new(112
+ device,113
+ allocator,114
+ exporter,115
+ Some(gbm),116
+ [Fourcc::Xrgb8888, Fourcc::Argb8888],117
+ render_formats,118
+ );119
+120
+ let mut libinput =121
+ Libinput::new_with_udev::<LibinputSessionInterface<LibSeatSession>>(session.clone().into());122
+ libinput123
+ .udev_assign_seat(&seat)124
+ .map_err(|()| "libinput could not take the seat")?;125
+ let handle = event_loop.handle();126
+ handle.insert_source(127
+ LibinputInputBackend::new(libinput.clone()),128
+ |event, _, state| {129
+ on_input(state, event);130
+ },131
+ )?;132
+ handle.insert_source(notifier, |event, _, state| match event {133
+ SessionEvent::PauseSession => {134
+ if let Some(drm) = state.drm.as_mut() {135
+ drm.libinput.suspend();136
+ drm.manager.pause();137
+ }138
+ }139
+ SessionEvent::ActivateSession => {140
+ if let Some(drm) = state.drm.as_mut() {141
+ if drm.libinput.resume().is_err() {142
+ tracing::warn!("libinput did not resume");143
+ }144
+ if let Err(e) = drm.manager.lock().activate(false) {145
+ tracing::warn!("could not reactivate the GPU: {e}");146
+ }147
+ for s in drm.surfaces.values_mut() {148
+ s.queued = false;149
+ s.redraw = true;150
+ }151
+ }152
+ state.needs_redraw = true;153
+ }154
+ })?;155
+ handle.insert_source(drm_events, |event, meta, state| match event {156
+ DrmEvent::VBlank(crtc) => on_vblank(state, crtc, meta),157
+ DrmEvent::Error(e) => tracing::warn!("drm: {e}"),158
+ })?;159
+ let udev = UdevBackend::new(&seat)?;160
+ handle.insert_source(udev, move |event, _, state| {161
+ if let UdevEvent::Changed { device_id } = event162
+ && state163
+ .drm164
+ .as_ref()165
+ .is_some_and(|d| d.node.dev_id() == device_id)166
+ {167
+ scan(state);168
+ }169
+ })?;170
+171
+ state.drm = Some(Drm {172
+ session,173
+ libinput,174
+ handle,175
+ node,176
+ manager,177
+ scanner: DrmScanner::new(),178
+ nvidia,179
+ surfaces: HashMap::new(),180
+ });181
+ scan(state);182
+ Ok(())183
+}184
+185
+/// Brings outputs in line with what is plugged in.186
+fn scan(state: &mut State) {187
+ let Some(drm) = state.drm.as_mut() else {188
+ return;189
+ };190
+ let events: Vec<DrmScanEvent> = match drm.scanner.scan_connectors(drm.manager.device()) {191
+ Ok(result) => result.into_iter().collect(),192
+ Err(e) => {193
+ tracing::warn!("scanning connectors failed: {e}");194
+ return;195
+ }196
+ };197
+ for event in events {198
+ match event {199
+ DrmScanEvent::Connected {200
+ connector,201
+ crtc: Some(crtc),202
+ } => connect(state, connector, crtc),203
+ DrmScanEvent::Disconnected {204
+ crtc: Some(crtc), ..205
+ } => disconnect(state, crtc),206
+ _ => {}207
+ }208
+ }209
+ state.apply_layout();210
+}211
+212
+fn connect(state: &mut State, connector: connector::Info, crtc: crtc::Handle) {213
+ let name = format!(214
+ "{}-{}",215
+ connector.interface().as_str(),216
+ connector.interface_id()217
+ );218
+ let Some(&mode) = connector219
+ .modes()220
+ .iter()221
+ .find(|m| m.mode_type().contains(ModeTypeFlags::PREFERRED))222
+ .or_else(|| connector.modes().first())223
+ else {224
+ tracing::warn!(name, "connector has no modes");225
+ return;226
+ };227
+ let State {228
+ drm: Some(drm),229
+ renderer,230
+ layout,231
+ space,232
+ display,233
+ ..234
+ } = state235
+ else {236
+ return;237
+ };238
+ let (mm_w, mm_h) = connector.size().unwrap_or((0, 0));239
+ let output = Output::new(240
+ name.clone(),241
+ PhysicalProperties {242
+ size: (mm_w as i32, mm_h as i32).into(),243
+ subpixel: Subpixel::Unknown,244
+ make: "Unknown".into(),245
+ model: name.clone(),246
+ serial_number: String::new(),247
+ },248
+ );249
+ let wl_mode = Mode::from(mode);250
+ let x = layout251
+ .outputs()252
+ .map(|(_, o)| o.area.right())253
+ .fold(0.0, f64::max);254
+ let position = Point::from((x as i32, 0));255
+ output.change_current_state(256
+ Some(wl_mode),257
+ Some(Transform::Normal),258
+ Some(Scale::Integer(1)),259
+ Some(position),260
+ );261
+ output.set_preferred(wl_mode);262
+ output.create_global::<State>(display);263
+ let mut planes = drm.manager.device().planes(&crtc).ok();264
+ if drm.nvidia265
+ && let Some(p) = planes.as_mut()266
+ {267
+ // NVIDIA's overlay planes break composition, as anvil also found.268
+ p.overlay.clear();269
+ }270
+ let out = match drm.manager.lock().initialize_output::<_, Draw>(271
+ crtc,272
+ mode,273
+ &[connector.handle()],274
+ &output,275
+ planes,276
+ renderer,277
+ &DrmOutputRenderElements::default(),278
+ ) {279
+ Ok(out) => out,280
+ Err(e) => {281
+ tracing::warn!(name, "could not light up the output: {e}");282
+ return;283
+ }284
+ };285
+ space.map_output(&output, position);286
+ let (w, h) = mode.size();287
+ let area = Rect::new(x, 0.0, f64::from(w), f64::from(h));288
+ let id = layout.add_output(&name, area);289
+ tracing::info!(name, w, h, refresh = mode.vrefresh(), "output connected");290
+ drm.surfaces.insert(291
+ crtc,292
+ Surface {293
+ output,294
+ id,295
+ drm: out,296
+ queued: false,297
+ redraw: true,298
+ last_vblank_ns: None,299
+ refresh_ns: 1_000_000_000_000 / u64::from(wl_mode.refresh.max(1) as u32),300
+ },301
+ );302
+}303
+304
+fn disconnect(state: &mut State, crtc: crtc::Handle) {305
+ let Some(surface) = state.drm.as_mut().and_then(|d| d.surfaces.remove(&crtc)) else {306
+ return;307
+ };308
+ tracing::info!(name = surface.output.name(), "output disconnected");309
+ state.space.unmap_output(&surface.output);310
+ state.layout.remove_output(surface.id);311
+}312
+313
+/// The smithay output for a layout output, for protocols that need one.314
+pub fn output_for(drm: &Drm, id: OutputId) -> Option<Output> {315
+ drm.surfaces316
+ .values()317
+ .find(|s| s.id == id)318
+ .map(|s| s.output.clone())319
+}320
+321
+fn on_vblank(state: &mut State, crtc: crtc::Handle, meta: &mut Option<DrmEventMetadata>) {322
+ let Some(surface) = state.drm.as_mut().and_then(|d| d.surfaces.get_mut(&crtc)) else {323
+ return;324
+ };325
+ if let Err(e) = surface.drm.frame_submitted() {326
+ tracing::warn!("frame was not submitted: {e}");327
+ }328
+ surface.queued = false;329
+ if let Some(DrmEventTime::Monotonic(t)) = meta.as_ref().map(|m| m.time) {330
+ surface.last_vblank_ns = Some(t.as_nanos() as u64);331
+ }332
+ render_surface(state, crtc);333
+}334
+335
+/// Renders every output that has something new and no frame in flight.336
+pub fn render(state: &mut State) {337
+ let Some(drm) = state.drm.as_mut() else {338
+ return;339
+ };340
+ if std::mem::take(&mut state.needs_redraw) {341
+ for s in drm.surfaces.values_mut() {342
+ s.redraw = true;343
+ }344
+ }345
+ let ready: Vec<crtc::Handle> = drm346
+ .surfaces347
+ .iter()348
+ .filter(|(_, s)| s.redraw && !s.queued)349
+ .map(|(c, _)| *c)350
+ .collect();351
+ for crtc in ready {352
+ render_surface(state, crtc);353
+ }354
+}355
+356
+fn render_surface(state: &mut State, crtc: crtc::Handle) {357
+ let State {358
+ drm: Some(drm),359
+ renderer,360
+ layout,361
+ windows,362
+ anim,363
+ borders,364
+ config,365
+ space,366
+ start,367
+ cursor,368
+ cursor_status,369
+ pointer_pos,370
+ ..371
+ } = state372
+ else {373
+ return;374
+ };375
+ if !drm.session.is_active() {376
+ return;377
+ }378
+ let Some(surface) = drm.surfaces.get_mut(&crtc) else {379
+ return;380
+ };381
+ if surface.queued {382
+ return;383
+ }384
+ let now = surface.predict(now_ns());385
+ let Some(area) = layout.output(surface.id).map(|o| o.area) else {386
+ return;387
+ };388
+ let mut elements = Vec::new();389
+ let local = *pointer_pos - Point::from((area.x, area.y));390
+ if local.x >= 0.0 && local.y >= 0.0 && local.x < area.w && local.y < area.h {391
+ elements.extend(cursor.elements(renderer, cursor_status, local));392
+ }393
+ elements.extend(crate::scene::build(394
+ layout,395
+ surface.id,396
+ windows,397
+ anim,398
+ renderer,399
+ now,400
+ &config.decoration,401
+ borders,402
+ ));403
+ let animating = anim.active(now);404
+ anim.prune(now);405
+ match surface406
+ .drm407
+ .render_frame(renderer, &elements, CLEAR, FrameFlags::DEFAULT)408
+ {409
+ Ok(result) if !result.is_empty => match surface.drm.queue_frame(()) {410
+ Ok(()) => surface.queued = true,411
+ Err(e) => tracing::warn!("queueing a frame failed: {e}"),412
+ },413
+ Ok(_) => {}414
+ Err(e) => tracing::warn!("rendering failed: {e}"),415
+ }416
+ surface.redraw = animating;417
+ if animating && !surface.queued {418
+ let wait = Duration::from_nanos(surface.refresh_ns);419
+ let _ = drm420
+ .handle421
+ .insert_source(Timer::from_duration(wait), move |_, _, state| {422
+ if let Some(s) = state.drm.as_mut().and_then(|d| d.surfaces.get_mut(&crtc)) {423
+ s.redraw = true;424
+ }425
+ render(state);426
+ TimeoutAction::Drop427
+ });428
+ }429
+ let elapsed = start.elapsed();430
+ let output = surface.output.clone();431
+ for window in space.elements_for_output(&output) {432
+ window.send_frame(&output, elapsed, Some(Duration::ZERO), |_, _| {433
+ Some(output.clone())434
+ });435
+ }436
+}437
+438
+fn on_input(state: &mut State, event: InputEvent<LibinputInputBackend>) {439
+ match event {440
+ InputEvent::Keyboard { event } => {441
+ let time = event.time();442
+ state.key(event.key_code(), event.state(), time);443
+ }444
+ InputEvent::PointerMotion { event } => {445
+ let pos = clamp(state, state.pointer_pos + event.delta());446
+ state.pointer_to(pos, event.time());447
+ state.pointer_frame();448
+ state.needs_redraw = true;449
+ }450
+ InputEvent::PointerMotionAbsolute { event } => {451
+ let Some(area) = state.layout.outputs().next().map(|(_, o)| o.area) else {452
+ return;453
+ };454
+ let p = event.position_transformed((area.w as i32, area.h as i32).into());455
+ let pos = Point::from((area.x + p.x, area.y + p.y));456
+ state.pointer_to(pos, event.time());457
+ state.pointer_frame();458
+ state.needs_redraw = true;459
+ }460
+ InputEvent::PointerButton { event } => {461
+ let time = event.time();462
+ state.pointer_button(event.button_code(), event.state(), time);463
+ state.pointer_frame();464
+ }465
+ InputEvent::PointerAxis { event } => {466
+ let source = event.source();467
+ let mut frame = AxisFrame::new(event.time()).source(source);468
+ for axis in [Axis::Horizontal, Axis::Vertical] {469
+ if let Some(v120) = event.amount_v120(axis) {470
+ frame = frame.v120(axis, v120 as i32);471
+ }472
+ let amount = event473
+ .amount(axis)474
+ .or_else(|| event.amount_v120(axis).map(|v| v * 15.0 / 120.0));475
+ match amount {476
+ Some(a) if a != 0.0 => frame = frame.value(axis, a),477
+ Some(_) if source == AxisSource::Finger => frame = frame.stop(axis),478
+ _ => {}479
+ }480
+ }481
+ state.pointer_axis(frame);482
+ state.pointer_frame();483
+ }484
+ _ => {}485
+ }486
+}487
+488
+/// Keeps the pointer on some output, sliding along the edge it hit.489
+fn clamp(490
+ state: &State,491
+ pos: Point<f64, smithay::utils::Logical>,492
+) -> Point<f64, smithay::utils::Logical> {493
+ let inside = |a: &Rect, p: Point<f64, smithay::utils::Logical>| {494
+ p.x >= a.x && p.y >= a.y && p.x < a.right() && p.y < a.bottom()495
+ };496
+ let areas: Vec<Rect> = state.layout.outputs().map(|(_, o)| o.area).collect();497
+ if areas.iter().any(|a| inside(a, pos)) {498
+ return pos;499
+ }500
+ let Some(a) = areas501
+ .iter()502
+ .find(|a| inside(a, state.pointer_pos))503
+ .or(areas.first())504
+ else {505
+ return pos;506
+ };507
+ Point::from((508
+ pos.x.clamp(a.x, a.right() - 1.0),509
+ pos.y.clamp(a.y, a.bottom() - 1.0),510
+ ))511
+}@@ -160,7 +160,10 @@ impl SeatHandler for State {
160160
&mut self.seat_state161161
}162162
163
- fn cursor_image(&mut self, _seat: &Seat<Self>, _image: CursorImageStatus) {}163
+ fn cursor_image(&mut self, _seat: &Seat<Self>, image: CursorImageStatus) {164
+ self.cursor_status = image;165
+ self.needs_redraw = true;166
+ }164167
165168
fn focus_changed(&mut self, seat: &Seat<Self>, focused: Option<&WlSurface>) {166169
let client = focused.and_then(|s| self.display.get_client(s.id()).ok());@@ -5,7 +5,8 @@ use crate::state::State;
55
use monolith_config::Modifier;66
use monolith_layout::{Rect, WindowId};77
use smithay::backend::input::{ButtonState, InputTime, KeyState};8
-use smithay::input::keyboard::{FilterResult, Keycode};8
+use smithay::backend::session::Session;9
+use smithay::input::keyboard::{FilterResult, Keycode, xkb};910
use smithay::input::pointer::{AxisFrame, ButtonEvent, MotionEvent};1011
use smithay::utils::{Logical, Point, SERIAL_COUNTER};1112
@@ -86,24 +87,47 @@ impl State {
8687
FilterResult::Forward8788
};8889
}90
+ let sym = handle.modified_sym().raw();91
+ if (xkb::keysyms::KEY_XF86Switch_VT_1..=xkb::keysyms::KEY_XF86Switch_VT_12)92
+ .contains(&sym)93
+ {94
+ state.suppressed.push(code);95
+ let vt = (sym - xkb::keysyms::KEY_XF86Switch_VT_1 + 1) as i32;96
+ return FilterResult::Intercept(Some(Action::Vt(vt)));97
+ }8998
match state.binds.find(mods, &handle.raw_syms()) {9099
Some(cmd) => {91100
tracing::debug!(?cmd, "bind");92101
state.suppressed.push(code);93
- FilterResult::Intercept(Some(cmd))102
+ FilterResult::Intercept(Some(Action::Run(cmd)))94103
}95104
None => FilterResult::Forward,96105
}97106
},98107
);99
- if let Some(Some(cmd)) = action100
- && let Err(e) = self.run(cmd)101
- {102
- tracing::warn!("bind failed: {e}");108
+ match action {109
+ Some(Some(Action::Run(cmd))) => {110
+ if let Err(e) = self.run(cmd) {111
+ tracing::warn!("bind failed: {e}");112
+ }113
+ }114
+ Some(Some(Action::Vt(vt))) => {115
+ if let Some(drm) = self.drm.as_mut()116
+ && let Err(e) = drm.session.change_vt(vt)117
+ {118
+ tracing::warn!("switching to VT {vt} failed: {e}");119
+ }120
+ }121
+ _ => {}103122
}104123
}105124
}106125
126
+enum Action {127
+ Run(monolith_ipc::Command),128
+ Vt(i32),129
+}130
+107131
fn focus_under_pointer(state: &mut State) {108132
let pos = state.pointer_pos;109133
let id = state@@ -1,5 +1,7 @@
11
mod anim;22
mod commands;3
+mod cursor;4
+mod drm;35
mod handlers;46
mod input;57
mod ipc;@@ -32,6 +34,9 @@ enum Cmd {
3234
/// read this config instead of ~/.config/monolith/config.toml3335
#[pound(long)]3436
config: Option<String>,37
+ /// where to run, auto picks nested inside another compositor38
+ #[pound(long)]39
+ backend: Option<Backend>,3540
/// a program to start inside, with its arguments3641
#[pound(trailing)]3742
program: Vec<String>,@@ -47,6 +52,13 @@ enum Cmd {
4752
Subscribe,4853
}4954
55
+#[derive(ValueEnum, Clone, Copy, PartialEq, Eq)]56
+enum Backend {57
+ Auto,58
+ Nested,59
+ Drm,60
+}61
+5062
#[derive(ValueEnum)]5163
enum What {5264
State,@@ -54,9 +66,17 @@ enum What {
5466
}5567
5668
fn main() -> ExitCode {57
- let (config, program) = match Cli::parse().cmd {58
- None => (None, Vec::new()),59
- Some(Cmd::Run { config, program }) => (config.map(PathBuf::from), program),69
+ let (config, backend, program) = match Cli::parse().cmd {70
+ None => (None, Backend::Auto, Vec::new()),71
+ Some(Cmd::Run {72
+ config,73
+ backend,74
+ program,75
+ }) => (76
+ config.map(PathBuf::from),77
+ backend.unwrap_or(Backend::Auto),78
+ program,79
+ ),6080
Some(Cmd::Msg { command }) => {6181
return match Command::parse(&command) {6282
Ok(cmd) => ipc::client(Request::Run(cmd)),@@ -76,7 +96,12 @@ fn main() -> ExitCode {
7696
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),7797
)7898
.init();79
- match run(config, &program) {99
+ let backend = match backend {100
+ Backend::Auto if std::env::var_os("WAYLAND_DISPLAY").is_some() => Backend::Nested,101
+ Backend::Auto => Backend::Drm,102
+ b => b,103
+ };104
+ match run(config, backend, &program) {80105
Ok(()) => ExitCode::SUCCESS,81106
Err(e) => {82107
tracing::error!("{e}");@@ -85,13 +110,20 @@ fn main() -> ExitCode {
85110
}86111
}87112
88
-fn run(config: Option<PathBuf>, program: &[String]) -> Result<(), Box<dyn std::error::Error>> {113
+fn run(114
+ config: Option<PathBuf>,115
+ backend: Backend,116
+ program: &[String],117
+) -> Result<(), Box<dyn std::error::Error>> {89118
let mut event_loop: EventLoop<'static, State> = EventLoop::try_new()?;90119
let display: Display<State> = Display::new()?;91120
let mut state = State::new(&mut event_loop, display, config)?;92
- nested::init(&mut event_loop, &mut state)?;121
+ match backend {122
+ Backend::Drm => drm::init(&mut event_loop, &mut state)?,123
+ _ => nested::init(&mut event_loop, &mut state)?,124
+ }93125
ipc::init(&event_loop, &mut state)?;94
- tracing::info!(socket = ?state.socket_name, device = state.renderer.device_name(), "monolith running nested");126
+ tracing::info!(socket = ?state.socket_name, device = state.renderer.device_name(), "monolith running");95127
96128
let mut startup = state.config.startup.clone();97129
if !program.is_empty() {@@ -178,6 +178,7 @@ pub fn render(state: &mut State) {
178178
anim,179179
borders,180180
config,181
+ output_id,181182
..182183
} = state;183184
let Some(host) = host.as_mut() else { return };@@ -221,8 +222,10 @@ pub fn render(state: &mut State) {
221222
return;222223
}223224
};225
+ let Some(o) = *output_id else { return };224226
let elements = crate::scene::build(225227
layout,228
+ o,226229
windows,227230
anim,228231
renderer,@@ -3,8 +3,9 @@
33
44
use crate::anim::Animator;55
use monolith_config::Decoration;6
-use monolith_layout::{Layout, Rect, WindowId};6
+use monolith_layout::{Layout, OutputId, Rect, WindowId};77
use monolith_render_vk::{Effect, VulkanFrame, VulkanRenderer, VulkanTexture};8
+use smithay::backend::renderer::element::memory::MemoryRenderBufferRenderElement;89
use smithay::backend::renderer::element::solid::{SolidColorBuffer, SolidColorRenderElement};910
use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;1011
use smithay::backend::renderer::element::texture::TextureRenderElement;@@ -25,6 +26,7 @@ smithay::backend::renderer::element::render_elements! {
2526
Texture=TextureRenderElement<VulkanTexture>,2627
Effect=EffectElement,2728
Border=SolidColorRenderElement,29
+ Memory=MemoryRenderBufferRenderElement<VulkanRenderer>,2830
}2931
3032
/// Border strips per window, kept between frames so unchanged borders cause@@ -32,9 +34,11 @@ smithay::backend::renderer::element::render_elements! {
3234
#[derive(Default)]3335
pub struct Borders(HashMap<WindowId, [SolidColorBuffer; 4]>);3436
35
-/// Elements for every output, front to back.37
+/// Elements for output `o` in its own coordinates, front to back.38
+#[expect(clippy::too_many_arguments)]3639
pub fn build(3740
layout: &Layout,41
+ o: OutputId,3842
windows: &HashMap<WindowId, Window>,3943
anim: &Animator,4044
renderer: &mut VulkanRenderer,@@ -42,38 +46,40 @@ pub fn build(
4246
deco: &Decoration,4347
borders: &mut Borders,4448
) -> Vec<Draw> {49
+ let Some(area) = layout.output(o).map(|out| out.area) else {50
+ return Vec::new();51
+ };52
+ let local = |r: Rect| Rect::new(r.x - area.x, r.y - area.y, r.w, r.h);4553
let mut ghosts = Vec::new();4654
let mut tiled = Vec::new();4755
let mut above = Vec::new();48
- for (o, out) in layout.outputs() {49
- for g in anim.ghosts().iter().filter(|g| g.output == o) {50
- if let Some(r) = anim.tiled(o, g.id, now) {51
- ghosts.push((g, r));52
- }53
- }54
- let arr = layout.arrange(o);55
- let fullscreen_at_rest = arr56
- .fullscreen57
- .is_some_and(|w| anim.gliding(w, now).is_none());58
- for p in &arr.tiled {59
- if Some(p.window) == arr.fullscreen || fullscreen_at_rest {60
- continue;61
- }62
- match anim.gliding(p.window, now) {63
- Some(r) => above.push((p.window, r, true)),64
- None => {65
- let r = anim.tiled(o, p.window, now).unwrap_or(p.rect);66
- tiled.push((p.window, r, true));67
- }68
- }56
+ for g in anim.ghosts().iter().filter(|g| g.output == o) {57
+ if let Some(r) = anim.tiled(o, g.id, now) {58
+ ghosts.push((g, local(r)));6959
}70
- for &(w, r) in &arr.floating {71
- above.push((w, anim.gliding(w, now).unwrap_or(r), true));60
+ }61
+ let arr = layout.arrange(o);62
+ let fullscreen_at_rest = arr63
+ .fullscreen64
+ .is_some_and(|w| anim.gliding(w, now).is_none());65
+ for p in &arr.tiled {66
+ if Some(p.window) == arr.fullscreen || fullscreen_at_rest {67
+ continue;7268
}73
- if let Some(w) = arr.fullscreen {74
- above.push((w, anim.gliding(w, now).unwrap_or(out.area), false));69
+ match anim.gliding(p.window, now) {70
+ Some(r) => above.push((p.window, local(r), true)),71
+ None => {72
+ let r = anim.tiled(o, p.window, now).unwrap_or(p.rect);73
+ tiled.push((p.window, local(r), true));74
+ }7575
}7676
}77
+ for &(w, r) in &arr.floating {78
+ above.push((w, local(anim.gliding(w, now).unwrap_or(r)), true));79
+ }80
+ if let Some(w) = arr.fullscreen {81
+ above.push((w, local(anim.gliding(w, now).unwrap_or(area)), false));82
+ }7783
borders.0.retain(|id, _| windows.contains_key(id));7884
let focused = layout.focused();7985
let mut out = Vec::new();@@ -11,6 +11,7 @@ use smithay::backend::renderer::Renderer;
1111
use smithay::backend::renderer::utils::{import_surface_tree, with_renderer_surface_state};1212
use smithay::desktop::{PopupManager, Space, Window, WindowSurfaceType};1313
use smithay::input::keyboard::{Keycode, XkbConfig};14
+use smithay::input::pointer::CursorImageStatus;1415
use smithay::input::{Seat, SeatState};1516
use smithay::reexports::calloop::generic::Generic;1617
use smithay::reexports::calloop::{EventLoop, Interest, LoopSignal, Mode, PostAction};@@ -42,6 +43,9 @@ pub struct State {
4243
pub space: Space<Window>,4344
pub popups: PopupManager,4445
pub host: Option<Host>,46
+ pub drm: Option<crate::drm::Drm>,47
+ pub cursor: crate::cursor::Cursor,48
+ pub cursor_status: CursorImageStatus,4549
pub needs_redraw: bool,4650
4751
pub layout: Layout,@@ -132,6 +136,9 @@ impl State {
132136
space: Space::default(),133137
popups: PopupManager::default(),134138
host: None,139
+ drm: None,140
+ cursor: crate::cursor::Cursor::load(),141
+ cursor_status: CursorImageStatus::default_named(),135142
needs_redraw: true,136143
layout,137144
windows: HashMap::new(),@@ -169,6 +176,7 @@ impl State {
169176
self.space.refresh();170177
self.popups.cleanup();171178
crate::nested::render(self);179
+ crate::drm::render(self);172180
crate::ipc::broadcast(self);173181
self.publish_workspaces();174182
let _ = self.display.flush_clients();@@ -411,6 +419,14 @@ impl State {
411419
}412420
}413421
422
+ /// The smithay output behind a layout output, on whichever backend runs.423
+ pub fn smithay_output(&self, o: OutputId) -> Option<smithay::output::Output> {424
+ if let Some(host) = self.host.as_ref().filter(|_| Some(o) == self.output_id) {425
+ return Some(host.output.clone());426
+ }427
+ self.drm.as_ref().and_then(|d| crate::drm::output_for(d, o))428
+ }429
+414430
fn publish_workspaces(&mut self) {415431
if !self.workspaces.has_clients() {416432
return;@@ -419,12 +435,7 @@ impl State {
419435
let groups = l420436
.outputs()421437
.filter_map(|(o, _)| {422
- let output = self423
- .host424
- .as_ref()425
- .filter(|_| Some(o) == self.output_id)?426
- .output427
- .clone();438
+ let output = self.smithay_output(o)?;428439
let workspaces = l429440
.tags()430441
.enumerate()@@ -40,6 +40,11 @@
4040
buildInputs = with pkgs; [4141
libxkbcommon4242
vulkan-loader43
+ libinput44
+ seatd45
+ libgbm46
+ libdrm47
+ udev4348
];4449
};4550
cargoArtifacts = craneLib.buildDepsOnly commonArgs;