Compare commits
1082 Commits
0.12.0
...
dkondor-up
Author | SHA1 | Date |
---|---|---|
blank X | c5e3ca6249 | |
Simon Ser | 29938b7425 | |
Isaac Freund | eb1a451803 | |
Isaac Freund | a819c512ec | |
Alexander Orzechowski | c4824b680a | |
Simon Ser | ec3780e6ea | |
Kirill Primak | 68c5fa340d | |
Kirill Primak | 304c61307a | |
Tadeo Kondrak | b0fee56974 | |
Kirill Primak | 7dde2b66d6 | |
Thomas Hebb | ea77cc5cb2 | |
nyorain | a59a957f2b | |
Simon Ser | df945b665c | |
Isaac Freund | 4b358c2f91 | |
Simon Ser | 7864f26d73 | |
blank X | b309e8fc39 | |
Daniel Kondor | 80d10b9c98 | |
Simon Ser | 9f41627aa1 | |
Stacy Harper | 8e566f716c | |
Isaac Freund | 07ccc6e0b3 | |
Simon Ser | c0b120a30c | |
Simon Ser | bedfec94bb | |
Simon Ser | a15c327718 | |
Simon Ser | 4377b55292 | |
Guido Günther | 4c59f7d46a | |
David Rosca | 31914928d2 | |
Isaac Freund | 1c3e0816f3 | |
Isaac Freund | ad01cdf0b2 | |
Isaac Freund | fecde72be3 | |
Isaac Freund | fb1f613510 | |
Isaac Freund | 0215dffba5 | |
Chris Chamberlain | d8ca494558 | |
Chris Chamberlain | f6d3efbf4b | |
Simon Ser | e3fefda023 | |
Kirill Primak | 0fcc842291 | |
Kirill Primak | 7964bdae76 | |
Kirill Primak | df7d280343 | |
Kirill Primak | f463ca669a | |
Simon Ser | 818fc4a87b | |
Simon Ser | 36a2b19485 | |
Simon Ser | 1fbd13ec79 | |
Simon Ser | 90e9d327dd | |
Simon Ser | 83bdb3ad07 | |
Simon Ser | ad28490cf4 | |
Simon Ser | c50c4fc5cc | |
Simon Ser | 1d8340754b | |
Simon Ser | 77d811a21b | |
Kirill Primak | c9f3c2b4f7 | |
tiosgz | ca1af8119c | |
Simon Ser | efeb8346cf | |
Simon Ser | 45069fb623 | |
Simon Ser | 60b7267e18 | |
Simon Ser | f016eca97c | |
Simon Ser | 7201aae3d6 | |
Simon Ser | 92080b3a01 | |
Simon Ser | 0d32118a80 | |
Simon Ser | 1bf9676e87 | |
Simon Ser | de0bc78319 | |
Simon Ser | 051d1ce90e | |
Simon Ser | ffd4a27714 | |
Quantum | 812ab2e716 | |
Moon Sungjoon | 611b9ca843 | |
Isaac Freund | a44b2af672 | |
Simon Ser | ba974a4e9f | |
Simon Zeni | dd84c5a1cc | |
Simon Ser | 697a1cd0f5 | |
Simon Ser | e93435016e | |
Simon Ser | 2540de494e | |
Simon Ser | 456b971099 | |
Rouven Czerwinski | 6bfb930aa7 | |
Simon Ser | fbaefd90fc | |
John Lindgren | bff5b2c559 | |
Jonathan Wong | 0fb479ca61 | |
Rouven Czerwinski | d37eb5c2ea | |
Simon Ser | 254ab890e7 | |
Simon Ser | 83d78f9fd4 | |
Simon Ser | ef1669d33e | |
Simon Ser | 98f2efde98 | |
Simon Ser | d5df8d5cbf | |
Simon Ser | e163a7cf48 | |
Simon Ser | affe9eda57 | |
Simon Ser | d78cb808b1 | |
Simon Ser | 585a908a01 | |
Simon Zeni | 1d3dd7fc08 | |
Simon Ser | b234edcf58 | |
Simon Ser | 2e33139ef7 | |
Simon Ser | f29abe4c77 | |
Simon Ser | e4f748c6e9 | |
Simon Ser | bf57825560 | |
Simon Ser | bcefb71cf6 | |
Joshua Ashton | f132d66816 | |
Simon Ser | 5332935afc | |
Simon Ser | 1d9c1bcea6 | |
Isaac Freund | c9ba9e82b6 | |
Simon Ser | 3b93da70a0 | |
Simon Ser | 3d73b899ff | |
Simon Zeni | d70d74ad4f | |
Simon Zeni | 52c34e8253 | |
Simon Ser | e656697a7d | |
Simon Ser | 6bb8973309 | |
Simon Ser | 86f5ecf468 | |
Simon Ser | a37f538ca0 | |
Manuel Stoeckl | d0bb7df630 | |
Manuel Stoeckl | e879d566bb | |
Manuel Stoeckl | 3d7d6ec06f | |
Manuel Stoeckl | 7508f87fcb | |
Simon Zeni | ee210758fc | |
Simon Zeni | c0fd60be63 | |
Simon Zeni | 25bb92faee | |
Simon Ser | 33eba9080c | |
Érico Nogueira | e736ebc63c | |
Simon Zeni | fdf3169b41 | |
Simon Zeni | d1ebd52ab2 | |
Simon Zeni | 42549a1c9a | |
Simon Zeni | a143093339 | |
Simon Zeni | 5f11198605 | |
Simon Zeni | 5a98eae0dc | |
Simon Zeni | d07c87f668 | |
Simon Zeni | 6dc6af1534 | |
Simon Zeni | 0c76aef202 | |
Simon Zeni | a6538ced35 | |
Simon Zeni | 6d6e70b9e0 | |
Simon Ser | 142d10e591 | |
Demi Marie Obenour | b5d4bc3c62 | |
Simon Ser | a04cfca4da | |
Simon Ser | 9a4e1095ca | |
Roman Gilg | 8274c85d21 | |
Raphael Robatsch | 4a8e681a5f | |
Cole Mickens | 3a685b10b6 | |
Simon Zeni | 02a1ae169e | |
Isaac Freund | ab16861e86 | |
Simon Ser | 76bab68e70 | |
Simon Ser | fa77aeb80e | |
Simon Ser | f20c49d78a | |
Isaac Freund | e326b76959 | |
Simon Ser | eb5f23d6d0 | |
Kirill Primak | fc1ed72bdc | |
Isaac Freund | 8634dd3e6a | |
Simon Ser | e13f3f8608 | |
Simon Ser | 2ff4e113e2 | |
Simon Ser | 3e801d68f2 | |
Jan Beich | 760e166578 | |
Simon Ser | 8bc1086cac | |
Simon Ser | d1b75674d4 | |
tiosgz | cc2ebd9fc0 | |
Ronan Pigott | 8e225261f0 | |
Ronan Pigott | e2aff8a9b0 | |
Ronan Pigott | 6ad0f819e2 | |
Simon Ser | 83090de034 | |
Simon Ser | b2f6ff45c2 | |
Simon Ser | 0817c52a21 | |
Simon Ser | 3b96aa04db | |
Simon Ser | a80f2b2816 | |
Haelwenn (lanodan) Monnier | a92293a15a | |
Haelwenn (lanodan) Monnier | 6666604f17 | |
Haelwenn (lanodan) Monnier | 4fb652c27f | |
MarkusVolk | ebe3cfaceb | |
Simon Ser | cbedbd0fc0 | |
Simon Ser | 5619cf368b | |
Simon Ser | c43130cb89 | |
Simon Ser | bf42630d32 | |
Simon Ser | 3d6ca9942d | |
Simon Ser | fb393ddf84 | |
Simon Ser | a4ccca0834 | |
Simon Ser | db4c93028d | |
Simon Zeni | 70e8277175 | |
Simon Ser | 7c10a77e0a | |
Kirill Primak | 6c3a71d9f6 | |
buffet | 3dc99ed281 | |
Simon Ser | 36cf387427 | |
Simon Ser | f7ea33da0a | |
Joshua Ashton | b62ce3c3c8 | |
Anthony Super | e22a386319 | |
nyorain | 8e34692250 | |
Kirill Primak | 2edf468aeb | |
Kirill Primak | 2af8cc769a | |
Kirill Primak | 1089b7b8d6 | |
Simon Ser | 1b65a80e9d | |
Isaac Freund | 4fae8f7be3 | |
Isaac Freund | 2a8d385386 | |
fwsmit | dc22a06184 | |
Kirill Primak | c3e54021f8 | |
Kirill Primak | cdaab82020 | |
Kirill Primak | 28248dd83b | |
Jan Beich | 31af2b67b0 | |
Simon Ser | 13cdb84ee8 | |
tiosgz | ce66244fd2 | |
tiosgz | 893434b2d4 | |
Elyes HAOUAS | dc3d1530bf | |
Simon Ser | 323b8498ad | |
Simon Ser | 1d7e438d8a | |
Simon Ser | 61b83441a1 | |
Simon Ser | 62be833aef | |
Simon Ser | 42138a073b | |
Simon Ser | 6d281d96cb | |
Simon Ser | 780052d4da | |
Simon Ser | d6be1d68b7 | |
Simon Ser | ea7357b703 | |
Simon Ser | 833437d592 | |
Simon Ser | 744a5c2fef | |
Simon Ser | 665a164f27 | |
Simon Ser | 0e34208344 | |
Kirill Primak | db4afc2408 | |
Simon Ser | 3d0848daae | |
José Expósito | 20d9448257 | |
José Expósito | 5f3e490c80 | |
José Expósito | 4c3e307ec8 | |
José Expósito | 62e62b6942 | |
José Expósito | 52d2491931 | |
José Expósito | 95970b3619 | |
José Expósito | d069a783bc | |
José Expósito | fb15538247 | |
Hubert Hirtz | d96d2f5f23 | |
Kirill Primak | 754f40f9cb | |
Kirill Primak | 59fa3637c3 | |
Simon Ser | 3c26244340 | |
Simon Ser | 43833fba64 | |
Simon Ser | 3d4afbe945 | |
Simon Ser | 27b529f8a0 | |
Simon Ser | 63040d6744 | |
Simon Ser | fdc22449d6 | |
Simon Ser | 7939bf8cc6 | |
Simon Ser | f6f0e010d1 | |
Simon Ser | b25759cd20 | |
Tadeo Kondrak | 30d3c76817 | |
Tadeo Kondrak | e0daa65aa6 | |
Simon Ser | 2e12de96ca | |
Simon Ser | 0c5ff5efab | |
Simon Ser | 2e590026e9 | |
Simon Ser | 597ba2b932 | |
Simon Ser | 211b3b760e | |
Kirill Primak | ccc84f11a4 | |
Kirill Primak | 0e2d369106 | |
Kirill Primak | b72a217fcc | |
Simon Zeni | 9579d62a16 | |
Simon Ser | 6cb25ebad7 | |
Kirill Primak | 52da68b591 | |
Guido Günther | e479dc1ef0 | |
Simon Ser | 4e7a8707cc | |
Andri Yngvason | 105fdecd0c | |
Andri Yngvason | 04d234bac1 | |
Simon Ser | a181a37b12 | |
Simon Ser | 7832005a1f | |
Quantum | 679f5ed966 | |
Simon Ser | e05c884891 | |
Simon Ser | 44f0f7a0a7 | |
Simon Ser | 9195b77e14 | |
Simon Ser | 04d105760d | |
Simon Ser | 968c1df7e9 | |
Simon Ser | 872993f95d | |
Simon Zeni | 3984c81faa | |
Simon Zeni | c67e3fe3b7 | |
Simon Zeni | 94ed8f9496 | |
Simon Zeni | e5a949a955 | |
Simon Ser | 42dba9dc90 | |
Simon Ser | b01d97a38f | |
Simon Ser | 04304c322e | |
muradm | 35f0a0d570 | |
Simon Ser | 0c8fba1a2f | |
Simon Ser | de1c73021c | |
Simon Ser | 274c8189d4 | |
Simon Ser | 3c74bd0c91 | |
Simon Ser | 3fbf6e02a3 | |
Simon Ser | 88919464ef | |
Simon Ser | ba0525c5c0 | |
Simon Ser | 0978a702d7 | |
Simon Ser | 0fe3b45361 | |
Simon Ser | 24c397dbf8 | |
Simon Ser | bb82b6dada | |
Simon Ser | cbe099dcc7 | |
Kirill Primak | 610f0c0805 | |
Kirill Primak | cf56596565 | |
Kirill Primak | ba55c7c4ff | |
Kirill Primak | 90e62390d9 | |
Simon Ser | 3ac99fa4dc | |
Simon Ser | 56b6b80b9a | |
Kirill Primak | 242c23743f | |
Simon Ser | d290b13871 | |
Simon Ser | 62924cc523 | |
Simon Ser | 55ca93469c | |
Simon Ser | 38cd1b4f4f | |
Simon Ser | 5aa5137fae | |
Simon Ser | 7df2ae88fa | |
Devin J. Pohly | 00c2bae1d3 | |
Devin J. Pohly | e2e68ff680 | |
Devin J. Pohly | 9ed16e39fa | |
Devin J. Pohly | b7cd06e8fa | |
Devin J. Pohly | 526652a554 | |
Simon Ser | b0972a94c3 | |
Simon Ser | 267eb02c31 | |
Simon Ser | d9523faa76 | |
Simon Ser | ee6c841d47 | |
Simon Ser | d9d8fc1ab9 | |
Simon Ser | c8d97e2791 | |
Devin J. Pohly | 7ec9523ea3 | |
Devin J. Pohly | d5263be355 | |
Devin J. Pohly | 0f534e32e4 | |
Devin J. Pohly | a1d462fa81 | |
Simon Ser | b18c254e5f | |
Simon Ser | 1ad3cd7f36 | |
Simon Ser | ea800b7418 | |
Simon Ser | 2ddd8e8036 | |
Tudor Brindus | bfc69decdd | |
Simon Ser | e4d0ec9ee1 | |
Simon Ser | 501b29db03 | |
Simon Ser | 97954154bc | |
Simon Ser | 86e9309808 | |
Simon Ser | c41bd320be | |
Simon Ser | c7d489b5b6 | |
Kirill Primak | 5f645598d8 | |
Simon Ser | 5dfaf5ea9c | |
Simon Ser | 749b3c00f0 | |
Simon Ser | 3ce2ea9e16 | |
Simon Ser | b37731cdbb | |
Simon Ser | 65c0ab00b6 | |
Kirill Primak | 72a156b18a | |
Kirill Primak | 664307f968 | |
Isaac Freund | f2f3df9fb1 | |
Guido Günther | de1522aeee | |
Rouven Czerwinski | 9b7803a9b3 | |
Simon Ser | 18c2dce65e | |
Simon Ser | 46c42e55c6 | |
Kirill Primak | 109405729b | |
Rouven Czerwinski | cdd9a60f72 | |
Rouven Czerwinski | aa78c50bf1 | |
Rouven Czerwinski | 59b292b691 | |
Simon Ser | 7544b7abf9 | |
Michele Sorcinelli | cae7b98136 | |
Tudor Brindus | 0c19a28266 | |
Isaac Freund | 3364eec07e | |
Simon Ser | ad7651a370 | |
Simon Ser | ee1156b62b | |
Simon Ser | 93964012e6 | |
Simon Ser | 20404ed8bb | |
Simon Ser | 3f9e4f7a44 | |
Kirill Primak | 111d4eafd7 | |
Kirill Primak | debd6c5f0b | |
Kirill Primak | 11f799e88e | |
Kirill Primak | a6a80850b7 | |
Simon Ser | 604674dc54 | |
Simon Ser | eb0ce659cf | |
Simon Ser | 88f65db87f | |
Tudor Brindus | 033c9cab74 | |
Simon Ser | c27263c105 | |
Simon Ser | d48ffac56b | |
Simon Ser | ca0b19fc9c | |
Simon Ser | 1936e136df | |
Simon Ser | df0e75ba05 | |
Simon Ser | 8a3cd28973 | |
Simon Ser | b913e64f95 | |
Simon Ser | 923258b0be | |
Kirill Primak | f12bacf4b4 | |
Quantum | 456c6e2279 | |
Manuel Stoeckl | f5df956c18 | |
Manuel Stoeckl | 44e8451cd9 | |
Manuel Stoeckl | 4dc52bcb6c | |
Simon Ser | f76960388f | |
Simon Ser | 6973361d60 | |
Simon Ser | 3132c0ab10 | |
Simon Ser | f211bc983a | |
Simon Ser | 4ddde1a7bd | |
Simon Ser | d17a009062 | |
Simon Ser | 55ac7e335a | |
Simon Ser | c55f70c8b7 | |
Simon Ser | c74dc45bb6 | |
Simon Ser | 9b99570869 | |
Simon Ser | ebb661532c | |
Simon Ser | f5900c1f00 | |
Simon Ser | 85d7ad2eef | |
Simon Ser | 6aadf811aa | |
Simon Ser | 0fb55c76d0 | |
Simon Ser | 1a5b6722a8 | |
Simon Ser | d6f0fc251e | |
Simon Ser | d1c931cbe8 | |
Dylan Araps | e5063ef3a3 | |
Simon Zeni | 6f19295647 | |
yuiiio | 7667ab73bd | |
Simon Ser | 770a561bce | |
Simon Ser | 4b316a3823 | |
Simon Ser | 3cf2535c23 | |
Simon Ser | ace2eda073 | |
Simon Zeni | 04d4fb536d | |
Simon Zeni | 0778151f94 | |
Simon Zeni | 646a25667e | |
Simon Zeni | f09c88c1b7 | |
Simon Ser | 2fa47c1837 | |
ayaka | 70fb21c35b | |
Simon Ser | 66c42f4fcb | |
Simon Ser | cc8bc0db20 | |
Simon Ser | 8afb4d8bf0 | |
Simon Ser | f94eb174c7 | |
Vyivel | a93b18dbd5 | |
Simon Ser | a47f89cf7c | |
Simon Ser | bcd5d8504c | |
Simon Ser | 709190c4c8 | |
Simon Ser | aec062d0d3 | |
Simon Ser | 87e8c60faf | |
Simon Ser | 28aa803916 | |
Simon Ser | 5544973814 | |
Simon Ser | 9dba176e8d | |
Simon Ser | 9b70eab194 | |
Simon Ser | 4c51a0f6eb | |
Simon Ser | 4554f17377 | |
Simon Ser | d7c68ce632 | |
Simon Ser | a0baba4fa0 | |
Simon Ser | 0abb67c478 | |
Simon Ser | 7b25b0ff88 | |
Simon Zeni | 60f4d8f409 | |
Simon Zeni | d086501fba | |
Simon Zeni | 6d8029b07e | |
Simon Ser | f67cfb2ce2 | |
Simon Ser | fde56c20b4 | |
Simon Ser | 017555651b | |
Vyivel | a362d21d6b | |
Simon Ser | c1b27cc499 | |
Simon Ser | d71ed635b9 | |
Simon Ser | e035f2b9c4 | |
Simon Ser | b934fbaf04 | |
Simon Ser | 22fd411bc3 | |
Simon Ser | c1902cdb3f | |
Simon Ser | 8eef6a8843 | |
Simon Ser | 2d36d7fb67 | |
Simon Ser | 84906a832f | |
Simon Ser | a48e569d38 | |
Simon Ser | c2bd63c186 | |
Simon Zeni | 4c7657ee62 | |
Simon Zeni | e192d87731 | |
Simon Zeni | d975f35bba | |
Simon Ser | 3fdf8cf07e | |
Simon Ser | 9a8097682b | |
Simon Ser | d3d1c69aca | |
Simon Ser | 8a4957570f | |
Simon Ser | e5b5592a95 | |
Simon Ser | 4e07d4cbf9 | |
Simon Ser | d7b19fb294 | |
Simon Ser | c868e509b7 | |
Simon Ser | f7e3d325fe | |
Simon Ser | 78121ad2d8 | |
Simon Ser | 4dee7a2f6f | |
Simon Ser | 5f26360bd8 | |
Simon Ser | e8c408b31b | |
Simon Ser | 82af6e7208 | |
Simon Ser | 475d9701e2 | |
Simon Ser | c7018a45b7 | |
Simon Ser | 0a522cb798 | |
Simon Ser | a38baec1f8 | |
Simon Ser | 29be2d47e4 | |
Simon Ser | 7ad44051a2 | |
Simon Ser | 08e5b909f9 | |
Simon Ser | 18adb43a44 | |
Simon Ser | 29c8df7e0a | |
Simon Ser | 7ec66a9990 | |
Simon Ser | ea585dba0f | |
Simon Ser | 57b70a478c | |
Simon Ser | 5888c96da8 | |
Simon Ser | e6cb11d882 | |
Simon Ser | a6ed4ae308 | |
Simon Ser | dbb0e2f75b | |
Simon Ser | 1db976cecb | |
Simon Ser | 1c4b5bcab3 | |
Simon Ser | 1b4fb4b537 | |
Simon Ser | bcbdee43f7 | |
Simon Ser | 7cbcc65ad0 | |
Simon Ser | ddc98bf593 | |
Simon Ser | 634a20d89c | |
Simon Ser | f6ae028e99 | |
Simon Ser | 787842c459 | |
Simon Ser | 31db232704 | |
Simon Ser | d2b6b570ea | |
Simon Ser | a2419eb4ea | |
Simon Ser | b69db15da6 | |
Simon Ser | 264d4e2bce | |
Simon Ser | 0467a7523a | |
Simon Ser | 8810e95082 | |
Simon Ser | 2f615468b6 | |
Kenny Levinsen | 15c8453ba1 | |
zccrs | 3c03639cd5 | |
zccrs | dc17ecd236 | |
zccrs | fdc40e071e | |
Simon Ser | 103edde481 | |
Simon Ser | f6e680ef94 | |
Kenny Levinsen | 8d2a94b0df | |
Kenny Levinsen | cb6db86a28 | |
Simon Ser | 72ee196efa | |
Simon Ser | fb933d3204 | |
Simon Ser | 6c3d080e25 | |
Simon Ser | 6259fd23fb | |
Simon Ser | 3345eaca89 | |
Simon Ser | eca5d2f37f | |
Simon Ser | 758f117442 | |
Simon Ser | f55b43ddd6 | |
Simon Ser | cb378600e4 | |
Simon Ser | b180d3482f | |
Simon Ser | 63f891e393 | |
Simon Ser | e89cf5f047 | |
Simon Ser | 2806154900 | |
Simon Ser | b2f6db3533 | |
Simon Ser | c87c849ec6 | |
Simon Ser | fbadadf36f | |
Simon Ser | a667175ec7 | |
Simon Ser | 69477051cc | |
Simon Ser | 543f5b35d0 | |
Simon Ser | 5f8092b045 | |
Simon Ser | a670ee7940 | |
Simon Ser | 68c4f15958 | |
Simon Ser | 44feb832f9 | |
Simon Ser | 1a06ea7750 | |
Simon Ser | 233a2617cf | |
Simon Ser | 4d603826c8 | |
Simon Ser | 534615cd55 | |
Simon Ser | 7c26345826 | |
Simon Ser | 625c66ef75 | |
Simon Ser | 6e43d642b2 | |
Simon Ser | 38ba5881a0 | |
Simon Ser | 9e58301df7 | |
zccrs | 11040d4942 | |
Simon Ser | 76f51a949f | |
Simon Ser | 8ff435831f | |
Simon Ser | b86a0c8d8f | |
Simon Ser | 2b0a1aeed5 | |
Simon Ser | 01e0f51fad | |
Simon Ser | e06ea4e84a | |
Simon Ser | 9e9be83a58 | |
Simon Ser | de51df2770 | |
Simon Ser | 91ee33e956 | |
Simon Ser | 6430230d1f | |
Simon Ser | b732f094c6 | |
Simon Ser | ce3e819b33 | |
Simon Ser | 766a24fa77 | |
Simon Ser | 5c30cf3d94 | |
Tudor Brindus | 6605d7c390 | |
Tudor Brindus | ae2f3ecb68 | |
Tudor Brindus | 699d724000 | |
Simon Ser | abf527b075 | |
Simon Ser | d0560e2597 | |
Simon Ser | b3ff6db730 | |
Simon Ser | 8fa4a6b303 | |
Simon Zeni | e6c00f7eea | |
Brian McKenna | 2fd20b17b6 | |
Simon Ser | fd2b1f018e | |
Simon Ser | c82f37542d | |
Simon Ser | 349553d011 | |
Simon Ser | 4dae12890f | |
Simon Ser | bcabe34a2e | |
Simon Ser | 7ec5bf6b10 | |
Simon Ser | beae3018cb | |
Simon Ser | f73c04b801 | |
Simon Ser | 66e100ffbf | |
Simon Ser | 9ca743f9fd | |
Simon Ser | a8d7c2d4ea | |
Simon Ser | 68758e8c21 | |
Simon Ser | f6ba26ff58 | |
Simon Ser | 9d55f712e3 | |
Simon Ser | 9221ed7b4c | |
Simon Ser | 6f39574ff5 | |
Simon Ser | 6f69e2f12e | |
Simon Ser | 6369f70931 | |
Thomas Weißschuh | 9f211b5dd4 | |
Simon Ser | 101b9a193d | |
Yuya Nishihara | 8008d21f5b | |
Simon Zeni | ed7f2651b6 | |
Simon Zeni | 2c90e0f521 | |
Tobias Stoeckmann | d0c1f0c0b6 | |
Simon Ser | 66d5805594 | |
Kenny Levinsen | 2603a5dee7 | |
Simon Ser | c85789a3a9 | |
Simon Ser | a1e8a639b3 | |
Simon Ser | e543e26206 | |
Simon Ser | e06c62af77 | |
Simon Ser | 218955ce95 | |
Simon Ser | 7aba881c47 | |
Simon Ser | 31082a0554 | |
Simon Ser | dfea0ff31d | |
Simon Ser | 485ecc11a6 | |
Simon Ser | c9c2d7539c | |
Simon Ser | 1a9701cd7c | |
Simon Ser | 8f90d7f8f5 | |
Simon Ser | 69d4cf19b5 | |
Simon Ser | e7f68ba081 | |
Simon Ser | 5be76bb047 | |
Simon Ser | 619a975025 | |
Simon Ser | 6bf2406dbf | |
Simon Ser | 1c1ef69326 | |
Simon Zeni | cc1b66364c | |
Simon Zeni | 318e3ac92c | |
Simon Zeni | 982498fab3 | |
Simon Zeni | 144189674e | |
Simon Zeni | 6ec6527855 | |
Simon Zeni | a8c91fbac9 | |
Simon Zeni | c75aa71816 | |
Simon Ser | 3a04fb4560 | |
Simon Ser | 24fde77c62 | |
Simon Zeni | ccbce0f0a6 | |
Simon Zeni | 144b41a45c | |
Simon Zeni | 30706b71fb | |
Simon Ser | 0411dc0663 | |
Simon Ser | 4839664a92 | |
Simon Ser | 9b0e0970f9 | |
Simon Ser | 5597776914 | |
Simon Ser | c49ea9ef4f | |
Simon Ser | e804de923d | |
Simon Ser | f9f90b4173 | |
Simon Ser | 1a5530d14d | |
Simon Ser | 5c699f09cb | |
Simon Ser | af78ecb86b | |
Simon Zeni | 8a27050b4e | |
Simon Ser | ce30a22159 | |
Simon Ser | b5cfaea705 | |
Simon Ser | 565f67f805 | |
Aleksei Bavshin | e48dcdf72c | |
Aleksei Bavshin | e0f239fa28 | |
Simon Ser | c314920a3d | |
Simon Ser | 8ca2b4cf49 | |
Simon Ser | 661ba49564 | |
Simon Ser | 9901d49fa5 | |
Simon Ser | 6622cd3277 | |
Simon Ser | 8e375ae340 | |
Simon Zeni | cdacf4f632 | |
Simon Zeni | 10c5199d85 | |
Tadeo Kondrak | 014c59aa40 | |
nyorain | 572b5910bb | |
Simon Zeni | 78b94a570c | |
Simon Zeni | 217c4f79a0 | |
tomKPZ | 7c9b61b18c | |
Simon Ser | e8df7c367a | |
Simon Ser | f64ed60c7b | |
Simon Ser | fbc2182b9f | |
Simon Ser | 83670fce65 | |
Simon Ser | fd7e565ce3 | |
Kenny Levinsen | 3432ab2ba7 | |
Kenny Levinsen | e7515529ce | |
ayaka | ed1924800d | |
Simon Zeni | 122d6c6988 | |
Simon Zeni | 0d90dddfab | |
Simon Ser | 9de93a866f | |
Simon Ser | 80865351bd | |
Simon Ser | c6b009ef85 | |
Simon Ser | 6c61de996c | |
Simon Zeni | 0b9288ec0b | |
Simon Ser | 9f33d8ad39 | |
Ryan Farley | d87ede0d69 | |
Simon Ser | 3c6826df71 | |
Simon Zeni | 84dea55b20 | |
Simon Ser | 004cf887b7 | |
Simon Ser | 1e5460d4c6 | |
Simon Ser | 053ebe7c27 | |
Simon Ser | 846e0838d6 | |
Kenny Levinsen | 7f09085461 | |
Kenny Levinsen | 3f87c2caea | |
Kenny Levinsen | d037c2dddc | |
Kenny Levinsen | 95b657ba80 | |
Kenny Levinsen | 21e8a940b8 | |
Yuya Nishihara | 3c5cc02b18 | |
Yuya Nishihara | a71d565138 | |
Roman Gilg | b36af22c94 | |
Ryan Farley | b29ac8fbac | |
Simon Ser | 5a178c4a23 | |
Benoit Gschwind | ee3640363e | |
Kenny Levinsen | d50bbf0bbc | |
Stephan Hilb | 9f012cac2f | |
Simon Ser | d5105c42e3 | |
Simon Ser | 1eb38e0015 | |
Isaac Freund | 78befa59f9 | |
Simon Ser | a109a80dca | |
Simon Ser | 9ecfa4343a | |
Simon Ser | 1cdef8da57 | |
Simon Ser | 1c10079a67 | |
Simon Ser | 69c71dbc8a | |
zccrs | 69e1997ebe | |
Simon Ser | 8ccb4bbb5f | |
Simon Ser | d0bf750916 | |
Simon Ser | 7efc2d05b7 | |
Simon Ser | 5088e25eaf | |
Simon Ser | 55aaeb25c5 | |
Simon Ser | 6721444114 | |
Simon Ser | e9361e0492 | |
Simon Ser | 6bfbf35618 | |
Simon Ser | 1ec97bdf4f | |
Simon Ser | 511e42be5e | |
Simon Ser | b514d4afe2 | |
Simon Ser | a9e5df44d8 | |
Simon Ser | c430cd7d53 | |
zccrs | e76583f1ad | |
Simon Ser | 07a5345aa5 | |
Simon Ser | 7709a965e5 | |
Simon Ser | a2535b80ce | |
Simon Ser | de5347d0f2 | |
Simon Ser | 96aa18ae44 | |
Simon Ser | b89bcffea4 | |
Kenny Levinsen | 741da702bc | |
Simon Ser | 80dbb9ba71 | |
Simon Ser | 8ecc557ab0 | |
Simon Ser | e0258f4506 | |
Simon Ser | 7ac76aba8a | |
Simon Zeni | 78d21fa131 | |
Simon Zeni | 9b3f2e327f | |
Simon Zeni | c8b3536b01 | |
Simon Zeni | 50d2985607 | |
Simon Zeni | 5fd82c6f54 | |
Simon Ser | d9cae04ffc | |
Simon Ser | 6230f7be4f | |
Simon Ser | e8ad05913f | |
Simon Ser | c740fccc9d | |
Simon Ser | 7720dde74d | |
Kenny Levinsen | 00bee2a6bd | |
Kenny Levinsen | 883d5b6e7c | |
Simon Ser | 2382684e94 | |
Simon Ser | 44fa2c4b49 | |
Simon Zeni | 9601a2abf0 | |
Simon Ser | 52e40025c4 | |
Ilia Mirkin | 10dbb00f5f | |
Lukas Märdian | 7dffe9339b | |
Lukas Märdian | d8a422575b | |
Kenny Levinsen | 46d2f80c38 | |
Simon Ser | a02da8e6f6 | |
Simon Ser | e6f6e1ad0a | |
Simon Ser | eec2e1d3b1 | |
Simon Ser | 3504bb587d | |
Simon Ser | 73137ace84 | |
Simon Ser | c5dad8b626 | |
Simon Ser | fda46ce56f | |
Simon Ser | 70a7c7f389 | |
Simon Ser | c5202b728a | |
Simon Ser | 675bc39658 | |
Simon Ser | c2815fd44d | |
Simon Ser | 3695ae97b4 | |
Simon Ser | 4b43aebdc7 | |
Simon Ser | cf5b09ede2 | |
Simon Ser | 27fba3df43 | |
Simon Ser | b54ef3372d | |
Simon Ser | 00bf6674b3 | |
Simon Ser | ddfee63055 | |
Simon Ser | 549435aee5 | |
Simon Ser | fab396f149 | |
Simon Ser | 5d6d76c61f | |
Simon Ser | bfd020047d | |
Simon Ser | 6ca59519c9 | |
Simon Ser | f3758d1d0a | |
Simon Ser | 641c223d3c | |
Simon Ser | 2530235139 | |
Simon Ser | 38ec1c0e73 | |
Simon Ser | ccb86448eb | |
Tadeo Kondrak | 78685ec6aa | |
Simon Ser | 27f65c2c77 | |
Simon Ser | fdd9088e05 | |
Simon Ser | 6f873078d4 | |
Tudor Brindus | 2118a3ce47 | |
Tudor Brindus | 2827a9554c | |
Tudor Brindus | 7d52b4d0b5 | |
Simon Ser | 4a9e70ccde | |
Simon Ser | b60c5fa450 | |
Simon Ser | 12cc465144 | |
Tadeo Kondrak | 5e19e0053a | |
Tadeo Kondrak | 99ef23b62c | |
Simon Ser | d595a4ebe3 | |
Simon Ser | 91fa2ff395 | |
Simon Ser | 8d76d3263d | |
Simon Ser | 533a36f05a | |
Justus Rossmeier | b9e9e0e133 | |
Brandon Dowdy | a02ac01be3 | |
Simon Ser | 9396d8433a | |
Ilia Mirkin | ef94e7e847 | |
Ilia Mirkin | 8ad078f46f | |
Manuel Stoeckl | a290d7a78d | |
Manuel Stoeckl | b6dea80907 | |
Manuel Stoeckl | 79be26ff1f | |
Simon Ser | b86eea0897 | |
Simon Ser | 90cdf43b5f | |
Tudor Brindus | 3d46d3f7a1 | |
Tudor Brindus | 2fa257313a | |
Tudor Brindus | 7964a313e8 | |
Brandon Dowdy | 0977633457 | |
Simon Ser | 8e27418dd3 | |
Simon Ser | a39dc1f7a8 | |
Simon Ser | 45f992b27b | |
Simon Ser | 3d7aa73867 | |
Simon Ser | 7ac2ce25e3 | |
Quantum | 975d14b799 | |
Simon Ser | 01d21cdd9f | |
Brandon Dowdy | f1d37c54c8 | |
Tudor Brindus | dd4c8aa45e | |
Tudor Brindus | b3d782f818 | |
Tudor Brindus | aa86a022fa | |
Tudor Brindus | b6ba595862 | |
Tudor Brindus | 3417fc0cca | |
Tudor Brindus | e0dfc14983 | |
Brandon Dowdy | c89dba9435 | |
Simon Ser | 7b50f5d324 | |
Tudor Brindus | 211c1e23be | |
Tudor Brindus | 703c17ae41 | |
Tudor Brindus | 23148d283f | |
Tudor Brindus | dea94f2bad | |
Tudor Brindus | 10a2d57055 | |
Tudor Brindus | 40b2e7669a | |
Brandon Dowdy | 8aa38fe73e | |
Brandon Dowdy | 705b3da7cb | |
Brandon Dowdy | 34e7f69d69 | |
Simon Ser | 50b9921642 | |
Simon Ser | f8a66072e7 | |
Simon Ser | a406f19479 | |
Simon Zeni | 6becc69ec9 | |
Simon Ser | 5a2ef794dc | |
Tudor Brindus | e75f483aeb | |
fwsmit | 1b8330d1f8 | |
Tudor Brindus | 0db191d3bf | |
Tudor Brindus | abb56152ff | |
Simon Ser | 73ffab70b0 | |
Simon Ser | 4af85f4c19 | |
Simon Ser | 44a3d6e74d | |
Simon Ser | 4f06ce2550 | |
Ilia Mirkin | 7bc8dbb991 | |
Ilia Mirkin | 922b7f415d | |
Ilia Mirkin | bb92fd4c90 | |
Simon Ser | 7c995b78b2 | |
Simon Ser | 54e5ef39c0 | |
Simon Zeni | 306cf11d87 | |
Simon Zeni | ee31be167b | |
Ilia Mirkin | 62f37ee319 | |
BrassyPanache | d6649a8a4b | |
Isaac Freund | f6fe439718 | |
Simon Ser | 702eed5cbd | |
Ilia Mirkin | 966e653935 | |
Simon Ser | 04d89a8bc5 | |
Simon Ser | f17b0f975d | |
Simon Ser | cb6f584496 | |
Chris Chamberlain | 6af748171a | |
Simon Ser | 879cadd34e | |
Simon Ser | e537382991 | |
Simon Ser | 284233c34f | |
Simon Ser | 5373187186 | |
Simon Ser | bf86110fc5 | |
Simon Ser | b5cefada92 | |
Simon Ser | c6c7fccd96 | |
Simon Ser | 9e98f497af | |
Simon Ser | 2f11914613 | |
Simon Ser | 32c30481d3 | |
Simon Ser | b3e76d6678 | |
Simon Ser | 1fb9535e99 | |
Simon Ser | afdf4dc890 | |
Simon Zeni | 08a4c62aac | |
Simon Zeni | e128e6c08d | |
Simon Ser | affc59454e | |
Simon Ser | 6dfc8ce00b | |
Simon Ser | b7c95d483a | |
Simon Ser | 400f4e7f27 | |
Simon Ser | ad3a455db9 | |
Simon Ser | a53ab146fe | |
Simon Ser | 87293d1b15 | |
Simon Ser | c73a8cde83 | |
Simon Ser | 9dd059376c | |
Simon Ser | f0303978e3 | |
Simon Ser | 642b349e94 | |
Simon Ser | cc56b4f073 | |
Simon Ser | aab43b3c76 | |
Simon Ser | f6f46b4ee2 | |
Simon Ser | a6a0568316 | |
Simon Ser | 5642b880c3 | |
Simon Ser | 02a086599c | |
Simon Ser | dc61f471da | |
Daniel Kondor | b7dc4f2990 | |
Simon Ser | 5d054258af | |
Simon Ser | 3f7e0cf5f0 | |
Simon Ser | 1d461687d2 | |
Simon Ser | 50b120927d | |
Simon Ser | 76ed2255ef | |
Isaac Freund | 07111828c5 | |
Isaac Freund | f574ca934c | |
Simon Ser | 672e8e99b7 | |
Simon Ser | 2585f322cb | |
Simon Ser | 248b8e647a | |
Simon Ser | 4f80fab337 | |
Simon Ser | edf5082a4c | |
Isaac Freund | d6890cb847 | |
Simon Ser | 098094c5cb | |
Simon Ser | 8f065810f6 | |
Simon Ser | 7036dceb0e | |
Simon Ser | c94728b53a | |
Simon Ser | d9bbc416a6 | |
Simon Ser | 91cb0fc443 | |
Simon Ser | 5bd86b94f9 | |
Simon Ser | cd64610c66 | |
Simon Ser | 5b1b43c68c | |
Simon Ser | 64da8f0c8d | |
Simon Ser | 41aa80d4a1 | |
Simon Ser | 34b14d2fee | |
Rouven Czerwinski | dd920f602e | |
Isaac Freund | b482c90e1a | |
Isaac Freund | 4ee4a36c0c | |
Isaac Freund | 8f63557ed7 | |
Isaac Freund | c5c5ab9724 | |
Isaac Freund | 129e02b57d | |
Simon Zeni | 9192c0480a | |
Simon Zeni | b899a412e3 | |
Simon Ser | 5773794baf | |
Simon Zeni | 1458f7d974 | |
Simon Zeni | 826108373c | |
Andri Yngvason | e136a4168b | |
Isaac Freund | 87e216b740 | |
Simon Ser | 07d75c99db | |
Ilia Bozhinov | 01dcfb360e | |
Ilia Bozhinov | 162f160def | |
Ilia Bozhinov | 37602e153b | |
Ilia Bozhinov | 42d033e738 | |
Ilia Bozhinov | bf4e2e0eac | |
Simon Ser | 3721dbfddb | |
Isaac Freund | 0cba1ce747 | |
Isaac Freund | bf926e31a0 | |
Kenny Levinsen | d3047011d0 | |
Isaac Freund | 83fdfa511d | |
Isaac Freund | 5d24f6e098 | |
Isaac Freund | 8b90d5e17f | |
Isaac Freund | abcab0331f | |
Simon Ser | e8d56ca415 | |
Simon Ser | 7febdc7334 | |
Simon Ser | 198560fc1f | |
Simon Ser | 9714638f3b | |
Simon Ser | adfb7cd35a | |
Simon Ser | 576ff57db0 | |
Ilia Bozhinov | eb30cde777 | |
Simon Ser | 4ffd537d2d | |
Simon Ser | 1491ec42da | |
Simon Ser | 4b03bdc3ab | |
Simon Ser | bec1e6b149 | |
Simon Ser | 92a0fc0435 | |
Simon Ser | c4635c68d2 | |
Simon Ser | 7ea0e9f277 | |
Simon Ser | caeed70f28 | |
Ariadne Conill | 23b6f3e3f5 | |
Simon Ser | c012d770f7 | |
Simon Ser | c5f239f411 | |
Simon Ser | b9460ab724 | |
Simon Ser | ae5275c09f | |
Simon Ser | 17dd4c9e9a | |
Simon Ser | a7a230ebef | |
Simon Ser | c011a0e2ed | |
Simon Ser | 5548406667 | |
Simon Ser | 71eaab9d8c | |
Simon Ser | 430d37846f | |
Simon Ser | 21ed6582ce | |
Simon Ser | 64a2ca4dba | |
Simon Ser | 54ec17ff64 | |
Simon Ser | 2de400a541 | |
Simon Ser | 85cf4b235d | |
Isaac Freund | f6fc4c2883 | |
Simon Ser | ad4dae0844 | |
Simon Ser | d3bcd63a40 | |
Simon Ser | 5ee8b12dc3 | |
Simon Ser | d09abe86c1 | |
Simon Ser | dabd2e7207 | |
Simon Ser | 83925f04c3 | |
Simon Ser | 55b02f753f | |
Simon Ser | d6dbdd97e9 | |
Simon Ser | defcd9b025 | |
Simon Ser | de896caceb | |
Isaac Freund | 6c08fe9796 | |
Ronan Pigott | 917ecca58e | |
Simon Ser | 352fdd1bb0 | |
Simon Ser | 248c7787c7 | |
Simon Ser | 019ffe8a5b | |
Simon Ser | c89b131f29 | |
Simon Ser | 0aefa18690 | |
Simon Ser | f0c1b32120 | |
Simon Ser | 94fda895ac | |
Simon Ser | 1e2c7fce86 | |
Simon Ser | d37214cb16 | |
Simon Ser | 253f447329 | |
Simon Ser | 9cd3f03f65 | |
Simon Ser | 60001a75a2 | |
Simon Ser | bdf26f87d5 | |
Simon Ser | 0dcdb5e7a1 | |
Simon Ser | 0aa2ba0c03 | |
Simon Ser | da2a216934 | |
Simon Ser | 87bd718de5 | |
Simon Ser | 1ca4d6b029 | |
Simon Ser | 3fd8098881 | |
Simon Ser | e57a52e7f7 | |
Simon Ser | 93cd3a79b2 | |
Simon Ser | 525fa6ada0 | |
Simon Ser | c59aacf944 | |
Simon Ser | d79a00bf02 | |
Simon Ser | 16a51bbab2 | |
Simon Ser | 858a1940b5 | |
Simon Ser | 441bac139f | |
Simon Ser | 3923ff005d | |
Simon Ser | 038285d496 | |
Simon Ser | 768131e488 | |
Simon Ser | 4c363a564f | |
Ilia Bozhinov | 12ede67c62 | |
Simon Ser | e9c1f0f7d3 | |
Simon Ser | f91e89fd9f | |
Simon Ser | be8403e73d | |
Simon Ser | 6ff478632a | |
Simon Ser | 8a6930c138 | |
Simon Ser | 06ab41a160 | |
Simon Ser | eb5886ddbe | |
Stephane Chauveau | b790e5ea34 | |
Simon Ser | 863acb26c0 | |
Simon Ser | 29da97c185 | |
Simon Ser | e69bbfd0d6 | |
Isaac Freund | c9760569ae | |
Dominik Honnef | 431ec52b9c | |
Simon Ser | 325cba6414 | |
Simon Ser | bfb59fd4d7 | |
Simon Ser | 037710b1d4 | |
Marten Ringwelski | 44b1ff16e9 | |
Isaac Freund | baf2319fd3 | |
Ilia Bozhinov | 54b7ca56c0 | |
Isaac Freund | 37cb3eb8dd | |
Isaac Freund | 1ecc1b5987 | |
Isaac Freund | 1477401acd | |
Simon Ser | 90c8452959 | |
Simon Ser | 1336ad2a23 | |
Simon Ser | 0e927533b0 | |
Simon Ser | 82443ea46b | |
Simon Ser | 237c2cf2fb | |
Simon Ser | 513eca8dab | |
Simon Ser | 50b5f8558e | |
Simon Ser | 8bc5a92a98 | |
Simon Ser | de9ff46629 | |
Simon Ser | 2649600fa1 | |
Ilia Bozhinov | d2329ac07a | |
Simon Ser | 83a5d03bf3 | |
Simon Ser | 1f15dd093d | |
Simon Ser | c94ab99ae2 | |
Simon Ser | 49115e9d5d | |
Simon Ser | c045253927 | |
Simon Ser | 5d008d9030 | |
Simon Ser | 61612ecb36 | |
Simon Ser | c15ca3793e | |
Simon Ser | 44cea53e72 | |
Isaac Freund | 78e9e692e8 | |
Simon Ser | 6485fadc16 | |
Simon Ser | 713c1661b7 | |
Simon Ser | 154fe8696f | |
Simon Ser | f6c36f8881 | |
Simon Ser | 52805feae9 | |
Isaac Freund | 262740bc9a | |
Kenny Levinsen | ebecc5404b | |
Simon Ser | 754179dacd | |
Simon Ser | c491a21d25 | |
Simon Ser | 0e76f92de7 | |
Simon Ser | cd95d70df0 | |
Simon Ser | fbf11a41e1 | |
Simon Ser | 76bcddf071 | |
Simon Ser | 768fbaad54 | |
Simon Ser | 44a4792fd8 | |
Simon Ser | 63df2bcbe6 | |
Kenny Levinsen | fb3bea8014 | |
Ilia Bozhinov | 6284af121f | |
Ronan Pigott | dc7c6c4860 | |
Simon Ser | e18599b05e | |
Simon Ser | 526ae5944c | |
Simon Ser | 02df7b7ac8 | |
Simon Ser | 61f8cdfb9e | |
Simon Ser | eb8360bda3 | |
Simon Ser | c8d95acc37 | |
Simon Ser | eef8b3dde8 | |
Simon Ser | c881008e1c | |
Simon Ser | 8058e338ea | |
Simon Ser | 68a8d99055 | |
Simon Ser | c11c6c4568 | |
Simon Ser | ef846a8839 | |
Simon Ser | 1245730ea2 | |
Simon Ser | 6136fe87d1 | |
Simon Ser | c88c54fb38 | |
Simon Ser | 0b40d09a21 | |
Simon Ser | b0a663d39d | |
Simon Ser | 7c6212a0f7 | |
Simon Ser | 5913040110 | |
Simon Ser | f47445f142 | |
Simon Ser | aaa3fcf66f | |
Mykola Orliuk | 2eae9ec7c8 | |
Mykola Orliuk | 44531e16e0 | |
Mykola Orliuk | ce8855ca2a | |
Mykola Orliuk | 07e2e0f60c | |
Mykola Orliuk | 44c4773d58 | |
Mykola Orliuk | 70ffda3ea3 | |
Mykola Orliuk | 85b0872650 | |
Mykola Orliuk | 40bfd9f8f7 | |
Isaac Freund | e06c9e43af | |
Isaac Freund | 0724b3c453 | |
Isaac Freund | 7693f61d81 |
|
@ -2,25 +2,35 @@ image: alpine/edge
|
|||
packages:
|
||||
- eudev-dev
|
||||
- ffmpeg-dev
|
||||
- glslang
|
||||
- libinput-dev
|
||||
- libxkbcommon-dev
|
||||
- mesa-dev
|
||||
- meson
|
||||
- pixman-dev
|
||||
- vulkan-headers
|
||||
- vulkan-loader-dev
|
||||
- wayland-dev
|
||||
- wayland-protocols
|
||||
- xcb-util-image-dev
|
||||
- xcb-util-renderutil-dev
|
||||
- xcb-util-wm-dev
|
||||
- xwayland
|
||||
- libseat-dev
|
||||
sources:
|
||||
- https://github.com/swaywm/wlroots
|
||||
- https://gitlab.freedesktop.org/wlroots/wlroots.git
|
||||
tasks:
|
||||
- setup: |
|
||||
cd wlroots
|
||||
meson build -Dauto_features=enabled -Dlogind=disabled -Dlibseat=disabled -Dxcb-errors=disabled
|
||||
meson build --fatal-meson-warnings --default-library=both -Dauto_features=enabled -Dxcb-errors=disabled
|
||||
- build: |
|
||||
cd wlroots
|
||||
ninja -C build
|
||||
sudo ninja -C build install
|
||||
- build-features-disabled: |
|
||||
cd wlroots
|
||||
meson build --reconfigure -Dauto_features=disabled
|
||||
ninja -C build
|
||||
- tinywl: |
|
||||
cd wlroots/tinywl
|
||||
make
|
||||
|
|
|
@ -11,18 +11,35 @@ packages:
|
|||
- wayland-protocols
|
||||
- xcb-util-errors
|
||||
- xcb-util-image
|
||||
- xcb-util-renderutil
|
||||
- xcb-util-wm
|
||||
- xorg-xwayland
|
||||
- seatd
|
||||
- vulkan-icd-loader
|
||||
- vulkan-headers
|
||||
- glslang
|
||||
sources:
|
||||
- https://github.com/swaywm/wlroots
|
||||
- https://gitlab.freedesktop.org/wlroots/wlroots.git
|
||||
tasks:
|
||||
- setup: |
|
||||
cd wlroots
|
||||
CC=gcc meson build-gcc -Dauto_features=enabled -Dlogind-provider=systemd
|
||||
CC=clang meson build-clang -Dauto_features=enabled -Dlogind-provider=systemd
|
||||
CC=gcc meson build-gcc --fatal-meson-warnings --default-library=both -Dauto_features=enabled --prefix /usr -Db_sanitize=address,undefined
|
||||
CC=clang meson build-clang --fatal-meson-warnings -Dauto_features=enabled
|
||||
- gcc: |
|
||||
cd wlroots/build-gcc
|
||||
ninja
|
||||
sudo ninja install
|
||||
cd ../tinywl
|
||||
CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" make
|
||||
- clang: |
|
||||
cd wlroots/build-clang
|
||||
ninja
|
||||
- smoke-test: |
|
||||
cd wlroots/tinywl
|
||||
sudo modprobe vkms
|
||||
udevadm settle
|
||||
export WLR_BACKENDS=drm
|
||||
export WLR_RENDERER=pixman
|
||||
export WLR_DRM_DEVICES=/dev/dri/by-path/platform-vkms-card
|
||||
sudo chmod ugo+rw /dev/dri/by-path/platform-vkms-card
|
||||
sudo -E seatd-launch -- ./tinywl -s 'kill $PPID' || [ $? = 143 ]
|
||||
|
|
|
@ -1,28 +1,38 @@
|
|||
image: freebsd/latest
|
||||
packages:
|
||||
- devel/evdev-proto
|
||||
- devel/libepoll-shim
|
||||
- devel/libudev-devd
|
||||
- devel/meson # implies ninja
|
||||
- devel/pkgconf
|
||||
- graphics/libdrm
|
||||
- graphics/mesa-libs
|
||||
- graphics/png
|
||||
- graphics/wayland
|
||||
- graphics/wayland-protocols
|
||||
- multimedia/ffmpeg
|
||||
- x11/libX11
|
||||
- x11/libinput
|
||||
- x11/libxcb
|
||||
- x11/libxkbcommon
|
||||
- x11/pixman
|
||||
- x11/xcb-util-errors
|
||||
- x11/xcb-util-wm
|
||||
- sysutils/seatd
|
||||
- devel/evdev-proto
|
||||
- devel/libepoll-shim
|
||||
- devel/libudev-devd
|
||||
- devel/meson # implies ninja
|
||||
- devel/pkgconf
|
||||
- graphics/glslang
|
||||
- graphics/libdrm
|
||||
- graphics/mesa-libs
|
||||
- graphics/png
|
||||
- graphics/vulkan-headers
|
||||
- graphics/vulkan-loader
|
||||
- graphics/wayland
|
||||
- graphics/wayland-protocols
|
||||
- multimedia/ffmpeg
|
||||
- x11/libX11
|
||||
- x11/libinput
|
||||
- x11/libxcb
|
||||
- x11/libxkbcommon
|
||||
- x11/pixman
|
||||
- x11/xcb-util-errors
|
||||
- x11/xcb-util-renderutil
|
||||
- x11/xcb-util-wm
|
||||
- x11-servers/xwayland
|
||||
- sysutils/seatd
|
||||
- gmake
|
||||
sources:
|
||||
- https://github.com/swaywm/wlroots
|
||||
- https://gitlab.freedesktop.org/wlroots/wlroots.git
|
||||
tasks:
|
||||
- wlroots: |
|
||||
cd wlroots
|
||||
meson build -Dauto_features=enabled -Dlogind=disabled
|
||||
ninja -C build
|
||||
- wlroots: |
|
||||
cd wlroots
|
||||
meson build --fatal-meson-warnings -Dauto_features=enabled
|
||||
ninja -C build
|
||||
sudo ninja -C build install
|
||||
- tinywl: |
|
||||
cd wlroots/tinywl
|
||||
gmake
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
include: https://git.sr.ht/~emersion/dalligi/blob/master/templates/multi.yml
|
||||
alpine:
|
||||
extends: .dalligi
|
||||
archlinux:
|
||||
extends: .dalligi
|
||||
freebsd:
|
||||
extends: .dalligi
|
107
CONTRIBUTING.md
107
CONTRIBUTING.md
|
@ -1,22 +1,21 @@
|
|||
# Contributing to wlroots
|
||||
|
||||
Contributing just involves sending a pull request. You will probably be more
|
||||
successful with your contribution if you visit
|
||||
[#sway-devel](https://webchat.freenode.net/?channels=sway-devel) on
|
||||
irc.freenode.net upfront and discuss your plans.
|
||||
Contributing just involves sending a merge request. You will probably be more
|
||||
successful with your contribution if you visit [#sway-devel on Libera Chat]
|
||||
upfront and discuss your plans.
|
||||
|
||||
Note: rules are made to be broken. Adjust or ignore any/all of these as you see
|
||||
fit, but be prepared to justify it to your peers.
|
||||
|
||||
## Pull Requests
|
||||
## Merge Requests
|
||||
|
||||
If you already have your own pull request habits, feel free to use them. If you
|
||||
If you already have your own merge request habits, feel free to use them. If you
|
||||
don't, however, allow me to make a suggestion: feature branches pulled from
|
||||
upstream. Try this:
|
||||
|
||||
1. Fork wlroots
|
||||
2. `git clone https://github.com/username/wlroots && cd wlroots`
|
||||
3. `git remote add upstream https://github.com/swaywm/wlroots`
|
||||
2. `git clone git@gitlab.freedesktop.org:<username>/wlroots.git && cd wlroots`
|
||||
3. `git remote add upstream https://gitlab.freedesktop.org/wlroots/wlroots.git`
|
||||
|
||||
You only need to do this once. You're never going to use your fork's master
|
||||
branch. Instead, when you start working on a feature, do this:
|
||||
|
@ -25,42 +24,74 @@ branch. Instead, when you start working on a feature, do this:
|
|||
2. `git checkout -b add-so-and-so-feature upstream/master`
|
||||
3. Add and commit your changes
|
||||
4. `git push -u origin add-so-and-so-feature`
|
||||
5. Make a pull request from your feature branch
|
||||
5. Make a merge request from your feature branch
|
||||
|
||||
When you submit your pull request, your commit log should do most of the talking
|
||||
When you submit your merge request, your commit log should do most of the talking
|
||||
when it comes to describing your changes and their motivation. In addition to
|
||||
this, your pull request's comments will ideally include a test plan that the
|
||||
this, your merge request's comments will ideally include a test plan that the
|
||||
reviewers can use to (1) demonstrate the problem on master, if applicable and
|
||||
(2) verify that the problem no longer exists with your changes applied (or that
|
||||
your new features work correctly). Document all of the edge cases you're aware
|
||||
of so we can adequately test them - then verify the test plan yourself before
|
||||
submitting.
|
||||
|
||||
## Commit Log
|
||||
|
||||
Unlike many projects using GitHub and GitLab, wlroots has a [linear, "recipe"
|
||||
style] history. This means that every commit should be small, digestible,
|
||||
stand-alone, and functional. Rather than a purely chronological commit history
|
||||
like this:
|
||||
|
||||
```
|
||||
doc: final docs for view transforms
|
||||
fix tests when disabled, redo broken doc formatting
|
||||
better transformed-view iteration (thanks Hannah!)
|
||||
try to catch more cases in tests
|
||||
tests: add new spline test
|
||||
fix compilation on splines
|
||||
doc: notes on reticulating splines
|
||||
compositor: add spline reticulation for view transforms
|
||||
```
|
||||
|
||||
We aim to have a clean history which only reflects the final state, broken up
|
||||
into functional groupings:
|
||||
|
||||
```
|
||||
compositor: add spline reticulation for view transforms
|
||||
compositor: new iterator for view transforms
|
||||
tests: add view-transform correctness tests
|
||||
doc: fix formatting for view transforms
|
||||
```
|
||||
|
||||
This ensures that the final patch series only contains the final state,
|
||||
without the changes and missteps taken along the development process. A linear
|
||||
history eases reviewing, cherry-picking and reverting changes.
|
||||
|
||||
If you aren't comfortable with manipulating the Git history, have a look at
|
||||
[git-rebase.io].
|
||||
|
||||
## Commit Messages
|
||||
|
||||
Please strive to write good commit messages. Here's some guidelines to follow:
|
||||
|
||||
The first line should be limited to 50 characters and should be a sentence that
|
||||
completes the thought [When applied, this commit will...] *"Implement
|
||||
cmd_move"* or *"Fix #742"* or *"Improve performance of arrange_windows on ARM"*
|
||||
or similar.
|
||||
cmd_move"* or *"Improve performance of arrange_windows on ARM"* or similar.
|
||||
|
||||
The subsequent lines should be separated from the subject line by a single
|
||||
blank line, and include optional details. In this you can give justification
|
||||
for the change, [reference Github
|
||||
issues](https://help.github.com/articles/closing-issues-via-commit-messages/),
|
||||
or explain some of the subtler details of your patch. This is important because
|
||||
when someone finds a line of code they don't understand later, they can use the
|
||||
`git blame` command to find out what the author was thinking when they wrote
|
||||
it. It's also easier to review your pull requests if they're separated into
|
||||
logical commits that have good commit messages and justify themselves in the
|
||||
extended commit description.
|
||||
for the change, [reference issues], or explain some of the subtler
|
||||
details of your patch. This is important because when someone finds a line of
|
||||
code they don't understand later, they can use the `git blame` command to find
|
||||
out what the author was thinking when they wrote it. It's also easier to review
|
||||
your merge requests if they're separated into logical commits that have good
|
||||
commit messages and justify themselves in the extended commit description.
|
||||
|
||||
As a good rule of thumb, anything you might put into the pull request
|
||||
description on Github is probably fair game for going into the extended commit
|
||||
As a good rule of thumb, anything you might put into the merge request
|
||||
description on GitLab is probably fair game for going into the extended commit
|
||||
message as well.
|
||||
|
||||
See [here](https://chris.beams.io/posts/git-commit/) for more details.
|
||||
See [How to Write a Git Commit Message] for more details.
|
||||
|
||||
## Code Review
|
||||
|
||||
|
@ -70,23 +101,29 @@ changes will typically see review from several people. Be prepared to receive
|
|||
some feedback - you may be asked to make changes to your work. Our code review
|
||||
process is:
|
||||
|
||||
1. **Triage** the pull request. Do the commit messages make sense? Is a test
|
||||
1. **Triage** the merge request. Do the commit messages make sense? Is a test
|
||||
plan necessary and/or present? Add anyone as reviewers that you think should
|
||||
be there (using the relevant GitHub feature, if you have the permissions, or
|
||||
be there (using the relevant GitLab feature, if you have the permissions, or
|
||||
with an @mention if necessary).
|
||||
2. **Review** the code. Look for code style violations, naming convention
|
||||
violations, buffer overflows, memory leaks, logic errors, non-portable code
|
||||
(including GNU-isms), etc. For significant changes to the public API, loop in
|
||||
a couple more people for discussion.
|
||||
3. **Execute** the test plan, if present.
|
||||
4. **Merge** the pull request when all reviewers approve.
|
||||
4. **Merge** the merge request when all reviewers approve.
|
||||
5. **File** follow-up tickets if appropriate.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Note that as a project hosted on freedesktop.org, wlroots follows its
|
||||
[Code of Conduct], based on the Contributor Covenant. Please conduct yourself
|
||||
in a respectful and civilized manner when communicating with community members
|
||||
on IRC and bug tracker.
|
||||
|
||||
## Style Reference
|
||||
|
||||
wlroots is written in C with a style similar to the [kernel
|
||||
style](https://www.kernel.org/doc/Documentation/process/coding-style.rst), but
|
||||
with a few notable differences.
|
||||
wlroots is written in C with a style similar to the [kernel style], but with a
|
||||
few notable differences.
|
||||
|
||||
Try to keep your code conforming to C11 and POSIX as much as possible, and do
|
||||
not use GNU extensions.
|
||||
|
@ -163,7 +200,7 @@ zeroed value and exit cleanly; this simplifies error handling a lot.
|
|||
### Error Codes
|
||||
|
||||
For functions not returning a value, they should return a (stdbool.h) bool to
|
||||
indicated if they succeeded or not.
|
||||
indicate whether they succeeded or not.
|
||||
|
||||
### Macros
|
||||
|
||||
|
@ -362,3 +399,11 @@ static void subsurface_handle_surface_destroy(struct wl_listener *listener,
|
|||
subsurface_destroy(subsurface);
|
||||
}
|
||||
```
|
||||
|
||||
[#sway-devel on Libera Chat]: https://web.libera.chat/gamja/?channels=#sway-devel
|
||||
[linear, "recipe" style]: https://www.bitsnbites.eu/git-history-work-log-vs-recipe/
|
||||
[git-rebase.io]: https://git-rebase.io/
|
||||
[reference issues]: https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically
|
||||
[Code of Conduct]: https://www.freedesktop.org/wiki/CodeOfConduct/
|
||||
[How to Write a Git Commit Message]: https://chris.beams.io/posts/git-commit/
|
||||
[kernel style]: https://www.kernel.org/doc/Documentation/process/coding-style.rst
|
||||
|
|
85
README.md
85
README.md
|
@ -1,26 +1,31 @@
|
|||
# blankie/wlroots - dkondor-upstream-pr-2551
|
||||
|
||||
This branch "frontports" https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/2551,
|
||||
should be updated when there's a new wlroots version and be unmaintained
|
||||
when the pull request above is merged
|
||||
|
||||
# wlroots
|
||||
|
||||
Pluggable, composable, unopinionated modules for building a
|
||||
[Wayland](http://wayland.freedesktop.org/) compositor; or about 50,000 lines of
|
||||
code you were going to write anyway.
|
||||
Pluggable, composable, unopinionated modules for building a [Wayland]
|
||||
compositor; or about 60,000 lines of code you were going to write anyway.
|
||||
|
||||
- wlroots provides backends that abstract the underlying display and input
|
||||
hardware, including KMS/DRM, libinput, Wayland, X11, and headless backends,
|
||||
plus any custom backends you choose to write, which can all be created or
|
||||
destroyed at runtime and used in concert with each other.
|
||||
hardware, including KMS/DRM, libinput, Wayland, X11, and headless backends,
|
||||
plus any custom backends you choose to write, which can all be created or
|
||||
destroyed at runtime and used in concert with each other.
|
||||
- wlroots provides unopinionated, mostly standalone implementations of many
|
||||
Wayland interfaces, both from wayland.xml and various protocol extensions.
|
||||
We also promote the standardization of portable extensions across
|
||||
many compositors.
|
||||
Wayland interfaces, both from wayland.xml and various protocol extensions.
|
||||
We also promote the standardization of portable extensions across
|
||||
many compositors.
|
||||
- wlroots provides several powerful, standalone, and optional tools that
|
||||
implement components common to many compositors, such as the arrangement of
|
||||
outputs in physical space.
|
||||
implement components common to many compositors, such as the arrangement of
|
||||
outputs in physical space.
|
||||
- wlroots provides an Xwayland abstraction that allows you to have excellent
|
||||
Xwayland support without worrying about writing your own X11 window manager
|
||||
on top of writing your compositor.
|
||||
Xwayland support without worrying about writing your own X11 window manager
|
||||
on top of writing your compositor.
|
||||
- wlroots provides a renderer abstraction that simple compositors can use to
|
||||
avoid writing GL code directly, but which steps out of the way when your
|
||||
needs demand custom rendering code.
|
||||
avoid writing GL code directly, but which steps out of the way when your
|
||||
needs demand custom rendering code.
|
||||
|
||||
wlroots implements a huge variety of Wayland compositor features and implements
|
||||
them *right*, so you can focus on the features that make your compositor
|
||||
|
@ -30,13 +35,12 @@ development tools - or any subset of these features you like, because all of
|
|||
them work independently of one another and freely compose with anything you want
|
||||
to implement yourself.
|
||||
|
||||
Check out our [wiki](https://github.com/swaywm/wlroots/wiki/Getting-started) to
|
||||
get started with wlroots.
|
||||
Check out our [wiki] to get started with wlroots. Join our IRC channel:
|
||||
[#sway-devel on Libera Chat].
|
||||
|
||||
wlroots is developed under the direction of the
|
||||
[sway](https://github.com/swaywm/sway) project. A variety of wrapper libraries
|
||||
[are available](https://github.com/swaywm) for using it with your favorite
|
||||
programming language.
|
||||
wlroots is developed under the direction of the [sway] project. A variety of
|
||||
[wrapper libraries] are available for using it with your favorite programming
|
||||
language.
|
||||
|
||||
## Building
|
||||
|
||||
|
@ -45,38 +49,41 @@ Install dependencies:
|
|||
* meson
|
||||
* wayland
|
||||
* wayland-protocols
|
||||
* EGL
|
||||
* GLESv2
|
||||
* EGL and GLESv2 (optional, for the GLES2 renderer)
|
||||
* Vulkan loader, headers and glslang (optional, for the Vulkan renderer)
|
||||
* libdrm
|
||||
* GBM
|
||||
* libinput
|
||||
* libinput (optional, for the libinput backend)
|
||||
* xkbcommon
|
||||
* udev
|
||||
* pixman
|
||||
* systemd (optional, for logind support)
|
||||
* elogind (optional, for logind support on systems without systemd)
|
||||
* [libseat]
|
||||
|
||||
If you choose to enable X11 support:
|
||||
|
||||
* xcb
|
||||
* xcb-composite
|
||||
* xcb-xfixes
|
||||
* xcb-xinput
|
||||
* xcb-image
|
||||
* xcb-render
|
||||
* x11-xcb
|
||||
* xcb-errors (optional, for improved error reporting)
|
||||
* x11-icccm (optional, for improved Xwayland introspection)
|
||||
* xwayland (build-time only, optional at runtime)
|
||||
* libxcb
|
||||
* libxcb-render-util
|
||||
* libxcb-wm
|
||||
* libxcb-errors (optional, for improved error reporting)
|
||||
|
||||
Run these commands:
|
||||
|
||||
meson build
|
||||
ninja -C build
|
||||
meson build/
|
||||
ninja -C build/
|
||||
|
||||
Install like so:
|
||||
|
||||
sudo ninja -C build install
|
||||
sudo ninja -C build/ install
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](https://github.com/swaywm/wlroots/blob/master/CONTRIBUTING.md).
|
||||
See [CONTRIBUTING.md].
|
||||
|
||||
[Wayland]: https://wayland.freedesktop.org/
|
||||
[wiki]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Getting-started
|
||||
[#sway-devel on Libera Chat]: https://web.libera.chat/gamja/?channels=#sway-devel
|
||||
[Sway]: https://github.com/swaywm/sway
|
||||
[wrapper libraries]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Projects-which-use-wlroots#wrapper-libraries
|
||||
[libseat]: https://git.sr.ht/~kennylevinsen/seatd
|
||||
[CONTRIBUTING.md]: https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/master/CONTRIBUTING.md
|
||||
|
|
|
@ -1,27 +1,39 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <libinput.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend/drm.h>
|
||||
|
||||
#include <wlr/backend/headless.h>
|
||||
#include <wlr/backend/interface.h>
|
||||
#include <wlr/backend/libinput.h>
|
||||
#include <wlr/backend/multi.h>
|
||||
#include <wlr/backend/noop.h>
|
||||
#include <wlr/backend/session.h>
|
||||
#include <wlr/backend/wayland.h>
|
||||
#include <wlr/config.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "backend/backend.h"
|
||||
#include "backend/multi.h"
|
||||
#include "render/allocator/allocator.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
#if WLR_HAS_DRM_BACKEND
|
||||
#include <wlr/backend/drm.h>
|
||||
#include "backend/drm/monitor.h"
|
||||
#endif
|
||||
|
||||
#if WLR_HAS_LIBINPUT_BACKEND
|
||||
#include <wlr/backend/libinput.h>
|
||||
#endif
|
||||
|
||||
#if WLR_HAS_X11_BACKEND
|
||||
#include <wlr/backend/x11.h>
|
||||
#endif
|
||||
|
||||
#define WAIT_SESSION_TIMEOUT 10000 // ms
|
||||
|
||||
void wlr_backend_init(struct wlr_backend *backend,
|
||||
const struct wlr_backend_impl *impl) {
|
||||
assert(backend);
|
||||
|
@ -31,6 +43,10 @@ void wlr_backend_init(struct wlr_backend *backend,
|
|||
wl_signal_init(&backend->events.new_output);
|
||||
}
|
||||
|
||||
void wlr_backend_finish(struct wlr_backend *backend) {
|
||||
wlr_signal_emit_safe(&backend->events.destroy, backend);
|
||||
}
|
||||
|
||||
bool wlr_backend_start(struct wlr_backend *backend) {
|
||||
if (backend->impl->start) {
|
||||
return backend->impl->start(backend);
|
||||
|
@ -50,13 +66,6 @@ void wlr_backend_destroy(struct wlr_backend *backend) {
|
|||
}
|
||||
}
|
||||
|
||||
struct wlr_renderer *wlr_backend_get_renderer(struct wlr_backend *backend) {
|
||||
if (backend->impl->get_renderer) {
|
||||
return backend->impl->get_renderer(backend);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend) {
|
||||
if (backend->impl->get_session) {
|
||||
return backend->impl->get_session(backend);
|
||||
|
@ -64,6 +73,52 @@ struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static uint64_t get_current_time_ms(void) {
|
||||
struct timespec ts = {0};
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
static struct wlr_session *session_create_and_wait(struct wl_display *disp) {
|
||||
struct wlr_session *session = wlr_session_create(disp);
|
||||
|
||||
if (!session) {
|
||||
wlr_log(WLR_ERROR, "Failed to start a session");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!session->active) {
|
||||
wlr_log(WLR_INFO, "Waiting for a session to become active");
|
||||
|
||||
uint64_t started_at = get_current_time_ms();
|
||||
uint64_t timeout = WAIT_SESSION_TIMEOUT;
|
||||
struct wl_event_loop *event_loop =
|
||||
wl_display_get_event_loop(session->display);
|
||||
|
||||
while (!session->active) {
|
||||
int ret = wl_event_loop_dispatch(event_loop, (int)timeout);
|
||||
if (ret < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to wait for session active: "
|
||||
"wl_event_loop_dispatch failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t now = get_current_time_ms();
|
||||
if (now >= started_at + WAIT_SESSION_TIMEOUT) {
|
||||
break;
|
||||
}
|
||||
timeout = started_at + WAIT_SESSION_TIMEOUT - now;
|
||||
}
|
||||
|
||||
if (!session->active) {
|
||||
wlr_log(WLR_ERROR, "Timeout waiting session to become active");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
clockid_t wlr_backend_get_presentation_clock(struct wlr_backend *backend) {
|
||||
if (backend->impl->get_presentation_clock) {
|
||||
return backend->impl->get_presentation_clock(backend);
|
||||
|
@ -71,6 +126,21 @@ clockid_t wlr_backend_get_presentation_clock(struct wlr_backend *backend) {
|
|||
return CLOCK_MONOTONIC;
|
||||
}
|
||||
|
||||
int wlr_backend_get_drm_fd(struct wlr_backend *backend) {
|
||||
if (!backend->impl->get_drm_fd) {
|
||||
return -1;
|
||||
}
|
||||
return backend->impl->get_drm_fd(backend);
|
||||
}
|
||||
|
||||
uint32_t backend_get_buffer_caps(struct wlr_backend *backend) {
|
||||
if (!backend->impl->get_buffer_caps) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return backend->impl->get_buffer_caps(backend);
|
||||
}
|
||||
|
||||
static size_t parse_outputs_env(const char *name) {
|
||||
const char *outputs_str = getenv(name);
|
||||
if (outputs_str == NULL) {
|
||||
|
@ -87,9 +157,8 @@ static size_t parse_outputs_env(const char *name) {
|
|||
return outputs;
|
||||
}
|
||||
|
||||
static struct wlr_backend *attempt_wl_backend(struct wl_display *display,
|
||||
wlr_renderer_create_func_t create_renderer_func) {
|
||||
struct wlr_backend *backend = wlr_wl_backend_create(display, NULL, create_renderer_func);
|
||||
static struct wlr_backend *attempt_wl_backend(struct wl_display *display) {
|
||||
struct wlr_backend *backend = wlr_wl_backend_create(display, NULL);
|
||||
if (backend == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -104,8 +173,8 @@ static struct wlr_backend *attempt_wl_backend(struct wl_display *display,
|
|||
|
||||
#if WLR_HAS_X11_BACKEND
|
||||
static struct wlr_backend *attempt_x11_backend(struct wl_display *display,
|
||||
const char *x11_display, wlr_renderer_create_func_t create_renderer_func) {
|
||||
struct wlr_backend *backend = wlr_x11_backend_create(display, x11_display, create_renderer_func);
|
||||
const char *x11_display) {
|
||||
struct wlr_backend *backend = wlr_x11_backend_create(display, x11_display);
|
||||
if (backend == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -120,8 +189,8 @@ static struct wlr_backend *attempt_x11_backend(struct wl_display *display,
|
|||
#endif
|
||||
|
||||
static struct wlr_backend *attempt_headless_backend(
|
||||
struct wl_display *display, wlr_renderer_create_func_t create_renderer_func) {
|
||||
struct wlr_backend *backend = wlr_headless_backend_create(display, create_renderer_func);
|
||||
struct wl_display *display) {
|
||||
struct wlr_backend *backend = wlr_headless_backend_create(display);
|
||||
if (backend == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -134,33 +203,29 @@ static struct wlr_backend *attempt_headless_backend(
|
|||
return backend;
|
||||
}
|
||||
|
||||
static struct wlr_backend *attempt_noop_backend(struct wl_display *display) {
|
||||
struct wlr_backend *backend = wlr_noop_backend_create(display);
|
||||
if (backend == NULL) {
|
||||
#if WLR_HAS_DRM_BACKEND
|
||||
static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
|
||||
struct wlr_backend *backend, struct wlr_session *session) {
|
||||
struct wlr_device *gpus[8];
|
||||
ssize_t num_gpus = wlr_session_find_gpus(session, 8, gpus);
|
||||
if (num_gpus < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to find GPUs");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t outputs = parse_outputs_env("WLR_NOOP_OUTPUTS");
|
||||
for (size_t i = 0; i < outputs; ++i) {
|
||||
wlr_noop_add_output(backend);
|
||||
if (num_gpus == 0) {
|
||||
wlr_log(WLR_ERROR, "Found 0 GPUs, cannot create backend");
|
||||
return NULL;
|
||||
} else {
|
||||
wlr_log(WLR_INFO, "Found %zu GPUs", num_gpus);
|
||||
}
|
||||
|
||||
return backend;
|
||||
}
|
||||
|
||||
static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
|
||||
struct wlr_backend *backend, struct wlr_session *session,
|
||||
wlr_renderer_create_func_t create_renderer_func) {
|
||||
int gpus[8];
|
||||
size_t num_gpus = wlr_session_find_gpus(session, 8, gpus);
|
||||
struct wlr_backend *primary_drm = NULL;
|
||||
wlr_log(WLR_INFO, "Found %zu GPUs", num_gpus);
|
||||
|
||||
for (size_t i = 0; i < num_gpus; ++i) {
|
||||
for (size_t i = 0; i < (size_t)num_gpus; ++i) {
|
||||
struct wlr_backend *drm = wlr_drm_backend_create(display, session,
|
||||
gpus[i], primary_drm, create_renderer_func);
|
||||
gpus[i], primary_drm);
|
||||
if (!drm) {
|
||||
wlr_log(WLR_ERROR, "Failed to open DRM device %d", gpus[i]);
|
||||
wlr_log(WLR_ERROR, "Failed to create DRM backend");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -170,46 +235,56 @@ static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
|
|||
|
||||
wlr_multi_backend_add(backend, drm);
|
||||
}
|
||||
if (!primary_drm) {
|
||||
wlr_log(WLR_ERROR, "Could not successfully create backend on any GPU");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return primary_drm;
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct wlr_backend *attempt_backend_by_name(struct wl_display *display,
|
||||
struct wlr_backend *backend, struct wlr_session **session,
|
||||
const char *name, wlr_renderer_create_func_t create_renderer_func) {
|
||||
static bool attempt_backend_by_name(struct wl_display *display,
|
||||
struct wlr_multi_backend *multi, char *name) {
|
||||
struct wlr_backend *backend = NULL;
|
||||
if (strcmp(name, "wayland") == 0) {
|
||||
return attempt_wl_backend(display, create_renderer_func);
|
||||
backend = attempt_wl_backend(display);
|
||||
#if WLR_HAS_X11_BACKEND
|
||||
} else if (strcmp(name, "x11") == 0) {
|
||||
return attempt_x11_backend(display, NULL, create_renderer_func);
|
||||
backend = attempt_x11_backend(display, NULL);
|
||||
#endif
|
||||
} else if (strcmp(name, "headless") == 0) {
|
||||
return attempt_headless_backend(display, create_renderer_func);
|
||||
} else if (strcmp(name, "noop") == 0) {
|
||||
return attempt_noop_backend(display);
|
||||
backend = attempt_headless_backend(display);
|
||||
} else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) {
|
||||
// DRM and libinput need a session
|
||||
if (!*session) {
|
||||
*session = wlr_session_create(display);
|
||||
if (!*session) {
|
||||
if (multi->session == NULL) {
|
||||
multi->session = session_create_and_wait(display);
|
||||
if (multi->session == NULL) {
|
||||
wlr_log(WLR_ERROR, "failed to start a session");
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(name, "libinput") == 0) {
|
||||
return wlr_libinput_backend_create(display, *session);
|
||||
#if WLR_HAS_LIBINPUT_BACKEND
|
||||
backend = wlr_libinput_backend_create(display, multi->session);
|
||||
#endif
|
||||
} else {
|
||||
return attempt_drm_backend(display, backend, *session, create_renderer_func);
|
||||
#if WLR_HAS_DRM_BACKEND
|
||||
// attempt_drm_backend adds the multi drm backends itself
|
||||
return attempt_drm_backend(display, &multi->backend,
|
||||
multi->session) != NULL;
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
wlr_log(WLR_ERROR, "unrecognized backend '%s'", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
wlr_log(WLR_ERROR, "unrecognized backend '%s'", name);
|
||||
return NULL;
|
||||
return wlr_multi_backend_add(&multi->backend, backend);
|
||||
}
|
||||
|
||||
struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
|
||||
wlr_renderer_create_func_t create_renderer_func) {
|
||||
struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
|
||||
struct wlr_backend *backend = wlr_multi_backend_create(display);
|
||||
struct wlr_multi_backend *multi = (struct wlr_multi_backend *)backend;
|
||||
if (!backend) {
|
||||
|
@ -219,6 +294,9 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
|
|||
|
||||
char *names = getenv("WLR_BACKENDS");
|
||||
if (names) {
|
||||
wlr_log(WLR_INFO, "Loading user-specified backends due to WLR_BACKENDS: %s",
|
||||
names);
|
||||
|
||||
names = strdup(names);
|
||||
if (names == NULL) {
|
||||
wlr_log(WLR_ERROR, "allocation failed");
|
||||
|
@ -229,17 +307,7 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
|
|||
char *saveptr;
|
||||
char *name = strtok_r(names, ",", &saveptr);
|
||||
while (name != NULL) {
|
||||
struct wlr_backend *subbackend = attempt_backend_by_name(display,
|
||||
backend, &multi->session, name, create_renderer_func);
|
||||
if (subbackend == NULL) {
|
||||
wlr_log(WLR_ERROR, "failed to start backend '%s'", name);
|
||||
wlr_session_destroy(multi->session);
|
||||
wlr_backend_destroy(backend);
|
||||
free(names);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!wlr_multi_backend_add(backend, subbackend)) {
|
||||
if (!attempt_backend_by_name(display, multi, name)) {
|
||||
wlr_log(WLR_ERROR, "failed to add backend '%s'", name);
|
||||
wlr_session_destroy(multi->session);
|
||||
wlr_backend_destroy(backend);
|
||||
|
@ -255,8 +323,7 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
|
|||
}
|
||||
|
||||
if (getenv("WAYLAND_DISPLAY") || getenv("WAYLAND_SOCKET")) {
|
||||
struct wlr_backend *wl_backend = attempt_wl_backend(display,
|
||||
create_renderer_func);
|
||||
struct wlr_backend *wl_backend = attempt_wl_backend(display);
|
||||
if (!wl_backend) {
|
||||
goto error;
|
||||
}
|
||||
|
@ -269,7 +336,7 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
|
|||
const char *x11_display = getenv("DISPLAY");
|
||||
if (x11_display) {
|
||||
struct wlr_backend *x11_backend =
|
||||
attempt_x11_backend(display, x11_display, create_renderer_func);
|
||||
attempt_x11_backend(display, x11_display);
|
||||
if (!x11_backend) {
|
||||
goto error;
|
||||
}
|
||||
|
@ -280,13 +347,14 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
|
|||
#endif
|
||||
|
||||
// Attempt DRM+libinput
|
||||
multi->session = wlr_session_create(display);
|
||||
multi->session = session_create_and_wait(display);
|
||||
if (!multi->session) {
|
||||
wlr_log(WLR_ERROR, "Failed to start a DRM session");
|
||||
wlr_backend_destroy(backend);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if WLR_HAS_LIBINPUT_BACKEND
|
||||
struct wlr_backend *libinput = wlr_libinput_backend_create(display,
|
||||
multi->session);
|
||||
if (!libinput) {
|
||||
|
@ -296,18 +364,35 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
|
|||
return NULL;
|
||||
}
|
||||
wlr_multi_backend_add(backend, libinput);
|
||||
#else
|
||||
const char *no_devs = getenv("WLR_LIBINPUT_NO_DEVICES");
|
||||
if (no_devs && strcmp(no_devs, "1") == 0) {
|
||||
wlr_log(WLR_INFO, "WLR_LIBINPUT_NO_DEVICES is set, "
|
||||
"starting without libinput backend");
|
||||
} else {
|
||||
wlr_log(WLR_ERROR, "libinput support is not compiled in, "
|
||||
"refusing to start");
|
||||
wlr_log(WLR_ERROR, "Set WLR_LIBINPUT_NO_DEVICES=1 to suppress this check");
|
||||
wlr_session_destroy(multi->session);
|
||||
wlr_backend_destroy(backend);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct wlr_backend *primary_drm = attempt_drm_backend(display, backend,
|
||||
multi->session, create_renderer_func);
|
||||
#if WLR_HAS_DRM_BACKEND
|
||||
struct wlr_backend *primary_drm =
|
||||
attempt_drm_backend(display, backend, multi->session);
|
||||
if (!primary_drm) {
|
||||
wlr_log(WLR_ERROR, "Failed to open any DRM device");
|
||||
wlr_backend_destroy(libinput);
|
||||
wlr_session_destroy(multi->session);
|
||||
wlr_backend_destroy(backend);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
drm_backend_monitor_create(backend, primary_drm, multi->session);
|
||||
|
||||
return backend;
|
||||
#endif
|
||||
|
||||
error:
|
||||
wlr_backend_destroy(backend);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#include <gbm.h>
|
||||
#include <stdlib.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <xf86drm.h>
|
||||
|
@ -25,16 +24,16 @@ static void atomic_begin(struct atomic *atom) {
|
|||
|
||||
static bool atomic_commit(struct atomic *atom,
|
||||
struct wlr_drm_connector *conn, uint32_t flags) {
|
||||
struct wlr_drm_backend *drm =
|
||||
get_drm_backend_from_backend(conn->output.backend);
|
||||
struct wlr_drm_backend *drm = conn->backend;
|
||||
if (atom->failed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int ret = drmModeAtomicCommit(drm->fd, atom->req, flags, drm);
|
||||
if (ret) {
|
||||
wlr_log_errno(WLR_ERROR, "%s: Atomic %s failed (%s)",
|
||||
conn->output.name,
|
||||
if (ret != 0) {
|
||||
wlr_drm_conn_log_errno(conn,
|
||||
(flags & DRM_MODE_ATOMIC_TEST_ONLY) ? WLR_DEBUG : WLR_ERROR,
|
||||
"Atomic %s failed (%s)",
|
||||
(flags & DRM_MODE_ATOMIC_TEST_ONLY) ? "test" : "commit",
|
||||
(flags & DRM_MODE_ATOMIC_ALLOW_MODESET) ? "modeset" : "pageflip");
|
||||
return false;
|
||||
|
@ -55,13 +54,14 @@ static void atomic_add(struct atomic *atom, uint32_t id, uint32_t prop, uint64_t
|
|||
}
|
||||
|
||||
static bool create_mode_blob(struct wlr_drm_backend *drm,
|
||||
struct wlr_drm_crtc *crtc, uint32_t *blob_id) {
|
||||
if (!crtc->pending.active) {
|
||||
struct wlr_drm_connector *conn,
|
||||
const struct wlr_drm_connector_state *state, uint32_t *blob_id) {
|
||||
if (!state->active) {
|
||||
*blob_id = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (drmModeCreatePropertyBlob(drm->fd, &crtc->pending.mode->drm_mode,
|
||||
if (drmModeCreatePropertyBlob(drm->fd, &state->mode,
|
||||
sizeof(drmModeModeInfo), blob_id)) {
|
||||
wlr_log_errno(WLR_ERROR, "Unable to create mode property blob");
|
||||
return false;
|
||||
|
@ -136,24 +136,22 @@ static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm,
|
|||
uint32_t id = plane->id;
|
||||
const union wlr_drm_plane_props *props = &plane->props;
|
||||
struct wlr_drm_fb *fb = plane_get_next_fb(plane);
|
||||
struct gbm_bo *bo = drm_fb_acquire(fb, drm, &plane->mgpu_surf);
|
||||
if (!bo) {
|
||||
if (fb == NULL) {
|
||||
wlr_log(WLR_ERROR, "Failed to acquire FB");
|
||||
goto error;
|
||||
}
|
||||
|
||||
uint32_t fb_id = get_fb_for_bo(bo, drm->addfb2_modifiers);
|
||||
if (!fb_id) {
|
||||
goto error;
|
||||
}
|
||||
uint32_t width = fb->wlr_buf->width;
|
||||
uint32_t height = fb->wlr_buf->height;
|
||||
|
||||
// The src_* properties are in 16.16 fixed point
|
||||
atomic_add(atom, id, props->src_x, 0);
|
||||
atomic_add(atom, id, props->src_y, 0);
|
||||
atomic_add(atom, id, props->src_w, (uint64_t)plane->surf.width << 16);
|
||||
atomic_add(atom, id, props->src_h, (uint64_t)plane->surf.height << 16);
|
||||
atomic_add(atom, id, props->crtc_w, plane->surf.width);
|
||||
atomic_add(atom, id, props->crtc_h, plane->surf.height);
|
||||
atomic_add(atom, id, props->fb_id, fb_id);
|
||||
atomic_add(atom, id, props->src_w, (uint64_t)width << 16);
|
||||
atomic_add(atom, id, props->src_h, (uint64_t)height << 16);
|
||||
atomic_add(atom, id, props->crtc_w, width);
|
||||
atomic_add(atom, id, props->crtc_h, height);
|
||||
atomic_add(atom, id, props->fb_id, fb->id);
|
||||
atomic_add(atom, id, props->crtc_id, crtc_id);
|
||||
atomic_add(atom, id, props->crtc_x, (uint64_t)x);
|
||||
atomic_add(atom, id, props->crtc_y, (uint64_t)y);
|
||||
|
@ -165,63 +163,82 @@ error:
|
|||
atom->failed = true;
|
||||
}
|
||||
|
||||
static bool atomic_crtc_commit(struct wlr_drm_backend *drm,
|
||||
struct wlr_drm_connector *conn, uint32_t flags) {
|
||||
static bool atomic_crtc_commit(struct wlr_drm_connector *conn,
|
||||
const struct wlr_drm_connector_state *state, uint32_t flags,
|
||||
bool test_only) {
|
||||
struct wlr_drm_backend *drm = conn->backend;
|
||||
struct wlr_output *output = &conn->output;
|
||||
struct wlr_drm_crtc *crtc = conn->crtc;
|
||||
|
||||
bool modeset = state->modeset;
|
||||
bool active = state->active;
|
||||
|
||||
uint32_t mode_id = crtc->mode_id;
|
||||
if (crtc->pending_modeset) {
|
||||
if (!create_mode_blob(drm, crtc, &mode_id)) {
|
||||
if (modeset) {
|
||||
if (!create_mode_blob(drm, conn, state, &mode_id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t gamma_lut = crtc->gamma_lut;
|
||||
if (output->pending.committed & WLR_OUTPUT_STATE_GAMMA_LUT) {
|
||||
if (state->base->committed & WLR_OUTPUT_STATE_GAMMA_LUT) {
|
||||
// Fallback to legacy gamma interface when gamma properties are not
|
||||
// available (can happen on older Intel GPUs that support gamma but not
|
||||
// degamma).
|
||||
if (crtc->props.gamma_lut == 0) {
|
||||
if (!drm_legacy_crtc_set_gamma(drm, crtc,
|
||||
output->pending.gamma_lut_size,
|
||||
output->pending.gamma_lut)) {
|
||||
state->base->gamma_lut_size,
|
||||
state->base->gamma_lut)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!create_gamma_lut_blob(drm, output->pending.gamma_lut_size,
|
||||
output->pending.gamma_lut, &gamma_lut)) {
|
||||
if (!create_gamma_lut_blob(drm, state->base->gamma_lut_size,
|
||||
state->base->gamma_lut, &gamma_lut)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t fb_damage_clips = 0;
|
||||
if ((state->base->committed & WLR_OUTPUT_STATE_DAMAGE) &&
|
||||
pixman_region32_not_empty((pixman_region32_t *)&state->base->damage) &&
|
||||
crtc->primary->props.fb_damage_clips != 0) {
|
||||
int rects_len;
|
||||
const pixman_box32_t *rects = pixman_region32_rectangles(
|
||||
(pixman_region32_t *)&state->base->damage, &rects_len);
|
||||
if (drmModeCreatePropertyBlob(drm->fd, rects,
|
||||
sizeof(*rects) * rects_len, &fb_damage_clips) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to create FB_DAMAGE_CLIPS property blob");
|
||||
}
|
||||
}
|
||||
|
||||
bool prev_vrr_enabled =
|
||||
output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED;
|
||||
bool vrr_enabled = prev_vrr_enabled;
|
||||
if ((output->pending.committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) &&
|
||||
if ((state->base->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) &&
|
||||
drm_connector_supports_vrr(conn)) {
|
||||
vrr_enabled = output->pending.adaptive_sync_enabled;
|
||||
vrr_enabled = state->base->adaptive_sync_enabled;
|
||||
}
|
||||
|
||||
if (crtc->pending_modeset) {
|
||||
if (test_only) {
|
||||
flags |= DRM_MODE_ATOMIC_TEST_ONLY;
|
||||
}
|
||||
if (modeset) {
|
||||
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
||||
} else {
|
||||
} else if (!test_only) {
|
||||
flags |= DRM_MODE_ATOMIC_NONBLOCK;
|
||||
}
|
||||
|
||||
struct atomic atom;
|
||||
atomic_begin(&atom);
|
||||
atomic_add(&atom, conn->id, conn->props.crtc_id,
|
||||
crtc->pending.active ? crtc->id : 0);
|
||||
if (crtc->pending_modeset && crtc->pending.active &&
|
||||
conn->props.link_status != 0) {
|
||||
atomic_add(&atom, conn->id, conn->props.crtc_id, active ? crtc->id : 0);
|
||||
if (modeset && active && conn->props.link_status != 0) {
|
||||
atomic_add(&atom, conn->id, conn->props.link_status,
|
||||
DRM_MODE_LINK_STATUS_GOOD);
|
||||
}
|
||||
atomic_add(&atom, crtc->id, crtc->props.mode_id, mode_id);
|
||||
atomic_add(&atom, crtc->id, crtc->props.active, crtc->pending.active);
|
||||
if (crtc->pending.active) {
|
||||
atomic_add(&atom, crtc->id, crtc->props.active, active);
|
||||
if (active) {
|
||||
if (crtc->props.gamma_lut != 0) {
|
||||
atomic_add(&atom, crtc->id, crtc->props.gamma_lut, gamma_lut);
|
||||
}
|
||||
|
@ -229,6 +246,10 @@ static bool atomic_crtc_commit(struct wlr_drm_backend *drm,
|
|||
atomic_add(&atom, crtc->id, crtc->props.vrr_enabled, vrr_enabled);
|
||||
}
|
||||
set_plane_props(&atom, drm, crtc->primary, crtc->id, 0, 0);
|
||||
if (crtc->primary->props.fb_damage_clips != 0) {
|
||||
atomic_add(&atom, crtc->primary->id,
|
||||
crtc->primary->props.fb_damage_clips, fb_damage_clips);
|
||||
}
|
||||
if (crtc->cursor) {
|
||||
if (drm_connector_is_cursor_visible(conn)) {
|
||||
set_plane_props(&atom, drm, crtc->cursor, crtc->id,
|
||||
|
@ -247,7 +268,7 @@ static bool atomic_crtc_commit(struct wlr_drm_backend *drm,
|
|||
bool ok = atomic_commit(&atom, conn, flags);
|
||||
atomic_finish(&atom);
|
||||
|
||||
if (ok && !(flags & DRM_MODE_ATOMIC_TEST_ONLY)) {
|
||||
if (ok && !test_only) {
|
||||
commit_blob(drm, &crtc->mode_id, mode_id);
|
||||
commit_blob(drm, &crtc->gamma_lut, gamma_lut);
|
||||
|
||||
|
@ -255,14 +276,19 @@ static bool atomic_crtc_commit(struct wlr_drm_backend *drm,
|
|||
output->adaptive_sync_status = vrr_enabled ?
|
||||
WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED :
|
||||
WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED;
|
||||
wlr_log(WLR_DEBUG, "VRR %s on connector '%s'",
|
||||
vrr_enabled ? "enabled" : "disabled", output->name);
|
||||
wlr_drm_conn_log(conn, WLR_DEBUG, "VRR %s",
|
||||
vrr_enabled ? "enabled" : "disabled");
|
||||
}
|
||||
} else {
|
||||
rollback_blob(drm, &crtc->mode_id, mode_id);
|
||||
rollback_blob(drm, &crtc->gamma_lut, gamma_lut);
|
||||
}
|
||||
|
||||
if (fb_damage_clips != 0 &&
|
||||
drmModeDestroyPropertyBlob(drm->fd, fb_damage_clips) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to destroy FB_DAMAGE_CLIPS property blob");
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <drm_fourcc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -8,8 +9,6 @@
|
|||
#include <wlr/backend/interface.h>
|
||||
#include <wlr/backend/session.h>
|
||||
#include <wlr/interfaces/wlr_output.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include <wlr/types/wlr_list.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <xf86drm.h>
|
||||
#include "backend/drm/drm.h"
|
||||
|
@ -23,7 +22,7 @@ struct wlr_drm_backend *get_drm_backend_from_backend(
|
|||
|
||||
static bool backend_start(struct wlr_backend *backend) {
|
||||
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
||||
scan_drm_connectors(drm);
|
||||
scan_drm_connectors(drm, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -34,36 +33,35 @@ static void backend_destroy(struct wlr_backend *backend) {
|
|||
|
||||
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
||||
|
||||
restore_drm_outputs(drm);
|
||||
|
||||
struct wlr_drm_connector *conn, *next;
|
||||
wl_list_for_each_safe(conn, next, &drm->outputs, link) {
|
||||
wlr_output_destroy(&conn->output);
|
||||
destroy_drm_connector(conn);
|
||||
}
|
||||
|
||||
wlr_signal_emit_safe(&backend->events.destroy, backend);
|
||||
wlr_backend_finish(backend);
|
||||
|
||||
struct wlr_drm_fb *fb, *fb_tmp;
|
||||
wl_list_for_each_safe(fb, fb_tmp, &drm->fbs, link) {
|
||||
drm_fb_destroy(fb);
|
||||
}
|
||||
|
||||
wl_list_remove(&drm->display_destroy.link);
|
||||
wl_list_remove(&drm->session_destroy.link);
|
||||
wl_list_remove(&drm->session_signal.link);
|
||||
wl_list_remove(&drm->drm_invalidated.link);
|
||||
|
||||
finish_drm_resources(drm);
|
||||
finish_drm_renderer(&drm->renderer);
|
||||
wlr_session_close_file(drm->session, drm->fd);
|
||||
wl_event_source_remove(drm->drm_event);
|
||||
free(drm);
|
||||
}
|
||||
|
||||
static struct wlr_renderer *backend_get_renderer(
|
||||
struct wlr_backend *backend) {
|
||||
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
||||
wl_list_remove(&drm->session_active.link);
|
||||
wl_list_remove(&drm->parent_destroy.link);
|
||||
wl_list_remove(&drm->dev_change.link);
|
||||
wl_list_remove(&drm->dev_remove.link);
|
||||
|
||||
if (drm->parent) {
|
||||
return drm->parent->renderer.wlr_rend;
|
||||
} else {
|
||||
return drm->renderer.wlr_rend;
|
||||
finish_drm_renderer(&drm->mgpu_renderer);
|
||||
}
|
||||
|
||||
finish_drm_resources(drm);
|
||||
|
||||
free(drm->name);
|
||||
wlr_session_close_file(drm->session, drm->dev);
|
||||
wl_event_source_remove(drm->drm_event);
|
||||
free(drm);
|
||||
}
|
||||
|
||||
static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) {
|
||||
|
@ -71,48 +69,89 @@ static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) {
|
|||
return drm->clock;
|
||||
}
|
||||
|
||||
static struct wlr_backend_impl backend_impl = {
|
||||
static int backend_get_drm_fd(struct wlr_backend *backend) {
|
||||
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
||||
|
||||
if (drm->parent) {
|
||||
return drm->parent->fd;
|
||||
} else {
|
||||
return drm->fd;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t drm_backend_get_buffer_caps(struct wlr_backend *backend) {
|
||||
return WLR_BUFFER_CAP_DMABUF;
|
||||
}
|
||||
|
||||
static const struct wlr_backend_impl backend_impl = {
|
||||
.start = backend_start,
|
||||
.destroy = backend_destroy,
|
||||
.get_renderer = backend_get_renderer,
|
||||
.get_presentation_clock = backend_get_presentation_clock,
|
||||
.get_drm_fd = backend_get_drm_fd,
|
||||
.get_buffer_caps = drm_backend_get_buffer_caps,
|
||||
};
|
||||
|
||||
bool wlr_backend_is_drm(struct wlr_backend *b) {
|
||||
return b->impl == &backend_impl;
|
||||
}
|
||||
|
||||
static void session_signal(struct wl_listener *listener, void *data) {
|
||||
static void handle_session_active(struct wl_listener *listener, void *data) {
|
||||
struct wlr_drm_backend *drm =
|
||||
wl_container_of(listener, drm, session_signal);
|
||||
struct wlr_session *session = data;
|
||||
wl_container_of(listener, drm, session_active);
|
||||
struct wlr_session *session = drm->session;
|
||||
|
||||
if (session->active) {
|
||||
wlr_log(WLR_INFO, "DRM fd resumed");
|
||||
scan_drm_connectors(drm);
|
||||
scan_drm_connectors(drm, NULL);
|
||||
|
||||
struct wlr_drm_connector *conn;
|
||||
wl_list_for_each(conn, &drm->outputs, link){
|
||||
wl_list_for_each(conn, &drm->outputs, link) {
|
||||
struct wlr_output_mode *mode = NULL;
|
||||
uint32_t committed = WLR_OUTPUT_STATE_ENABLED;
|
||||
if (conn->output.enabled && conn->output.current_mode != NULL) {
|
||||
drm_connector_set_mode(conn, conn->output.current_mode);
|
||||
} else {
|
||||
drm_connector_set_mode(conn, NULL);
|
||||
committed |= WLR_OUTPUT_STATE_MODE;
|
||||
mode = conn->output.current_mode;
|
||||
}
|
||||
struct wlr_output_state state = {
|
||||
.committed = committed,
|
||||
.enabled = mode != NULL,
|
||||
.mode_type = WLR_OUTPUT_STATE_MODE_FIXED,
|
||||
.mode = mode,
|
||||
};
|
||||
drm_connector_commit_state(conn, &state);
|
||||
}
|
||||
} else {
|
||||
wlr_log(WLR_INFO, "DRM fd paused");
|
||||
}
|
||||
}
|
||||
|
||||
static void drm_invalidated(struct wl_listener *listener, void *data) {
|
||||
struct wlr_drm_backend *drm =
|
||||
wl_container_of(listener, drm, drm_invalidated);
|
||||
static void handle_dev_change(struct wl_listener *listener, void *data) {
|
||||
struct wlr_drm_backend *drm = wl_container_of(listener, drm, dev_change);
|
||||
struct wlr_device_change_event *change = data;
|
||||
|
||||
char *name = drmGetDeviceNameFromFd2(drm->fd);
|
||||
wlr_log(WLR_DEBUG, "%s invalidated", name);
|
||||
free(name);
|
||||
if (!drm->session->active) {
|
||||
return;
|
||||
}
|
||||
|
||||
scan_drm_connectors(drm);
|
||||
switch (change->type) {
|
||||
case WLR_DEVICE_HOTPLUG:
|
||||
wlr_log(WLR_DEBUG, "Received hotplug event for %s", drm->name);
|
||||
scan_drm_connectors(drm, &change->hotplug);
|
||||
break;
|
||||
case WLR_DEVICE_LEASE:
|
||||
wlr_log(WLR_DEBUG, "Received lease event for %s", drm->name);
|
||||
scan_drm_leases(drm);
|
||||
break;
|
||||
default:
|
||||
wlr_log(WLR_DEBUG, "Received unknown change event for %s", drm->name);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_dev_remove(struct wl_listener *listener, void *data) {
|
||||
struct wlr_drm_backend *drm = wl_container_of(listener, drm, dev_remove);
|
||||
|
||||
wlr_log(WLR_INFO, "Destroying DRM backend for %s", drm->name);
|
||||
backend_destroy(&drm->backend);
|
||||
}
|
||||
|
||||
static void handle_session_destroy(struct wl_listener *listener, void *data) {
|
||||
|
@ -127,16 +166,21 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
|||
backend_destroy(&drm->backend);
|
||||
}
|
||||
|
||||
static void handle_parent_destroy(struct wl_listener *listener, void *data) {
|
||||
struct wlr_drm_backend *drm =
|
||||
wl_container_of(listener, drm, parent_destroy);
|
||||
backend_destroy(&drm->backend);
|
||||
}
|
||||
|
||||
struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
|
||||
struct wlr_session *session, int gpu_fd, struct wlr_backend *parent,
|
||||
wlr_renderer_create_func_t create_renderer_func) {
|
||||
assert(display && session && gpu_fd >= 0);
|
||||
struct wlr_session *session, struct wlr_device *dev,
|
||||
struct wlr_backend *parent) {
|
||||
assert(display && session && dev);
|
||||
assert(!parent || wlr_backend_is_drm(parent));
|
||||
|
||||
char *name = drmGetDeviceNameFromFd2(gpu_fd);
|
||||
drmVersion *version = drmGetVersion(gpu_fd);
|
||||
char *name = drmGetDeviceNameFromFd2(dev->fd);
|
||||
drmVersion *version = drmGetVersion(dev->fd);
|
||||
wlr_log(WLR_INFO, "Initializing DRM backend for %s (%s)", name, version->name);
|
||||
free(name);
|
||||
drmFreeVersion(version);
|
||||
|
||||
struct wlr_drm_backend *drm = calloc(1, sizeof(struct wlr_drm_backend));
|
||||
|
@ -147,28 +191,40 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
|
|||
wlr_backend_init(&drm->backend, &backend_impl);
|
||||
|
||||
drm->session = session;
|
||||
wl_list_init(&drm->fbs);
|
||||
wl_list_init(&drm->outputs);
|
||||
|
||||
drm->fd = gpu_fd;
|
||||
drm->dev = dev;
|
||||
drm->fd = dev->fd;
|
||||
drm->name = name;
|
||||
|
||||
if (parent != NULL) {
|
||||
drm->parent = get_drm_backend_from_backend(parent);
|
||||
|
||||
drm->parent_destroy.notify = handle_parent_destroy;
|
||||
wl_signal_add(&parent->events.destroy, &drm->parent_destroy);
|
||||
} else {
|
||||
wl_list_init(&drm->parent_destroy.link);
|
||||
}
|
||||
|
||||
drm->drm_invalidated.notify = drm_invalidated;
|
||||
wlr_session_signal_add(session, gpu_fd, &drm->drm_invalidated);
|
||||
drm->dev_change.notify = handle_dev_change;
|
||||
wl_signal_add(&dev->events.change, &drm->dev_change);
|
||||
|
||||
drm->dev_remove.notify = handle_dev_remove;
|
||||
wl_signal_add(&dev->events.remove, &drm->dev_remove);
|
||||
|
||||
drm->display = display;
|
||||
struct wl_event_loop *event_loop = wl_display_get_event_loop(display);
|
||||
|
||||
struct wl_event_loop *event_loop = wl_display_get_event_loop(display);
|
||||
drm->drm_event = wl_event_loop_add_fd(event_loop, drm->fd,
|
||||
WL_EVENT_READABLE, handle_drm_event, NULL);
|
||||
WL_EVENT_READABLE, handle_drm_event, drm);
|
||||
if (!drm->drm_event) {
|
||||
wlr_log(WLR_ERROR, "Failed to create DRM event source");
|
||||
goto error_fd;
|
||||
}
|
||||
|
||||
drm->session_signal.notify = session_signal;
|
||||
wl_signal_add(&session->session_signal, &drm->session_signal);
|
||||
drm->session_active.notify = handle_session_active;
|
||||
wl_signal_add(&session->events.active, &drm->session_active);
|
||||
|
||||
if (!check_drm_features(drm)) {
|
||||
goto error_event;
|
||||
|
@ -178,9 +234,32 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
|
|||
goto error_event;
|
||||
}
|
||||
|
||||
if (!init_drm_renderer(drm, &drm->renderer, create_renderer_func)) {
|
||||
wlr_log(WLR_ERROR, "Failed to initialize renderer");
|
||||
goto error_event;
|
||||
if (drm->parent) {
|
||||
if (!init_drm_renderer(drm, &drm->mgpu_renderer)) {
|
||||
wlr_log(WLR_ERROR, "Failed to initialize renderer");
|
||||
goto error_resources;
|
||||
}
|
||||
|
||||
// We'll perform a multi-GPU copy for all submitted buffers, we need
|
||||
// to be able to texture from them
|
||||
struct wlr_renderer *renderer = drm->mgpu_renderer.wlr_rend;
|
||||
const struct wlr_drm_format_set *texture_formats =
|
||||
wlr_renderer_get_dmabuf_texture_formats(renderer);
|
||||
if (texture_formats == NULL) {
|
||||
wlr_log(WLR_ERROR, "Failed to query renderer texture formats");
|
||||
goto error_mgpu_renderer;
|
||||
}
|
||||
|
||||
// Force a linear layout. In case explicit modifiers aren't supported,
|
||||
// the meaning of implicit modifiers changes from one GPU to the other.
|
||||
// In case explicit modifiers are supported, we still have no guarantee
|
||||
// that the buffer producer will support these, so they might fallback
|
||||
// to implicit modifiers.
|
||||
for (size_t i = 0; i < texture_formats->len; i++) {
|
||||
const struct wlr_drm_format *fmt = texture_formats->formats[i];
|
||||
wlr_drm_format_set_add(&drm->mgpu_formats, fmt->format,
|
||||
DRM_FORMAT_MOD_LINEAR);
|
||||
}
|
||||
}
|
||||
|
||||
drm->session_destroy.notify = handle_session_destroy;
|
||||
|
@ -191,11 +270,18 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
|
|||
|
||||
return &drm->backend;
|
||||
|
||||
error_mgpu_renderer:
|
||||
finish_drm_renderer(&drm->mgpu_renderer);
|
||||
error_resources:
|
||||
finish_drm_resources(drm);
|
||||
error_event:
|
||||
wl_list_remove(&drm->session_signal.link);
|
||||
wl_list_remove(&drm->session_active.link);
|
||||
wl_event_source_remove(drm->drm_event);
|
||||
error_fd:
|
||||
wlr_session_close_file(drm->session, drm->fd);
|
||||
wl_list_remove(&drm->dev_remove.link);
|
||||
wl_list_remove(&drm->dev_change.link);
|
||||
wl_list_remove(&drm->parent_destroy.link);
|
||||
wlr_session_close_file(drm->session, dev);
|
||||
free(drm);
|
||||
return NULL;
|
||||
}
|
||||
|
|
1500
backend/drm/drm.c
1500
backend/drm/drm.c
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,4 @@
|
|||
#include <assert.h>
|
||||
#include <gbm.h>
|
||||
#include <stdlib.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <xf86drm.h>
|
||||
|
@ -8,105 +7,168 @@
|
|||
#include "backend/drm/iface.h"
|
||||
#include "backend/drm/util.h"
|
||||
|
||||
static bool legacy_crtc_commit(struct wlr_drm_backend *drm,
|
||||
struct wlr_drm_connector *conn, uint32_t flags) {
|
||||
static bool legacy_fb_props_match(struct wlr_drm_fb *fb1,
|
||||
struct wlr_drm_fb *fb2) {
|
||||
struct wlr_dmabuf_attributes dmabuf1 = {0}, dmabuf2 = {0};
|
||||
if (!wlr_buffer_get_dmabuf(fb1->wlr_buf, &dmabuf1) ||
|
||||
!wlr_buffer_get_dmabuf(fb2->wlr_buf, &dmabuf2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dmabuf1.width != dmabuf2.width ||
|
||||
dmabuf1.height != dmabuf2.height ||
|
||||
dmabuf1.format != dmabuf2.format ||
|
||||
dmabuf1.modifier != dmabuf2.modifier ||
|
||||
dmabuf1.n_planes != dmabuf2.n_planes) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < dmabuf1.n_planes; i++) {
|
||||
if (dmabuf1.stride[i] != dmabuf2.stride[i] ||
|
||||
dmabuf1.offset[i] != dmabuf2.offset[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool legacy_crtc_test(struct wlr_drm_connector *conn,
|
||||
const struct wlr_drm_connector_state *state) {
|
||||
struct wlr_drm_crtc *crtc = conn->crtc;
|
||||
|
||||
if ((state->base->committed & WLR_OUTPUT_STATE_BUFFER) && !state->modeset) {
|
||||
struct wlr_drm_fb *pending_fb = crtc->primary->pending_fb;
|
||||
|
||||
struct wlr_drm_fb *prev_fb = crtc->primary->queued_fb;
|
||||
if (!prev_fb) {
|
||||
prev_fb = crtc->primary->current_fb;
|
||||
}
|
||||
|
||||
/* Legacy is only guaranteed to be able to display a FB if it's been
|
||||
* allocated the same way as the previous one. */
|
||||
if (prev_fb != NULL && !legacy_fb_props_match(prev_fb, pending_fb)) {
|
||||
wlr_drm_conn_log(conn, WLR_DEBUG,
|
||||
"Cannot change scan-out buffer parameters with legacy KMS API");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool legacy_crtc_commit(struct wlr_drm_connector *conn,
|
||||
const struct wlr_drm_connector_state *state,
|
||||
uint32_t flags, bool test_only) {
|
||||
if (!legacy_crtc_test(conn, state)) {
|
||||
return false;
|
||||
}
|
||||
if (test_only) {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct wlr_drm_backend *drm = conn->backend;
|
||||
struct wlr_output *output = &conn->output;
|
||||
struct wlr_drm_crtc *crtc = conn->crtc;
|
||||
struct wlr_drm_plane *cursor = crtc->cursor;
|
||||
|
||||
uint32_t fb_id = 0;
|
||||
if (crtc->pending.active) {
|
||||
if (state->active) {
|
||||
struct wlr_drm_fb *fb = plane_get_next_fb(crtc->primary);
|
||||
struct gbm_bo *bo = drm_fb_acquire(fb, drm, &crtc->primary->mgpu_surf);
|
||||
if (!bo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fb_id = get_fb_for_bo(bo, drm->addfb2_modifiers);
|
||||
if (!fb_id) {
|
||||
if (fb == NULL) {
|
||||
wlr_log(WLR_ERROR, "%s: failed to acquire primary FB",
|
||||
conn->output.name);
|
||||
return false;
|
||||
}
|
||||
fb_id = fb->id;
|
||||
}
|
||||
|
||||
if (crtc->pending_modeset) {
|
||||
if (state->modeset) {
|
||||
uint32_t *conns = NULL;
|
||||
size_t conns_len = 0;
|
||||
drmModeModeInfo *mode = NULL;
|
||||
if (crtc->pending.active) {
|
||||
if (state->active) {
|
||||
conns = &conn->id;
|
||||
conns_len = 1;
|
||||
mode = &crtc->pending.mode->drm_mode;
|
||||
mode = (drmModeModeInfo *)&state->mode;
|
||||
}
|
||||
|
||||
uint32_t dpms = crtc->pending.active ?
|
||||
DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF;
|
||||
uint32_t dpms = state->active ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF;
|
||||
if (drmModeConnectorSetProperty(drm->fd, conn->id, conn->props.dpms,
|
||||
dpms) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "%s: failed to set DPMS property",
|
||||
conn->output.name);
|
||||
wlr_drm_conn_log_errno(conn, WLR_ERROR,
|
||||
"Failed to set DPMS property");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (drmModeSetCrtc(drm->fd, crtc->id, fb_id, 0, 0,
|
||||
conns, conns_len, mode)) {
|
||||
wlr_log_errno(WLR_ERROR, "%s: failed to set CRTC",
|
||||
conn->output.name);
|
||||
wlr_drm_conn_log_errno(conn, WLR_ERROR, "Failed to set CRTC");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (output->pending.committed & WLR_OUTPUT_STATE_GAMMA_LUT) {
|
||||
if (state->base->committed & WLR_OUTPUT_STATE_GAMMA_LUT) {
|
||||
if (!drm_legacy_crtc_set_gamma(drm, crtc,
|
||||
output->pending.gamma_lut_size, output->pending.gamma_lut)) {
|
||||
state->base->gamma_lut_size, state->base->gamma_lut)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((output->pending.committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) &&
|
||||
if ((state->base->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) &&
|
||||
drm_connector_supports_vrr(conn)) {
|
||||
if (drmModeObjectSetProperty(drm->fd, crtc->id, DRM_MODE_OBJECT_CRTC,
|
||||
crtc->props.vrr_enabled,
|
||||
output->pending.adaptive_sync_enabled) != 0) {
|
||||
wlr_log_errno(WLR_ERROR,
|
||||
state->base->adaptive_sync_enabled) != 0) {
|
||||
wlr_drm_conn_log_errno(conn, WLR_ERROR,
|
||||
"drmModeObjectSetProperty(VRR_ENABLED) failed");
|
||||
return false;
|
||||
}
|
||||
output->adaptive_sync_status = output->pending.adaptive_sync_enabled ?
|
||||
output->adaptive_sync_status = state->base->adaptive_sync_enabled ?
|
||||
WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED :
|
||||
WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED;
|
||||
wlr_log(WLR_DEBUG, "VRR %s on connector '%s'",
|
||||
output->pending.adaptive_sync_enabled ? "enabled" : "disabled",
|
||||
output->name);
|
||||
wlr_drm_conn_log(conn, WLR_DEBUG, "VRR %s",
|
||||
state->base->adaptive_sync_enabled ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
if (cursor != NULL && drm_connector_is_cursor_visible(conn)) {
|
||||
struct wlr_drm_fb *cursor_fb = plane_get_next_fb(cursor);
|
||||
struct gbm_bo *cursor_bo =
|
||||
drm_fb_acquire(cursor_fb, drm, &cursor->mgpu_surf);
|
||||
if (!cursor_bo) {
|
||||
wlr_log_errno(WLR_DEBUG, "%s: failed to acquire cursor FB",
|
||||
conn->output.name);
|
||||
if (cursor_fb == NULL) {
|
||||
wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to acquire cursor FB");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (drmModeSetCursor(drm->fd, crtc->id,
|
||||
gbm_bo_get_handle(cursor_bo).u32,
|
||||
cursor->surf.width, cursor->surf.height)) {
|
||||
wlr_log_errno(WLR_DEBUG, "%s: failed to set hardware cursor",
|
||||
conn->output.name);
|
||||
drmModeFB *drm_fb = drmModeGetFB(drm->fd, cursor_fb->id);
|
||||
if (drm_fb == NULL) {
|
||||
wlr_drm_conn_log_errno(conn, WLR_DEBUG, "Failed to get cursor "
|
||||
"BO handle: drmModeGetFB failed");
|
||||
return false;
|
||||
}
|
||||
uint32_t cursor_handle = drm_fb->handle;
|
||||
uint32_t cursor_width = drm_fb->width;
|
||||
uint32_t cursor_height = drm_fb->height;
|
||||
drmModeFreeFB(drm_fb);
|
||||
|
||||
int ret = drmModeSetCursor(drm->fd, crtc->id, cursor_handle,
|
||||
cursor_width, cursor_height);
|
||||
int set_cursor_errno = errno;
|
||||
if (drmCloseBufferHandle(drm->fd, cursor_handle) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed");
|
||||
}
|
||||
if (ret != 0) {
|
||||
wlr_drm_conn_log(conn, WLR_DEBUG, "drmModeSetCursor failed: %s",
|
||||
strerror(set_cursor_errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (drmModeMoveCursor(drm->fd,
|
||||
crtc->id, conn->cursor_x, conn->cursor_y) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "%s: failed to move cursor",
|
||||
conn->output.name);
|
||||
wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModeMoveCursor failed");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (drmModeSetCursor(drm->fd, crtc->id, 0, 0, 0)) {
|
||||
wlr_log_errno(WLR_DEBUG, "%s: failed to unset hardware cursor",
|
||||
conn->output.name);
|
||||
wlr_drm_conn_log_errno(conn, WLR_DEBUG, "drmModeSetCursor failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -114,7 +176,7 @@ static bool legacy_crtc_commit(struct wlr_drm_backend *drm,
|
|||
if (flags & DRM_MODE_PAGE_FLIP_EVENT) {
|
||||
if (drmModePageFlip(drm->fd, crtc->id, fb_id,
|
||||
DRM_MODE_PAGE_FLIP_EVENT, drm)) {
|
||||
wlr_log_errno(WLR_ERROR, "%s: Failed to page flip", conn->output.name);
|
||||
wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModePageFlip failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -156,6 +218,7 @@ bool drm_legacy_crtc_set_gamma(struct wlr_drm_backend *drm,
|
|||
if (drmModeCrtcSetGamma(drm->fd, crtc->id, size, r, g, b) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to set gamma LUT on CRTC %"PRIu32,
|
||||
crtc->id);
|
||||
free(linear_lut);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,10 @@ wlr_files += files(
|
|||
'cvt.c',
|
||||
'drm.c',
|
||||
'legacy.c',
|
||||
'monitor.c',
|
||||
'properties.c',
|
||||
'renderer.c',
|
||||
'util.c',
|
||||
)
|
||||
|
||||
features += { 'drm-backend': true }
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
#include <wlr/util/log.h>
|
||||
#include <stdlib.h>
|
||||
#include "backend/drm/monitor.h"
|
||||
#include "backend/multi.h"
|
||||
#include "backend/session/session.h"
|
||||
|
||||
static void drm_backend_monitor_destroy(struct wlr_drm_backend_monitor* monitor) {
|
||||
wl_list_remove(&monitor->session_add_drm_card.link);
|
||||
wl_list_remove(&monitor->session_destroy.link);
|
||||
wl_list_remove(&monitor->primary_drm_destroy.link);
|
||||
wl_list_remove(&monitor->multi_destroy.link);
|
||||
free(monitor);
|
||||
}
|
||||
|
||||
static void handle_add_drm_card(struct wl_listener *listener, void *data) {
|
||||
struct wlr_session_add_event *event = data;
|
||||
struct wlr_drm_backend_monitor *backend_monitor =
|
||||
wl_container_of(listener, backend_monitor, session_add_drm_card);
|
||||
|
||||
struct wlr_device *dev =
|
||||
session_open_if_kms(backend_monitor->session, event->path);
|
||||
if (!dev) {
|
||||
wlr_log(WLR_ERROR, "Unable to open %s as DRM device", event->path);
|
||||
return;
|
||||
}
|
||||
|
||||
wlr_log(WLR_DEBUG, "Creating DRM backend for %s after hotplug", event->path);
|
||||
struct wlr_backend *child_drm = wlr_drm_backend_create(
|
||||
backend_monitor->session->display, backend_monitor->session,
|
||||
dev, backend_monitor->primary_drm);
|
||||
if (!child_drm) {
|
||||
wlr_log(WLR_ERROR, "Failed to create DRM backend after hotplug");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!wlr_multi_backend_add(backend_monitor->multi, child_drm)) {
|
||||
wlr_log(WLR_ERROR, "Failed to add new drm backend to multi backend");
|
||||
wlr_backend_destroy(child_drm);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!wlr_backend_start(child_drm)) {
|
||||
wlr_log(WLR_ERROR, "Failed to start new child DRM backend");
|
||||
wlr_backend_destroy(child_drm);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_session_destroy(struct wl_listener *listener, void *data) {
|
||||
struct wlr_drm_backend_monitor *backend_monitor =
|
||||
wl_container_of(listener, backend_monitor, session_destroy);
|
||||
drm_backend_monitor_destroy(backend_monitor);
|
||||
}
|
||||
|
||||
static void handle_primary_drm_destroy(struct wl_listener *listener, void *data) {
|
||||
struct wlr_drm_backend_monitor *backend_monitor =
|
||||
wl_container_of(listener, backend_monitor, primary_drm_destroy);
|
||||
drm_backend_monitor_destroy(backend_monitor);
|
||||
}
|
||||
|
||||
static void handle_multi_destroy(struct wl_listener *listener, void *data) {
|
||||
struct wlr_drm_backend_monitor *backend_monitor =
|
||||
wl_container_of(listener, backend_monitor, multi_destroy);
|
||||
drm_backend_monitor_destroy(backend_monitor);
|
||||
}
|
||||
|
||||
struct wlr_drm_backend_monitor *drm_backend_monitor_create(
|
||||
struct wlr_backend *multi,
|
||||
struct wlr_backend *primary_drm,
|
||||
struct wlr_session *session) {
|
||||
struct wlr_drm_backend_monitor *monitor =
|
||||
calloc(1, sizeof(struct wlr_drm_backend_monitor));
|
||||
if (!monitor) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
monitor->multi = multi;
|
||||
monitor->primary_drm = primary_drm;
|
||||
monitor->session = session;
|
||||
|
||||
monitor->session_add_drm_card.notify = handle_add_drm_card;
|
||||
wl_signal_add(&session->events.add_drm_card, &monitor->session_add_drm_card);
|
||||
|
||||
monitor->session_destroy.notify = handle_session_destroy;
|
||||
wl_signal_add(&session->events.destroy, &monitor->session_destroy);
|
||||
|
||||
monitor->primary_drm_destroy.notify = handle_primary_drm_destroy;
|
||||
wl_signal_add(&primary_drm->events.destroy, &monitor->primary_drm_destroy);
|
||||
|
||||
monitor->multi_destroy.notify = handle_multi_destroy;
|
||||
wl_signal_add(&multi->events.destroy, &monitor->multi_destroy);
|
||||
|
||||
return monitor;
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
@ -24,6 +25,9 @@ static const struct prop_info connector_info[] = {
|
|||
{ "EDID", INDEX(edid) },
|
||||
{ "PATH", INDEX(path) },
|
||||
{ "link-status", INDEX(link_status) },
|
||||
{ "non-desktop", INDEX(non_desktop) },
|
||||
{ "panel orientation", INDEX(panel_orientation) },
|
||||
{ "subconnector", INDEX(subconnector) },
|
||||
{ "vrr_capable", INDEX(vrr_capable) },
|
||||
#undef INDEX
|
||||
};
|
||||
|
@ -35,8 +39,6 @@ static const struct prop_info crtc_info[] = {
|
|||
{ "GAMMA_LUT_SIZE", INDEX(gamma_lut_size) },
|
||||
{ "MODE_ID", INDEX(mode_id) },
|
||||
{ "VRR_ENABLED", INDEX(vrr_enabled) },
|
||||
{ "rotation", INDEX(rotation) },
|
||||
{ "scaling mode", INDEX(scaling_mode) },
|
||||
#undef INDEX
|
||||
};
|
||||
|
||||
|
@ -47,12 +49,14 @@ static const struct prop_info plane_info[] = {
|
|||
{ "CRTC_W", INDEX(crtc_w) },
|
||||
{ "CRTC_X", INDEX(crtc_x) },
|
||||
{ "CRTC_Y", INDEX(crtc_y) },
|
||||
{ "FB_DAMAGE_CLIPS", INDEX(fb_damage_clips) },
|
||||
{ "FB_ID", INDEX(fb_id) },
|
||||
{ "IN_FORMATS", INDEX(in_formats) },
|
||||
{ "SRC_H", INDEX(src_h) },
|
||||
{ "SRC_W", INDEX(src_w) },
|
||||
{ "SRC_X", INDEX(src_x) },
|
||||
{ "SRC_Y", INDEX(src_y) },
|
||||
{ "rotation", INDEX(rotation) },
|
||||
{ "type", INDEX(type) },
|
||||
#undef INDEX
|
||||
};
|
||||
|
@ -152,3 +156,27 @@ void *get_drm_prop_blob(int fd, uint32_t obj, uint32_t prop, size_t *ret_len) {
|
|||
drmModeFreePropertyBlob(blob);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
char *get_drm_prop_enum(int fd, uint32_t obj, uint32_t prop_id) {
|
||||
uint64_t value;
|
||||
if (!get_drm_prop(fd, obj, prop_id, &value)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
drmModePropertyRes *prop = drmModeGetProperty(fd, prop_id);
|
||||
if (!prop) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *str = NULL;
|
||||
for (int i = 0; i < prop->count_enums; i++) {
|
||||
if (prop->enums[i].value == value) {
|
||||
str = strdup(prop->enums[i].name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
drmModeFreeProperty(prop);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
|
|
@ -1,55 +1,42 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <drm_fourcc.h>
|
||||
#include <gbm.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-util.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include <wlr/render/gles2.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_matrix.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "backend/drm/drm.h"
|
||||
#include "backend/drm/util.h"
|
||||
#include "render/drm_format_set.h"
|
||||
#include "render/allocator/allocator.h"
|
||||
#include "render/pixel_format.h"
|
||||
#include "render/swapchain.h"
|
||||
#include "render/wlr_renderer.h"
|
||||
|
||||
bool init_drm_renderer(struct wlr_drm_backend *drm,
|
||||
struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_renderer_func) {
|
||||
renderer->gbm = gbm_create_device(drm->fd);
|
||||
if (!renderer->gbm) {
|
||||
wlr_log(WLR_ERROR, "Failed to create GBM device");
|
||||
struct wlr_drm_renderer *renderer) {
|
||||
renderer->backend = drm;
|
||||
|
||||
renderer->wlr_rend = renderer_autocreate_with_drm_fd(drm->fd);
|
||||
if (!renderer->wlr_rend) {
|
||||
wlr_log(WLR_ERROR, "Failed to create renderer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!create_renderer_func) {
|
||||
create_renderer_func = wlr_renderer_autocreate;
|
||||
renderer->allocator = allocator_autocreate_with_drm_fd(&drm->backend,
|
||||
renderer->wlr_rend, drm->fd);
|
||||
if (renderer->allocator == NULL) {
|
||||
wlr_log(WLR_ERROR, "Failed to create allocator");
|
||||
wlr_renderer_destroy(renderer->wlr_rend);
|
||||
return false;
|
||||
}
|
||||
|
||||
static EGLint config_attribs[] = {
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_RED_SIZE, 1,
|
||||
EGL_GREEN_SIZE, 1,
|
||||
EGL_BLUE_SIZE, 1,
|
||||
EGL_ALPHA_SIZE, 1,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
renderer->gbm_format = GBM_FORMAT_ARGB8888;
|
||||
renderer->wlr_rend = create_renderer_func(&renderer->egl,
|
||||
EGL_PLATFORM_GBM_MESA, renderer->gbm,
|
||||
config_attribs, renderer->gbm_format);
|
||||
if (!renderer->wlr_rend) {
|
||||
wlr_log(WLR_ERROR, "Failed to create EGL/WLR renderer");
|
||||
goto error_gbm;
|
||||
}
|
||||
|
||||
renderer->fd = drm->fd;
|
||||
return true;
|
||||
|
||||
error_gbm:
|
||||
gbm_device_destroy(renderer->gbm);
|
||||
return false;
|
||||
}
|
||||
|
||||
void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
|
||||
|
@ -57,14 +44,13 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
|
|||
return;
|
||||
}
|
||||
|
||||
wlr_allocator_destroy(renderer->allocator);
|
||||
wlr_renderer_destroy(renderer->wlr_rend);
|
||||
wlr_egl_finish(&renderer->egl);
|
||||
gbm_device_destroy(renderer->gbm);
|
||||
}
|
||||
|
||||
static bool init_drm_surface(struct wlr_drm_surface *surf,
|
||||
bool init_drm_surface(struct wlr_drm_surface *surf,
|
||||
struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height,
|
||||
uint32_t format, struct wlr_drm_format_set *set, uint32_t flags) {
|
||||
const struct wlr_drm_format *drm_format) {
|
||||
if (surf->width == width && surf->height == height) {
|
||||
return true;
|
||||
}
|
||||
|
@ -73,43 +59,18 @@ static bool init_drm_surface(struct wlr_drm_surface *surf,
|
|||
surf->width = width;
|
||||
surf->height = height;
|
||||
|
||||
if (surf->gbm) {
|
||||
gbm_surface_destroy(surf->gbm);
|
||||
surf->gbm = NULL;
|
||||
}
|
||||
wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl);
|
||||
wlr_swapchain_destroy(surf->swapchain);
|
||||
surf->swapchain = NULL;
|
||||
|
||||
if (!(flags & GBM_BO_USE_LINEAR) && set != NULL) {
|
||||
const struct wlr_drm_format *drm_format =
|
||||
wlr_drm_format_set_get(set, format);
|
||||
if (drm_format != NULL) {
|
||||
surf->gbm = gbm_surface_create_with_modifiers(renderer->gbm,
|
||||
width, height, format, drm_format->modifiers, drm_format->len);
|
||||
}
|
||||
}
|
||||
|
||||
if (surf->gbm == NULL) {
|
||||
surf->gbm = gbm_surface_create(renderer->gbm, width, height,
|
||||
format, GBM_BO_USE_RENDERING | flags);
|
||||
}
|
||||
if (!surf->gbm) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to create GBM surface");
|
||||
goto error_zero;
|
||||
}
|
||||
|
||||
surf->egl = wlr_egl_create_surface(&renderer->egl, surf->gbm);
|
||||
if (surf->egl == EGL_NO_SURFACE) {
|
||||
wlr_log(WLR_ERROR, "Failed to create EGL surface");
|
||||
goto error_gbm;
|
||||
surf->swapchain = wlr_swapchain_create(renderer->allocator, width, height,
|
||||
drm_format);
|
||||
if (surf->swapchain == NULL) {
|
||||
wlr_log(WLR_ERROR, "Failed to create swapchain");
|
||||
memset(surf, 0, sizeof(*surf));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error_gbm:
|
||||
gbm_surface_destroy(surf->gbm);
|
||||
error_zero:
|
||||
memset(surf, 0, sizeof(*surf));
|
||||
return false;
|
||||
}
|
||||
|
||||
static void finish_drm_surface(struct wlr_drm_surface *surf) {
|
||||
|
@ -117,74 +78,53 @@ static void finish_drm_surface(struct wlr_drm_surface *surf) {
|
|||
return;
|
||||
}
|
||||
|
||||
wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl);
|
||||
if (surf->gbm) {
|
||||
gbm_surface_destroy(surf->gbm);
|
||||
}
|
||||
wlr_swapchain_destroy(surf->swapchain);
|
||||
|
||||
memset(surf, 0, sizeof(*surf));
|
||||
}
|
||||
|
||||
bool drm_surface_make_current(struct wlr_drm_surface *surf,
|
||||
int *buffer_age) {
|
||||
return wlr_egl_make_current(&surf->renderer->egl, surf->egl, buffer_age);
|
||||
}
|
||||
struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf,
|
||||
struct wlr_buffer *buffer) {
|
||||
struct wlr_renderer *renderer = surf->renderer->wlr_rend;
|
||||
|
||||
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs) {
|
||||
memset(attribs, 0, sizeof(struct wlr_dmabuf_attributes));
|
||||
|
||||
attribs->n_planes = gbm_bo_get_plane_count(bo);
|
||||
if (attribs->n_planes > WLR_DMABUF_MAX_PLANES) {
|
||||
return false;
|
||||
}
|
||||
|
||||
attribs->width = gbm_bo_get_width(bo);
|
||||
attribs->height = gbm_bo_get_height(bo);
|
||||
attribs->format = gbm_bo_get_format(bo);
|
||||
attribs->modifier = gbm_bo_get_modifier(bo);
|
||||
|
||||
for (int i = 0; i < attribs->n_planes; ++i) {
|
||||
attribs->offset[i] = gbm_bo_get_offset(bo, i);
|
||||
attribs->stride[i] = gbm_bo_get_stride_for_plane(bo, i);
|
||||
attribs->fd[i] = gbm_bo_get_fd(bo);
|
||||
if (attribs->fd[i] < 0) {
|
||||
for (int j = 0; j < i; ++j) {
|
||||
close(attribs->fd[j]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void free_tex(struct gbm_bo *bo, void *data) {
|
||||
struct wlr_texture *tex = data;
|
||||
wlr_texture_destroy(tex);
|
||||
}
|
||||
|
||||
static struct wlr_texture *get_tex_for_bo(struct wlr_drm_renderer *renderer,
|
||||
struct gbm_bo *bo) {
|
||||
struct wlr_texture *tex = gbm_bo_get_user_data(bo);
|
||||
if (tex) {
|
||||
return tex;
|
||||
}
|
||||
|
||||
struct wlr_dmabuf_attributes attribs;
|
||||
if (!export_drm_bo(bo, &attribs)) {
|
||||
if (surf->width != (uint32_t)buffer->width ||
|
||||
surf->height != (uint32_t)buffer->height) {
|
||||
wlr_log(WLR_ERROR, "Surface size doesn't match buffer size");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tex = wlr_texture_from_dmabuf(renderer->wlr_rend, &attribs);
|
||||
if (tex) {
|
||||
gbm_bo_set_user_data(bo, tex, free_tex);
|
||||
struct wlr_texture *tex = wlr_texture_from_buffer(renderer, buffer);
|
||||
if (tex == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wlr_dmabuf_attributes_finish(&attribs);
|
||||
struct wlr_buffer *dst = wlr_swapchain_acquire(surf->swapchain, NULL);
|
||||
if (!dst) {
|
||||
wlr_texture_destroy(tex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tex;
|
||||
float mat[9];
|
||||
wlr_matrix_identity(mat);
|
||||
wlr_matrix_scale(mat, surf->width, surf->height);
|
||||
|
||||
if (!wlr_renderer_begin_with_buffer(renderer, dst)) {
|
||||
wlr_buffer_unlock(dst);
|
||||
wlr_texture_destroy(tex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 });
|
||||
wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f);
|
||||
|
||||
wlr_renderer_end(renderer);
|
||||
|
||||
wlr_texture_destroy(tex);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
void drm_plane_finish_surface(struct wlr_drm_plane *plane) {
|
||||
if (!plane) {
|
||||
return;
|
||||
|
@ -194,233 +134,282 @@ void drm_plane_finish_surface(struct wlr_drm_plane *plane) {
|
|||
drm_fb_clear(&plane->queued_fb);
|
||||
drm_fb_clear(&plane->current_fb);
|
||||
|
||||
finish_drm_surface(&plane->surf);
|
||||
finish_drm_surface(&plane->mgpu_surf);
|
||||
}
|
||||
|
||||
static uint32_t strip_alpha_channel(uint32_t format) {
|
||||
switch (format) {
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
return DRM_FORMAT_XRGB8888;
|
||||
default:
|
||||
return DRM_FORMAT_INVALID;
|
||||
struct wlr_drm_format *drm_plane_pick_render_format(
|
||||
struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer) {
|
||||
const struct wlr_drm_format_set *render_formats =
|
||||
wlr_renderer_get_render_formats(renderer->wlr_rend);
|
||||
if (render_formats == NULL) {
|
||||
wlr_log(WLR_ERROR, "Failed to get render formats");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct wlr_drm_format_set *plane_formats = &plane->formats;
|
||||
|
||||
uint32_t fmt = DRM_FORMAT_ARGB8888;
|
||||
if (!wlr_drm_format_set_get(&plane->formats, fmt)) {
|
||||
const struct wlr_pixel_format_info *format_info =
|
||||
drm_get_pixel_format_info(fmt);
|
||||
assert(format_info != NULL &&
|
||||
format_info->opaque_substitute != DRM_FORMAT_INVALID);
|
||||
fmt = format_info->opaque_substitute;
|
||||
}
|
||||
|
||||
const struct wlr_drm_format *render_format =
|
||||
wlr_drm_format_set_get(render_formats, fmt);
|
||||
if (render_format == NULL) {
|
||||
wlr_log(WLR_DEBUG, "Renderer doesn't support format 0x%"PRIX32, fmt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct wlr_drm_format *plane_format =
|
||||
wlr_drm_format_set_get(plane_formats, fmt);
|
||||
if (plane_format == NULL) {
|
||||
wlr_log(WLR_DEBUG, "Plane %"PRIu32" doesn't support format 0x%"PRIX32,
|
||||
plane->id, fmt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_drm_format *format =
|
||||
wlr_drm_format_intersect(plane_format, render_format);
|
||||
if (format == NULL) {
|
||||
wlr_log(WLR_DEBUG, "Failed to intersect plane and render "
|
||||
"modifiers for format 0x%"PRIX32, fmt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
void drm_fb_clear(struct wlr_drm_fb **fb_ptr) {
|
||||
if (*fb_ptr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct wlr_drm_fb *fb = *fb_ptr;
|
||||
wlr_buffer_unlock(fb->wlr_buf); // may destroy the buffer
|
||||
|
||||
*fb_ptr = NULL;
|
||||
}
|
||||
|
||||
static void drm_fb_handle_destroy(struct wlr_addon *addon) {
|
||||
struct wlr_drm_fb *fb = wl_container_of(addon, fb, addon);
|
||||
drm_fb_destroy(fb);
|
||||
}
|
||||
|
||||
static const struct wlr_addon_interface fb_addon_impl = {
|
||||
.name = "wlr_drm_fb",
|
||||
.destroy = drm_fb_handle_destroy,
|
||||
};
|
||||
|
||||
static uint32_t get_fb_for_bo(struct wlr_drm_backend *drm,
|
||||
struct wlr_dmabuf_attributes *dmabuf, uint32_t handles[static 4]) {
|
||||
uint64_t modifiers[4] = {0};
|
||||
for (int i = 0; i < dmabuf->n_planes; i++) {
|
||||
// KMS requires all BO planes to have the same modifier
|
||||
modifiers[i] = dmabuf->modifier;
|
||||
}
|
||||
|
||||
uint32_t id = 0;
|
||||
if (drm->addfb2_modifiers && dmabuf->modifier != DRM_FORMAT_MOD_INVALID) {
|
||||
if (drmModeAddFB2WithModifiers(drm->fd, dmabuf->width, dmabuf->height,
|
||||
dmabuf->format, handles, dmabuf->stride, dmabuf->offset,
|
||||
modifiers, &id, DRM_MODE_FB_MODIFIERS) != 0) {
|
||||
wlr_log_errno(WLR_DEBUG, "drmModeAddFB2WithModifiers failed");
|
||||
}
|
||||
} else {
|
||||
if (dmabuf->modifier != DRM_FORMAT_MOD_INVALID &&
|
||||
dmabuf->modifier != DRM_FORMAT_MOD_LINEAR) {
|
||||
wlr_log(WLR_ERROR, "Cannot import DRM framebuffer with explicit "
|
||||
"modifier 0x%"PRIX64, dmabuf->modifier);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ret = drmModeAddFB2(drm->fd, dmabuf->width, dmabuf->height,
|
||||
dmabuf->format, handles, dmabuf->stride, dmabuf->offset, &id, 0);
|
||||
if (ret != 0 && dmabuf->format == DRM_FORMAT_ARGB8888 &&
|
||||
dmabuf->n_planes == 1 && dmabuf->offset[0] == 0) {
|
||||
// Some big-endian machines don't support drmModeAddFB2. Try a
|
||||
// last-resort fallback for ARGB8888 buffers, like Xorg's
|
||||
// modesetting driver does.
|
||||
wlr_log(WLR_DEBUG, "drmModeAddFB2 failed (%s), falling back to "
|
||||
"legacy drmModeAddFB", strerror(-ret));
|
||||
|
||||
uint32_t depth = 32;
|
||||
uint32_t bpp = 32;
|
||||
ret = drmModeAddFB(drm->fd, dmabuf->width, dmabuf->height, depth,
|
||||
bpp, dmabuf->stride[0], handles[0], &id);
|
||||
if (ret != 0) {
|
||||
wlr_log_errno(WLR_DEBUG, "drmModeAddFB failed");
|
||||
}
|
||||
} else if (ret != 0) {
|
||||
wlr_log_errno(WLR_DEBUG, "drmModeAddFB2 failed");
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static void close_all_bo_handles(struct wlr_drm_backend *drm,
|
||||
uint32_t handles[static 4]) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (handles[i] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If multiple planes share the same BO handle, avoid double-closing it
|
||||
bool already_closed = false;
|
||||
for (int j = 0; j < i; ++j) {
|
||||
if (handles[i] == handles[j]) {
|
||||
already_closed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (already_closed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (drmCloseBufferHandle(drm->fd, handles[i]) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool drm_plane_init_surface(struct wlr_drm_plane *plane,
|
||||
struct wlr_drm_backend *drm, int32_t width, uint32_t height,
|
||||
uint32_t format, uint32_t flags, bool with_modifiers) {
|
||||
if (!wlr_drm_format_set_has(&plane->formats, format, DRM_FORMAT_MOD_INVALID)) {
|
||||
format = strip_alpha_channel(format);
|
||||
}
|
||||
if (!wlr_drm_format_set_has(&plane->formats, format, DRM_FORMAT_MOD_INVALID)) {
|
||||
wlr_log(WLR_ERROR, "Plane %"PRIu32" doesn't support format 0x%"PRIX32,
|
||||
plane->id, format);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct wlr_drm_format_set *format_set =
|
||||
with_modifiers ? &plane->formats : NULL;
|
||||
|
||||
drm_plane_finish_surface(plane);
|
||||
|
||||
if (!drm->parent) {
|
||||
return init_drm_surface(&plane->surf, &drm->renderer, width, height,
|
||||
format, format_set, flags | GBM_BO_USE_SCANOUT);
|
||||
}
|
||||
|
||||
if (!init_drm_surface(&plane->surf, &drm->parent->renderer,
|
||||
width, height, format, NULL,
|
||||
flags | GBM_BO_USE_LINEAR)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!init_drm_surface(&plane->mgpu_surf, &drm->renderer,
|
||||
width, height, format, format_set,
|
||||
flags | GBM_BO_USE_SCANOUT)) {
|
||||
finish_drm_surface(&plane->surf);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
static void drm_poisoned_fb_handle_destroy(struct wlr_addon *addon) {
|
||||
wlr_addon_finish(addon);
|
||||
free(addon);
|
||||
}
|
||||
|
||||
void drm_fb_clear(struct wlr_drm_fb *fb) {
|
||||
switch (fb->type) {
|
||||
case WLR_DRM_FB_TYPE_NONE:
|
||||
assert(!fb->bo);
|
||||
break;
|
||||
case WLR_DRM_FB_TYPE_SURFACE:
|
||||
gbm_surface_release_buffer(fb->surf->gbm, fb->bo);
|
||||
break;
|
||||
case WLR_DRM_FB_TYPE_WLR_BUFFER:
|
||||
gbm_bo_destroy(fb->bo);
|
||||
wlr_buffer_unlock(fb->wlr_buf);
|
||||
fb->wlr_buf = NULL;
|
||||
break;
|
||||
}
|
||||
static const struct wlr_addon_interface poisoned_fb_addon_impl = {
|
||||
.name = "wlr_drm_poisoned_fb",
|
||||
.destroy = drm_poisoned_fb_handle_destroy,
|
||||
};
|
||||
|
||||
fb->type = WLR_DRM_FB_TYPE_NONE;
|
||||
fb->bo = NULL;
|
||||
|
||||
if (fb->mgpu_bo) {
|
||||
assert(fb->mgpu_surf);
|
||||
gbm_surface_release_buffer(fb->mgpu_surf->gbm, fb->mgpu_bo);
|
||||
fb->mgpu_bo = NULL;
|
||||
fb->mgpu_surf = NULL;
|
||||
}
|
||||
static bool is_buffer_poisoned(struct wlr_drm_backend *drm,
|
||||
struct wlr_buffer *buf) {
|
||||
return wlr_addon_find(&buf->addons, drm, &poisoned_fb_addon_impl) != NULL;
|
||||
}
|
||||
|
||||
bool drm_fb_lock_surface(struct wlr_drm_fb *fb, struct wlr_drm_surface *surf) {
|
||||
drm_fb_clear(fb);
|
||||
|
||||
if (!wlr_egl_swap_buffers(&surf->renderer->egl, surf->egl, NULL)) {
|
||||
wlr_log(WLR_ERROR, "Failed to swap buffers");
|
||||
return false;
|
||||
/**
|
||||
* Mark the buffer as "poisoned", ie. it cannot be imported into KMS. This
|
||||
* allows us to avoid repeatedly trying to import it when it's not
|
||||
* scanout-capable.
|
||||
*/
|
||||
static void poison_buffer(struct wlr_drm_backend *drm,
|
||||
struct wlr_buffer *buf) {
|
||||
struct wlr_addon *addon = calloc(1, sizeof(*addon));
|
||||
if (addon == NULL) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
fb->bo = gbm_surface_lock_front_buffer(surf->gbm);
|
||||
if (!fb->bo) {
|
||||
wlr_log(WLR_ERROR, "Failed to lock front buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
fb->type = WLR_DRM_FB_TYPE_SURFACE;
|
||||
fb->surf = surf;
|
||||
return true;
|
||||
wlr_addon_init(addon, &buf->addons, drm, &poisoned_fb_addon_impl);
|
||||
wlr_log(WLR_DEBUG, "Poisoning buffer");
|
||||
}
|
||||
|
||||
bool drm_fb_import_wlr(struct wlr_drm_fb *fb, struct wlr_drm_renderer *renderer,
|
||||
struct wlr_buffer *buf, struct wlr_drm_format_set *set) {
|
||||
drm_fb_clear(fb);
|
||||
|
||||
static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm,
|
||||
struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) {
|
||||
struct wlr_dmabuf_attributes attribs;
|
||||
if (!wlr_buffer_get_dmabuf(buf, &attribs)) {
|
||||
return false;
|
||||
wlr_log(WLR_DEBUG, "Failed to get DMA-BUF from buffer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!wlr_drm_format_set_has(set, attribs.format, attribs.modifier)) {
|
||||
if (is_buffer_poisoned(drm, buf)) {
|
||||
wlr_log(WLR_DEBUG, "Buffer is poisoned");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_drm_fb *fb = calloc(1, sizeof(*fb));
|
||||
if (!fb) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (formats && !wlr_drm_format_set_has(formats, attribs.format,
|
||||
attribs.modifier)) {
|
||||
// The format isn't supported by the plane. Try stripping the alpha
|
||||
// channel, if any.
|
||||
uint32_t format = strip_alpha_channel(attribs.format);
|
||||
if (wlr_drm_format_set_has(set, format, attribs.modifier)) {
|
||||
attribs.format = format;
|
||||
const struct wlr_pixel_format_info *info =
|
||||
drm_get_pixel_format_info(attribs.format);
|
||||
if (info != NULL && info->opaque_substitute != DRM_FORMAT_INVALID &&
|
||||
wlr_drm_format_set_has(formats, info->opaque_substitute, attribs.modifier)) {
|
||||
attribs.format = info->opaque_substitute;
|
||||
} else {
|
||||
return false;
|
||||
wlr_log(WLR_DEBUG, "Buffer format 0x%"PRIX32" with modifier "
|
||||
"0x%"PRIX64" cannot be scanned out",
|
||||
attribs.format, attribs.modifier);
|
||||
goto error_fb;
|
||||
}
|
||||
}
|
||||
|
||||
if (attribs.modifier != DRM_FORMAT_MOD_INVALID ||
|
||||
attribs.n_planes > 1 || attribs.offset[0] != 0) {
|
||||
struct gbm_import_fd_modifier_data data = {
|
||||
.width = attribs.width,
|
||||
.height = attribs.height,
|
||||
.format = attribs.format,
|
||||
.num_fds = attribs.n_planes,
|
||||
.modifier = attribs.modifier,
|
||||
};
|
||||
|
||||
if ((size_t)attribs.n_planes > sizeof(data.fds) / sizeof(data.fds[0])) {
|
||||
return false;
|
||||
uint32_t handles[4] = {0};
|
||||
for (int i = 0; i < attribs.n_planes; ++i) {
|
||||
int ret = drmPrimeFDToHandle(drm->fd, attribs.fd[i], &handles[i]);
|
||||
if (ret != 0) {
|
||||
wlr_log_errno(WLR_DEBUG, "drmPrimeFDToHandle failed");
|
||||
goto error_bo_handle;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < (size_t)attribs.n_planes; ++i) {
|
||||
data.fds[i] = attribs.fd[i];
|
||||
data.strides[i] = attribs.stride[i];
|
||||
data.offsets[i] = attribs.offset[i];
|
||||
}
|
||||
fb->id = get_fb_for_bo(drm, &attribs, handles);
|
||||
if (!fb->id) {
|
||||
wlr_log(WLR_DEBUG, "Failed to import BO in KMS");
|
||||
poison_buffer(drm, buf);
|
||||
goto error_bo_handle;
|
||||
}
|
||||
|
||||
fb->bo = gbm_bo_import(renderer->gbm, GBM_BO_IMPORT_FD_MODIFIER,
|
||||
&data, GBM_BO_USE_SCANOUT);
|
||||
close_all_bo_handles(drm, handles);
|
||||
|
||||
fb->backend = drm;
|
||||
fb->wlr_buf = buf;
|
||||
|
||||
wlr_addon_init(&fb->addon, &buf->addons, drm, &fb_addon_impl);
|
||||
wl_list_insert(&drm->fbs, &fb->link);
|
||||
|
||||
return fb;
|
||||
|
||||
error_bo_handle:
|
||||
close_all_bo_handles(drm, handles);
|
||||
error_fb:
|
||||
free(fb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void drm_fb_destroy(struct wlr_drm_fb *fb) {
|
||||
struct wlr_drm_backend *drm = fb->backend;
|
||||
|
||||
wl_list_remove(&fb->link);
|
||||
wlr_addon_finish(&fb->addon);
|
||||
|
||||
if (drmModeRmFB(drm->fd, fb->id) != 0) {
|
||||
wlr_log(WLR_ERROR, "drmModeRmFB failed");
|
||||
}
|
||||
|
||||
free(fb);
|
||||
}
|
||||
|
||||
bool drm_fb_import(struct wlr_drm_fb **fb_ptr, struct wlr_drm_backend *drm,
|
||||
struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) {
|
||||
struct wlr_drm_fb *fb;
|
||||
struct wlr_addon *addon = wlr_addon_find(&buf->addons, drm, &fb_addon_impl);
|
||||
if (addon != NULL) {
|
||||
fb = wl_container_of(addon, fb, addon);
|
||||
} else {
|
||||
struct gbm_import_fd_data data = {
|
||||
.fd = attribs.fd[0],
|
||||
.width = attribs.width,
|
||||
.height = attribs.height,
|
||||
.stride = attribs.stride[0],
|
||||
.format = attribs.format,
|
||||
};
|
||||
|
||||
fb->bo = gbm_bo_import(renderer->gbm, GBM_BO_IMPORT_FD,
|
||||
&data, GBM_BO_USE_SCANOUT);
|
||||
fb = drm_fb_create(drm, buf, formats);
|
||||
if (!fb) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fb->bo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fb->type = WLR_DRM_FB_TYPE_WLR_BUFFER;
|
||||
fb->wlr_buf = wlr_buffer_lock(buf);
|
||||
|
||||
wlr_buffer_lock(buf);
|
||||
drm_fb_move(fb_ptr, &fb);
|
||||
return true;
|
||||
}
|
||||
|
||||
void drm_fb_move(struct wlr_drm_fb *new, struct wlr_drm_fb *old) {
|
||||
void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old) {
|
||||
drm_fb_clear(new);
|
||||
|
||||
*new = *old;
|
||||
memset(old, 0, sizeof(*old));
|
||||
}
|
||||
|
||||
bool drm_surface_render_black_frame(struct wlr_drm_surface *surf) {
|
||||
if (!drm_surface_make_current(surf, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct wlr_renderer *renderer = surf->renderer->wlr_rend;
|
||||
wlr_renderer_begin(renderer, surf->width, surf->height);
|
||||
wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 1.0 });
|
||||
wlr_renderer_end(renderer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct gbm_bo *drm_fb_acquire(struct wlr_drm_fb *fb, struct wlr_drm_backend *drm,
|
||||
struct wlr_drm_surface *mgpu) {
|
||||
if (!fb->bo) {
|
||||
wlr_log(WLR_ERROR, "Tried to acquire an FB with a NULL BO");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!drm->parent) {
|
||||
return fb->bo;
|
||||
}
|
||||
|
||||
if (fb->mgpu_bo) {
|
||||
return fb->mgpu_bo;
|
||||
}
|
||||
|
||||
/* Perform copy across GPUs */
|
||||
|
||||
struct wlr_texture *tex = get_tex_for_bo(mgpu->renderer, fb->bo);
|
||||
if (!tex) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!drm_surface_make_current(mgpu, NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
float mat[9];
|
||||
wlr_matrix_projection(mat, 1, 1, WL_OUTPUT_TRANSFORM_NORMAL);
|
||||
|
||||
struct wlr_renderer *renderer = mgpu->renderer->wlr_rend;
|
||||
wlr_renderer_begin(renderer, mgpu->width, mgpu->height);
|
||||
wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 });
|
||||
wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f);
|
||||
wlr_renderer_end(renderer);
|
||||
|
||||
if (!wlr_egl_swap_buffers(&mgpu->renderer->egl, mgpu->egl, NULL)) {
|
||||
wlr_log(WLR_ERROR, "Failed to swap buffers");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fb->mgpu_bo = gbm_surface_lock_front_buffer(mgpu->gbm);
|
||||
if (!fb->mgpu_bo) {
|
||||
wlr_log(WLR_ERROR, "Failed to lock front buffer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fb->mgpu_surf = mgpu;
|
||||
return fb->mgpu_bo;
|
||||
*old = NULL;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#include <drm_fourcc.h>
|
||||
#include <drm_mode.h>
|
||||
#include <drm.h>
|
||||
#include <gbm.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
@ -95,6 +94,7 @@ static const char *get_manufacturer(uint16_t id) {
|
|||
case ID('V', 'E', 'S'): return "Vestel Elektronik Sanayi ve Ticaret A. S.";
|
||||
case ID('V', 'I', 'T'): return "Visitech AS";
|
||||
case ID('V', 'I', 'Z'): return "VIZIO, Inc";
|
||||
case ID('V', 'L', 'V'): return "Valve";
|
||||
case ID('V', 'S', 'C'): return "ViewSonic Corporation";
|
||||
case ID('Y', 'M', 'H'): return "Yamaha Corporation";
|
||||
default: return "Unknown";
|
||||
|
@ -163,65 +163,19 @@ const char *conn_get_name(uint32_t type_id) {
|
|||
case DRM_MODE_CONNECTOR_eDP: return "eDP";
|
||||
case DRM_MODE_CONNECTOR_VIRTUAL: return "Virtual";
|
||||
case DRM_MODE_CONNECTOR_DSI: return "DSI";
|
||||
#ifdef DRM_MODE_CONNECTOR_DPI
|
||||
case DRM_MODE_CONNECTOR_DPI: return "DPI";
|
||||
case DRM_MODE_CONNECTOR_WRITEBACK: return "Writeback";
|
||||
#ifdef DRM_MODE_CONNECTOR_SPI
|
||||
case DRM_MODE_CONNECTOR_SPI: return "SPI";
|
||||
#endif
|
||||
#ifdef DRM_MODE_CONNECTOR_USB
|
||||
case DRM_MODE_CONNECTOR_USB: return "USB";
|
||||
#endif
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void free_fb(struct gbm_bo *bo, void *data) {
|
||||
uint32_t id = (uintptr_t)data;
|
||||
|
||||
if (id) {
|
||||
struct gbm_device *gbm = gbm_bo_get_device(bo);
|
||||
drmModeRmFB(gbm_device_get_fd(gbm), id);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t get_fb_for_bo(struct gbm_bo *bo, bool with_modifiers) {
|
||||
uint32_t id = (uintptr_t)gbm_bo_get_user_data(bo);
|
||||
if (id) {
|
||||
return id;
|
||||
}
|
||||
|
||||
struct gbm_device *gbm = gbm_bo_get_device(bo);
|
||||
|
||||
int fd = gbm_device_get_fd(gbm);
|
||||
uint32_t width = gbm_bo_get_width(bo);
|
||||
uint32_t height = gbm_bo_get_height(bo);
|
||||
uint32_t format = gbm_bo_get_format(bo);
|
||||
|
||||
uint32_t handles[4] = {0};
|
||||
uint32_t strides[4] = {0};
|
||||
uint32_t offsets[4] = {0};
|
||||
uint64_t modifiers[4] = {0};
|
||||
for (int i = 0; i < gbm_bo_get_plane_count(bo); i++) {
|
||||
handles[i] = gbm_bo_get_handle_for_plane(bo, i).u32;
|
||||
strides[i] = gbm_bo_get_stride_for_plane(bo, i);
|
||||
offsets[i] = gbm_bo_get_offset(bo, i);
|
||||
// KMS requires all BO planes to have the same modifier
|
||||
modifiers[i] = gbm_bo_get_modifier(bo);
|
||||
}
|
||||
|
||||
if (with_modifiers && gbm_bo_get_modifier(bo) != DRM_FORMAT_MOD_INVALID) {
|
||||
if (drmModeAddFB2WithModifiers(fd, width, height, format, handles,
|
||||
strides, offsets, modifiers, &id, DRM_MODE_FB_MODIFIERS)) {
|
||||
wlr_log_errno(WLR_ERROR, "Unable to add DRM framebuffer");
|
||||
}
|
||||
} else {
|
||||
if (drmModeAddFB2(fd, width, height, format, handles, strides,
|
||||
offsets, &id, 0)) {
|
||||
wlr_log_errno(WLR_ERROR, "Unable to add DRM framebuffer");
|
||||
}
|
||||
}
|
||||
|
||||
gbm_bo_set_user_data(bo, (void *)(uintptr_t)id, free_fb);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static inline bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key) {
|
||||
static bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key) {
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (arr[i] == key) {
|
||||
return true;
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
#include <assert.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <stdlib.h>
|
||||
#include <wlr/interfaces/wlr_input_device.h>
|
||||
#include <wlr/interfaces/wlr_output.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "backend/headless.h"
|
||||
#include "util/signal.h"
|
||||
|
@ -29,8 +26,7 @@ static bool backend_start(struct wlr_backend *wlr_backend) {
|
|||
}
|
||||
|
||||
struct wlr_headless_input_device *input_device;
|
||||
wl_list_for_each(input_device, &backend->input_devices,
|
||||
wlr_input_device.link) {
|
||||
wl_list_for_each(input_device, &backend->input_devices, link) {
|
||||
wlr_signal_emit_safe(&backend->backend.events.new_input,
|
||||
&input_device->wlr_input_device);
|
||||
}
|
||||
|
@ -47,7 +43,6 @@ static void backend_destroy(struct wlr_backend *wlr_backend) {
|
|||
}
|
||||
|
||||
wl_list_remove(&backend->display_destroy.link);
|
||||
wl_list_remove(&backend->renderer_destroy.link);
|
||||
|
||||
struct wlr_headless_output *output, *output_tmp;
|
||||
wl_list_for_each_safe(output, output_tmp, &backend->outputs, link) {
|
||||
|
@ -56,30 +51,25 @@ static void backend_destroy(struct wlr_backend *wlr_backend) {
|
|||
|
||||
struct wlr_headless_input_device *input_device, *input_device_tmp;
|
||||
wl_list_for_each_safe(input_device, input_device_tmp,
|
||||
&backend->input_devices, wlr_input_device.link) {
|
||||
&backend->input_devices, link) {
|
||||
wlr_input_device_destroy(&input_device->wlr_input_device);
|
||||
}
|
||||
|
||||
wlr_signal_emit_safe(&wlr_backend->events.destroy, backend);
|
||||
wlr_backend_finish(wlr_backend);
|
||||
|
||||
if (backend->egl == &backend->priv_egl) {
|
||||
wlr_renderer_destroy(backend->renderer);
|
||||
wlr_egl_finish(&backend->priv_egl);
|
||||
}
|
||||
free(backend);
|
||||
}
|
||||
|
||||
static struct wlr_renderer *backend_get_renderer(
|
||||
struct wlr_backend *wlr_backend) {
|
||||
struct wlr_headless_backend *backend =
|
||||
headless_backend_from_backend(wlr_backend);
|
||||
return backend->renderer;
|
||||
static uint32_t get_buffer_caps(struct wlr_backend *wlr_backend) {
|
||||
return WLR_BUFFER_CAP_DATA_PTR
|
||||
| WLR_BUFFER_CAP_DMABUF
|
||||
| WLR_BUFFER_CAP_SHM;
|
||||
}
|
||||
|
||||
static const struct wlr_backend_impl backend_impl = {
|
||||
.start = backend_start,
|
||||
.destroy = backend_destroy,
|
||||
.get_renderer = backend_get_renderer,
|
||||
.get_buffer_caps = get_buffer_caps,
|
||||
};
|
||||
|
||||
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||
|
@ -88,103 +78,25 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
|||
backend_destroy(&backend->backend);
|
||||
}
|
||||
|
||||
static void handle_renderer_destroy(struct wl_listener *listener, void *data) {
|
||||
struct wlr_headless_backend *backend =
|
||||
wl_container_of(listener, backend, renderer_destroy);
|
||||
backend_destroy(&backend->backend);
|
||||
}
|
||||
struct wlr_backend *wlr_headless_backend_create(struct wl_display *display) {
|
||||
wlr_log(WLR_INFO, "Creating headless backend");
|
||||
|
||||
struct wlr_headless_backend *backend =
|
||||
calloc(1, sizeof(struct wlr_headless_backend));
|
||||
if (!backend) {
|
||||
wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_backend");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool backend_init(struct wlr_headless_backend *backend,
|
||||
struct wl_display *display, struct wlr_renderer *renderer) {
|
||||
wlr_backend_init(&backend->backend, &backend_impl);
|
||||
|
||||
backend->display = display;
|
||||
wl_list_init(&backend->outputs);
|
||||
wl_list_init(&backend->input_devices);
|
||||
|
||||
backend->renderer = renderer;
|
||||
backend->egl = wlr_gles2_renderer_get_egl(renderer);
|
||||
|
||||
if (wlr_gles2_renderer_check_ext(backend->renderer, "GL_OES_rgb8_rgba8") ||
|
||||
wlr_gles2_renderer_check_ext(backend->renderer,
|
||||
"GL_OES_required_internalformat") ||
|
||||
wlr_gles2_renderer_check_ext(backend->renderer, "GL_ARM_rgba8")) {
|
||||
backend->internal_format = GL_RGBA8_OES;
|
||||
} else {
|
||||
wlr_log(WLR_INFO, "GL_RGBA8_OES not supported, "
|
||||
"falling back to GL_RGBA4 internal format "
|
||||
"(performance may be affected)");
|
||||
backend->internal_format = GL_RGBA4;
|
||||
}
|
||||
|
||||
backend->display_destroy.notify = handle_display_destroy;
|
||||
wl_display_add_destroy_listener(display, &backend->display_destroy);
|
||||
|
||||
wl_list_init(&backend->renderer_destroy.link);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct wlr_backend *wlr_headless_backend_create(struct wl_display *display,
|
||||
wlr_renderer_create_func_t create_renderer_func) {
|
||||
wlr_log(WLR_INFO, "Creating headless backend");
|
||||
|
||||
struct wlr_headless_backend *backend =
|
||||
calloc(1, sizeof(struct wlr_headless_backend));
|
||||
if (!backend) {
|
||||
wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_backend");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const EGLint config_attribs[] = {
|
||||
EGL_SURFACE_TYPE, 0,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_BLUE_SIZE, 1,
|
||||
EGL_GREEN_SIZE, 1,
|
||||
EGL_RED_SIZE, 1,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
if (!create_renderer_func) {
|
||||
create_renderer_func = wlr_renderer_autocreate;
|
||||
}
|
||||
|
||||
struct wlr_renderer *renderer = create_renderer_func(&backend->priv_egl,
|
||||
EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY,
|
||||
(EGLint*)config_attribs, 0);
|
||||
if (!renderer) {
|
||||
wlr_log(WLR_ERROR, "Failed to create renderer");
|
||||
free(backend);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!backend_init(backend, display, renderer)) {
|
||||
wlr_renderer_destroy(backend->renderer);
|
||||
free(backend);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &backend->backend;
|
||||
}
|
||||
|
||||
struct wlr_backend *wlr_headless_backend_create_with_renderer(
|
||||
struct wl_display *display, struct wlr_renderer *renderer) {
|
||||
wlr_log(WLR_INFO, "Creating headless backend");
|
||||
|
||||
struct wlr_headless_backend *backend =
|
||||
calloc(1, sizeof(struct wlr_headless_backend));
|
||||
if (!backend) {
|
||||
wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_backend");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!backend_init(backend, display, renderer)) {
|
||||
free(backend);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
backend->renderer_destroy.notify = handle_renderer_destroy;
|
||||
wl_signal_add(&renderer->events.destroy, &backend->renderer_destroy);
|
||||
|
||||
return &backend->backend;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,16 @@
|
|||
#include "backend/headless.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
static const struct wlr_input_device_impl input_device_impl = { 0 };
|
||||
static void input_device_destroy(struct wlr_input_device *wlr_dev) {
|
||||
struct wlr_headless_input_device *dev =
|
||||
wl_container_of(wlr_dev, dev, wlr_input_device);
|
||||
wl_list_remove(&dev->link);
|
||||
free(dev);
|
||||
}
|
||||
|
||||
static const struct wlr_input_device_impl input_device_impl = {
|
||||
.destroy = input_device_destroy,
|
||||
};
|
||||
|
||||
bool wlr_input_device_is_headless(struct wlr_input_device *wlr_dev) {
|
||||
return wlr_dev->impl == &input_device_impl;
|
||||
|
@ -86,7 +95,7 @@ struct wlr_input_device *wlr_headless_add_input_device(
|
|||
wlr_switch_init(wlr_device->switch_device, NULL);
|
||||
}
|
||||
|
||||
wl_list_insert(&backend->input_devices, &wlr_device->link);
|
||||
wl_list_insert(&backend->input_devices, &device->link);
|
||||
|
||||
if (backend->started) {
|
||||
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_device);
|
||||
|
|
|
@ -1,107 +1,40 @@
|
|||
#include <assert.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <wlr/interfaces/wlr_output.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "backend/headless.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
static const uint32_t SUPPORTED_OUTPUT_STATE =
|
||||
WLR_OUTPUT_STATE_BACKEND_OPTIONAL |
|
||||
WLR_OUTPUT_STATE_BUFFER |
|
||||
WLR_OUTPUT_STATE_MODE;
|
||||
|
||||
static struct wlr_headless_output *headless_output_from_output(
|
||||
struct wlr_output *wlr_output) {
|
||||
assert(wlr_output_is_headless(wlr_output));
|
||||
return (struct wlr_headless_output *)wlr_output;
|
||||
}
|
||||
|
||||
static bool create_fbo(struct wlr_headless_output *output,
|
||||
unsigned int width, unsigned int height) {
|
||||
if (!wlr_egl_make_current(output->backend->egl, EGL_NO_SURFACE, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GLuint rbo;
|
||||
glGenRenderbuffers(1, &rbo);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, output->backend->internal_format,
|
||||
width, height);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
GLuint fbo;
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_RENDERBUFFER, rbo);
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
wlr_egl_unset_current(output->backend->egl);
|
||||
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
wlr_log(WLR_ERROR, "Failed to create FBO");
|
||||
return false;
|
||||
}
|
||||
|
||||
output->fbo = fbo;
|
||||
output->rbo = rbo;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void destroy_fbo(struct wlr_headless_output *output) {
|
||||
if (!wlr_egl_make_current(output->backend->egl, EGL_NO_SURFACE, NULL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
glDeleteFramebuffers(1, &output->fbo);
|
||||
glDeleteRenderbuffers(1, &output->rbo);
|
||||
|
||||
wlr_egl_unset_current(output->backend->egl);
|
||||
|
||||
output->fbo = 0;
|
||||
output->rbo = 0;
|
||||
}
|
||||
|
||||
static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width,
|
||||
int32_t height, int32_t refresh) {
|
||||
struct wlr_headless_output *output =
|
||||
headless_output_from_output(wlr_output);
|
||||
|
||||
static bool output_set_custom_mode(struct wlr_headless_output *output,
|
||||
int32_t width, int32_t height, int32_t refresh) {
|
||||
if (refresh <= 0) {
|
||||
refresh = HEADLESS_DEFAULT_REFRESH;
|
||||
}
|
||||
|
||||
destroy_fbo(output);
|
||||
if (!create_fbo(output, width, height)) {
|
||||
wlr_output_destroy(wlr_output);
|
||||
return false;
|
||||
}
|
||||
|
||||
output->frame_delay = 1000000 / refresh;
|
||||
|
||||
wlr_output_update_custom_mode(&output->wlr_output, width, height, refresh);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool output_attach_render(struct wlr_output *wlr_output,
|
||||
int *buffer_age) {
|
||||
struct wlr_headless_output *output =
|
||||
headless_output_from_output(wlr_output);
|
||||
|
||||
if (!wlr_egl_make_current(output->backend->egl, EGL_NO_SURFACE, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, output->fbo);
|
||||
|
||||
if (buffer_age != NULL) {
|
||||
*buffer_age = 0; // We only have one buffer
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool output_test(struct wlr_output *wlr_output) {
|
||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_ENABLED) {
|
||||
wlr_log(WLR_DEBUG, "Cannot disable a headless output");
|
||||
uint32_t unsupported =
|
||||
wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE;
|
||||
if (unsupported != 0) {
|
||||
wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32,
|
||||
unsupported);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -121,7 +54,7 @@ static bool output_commit(struct wlr_output *wlr_output) {
|
|||
}
|
||||
|
||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
|
||||
if (!output_set_custom_mode(wlr_output,
|
||||
if (!output_set_custom_mode(output,
|
||||
wlr_output->pending.custom_mode.width,
|
||||
wlr_output->pending.custom_mode.height,
|
||||
wlr_output->pending.custom_mode.refresh)) {
|
||||
|
@ -130,38 +63,27 @@ static bool output_commit(struct wlr_output *wlr_output) {
|
|||
}
|
||||
|
||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
wlr_egl_unset_current(output->backend->egl);
|
||||
|
||||
// Nothing needs to be done for FBOs
|
||||
wlr_output_send_present(wlr_output, NULL);
|
||||
struct wlr_output_event_present present_event = {
|
||||
.commit_seq = wlr_output->commit_seq + 1,
|
||||
.presented = true,
|
||||
};
|
||||
wlr_output_send_present(wlr_output, &present_event);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void output_rollback_render(struct wlr_output *wlr_output) {
|
||||
struct wlr_headless_output *output =
|
||||
headless_output_from_output(wlr_output);
|
||||
assert(wlr_egl_is_current(output->backend->egl));
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
wlr_egl_unset_current(output->backend->egl);
|
||||
}
|
||||
|
||||
static void output_destroy(struct wlr_output *wlr_output) {
|
||||
struct wlr_headless_output *output =
|
||||
headless_output_from_output(wlr_output);
|
||||
wl_list_remove(&output->link);
|
||||
wl_event_source_remove(output->frame_timer);
|
||||
destroy_fbo(output);
|
||||
free(output);
|
||||
}
|
||||
|
||||
static const struct wlr_output_impl output_impl = {
|
||||
.destroy = output_destroy,
|
||||
.attach_render = output_attach_render,
|
||||
.commit = output_commit,
|
||||
.rollback_render = output_rollback_render,
|
||||
};
|
||||
|
||||
bool wlr_output_is_headless(struct wlr_output *wlr_output) {
|
||||
|
@ -191,29 +113,19 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
|
|||
backend->display);
|
||||
struct wlr_output *wlr_output = &output->wlr_output;
|
||||
|
||||
if (!create_fbo(output, width, height)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
output_set_custom_mode(wlr_output, width, height, 0);
|
||||
output_set_custom_mode(output, width, height, 0);
|
||||
strncpy(wlr_output->make, "headless", sizeof(wlr_output->make));
|
||||
strncpy(wlr_output->model, "headless", sizeof(wlr_output->model));
|
||||
snprintf(wlr_output->name, sizeof(wlr_output->name), "HEADLESS-%zd",
|
||||
++backend->last_output_num);
|
||||
|
||||
char name[64];
|
||||
snprintf(name, sizeof(name), "HEADLESS-%zu", ++backend->last_output_num);
|
||||
wlr_output_set_name(wlr_output, name);
|
||||
|
||||
char description[128];
|
||||
snprintf(description, sizeof(description),
|
||||
"Headless output %zd", backend->last_output_num);
|
||||
"Headless output %zu", backend->last_output_num);
|
||||
wlr_output_set_description(wlr_output, description);
|
||||
|
||||
if (!output_attach_render(wlr_output, NULL)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height);
|
||||
wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 });
|
||||
wlr_renderer_end(backend->renderer);
|
||||
|
||||
struct wl_event_loop *ev = wl_display_get_event_loop(backend->display);
|
||||
output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output);
|
||||
|
||||
|
@ -226,8 +138,4 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
|
|||
}
|
||||
|
||||
return wlr_output;
|
||||
|
||||
error:
|
||||
wlr_output_destroy(&output->wlr_output);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <assert.h>
|
||||
#include <libinput.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <wlr/backend/interface.h>
|
||||
#include <wlr/backend/session.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
@ -16,12 +17,27 @@ static struct wlr_libinput_backend *get_libinput_backend_from_backend(
|
|||
static int libinput_open_restricted(const char *path,
|
||||
int flags, void *_backend) {
|
||||
struct wlr_libinput_backend *backend = _backend;
|
||||
return wlr_session_open_file(backend->session, path);
|
||||
struct wlr_device *dev = wlr_session_open_file(backend->session, path);
|
||||
if (dev == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return dev->fd;
|
||||
}
|
||||
|
||||
static void libinput_close_restricted(int fd, void *_backend) {
|
||||
struct wlr_libinput_backend *backend = _backend;
|
||||
wlr_session_close_file(backend->session, fd);
|
||||
|
||||
struct wlr_device *dev;
|
||||
bool found = false;
|
||||
wl_list_for_each(dev, &backend->session->devices, link) {
|
||||
if (dev->fd == fd) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
wlr_session_close_file(backend->session, dev);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct libinput_interface libinput_impl = {
|
||||
|
@ -31,9 +47,10 @@ static const struct libinput_interface libinput_impl = {
|
|||
|
||||
static int handle_libinput_readable(int fd, uint32_t mask, void *_backend) {
|
||||
struct wlr_libinput_backend *backend = _backend;
|
||||
if (libinput_dispatch(backend->libinput_context) != 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to dispatch libinput");
|
||||
// TODO: some kind of abort?
|
||||
int ret = libinput_dispatch(backend->libinput_context);
|
||||
if (ret != 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to dispatch libinput: %s", strerror(-ret));
|
||||
wl_display_terminate(backend->display);
|
||||
return 0;
|
||||
}
|
||||
struct libinput_event *event;
|
||||
|
@ -67,7 +84,7 @@ static void log_libinput(struct libinput *libinput_context,
|
|||
static bool backend_start(struct wlr_backend *wlr_backend) {
|
||||
struct wlr_libinput_backend *backend =
|
||||
get_libinput_backend_from_backend(wlr_backend);
|
||||
wlr_log(WLR_DEBUG, "Initializing libinput");
|
||||
wlr_log(WLR_DEBUG, "Starting libinput backend");
|
||||
|
||||
backend->libinput_context = libinput_udev_create_context(&libinput_impl,
|
||||
backend, backend->session->udev);
|
||||
|
@ -93,9 +110,9 @@ static bool backend_start(struct wlr_backend *wlr_backend) {
|
|||
no_devs = NULL;
|
||||
}
|
||||
}
|
||||
if (!no_devs && backend->wlr_device_lists.length == 0) {
|
||||
if (!no_devs && backend->wlr_device_lists.size == 0) {
|
||||
handle_libinput_readable(libinput_fd, WL_EVENT_READABLE, backend);
|
||||
if (backend->wlr_device_lists.length == 0) {
|
||||
if (backend->wlr_device_lists.size == 0) {
|
||||
wlr_log(WLR_ERROR, "libinput initialization failed, no input devices");
|
||||
wlr_log(WLR_ERROR, "Set WLR_LIBINPUT_NO_DEVICES=1 to suppress this check");
|
||||
return false;
|
||||
|
@ -124,22 +141,22 @@ static void backend_destroy(struct wlr_backend *wlr_backend) {
|
|||
struct wlr_libinput_backend *backend =
|
||||
get_libinput_backend_from_backend(wlr_backend);
|
||||
|
||||
for (size_t i = 0; i < backend->wlr_device_lists.length; i++) {
|
||||
struct wl_list *wlr_devices = backend->wlr_device_lists.items[i];
|
||||
struct wlr_input_device *wlr_dev, *next;
|
||||
wl_list_for_each_safe(wlr_dev, next, wlr_devices, link) {
|
||||
wlr_input_device_destroy(wlr_dev);
|
||||
struct wl_list **wlr_devices_ptr;
|
||||
wl_array_for_each(wlr_devices_ptr, &backend->wlr_device_lists) {
|
||||
struct wlr_libinput_input_device *dev, *tmp;
|
||||
wl_list_for_each_safe(dev, tmp, *wlr_devices_ptr, link) {
|
||||
wlr_input_device_destroy(&dev->wlr_input_device);
|
||||
}
|
||||
free(wlr_devices);
|
||||
free(*wlr_devices_ptr);
|
||||
}
|
||||
|
||||
wlr_signal_emit_safe(&wlr_backend->events.destroy, wlr_backend);
|
||||
wlr_backend_finish(wlr_backend);
|
||||
|
||||
wl_list_remove(&backend->display_destroy.link);
|
||||
wl_list_remove(&backend->session_destroy.link);
|
||||
wl_list_remove(&backend->session_signal.link);
|
||||
|
||||
wlr_list_finish(&backend->wlr_device_lists);
|
||||
wl_array_release(&backend->wlr_device_lists);
|
||||
if (backend->input_event) {
|
||||
wl_event_source_remove(backend->input_event);
|
||||
}
|
||||
|
@ -159,7 +176,7 @@ bool wlr_backend_is_libinput(struct wlr_backend *b) {
|
|||
static void session_signal(struct wl_listener *listener, void *data) {
|
||||
struct wlr_libinput_backend *backend =
|
||||
wl_container_of(listener, backend, session_signal);
|
||||
struct wlr_session *session = data;
|
||||
struct wlr_session *session = backend->session;
|
||||
|
||||
if (!backend->libinput_context) {
|
||||
return;
|
||||
|
@ -194,16 +211,13 @@ struct wlr_backend *wlr_libinput_backend_create(struct wl_display *display,
|
|||
}
|
||||
wlr_backend_init(&backend->backend, &backend_impl);
|
||||
|
||||
if (!wlr_list_init(&backend->wlr_device_lists)) {
|
||||
wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno));
|
||||
goto error_backend;
|
||||
}
|
||||
wl_array_init(&backend->wlr_device_lists);
|
||||
|
||||
backend->session = session;
|
||||
backend->display = display;
|
||||
|
||||
backend->session_signal.notify = session_signal;
|
||||
wl_signal_add(&session->session_signal, &backend->session_signal);
|
||||
wl_signal_add(&session->events.active, &backend->session_signal);
|
||||
|
||||
backend->session_destroy.notify = handle_session_destroy;
|
||||
wl_signal_add(&session->events.destroy, &backend->session_destroy);
|
||||
|
@ -212,9 +226,6 @@ struct wlr_backend *wlr_libinput_backend_create(struct wl_display *display,
|
|||
wl_display_add_destroy_listener(display, &backend->display_destroy);
|
||||
|
||||
return &backend->backend;
|
||||
error_backend:
|
||||
free(backend);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct libinput_device *wlr_libinput_get_device_handle(
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <wlr/interfaces/wlr_input_device.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "backend/libinput.h"
|
||||
#include "util/array.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
static struct wlr_libinput_input_device *get_libinput_device_from_device(
|
||||
|
@ -22,10 +23,10 @@ struct wlr_input_device *get_appropriate_device(
|
|||
if (!wlr_devices) {
|
||||
return NULL;
|
||||
}
|
||||
struct wlr_input_device *dev;
|
||||
struct wlr_libinput_input_device *dev;
|
||||
wl_list_for_each(dev, wlr_devices, link) {
|
||||
if (dev->type == desired_type) {
|
||||
return dev;
|
||||
if (dev->wlr_input_device.type == desired_type) {
|
||||
return &dev->wlr_input_device;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
@ -35,7 +36,7 @@ static void input_device_destroy(struct wlr_input_device *wlr_dev) {
|
|||
struct wlr_libinput_input_device *dev =
|
||||
get_libinput_device_from_device(wlr_dev);
|
||||
libinput_device_unref(dev->handle);
|
||||
wl_list_remove(&dev->wlr_input_device.link);
|
||||
wl_list_remove(&dev->link);
|
||||
free(dev);
|
||||
}
|
||||
|
||||
|
@ -62,7 +63,7 @@ static struct wlr_input_device *allocate_device(
|
|||
if (output_name != NULL) {
|
||||
wlr_dev->output_name = strdup(output_name);
|
||||
}
|
||||
wl_list_insert(wlr_devices, &wlr_dev->link);
|
||||
wl_list_insert(wlr_devices, &dev->link);
|
||||
dev->handle = libinput_dev;
|
||||
libinput_device_ref(libinput_dev);
|
||||
wlr_input_device_init(wlr_dev, type, &input_device_impl,
|
||||
|
@ -183,8 +184,13 @@ static void handle_device_added(struct wlr_libinput_backend *backend,
|
|||
}
|
||||
|
||||
if (!wl_list_empty(wlr_devices)) {
|
||||
struct wl_list **dst = wl_array_add(&backend->wlr_device_lists, sizeof(wlr_devices));
|
||||
if (!dst) {
|
||||
goto fail;
|
||||
}
|
||||
*dst = wlr_devices;
|
||||
|
||||
libinput_device_set_user_data(libinput_dev, wlr_devices);
|
||||
wlr_list_push(&backend->wlr_device_lists, wlr_devices);
|
||||
} else {
|
||||
free(wlr_devices);
|
||||
}
|
||||
|
@ -192,7 +198,7 @@ static void handle_device_added(struct wlr_libinput_backend *backend,
|
|||
|
||||
fail:
|
||||
wlr_log(WLR_ERROR, "Could not allocate new device");
|
||||
struct wlr_input_device *dev, *tmp_dev;
|
||||
struct wlr_libinput_input_device *dev, *tmp_dev;
|
||||
wl_list_for_each_safe(dev, tmp_dev, wlr_devices, link) {
|
||||
free(dev);
|
||||
}
|
||||
|
@ -209,15 +215,20 @@ static void handle_device_removed(struct wlr_libinput_backend *backend,
|
|||
if (!wlr_devices) {
|
||||
return;
|
||||
}
|
||||
struct wlr_input_device *dev, *tmp_dev;
|
||||
struct wlr_libinput_input_device *dev, *tmp_dev;
|
||||
wl_list_for_each_safe(dev, tmp_dev, wlr_devices, link) {
|
||||
wlr_input_device_destroy(dev);
|
||||
wlr_input_device_destroy(&dev->wlr_input_device);
|
||||
}
|
||||
for (size_t i = 0; i < backend->wlr_device_lists.length; i++) {
|
||||
if (backend->wlr_device_lists.items[i] == wlr_devices) {
|
||||
wlr_list_del(&backend->wlr_device_lists, i);
|
||||
size_t i = 0;
|
||||
struct wl_list **ptr;
|
||||
wl_array_for_each(ptr, &backend->wlr_device_lists) {
|
||||
struct wl_list *iter = *ptr;
|
||||
if (iter == wlr_devices) {
|
||||
array_remove_at(&backend->wlr_device_lists,
|
||||
i * sizeof(struct wl_list *), sizeof(struct wl_list *));
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
free(wlr_devices);
|
||||
}
|
||||
|
@ -261,7 +272,7 @@ void handle_libinput_event(struct wlr_libinput_backend *backend,
|
|||
handle_touch_cancel(event, libinput_dev);
|
||||
break;
|
||||
case LIBINPUT_EVENT_TOUCH_FRAME:
|
||||
// no-op (at least for now)
|
||||
handle_touch_frame(event, libinput_dev);
|
||||
break;
|
||||
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
|
||||
handle_tablet_tool_axis(event, libinput_dev);
|
||||
|
@ -305,6 +316,14 @@ void handle_libinput_event(struct wlr_libinput_backend *backend,
|
|||
case LIBINPUT_EVENT_GESTURE_PINCH_END:
|
||||
handle_pointer_pinch_end(event, libinput_dev);
|
||||
break;
|
||||
#if LIBINPUT_HAS_HOLD_GESTURES
|
||||
case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN:
|
||||
handle_pointer_hold_begin(event, libinput_dev);
|
||||
break;
|
||||
case LIBINPUT_EVENT_GESTURE_HOLD_END:
|
||||
handle_pointer_hold_end(event, libinput_dev);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
wlr_log(WLR_DEBUG, "Unknown libinput event %d", event_type);
|
||||
break;
|
||||
|
|
|
@ -72,10 +72,10 @@ void handle_keyboard_key(struct libinput_event *event,
|
|||
libinput_event_keyboard_get_key_state(kbevent);
|
||||
switch (state) {
|
||||
case LIBINPUT_KEY_STATE_RELEASED:
|
||||
wlr_event.state = WLR_KEY_RELEASED;
|
||||
wlr_event.state = WL_KEYBOARD_KEY_STATE_RELEASED;
|
||||
break;
|
||||
case LIBINPUT_KEY_STATE_PRESSED:
|
||||
wlr_event.state = WLR_KEY_PRESSED;
|
||||
wlr_event.state = WL_KEYBOARD_KEY_STATE_PRESSED;
|
||||
break;
|
||||
}
|
||||
wlr_event.update_state = true;
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
msg = ['Required for libinput backend support.']
|
||||
if 'libinput' in backends
|
||||
msg += 'Install "libinput" or disable the libinput backend.'
|
||||
endif
|
||||
|
||||
libinput = dependency(
|
||||
'libinput',
|
||||
version: '>=1.14.0',
|
||||
required: 'libinput' in backends,
|
||||
not_found_message: '\n'.join(msg),
|
||||
)
|
||||
|
||||
if not libinput.found()
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
wlr_files += files(
|
||||
'backend.c',
|
||||
'events.c',
|
||||
|
@ -8,3 +24,12 @@ wlr_files += files(
|
|||
'tablet_tool.c',
|
||||
'touch.c',
|
||||
)
|
||||
|
||||
features += { 'libinput-backend': true }
|
||||
wlr_deps += libinput
|
||||
|
||||
# libinput hold gestures are available since 1.19.0
|
||||
add_project_arguments(
|
||||
'-DLIBINPUT_HAS_HOLD_GESTURES=@0@'.format(libinput.version().version_compare('>=1.19.0').to_int()),
|
||||
language: 'c',
|
||||
)
|
||||
|
|
|
@ -260,3 +260,41 @@ void handle_pointer_pinch_end(struct libinput_event *event,
|
|||
};
|
||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_end, &wlr_event);
|
||||
}
|
||||
|
||||
void handle_pointer_hold_begin(struct libinput_event *event,
|
||||
struct libinput_device *libinput_dev) {
|
||||
struct wlr_input_device *wlr_dev =
|
||||
get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev);
|
||||
if (!wlr_dev) {
|
||||
wlr_log(WLR_DEBUG, "Got a pointer gesture event for a device with no pointers?");
|
||||
return;
|
||||
}
|
||||
struct libinput_event_gesture *gevent =
|
||||
libinput_event_get_gesture_event(event);
|
||||
struct wlr_event_pointer_hold_begin wlr_event = {
|
||||
.device = wlr_dev,
|
||||
.time_msec =
|
||||
usec_to_msec(libinput_event_gesture_get_time_usec(gevent)),
|
||||
.fingers = libinput_event_gesture_get_finger_count(gevent),
|
||||
};
|
||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.hold_begin, &wlr_event);
|
||||
}
|
||||
|
||||
void handle_pointer_hold_end(struct libinput_event *event,
|
||||
struct libinput_device *libinput_dev) {
|
||||
struct wlr_input_device *wlr_dev =
|
||||
get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev);
|
||||
if (!wlr_dev) {
|
||||
wlr_log(WLR_DEBUG, "Got a pointer gesture event for a device with no pointers?");
|
||||
return;
|
||||
}
|
||||
struct libinput_event_gesture *gevent =
|
||||
libinput_event_get_gesture_event(event);
|
||||
struct wlr_event_pointer_hold_end wlr_event = {
|
||||
.device = wlr_dev,
|
||||
.time_msec =
|
||||
usec_to_msec(libinput_event_gesture_get_time_usec(gevent)),
|
||||
.cancelled = libinput_event_gesture_get_cancelled(gevent),
|
||||
};
|
||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.hold_end, &wlr_event);
|
||||
}
|
||||
|
|
|
@ -85,7 +85,8 @@ struct wlr_tablet_pad *create_libinput_tablet_pad(
|
|||
libinput_device_tablet_pad_get_num_strips(libinput_dev);
|
||||
|
||||
struct udev_device *udev = libinput_device_get_udev_device(libinput_dev);
|
||||
wlr_list_push(&wlr_tablet_pad->paths, strdup(udev_device_get_syspath(udev)));
|
||||
char **dst = wl_array_add(&wlr_tablet_pad->paths, sizeof(char *));
|
||||
*dst = strdup(udev_device_get_syspath(udev));
|
||||
|
||||
int groups = libinput_device_tablet_pad_get_num_mode_groups(libinput_dev);
|
||||
for (int i = 0; i < groups; ++i) {
|
||||
|
|
|
@ -11,9 +11,10 @@
|
|||
#include <wlr/types/wlr_input_device.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "backend/libinput.h"
|
||||
#include "util/array.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
static struct wlr_tablet_impl tablet_impl;
|
||||
static const struct wlr_tablet_impl tablet_impl;
|
||||
|
||||
static bool tablet_is_libinput(struct wlr_tablet *tablet) {
|
||||
return tablet->impl == &tablet_impl;
|
||||
|
@ -29,18 +30,9 @@ struct wlr_libinput_tablet_tool {
|
|||
size_t pad_refs;
|
||||
};
|
||||
|
||||
// TODO: Maybe this should be a wlr_list? Do we keep it, or want to get rid of
|
||||
// it?
|
||||
struct tablet_tool_list_elem {
|
||||
struct wl_list link;
|
||||
|
||||
struct wlr_libinput_tablet_tool *tool;
|
||||
};
|
||||
|
||||
struct wlr_libinput_tablet {
|
||||
struct wlr_tablet wlr_tablet;
|
||||
|
||||
struct wl_list tools; // tablet_tool_list_elem::link
|
||||
struct wl_array tools; // struct wlr_libinput_tablet_tool *
|
||||
};
|
||||
|
||||
static void destroy_tool(struct wlr_libinput_tablet_tool *tool) {
|
||||
|
@ -56,22 +48,19 @@ static void destroy_tablet(struct wlr_tablet *wlr_tablet) {
|
|||
struct wlr_libinput_tablet *tablet =
|
||||
wl_container_of(wlr_tablet, tablet, wlr_tablet);
|
||||
|
||||
struct tablet_tool_list_elem *pos;
|
||||
struct tablet_tool_list_elem *tmp;
|
||||
wl_list_for_each_safe(pos, tmp, &tablet->tools, link) {
|
||||
struct wlr_libinput_tablet_tool *tool = pos->tool;
|
||||
wl_list_remove(&pos->link);
|
||||
free(pos);
|
||||
|
||||
struct wlr_libinput_tablet_tool **tool_ptr;
|
||||
wl_array_for_each(tool_ptr, &tablet->tools) {
|
||||
struct wlr_libinput_tablet_tool *tool = *tool_ptr;
|
||||
if (--tool->pad_refs == 0) {
|
||||
destroy_tool(tool);
|
||||
}
|
||||
}
|
||||
wl_array_release(&tablet->tools);
|
||||
|
||||
free(tablet);
|
||||
}
|
||||
|
||||
static struct wlr_tablet_impl tablet_impl = {
|
||||
static const struct wlr_tablet_impl tablet_impl = {
|
||||
.destroy = destroy_tablet,
|
||||
};
|
||||
|
||||
|
@ -84,15 +73,18 @@ struct wlr_tablet *create_libinput_tablet(
|
|||
wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_tool");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_tablet *wlr_tablet = &libinput_tablet->wlr_tablet;
|
||||
|
||||
wlr_list_init(&wlr_tablet->paths);
|
||||
struct udev_device *udev = libinput_device_get_udev_device(libinput_dev);
|
||||
wlr_list_push(&wlr_tablet->paths, strdup(udev_device_get_syspath(udev)));
|
||||
wlr_tablet->name = strdup(libinput_device_get_name(libinput_dev));
|
||||
wl_list_init(&libinput_tablet->tools);
|
||||
|
||||
wlr_tablet_init(wlr_tablet, &tablet_impl);
|
||||
|
||||
struct udev_device *udev = libinput_device_get_udev_device(libinput_dev);
|
||||
char **dst = wl_array_add(&wlr_tablet->paths, sizeof(char *));
|
||||
*dst = strdup(udev_device_get_syspath(udev));
|
||||
|
||||
wlr_tablet->name = strdup(libinput_device_get_name(libinput_dev));
|
||||
|
||||
wl_array_init(&libinput_tablet->tools);
|
||||
|
||||
return wlr_tablet;
|
||||
}
|
||||
|
||||
|
@ -113,10 +105,8 @@ static enum wlr_tablet_tool_type wlr_type_from_libinput_type(
|
|||
return WLR_TABLET_TOOL_TYPE_MOUSE;
|
||||
case LIBINPUT_TABLET_TOOL_TYPE_LENS:
|
||||
return WLR_TABLET_TOOL_TYPE_LENS;
|
||||
#if LIBINPUT_MINOR >= 14
|
||||
case LIBINPUT_TABLET_TOOL_TYPE_TOTEM:
|
||||
return WLR_TABLET_TOOL_TYPE_TOTEM;
|
||||
#endif
|
||||
}
|
||||
abort(); // unreachable
|
||||
}
|
||||
|
@ -162,9 +152,9 @@ static void ensure_tool_reference(struct wlr_libinput_tablet_tool *tool,
|
|||
struct wlr_libinput_tablet *tablet =
|
||||
wl_container_of(wlr_dev, tablet, wlr_tablet);
|
||||
|
||||
struct tablet_tool_list_elem *pos;
|
||||
wl_list_for_each(pos, &tablet->tools, link) {
|
||||
if (pos->tool == tool) { // We already have a ref
|
||||
struct wlr_libinput_tablet_tool **tool_ptr;
|
||||
wl_array_for_each(tool_ptr, &tablet->tools) {
|
||||
if (*tool_ptr == tool) { // We already have a ref
|
||||
// XXX: We *could* optimize the tool to the front of
|
||||
// the list here, since we will probably get the next
|
||||
// couple of events from the same tool.
|
||||
|
@ -175,15 +165,13 @@ static void ensure_tool_reference(struct wlr_libinput_tablet_tool *tool,
|
|||
}
|
||||
}
|
||||
|
||||
struct tablet_tool_list_elem *new =
|
||||
calloc(1, sizeof(struct tablet_tool_list_elem));
|
||||
if (!new) {
|
||||
struct wlr_libinput_tablet_tool **dst =
|
||||
wl_array_add(&tablet->tools, sizeof(tool));
|
||||
if (!dst) {
|
||||
wlr_log(WLR_ERROR, "Failed to allocate memory for tracking tablet tool");
|
||||
return;
|
||||
}
|
||||
|
||||
new->tool = tool;
|
||||
wl_list_insert(&tablet->tools, &new->link);
|
||||
*dst = tool;
|
||||
++tool->pad_refs;
|
||||
}
|
||||
|
||||
|
@ -296,15 +284,15 @@ void handle_tablet_tool_proximity(struct libinput_event *event,
|
|||
assert(tablet_is_libinput(wlr_dev->tablet));
|
||||
struct wlr_libinput_tablet *tablet =
|
||||
wl_container_of(wlr_dev->tablet, tablet, wlr_tablet);
|
||||
struct tablet_tool_list_elem *pos;
|
||||
struct tablet_tool_list_elem *tmp;
|
||||
|
||||
wl_list_for_each_safe(pos, tmp, &tablet->tools, link) {
|
||||
if (pos->tool == tool) {
|
||||
wl_list_remove(&pos->link);
|
||||
free(pos);
|
||||
size_t i = 0;
|
||||
struct wlr_libinput_tablet_tool **tool_ptr;
|
||||
wl_array_for_each(tool_ptr, &tablet->tools) {
|
||||
if (*tool_ptr == tool) {
|
||||
array_remove_at(&tablet->tools, i * sizeof(tool), sizeof(tool));
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
destroy_tool(tool);
|
||||
|
|
|
@ -95,3 +95,14 @@ void handle_touch_cancel(struct libinput_event *event,
|
|||
wlr_event.touch_id = libinput_event_touch_get_seat_slot(tevent);
|
||||
wlr_signal_emit_safe(&wlr_dev->touch->events.cancel, &wlr_event);
|
||||
}
|
||||
|
||||
void handle_touch_frame(struct libinput_event *event,
|
||||
struct libinput_device *libinput_dev) {
|
||||
struct wlr_input_device *wlr_dev =
|
||||
get_appropriate_device(WLR_INPUT_DEVICE_TOUCH, libinput_dev);
|
||||
if (!wlr_dev) {
|
||||
wlr_log(WLR_DEBUG, "Got a touch event for a device with no touch?");
|
||||
return;
|
||||
}
|
||||
wlr_signal_emit_safe(&wlr_dev->touch->events.frame, NULL);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
wlr_files += files('backend.c')
|
||||
|
||||
subdir('drm')
|
||||
subdir('headless')
|
||||
subdir('libinput')
|
||||
all_backends = ['drm', 'libinput', 'x11']
|
||||
backends = get_option('backends')
|
||||
if 'auto' in backends and get_option('auto_features').enabled()
|
||||
backends = all_backends
|
||||
elif 'auto' in backends and get_option('auto_features').disabled()
|
||||
backends = []
|
||||
endif
|
||||
|
||||
foreach backend : all_backends
|
||||
if backend in backends or 'auto' in backends
|
||||
subdir(backend)
|
||||
endif
|
||||
endforeach
|
||||
|
||||
subdir('multi')
|
||||
subdir('noop')
|
||||
subdir('wayland')
|
||||
subdir('x11')
|
||||
subdir('headless')
|
||||
|
||||
subdir('session')
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
#include <time.h>
|
||||
#include <wlr/backend/interface.h>
|
||||
#include <wlr/backend/session.h>
|
||||
#include <wlr/types/wlr_buffer.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "backend/backend.h"
|
||||
#include "backend/multi.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
|
@ -58,24 +60,10 @@ static void multi_backend_destroy(struct wlr_backend *wlr_backend) {
|
|||
}
|
||||
|
||||
// Destroy this backend only after removing all sub-backends
|
||||
wlr_signal_emit_safe(&wlr_backend->events.destroy, backend);
|
||||
wlr_backend_finish(wlr_backend);
|
||||
free(backend);
|
||||
}
|
||||
|
||||
static struct wlr_renderer *multi_backend_get_renderer(
|
||||
struct wlr_backend *backend) {
|
||||
struct wlr_multi_backend *multi = multi_backend_from_backend(backend);
|
||||
|
||||
struct subbackend_state *sub;
|
||||
wl_list_for_each(sub, &multi->backends, link) {
|
||||
struct wlr_renderer *rend = wlr_backend_get_renderer(sub->backend);
|
||||
if (rend != NULL) {
|
||||
return rend;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct wlr_session *multi_backend_get_session(
|
||||
struct wlr_backend *_backend) {
|
||||
struct wlr_multi_backend *backend = multi_backend_from_backend(_backend);
|
||||
|
@ -96,12 +84,48 @@ static clockid_t multi_backend_get_presentation_clock(
|
|||
return CLOCK_MONOTONIC;
|
||||
}
|
||||
|
||||
struct wlr_backend_impl backend_impl = {
|
||||
static int multi_backend_get_drm_fd(struct wlr_backend *backend) {
|
||||
struct wlr_multi_backend *multi = multi_backend_from_backend(backend);
|
||||
|
||||
struct subbackend_state *sub;
|
||||
wl_list_for_each(sub, &multi->backends, link) {
|
||||
if (sub->backend->impl->get_drm_fd) {
|
||||
return wlr_backend_get_drm_fd(sub->backend);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static uint32_t multi_backend_get_buffer_caps(struct wlr_backend *backend) {
|
||||
struct wlr_multi_backend *multi = multi_backend_from_backend(backend);
|
||||
|
||||
if (wl_list_empty(&multi->backends)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t caps = WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_DMABUF
|
||||
| WLR_BUFFER_CAP_SHM;
|
||||
|
||||
struct subbackend_state *sub;
|
||||
wl_list_for_each(sub, &multi->backends, link) {
|
||||
uint32_t backend_caps = backend_get_buffer_caps(sub->backend);
|
||||
if (backend_caps != 0) {
|
||||
// only count backend capable of presenting a buffer
|
||||
caps = caps & backend_caps;
|
||||
}
|
||||
}
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
static const struct wlr_backend_impl backend_impl = {
|
||||
.start = multi_backend_start,
|
||||
.destroy = multi_backend_destroy,
|
||||
.get_renderer = multi_backend_get_renderer,
|
||||
.get_session = multi_backend_get_session,
|
||||
.get_presentation_clock = multi_backend_get_presentation_clock,
|
||||
.get_drm_fd = multi_backend_get_drm_fd,
|
||||
.get_buffer_caps = multi_backend_get_buffer_caps,
|
||||
};
|
||||
|
||||
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||
|
@ -165,6 +189,9 @@ static struct subbackend_state *multi_backend_get_subbackend(struct wlr_multi_ba
|
|||
|
||||
bool wlr_multi_backend_add(struct wlr_backend *_multi,
|
||||
struct wlr_backend *backend) {
|
||||
assert(_multi && backend);
|
||||
assert(_multi != backend);
|
||||
|
||||
struct wlr_multi_backend *multi = multi_backend_from_backend(_multi);
|
||||
|
||||
if (multi_backend_get_subbackend(multi, backend)) {
|
||||
|
@ -172,15 +199,6 @@ bool wlr_multi_backend_add(struct wlr_backend *_multi,
|
|||
return true;
|
||||
}
|
||||
|
||||
struct wlr_renderer *multi_renderer =
|
||||
multi_backend_get_renderer(&multi->backend);
|
||||
struct wlr_renderer *backend_renderer = wlr_backend_get_renderer(backend);
|
||||
if (multi_renderer != NULL && backend_renderer != NULL && multi_renderer != backend_renderer) {
|
||||
wlr_log(WLR_ERROR, "Could not add backend: multiple renderers at the "
|
||||
"same time aren't supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct subbackend_state *sub = calloc(1, sizeof(struct subbackend_state));
|
||||
if (sub == NULL) {
|
||||
wlr_log(WLR_ERROR, "Could not add backend: allocation failed");
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <wlr/interfaces/wlr_output.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "backend/noop.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
struct wlr_noop_backend *noop_backend_from_backend(
|
||||
struct wlr_backend *wlr_backend) {
|
||||
assert(wlr_backend_is_noop(wlr_backend));
|
||||
return (struct wlr_noop_backend *)wlr_backend;
|
||||
}
|
||||
|
||||
static bool backend_start(struct wlr_backend *wlr_backend) {
|
||||
struct wlr_noop_backend *backend = noop_backend_from_backend(wlr_backend);
|
||||
wlr_log(WLR_INFO, "Starting noop backend");
|
||||
|
||||
struct wlr_noop_output *output;
|
||||
wl_list_for_each(output, &backend->outputs, link) {
|
||||
wlr_output_update_enabled(&output->wlr_output, true);
|
||||
wlr_signal_emit_safe(&backend->backend.events.new_output,
|
||||
&output->wlr_output);
|
||||
}
|
||||
|
||||
backend->started = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void backend_destroy(struct wlr_backend *wlr_backend) {
|
||||
struct wlr_noop_backend *backend = noop_backend_from_backend(wlr_backend);
|
||||
if (!wlr_backend) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct wlr_noop_output *output, *output_tmp;
|
||||
wl_list_for_each_safe(output, output_tmp, &backend->outputs, link) {
|
||||
wlr_output_destroy(&output->wlr_output);
|
||||
}
|
||||
|
||||
wlr_signal_emit_safe(&wlr_backend->events.destroy, backend);
|
||||
|
||||
free(backend);
|
||||
}
|
||||
|
||||
static const struct wlr_backend_impl backend_impl = {
|
||||
.start = backend_start,
|
||||
.destroy = backend_destroy,
|
||||
};
|
||||
|
||||
struct wlr_backend *wlr_noop_backend_create(struct wl_display *display) {
|
||||
wlr_log(WLR_INFO, "Creating noop backend");
|
||||
|
||||
struct wlr_noop_backend *backend =
|
||||
calloc(1, sizeof(struct wlr_noop_backend));
|
||||
if (!backend) {
|
||||
wlr_log(WLR_ERROR, "Failed to allocate wlr_noop_backend");
|
||||
return NULL;
|
||||
}
|
||||
wlr_backend_init(&backend->backend, &backend_impl);
|
||||
backend->display = display;
|
||||
wl_list_init(&backend->outputs);
|
||||
|
||||
return &backend->backend;
|
||||
}
|
||||
|
||||
bool wlr_backend_is_noop(struct wlr_backend *backend) {
|
||||
return backend->impl == &backend_impl;
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
wlr_files += files(
|
||||
'backend.c',
|
||||
'output.c',
|
||||
)
|
|
@ -1,91 +0,0 @@
|
|||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <wlr/interfaces/wlr_output.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "backend/noop.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
static struct wlr_noop_output *noop_output_from_output(
|
||||
struct wlr_output *wlr_output) {
|
||||
assert(wlr_output_is_noop(wlr_output));
|
||||
return (struct wlr_noop_output *)wlr_output;
|
||||
}
|
||||
|
||||
static bool output_attach_render(struct wlr_output *wlr_output,
|
||||
int *buffer_age) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static void output_rollback_render(struct wlr_output *wlr_output) {
|
||||
// This space is intentionally left blank
|
||||
}
|
||||
|
||||
static bool output_commit(struct wlr_output *wlr_output) {
|
||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_ENABLED) {
|
||||
wlr_log(WLR_DEBUG, "Cannot disable a noop output");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
|
||||
assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM);
|
||||
wlr_output_update_custom_mode(wlr_output,
|
||||
wlr_output->pending.custom_mode.width,
|
||||
wlr_output->pending.custom_mode.height,
|
||||
wlr_output->pending.custom_mode.refresh);
|
||||
}
|
||||
|
||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void output_destroy(struct wlr_output *wlr_output) {
|
||||
struct wlr_noop_output *output =
|
||||
noop_output_from_output(wlr_output);
|
||||
|
||||
wl_list_remove(&output->link);
|
||||
|
||||
free(output);
|
||||
}
|
||||
|
||||
static const struct wlr_output_impl output_impl = {
|
||||
.destroy = output_destroy,
|
||||
.attach_render = output_attach_render,
|
||||
.rollback_render = output_rollback_render,
|
||||
.commit = output_commit,
|
||||
};
|
||||
|
||||
bool wlr_output_is_noop(struct wlr_output *wlr_output) {
|
||||
return wlr_output->impl == &output_impl;
|
||||
}
|
||||
|
||||
struct wlr_output *wlr_noop_add_output(struct wlr_backend *wlr_backend) {
|
||||
struct wlr_noop_backend *backend = noop_backend_from_backend(wlr_backend);
|
||||
|
||||
struct wlr_noop_output *output = calloc(1, sizeof(struct wlr_noop_output));
|
||||
if (output == NULL) {
|
||||
wlr_log(WLR_ERROR, "Failed to allocate wlr_noop_output");
|
||||
return NULL;
|
||||
}
|
||||
output->backend = backend;
|
||||
wlr_output_init(&output->wlr_output, &backend->backend, &output_impl,
|
||||
backend->display);
|
||||
struct wlr_output *wlr_output = &output->wlr_output;
|
||||
|
||||
strncpy(wlr_output->make, "noop", sizeof(wlr_output->make));
|
||||
strncpy(wlr_output->model, "noop", sizeof(wlr_output->model));
|
||||
snprintf(wlr_output->name, sizeof(wlr_output->name), "NOOP-%zd",
|
||||
++backend->last_output_num);
|
||||
|
||||
wl_list_insert(&backend->outputs, &output->link);
|
||||
|
||||
if (backend->started) {
|
||||
wlr_output_update_enabled(wlr_output, true);
|
||||
wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output);
|
||||
}
|
||||
|
||||
return wlr_output;
|
||||
}
|
|
@ -1,319 +0,0 @@
|
|||
#include <assert.h>
|
||||
#include <linux/input.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/consio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/kbio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend/session/interface.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <xf86drm.h>
|
||||
#include "backend/session/direct-ipc.h"
|
||||
#include "backend/session/session.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
const struct session_impl session_direct;
|
||||
|
||||
struct direct_session {
|
||||
struct wlr_session base;
|
||||
int tty_fd;
|
||||
int old_tty;
|
||||
int old_kbmode;
|
||||
int sock;
|
||||
pid_t child;
|
||||
|
||||
struct wl_event_source *vt_source;
|
||||
};
|
||||
|
||||
static struct direct_session *direct_session_from_session(
|
||||
struct wlr_session *base) {
|
||||
assert(base->impl == &session_direct);
|
||||
return (struct direct_session *)base;
|
||||
}
|
||||
|
||||
static int direct_session_open(struct wlr_session *base, const char *path) {
|
||||
struct direct_session *session = direct_session_from_session(base);
|
||||
|
||||
int fd = direct_ipc_open(session->sock, path);
|
||||
if (fd < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to open %s: %s%s", path, strerror(-fd),
|
||||
fd == -EINVAL ? "; is another display server running?" : "");
|
||||
return fd;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void direct_session_close(struct wlr_session *base, int fd) {
|
||||
struct direct_session *session = direct_session_from_session(base);
|
||||
|
||||
int ev;
|
||||
struct drm_version dv = {0};
|
||||
if (ioctl(fd, DRM_IOCTL_VERSION, &dv) == 0) {
|
||||
direct_ipc_dropmaster(session->sock, fd);
|
||||
} else if (ioctl(fd, EVIOCGVERSION, &ev) == 0) {
|
||||
ioctl(fd, EVIOCREVOKE, 0);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static bool direct_change_vt(struct wlr_session *base, unsigned vt) {
|
||||
struct direct_session *session = direct_session_from_session(base);
|
||||
|
||||
// Only seat0 has VTs associated with it
|
||||
if (strcmp(session->base.seat, "seat0") != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ioctl(session->tty_fd, VT_ACTIVATE, (int)vt) == 0;
|
||||
}
|
||||
|
||||
static void direct_session_destroy(struct wlr_session *base) {
|
||||
struct direct_session *session = direct_session_from_session(base);
|
||||
|
||||
if (strcmp(session->base.seat, "seat0") == 0) {
|
||||
struct vt_mode mode = {
|
||||
.mode = VT_AUTO,
|
||||
};
|
||||
|
||||
errno = 0;
|
||||
|
||||
ioctl(session->tty_fd, KDSKBMODE, session->old_kbmode);
|
||||
ioctl(session->tty_fd, KDSETMODE, KD_TEXT);
|
||||
ioctl(session->tty_fd, VT_SETMODE, &mode);
|
||||
|
||||
ioctl(session->tty_fd, VT_ACTIVATE, session->old_tty);
|
||||
|
||||
if (errno) {
|
||||
wlr_log(WLR_ERROR, "Failed to restore tty");
|
||||
}
|
||||
|
||||
wl_event_source_remove(session->vt_source);
|
||||
close(session->tty_fd);
|
||||
}
|
||||
|
||||
direct_ipc_finish(session->sock, session->child);
|
||||
close(session->sock);
|
||||
|
||||
free(session);
|
||||
}
|
||||
|
||||
static int vt_handler(int signo, void *data) {
|
||||
struct direct_session *session = data;
|
||||
struct drm_version dv = {0};
|
||||
struct wlr_device *dev;
|
||||
|
||||
if (session->base.active) {
|
||||
session->base.active = false;
|
||||
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||
|
||||
wl_list_for_each(dev, &session->base.devices, link) {
|
||||
if (ioctl(dev->fd, DRM_IOCTL_VERSION, &dv) == 0) {
|
||||
direct_ipc_dropmaster(session->sock, dev->fd);
|
||||
}
|
||||
}
|
||||
|
||||
ioctl(session->tty_fd, VT_RELDISP, 1);
|
||||
} else {
|
||||
ioctl(session->tty_fd, VT_RELDISP, VT_ACKACQ);
|
||||
|
||||
wl_list_for_each(dev, &session->base.devices, link) {
|
||||
if (ioctl(dev->fd, DRM_IOCTL_VERSION, &dv) == 0) {
|
||||
direct_ipc_setmaster(session->sock, dev->fd);
|
||||
}
|
||||
}
|
||||
|
||||
session->base.active = true;
|
||||
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool get_tty_path(int tty, char path[static 11], size_t len) {
|
||||
assert(tty > 0);
|
||||
|
||||
const char prefix[] = "/dev/ttyv";
|
||||
static_assert(sizeof(prefix) + 1 <= 11, "TTY path prefix is too long");
|
||||
|
||||
const size_t prefix_len = sizeof(prefix) - 1;
|
||||
strcpy(path, prefix);
|
||||
|
||||
size_t offset = prefix_len;
|
||||
const int num = tty - 1;
|
||||
if (num == 0) {
|
||||
path[offset++] = '0';
|
||||
path[offset++] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
const int base = 32;
|
||||
for (int remaning = num; remaning > 0; remaning /= base, offset++) {
|
||||
// Return early if the buffer is too small.
|
||||
if (offset + 1 >= len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int value = remaning % base;
|
||||
if (value >= 10) {
|
||||
path[offset] = 'a' + value - 10;
|
||||
} else {
|
||||
path[offset] = '0' + value;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t num_len = offset - prefix_len;
|
||||
for (size_t i = 0; i < num_len / 2; i++) {
|
||||
const size_t p1 = prefix_len + i;
|
||||
const size_t p2 = offset - 1 - i;
|
||||
const char tmp = path[p1];
|
||||
path[p1] = path[p2];
|
||||
path[p2] = tmp;
|
||||
}
|
||||
|
||||
path[offset++] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setup_tty(struct direct_session *session, struct wl_display *display) {
|
||||
int fd = -1, tty = -1, tty0_fd = -1, old_tty = 1;
|
||||
if ((tty0_fd = open("/dev/ttyv0", O_RDWR | O_CLOEXEC)) < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "Could not open /dev/ttyv0 to find a free vt");
|
||||
goto error;
|
||||
}
|
||||
if (ioctl(tty0_fd, VT_GETACTIVE, &old_tty) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "Could not get active vt");
|
||||
goto error;
|
||||
}
|
||||
if (ioctl(tty0_fd, VT_OPENQRY, &tty) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "Could not find a free vt");
|
||||
goto error;
|
||||
}
|
||||
close(tty0_fd);
|
||||
char tty_path[64];
|
||||
if (!get_tty_path(tty, tty_path, sizeof(tty_path))) {
|
||||
wlr_log(WLR_ERROR, "Could not get tty %d path", tty);
|
||||
goto error;
|
||||
}
|
||||
wlr_log(WLR_INFO, "Using tty %s", tty_path);
|
||||
fd = open(tty_path, O_RDWR | O_NOCTTY | O_CLOEXEC);
|
||||
|
||||
if (fd == -1) {
|
||||
wlr_log_errno(WLR_ERROR, "Cannot open tty");
|
||||
return false;
|
||||
}
|
||||
|
||||
ioctl(fd, VT_ACTIVATE, tty);
|
||||
ioctl(fd, VT_WAITACTIVE, tty);
|
||||
|
||||
int old_kbmode;
|
||||
if (ioctl(fd, KDGKBMODE, &old_kbmode)) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to read tty %d keyboard mode", tty);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ioctl(fd, KDSKBMODE, K_CODE)) {
|
||||
wlr_log_errno(WLR_ERROR,
|
||||
"Failed to set keyboard mode K_CODE on tty %d", tty);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ioctl(fd, KDSETMODE, KD_GRAPHICS)) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to set graphics mode on tty %d", tty);
|
||||
goto error;
|
||||
}
|
||||
|
||||
struct vt_mode mode = {
|
||||
.mode = VT_PROCESS,
|
||||
.relsig = SIGUSR2,
|
||||
.acqsig = SIGUSR2,
|
||||
.frsig = SIGIO, // has to be set
|
||||
};
|
||||
|
||||
if (ioctl(fd, VT_SETMODE, &mode) < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to take control of tty %d", tty);
|
||||
goto error;
|
||||
}
|
||||
|
||||
struct wl_event_loop *loop = wl_display_get_event_loop(display);
|
||||
session->vt_source = wl_event_loop_add_signal(loop, SIGUSR2,
|
||||
vt_handler, session);
|
||||
if (!session->vt_source) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
session->base.vtnr = tty;
|
||||
session->tty_fd = fd;
|
||||
session->old_tty = old_tty;
|
||||
session->old_kbmode = old_kbmode;
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
// In case we could not get the last active one, drop back to tty 1,
|
||||
// better than hanging in a useless blank console. Otherwise activate the
|
||||
// last active.
|
||||
ioctl(fd, VT_ACTIVATE, old_tty);
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct wlr_session *direct_session_create(struct wl_display *disp) {
|
||||
struct direct_session *session = calloc(1, sizeof(*session));
|
||||
if (!session) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
session_init(&session->base);
|
||||
session->sock = direct_ipc_init(&session->child);
|
||||
if (session->sock == -1) {
|
||||
goto error_session;
|
||||
}
|
||||
|
||||
const char *seat = getenv("XDG_SEAT");
|
||||
if (!seat) {
|
||||
seat = "seat0";
|
||||
}
|
||||
|
||||
if (strcmp(seat, "seat0") == 0) {
|
||||
if (!setup_tty(session, disp)) {
|
||||
goto error_ipc;
|
||||
}
|
||||
} else {
|
||||
session->base.vtnr = 0;
|
||||
session->tty_fd = -1;
|
||||
}
|
||||
|
||||
wlr_log(WLR_INFO, "Successfully loaded direct session");
|
||||
|
||||
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat);
|
||||
session->base.impl = &session_direct;
|
||||
session->base.active = true;
|
||||
return &session->base;
|
||||
|
||||
error_ipc:
|
||||
direct_ipc_finish(session->sock, session->child);
|
||||
close(session->sock);
|
||||
error_session:
|
||||
free(session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct session_impl session_direct = {
|
||||
.create = direct_session_create,
|
||||
.destroy = direct_session_destroy,
|
||||
.open = direct_session_open,
|
||||
.close = direct_session_close,
|
||||
.change_vt = direct_change_vt,
|
||||
};
|
|
@ -1,237 +0,0 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#ifdef __FreeBSD__
|
||||
#define __BSD_VISIBLE 1
|
||||
#include <linux/input.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <wlr/config.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <xf86drm.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/sysmacros.h>
|
||||
#include <linux/major.h>
|
||||
#endif
|
||||
#include "backend/session/direct-ipc.h"
|
||||
|
||||
enum { DRM_MAJOR = 226 };
|
||||
|
||||
static void send_msg(int sock, int fd, void *buf, size_t buf_len) {
|
||||
char control[CMSG_SPACE(sizeof(fd))] = {0};
|
||||
struct iovec iovec = { .iov_base = buf, .iov_len = buf_len };
|
||||
struct msghdr msghdr = {0};
|
||||
|
||||
if (buf) {
|
||||
msghdr.msg_iov = &iovec;
|
||||
msghdr.msg_iovlen = 1;
|
||||
}
|
||||
|
||||
if (fd >= 0) {
|
||||
msghdr.msg_control = &control;
|
||||
msghdr.msg_controllen = sizeof(control);
|
||||
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msghdr);
|
||||
*cmsg = (struct cmsghdr) {
|
||||
.cmsg_level = SOL_SOCKET,
|
||||
.cmsg_type = SCM_RIGHTS,
|
||||
.cmsg_len = CMSG_LEN(sizeof(fd)),
|
||||
};
|
||||
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
|
||||
}
|
||||
|
||||
ssize_t ret;
|
||||
do {
|
||||
ret = sendmsg(sock, &msghdr, 0);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
}
|
||||
|
||||
static ssize_t recv_msg(int sock, int *fd_out, void *buf, size_t buf_len) {
|
||||
char control[CMSG_SPACE(sizeof(*fd_out))] = {0};
|
||||
struct iovec iovec = { .iov_base = buf, .iov_len = buf_len };
|
||||
struct msghdr msghdr = {0};
|
||||
|
||||
if (buf) {
|
||||
msghdr.msg_iov = &iovec;
|
||||
msghdr.msg_iovlen = 1;
|
||||
}
|
||||
|
||||
if (fd_out) {
|
||||
msghdr.msg_control = &control;
|
||||
msghdr.msg_controllen = sizeof(control);
|
||||
}
|
||||
|
||||
ssize_t ret;
|
||||
do {
|
||||
ret = recvmsg(sock, &msghdr, MSG_CMSG_CLOEXEC);
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
|
||||
if (fd_out) {
|
||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msghdr);
|
||||
if (cmsg) {
|
||||
memcpy(fd_out, CMSG_DATA(cmsg), sizeof(*fd_out));
|
||||
} else {
|
||||
*fd_out = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum msg_type {
|
||||
MSG_OPEN,
|
||||
MSG_SETMASTER,
|
||||
MSG_DROPMASTER,
|
||||
MSG_END,
|
||||
};
|
||||
|
||||
struct msg {
|
||||
enum msg_type type;
|
||||
char path[256];
|
||||
};
|
||||
|
||||
static void communicate(int sock) {
|
||||
struct msg msg;
|
||||
int drm_fd = -1;
|
||||
bool running = true;
|
||||
|
||||
while (running && recv_msg(sock, &drm_fd, &msg, sizeof(msg)) > 0) {
|
||||
switch (msg.type) {
|
||||
case MSG_OPEN:
|
||||
errno = 0;
|
||||
|
||||
// These are the same flags that logind opens files with
|
||||
int fd = open(msg.path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
|
||||
int ret = errno;
|
||||
if (fd == -1) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifndef __FreeBSD__
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) < 0) {
|
||||
ret = errno;
|
||||
goto error;
|
||||
}
|
||||
|
||||
uint32_t maj = major(st.st_rdev);
|
||||
if (maj != INPUT_MAJOR && maj != DRM_MAJOR) {
|
||||
ret = ENOTSUP;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (maj == DRM_MAJOR && drmSetMaster(fd)) {
|
||||
ret = errno;
|
||||
}
|
||||
#else
|
||||
int ev;
|
||||
struct drm_version dv = {0};
|
||||
if (ioctl(fd, EVIOCGVERSION, &ev) == -1 &&
|
||||
ioctl(fd, DRM_IOCTL_VERSION, &dv) == -1) {
|
||||
ret = ENOTSUP;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (dv.version_major != 0 && drmSetMaster(fd)) {
|
||||
ret = errno;
|
||||
}
|
||||
#endif
|
||||
|
||||
error:
|
||||
send_msg(sock, ret ? -1 : fd, &ret, sizeof(ret));
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MSG_SETMASTER:
|
||||
drmSetMaster(drm_fd);
|
||||
close(drm_fd);
|
||||
send_msg(sock, -1, NULL, 0);
|
||||
break;
|
||||
|
||||
case MSG_DROPMASTER:
|
||||
drmDropMaster(drm_fd);
|
||||
close(drm_fd);
|
||||
send_msg(sock, -1, NULL, 0);
|
||||
break;
|
||||
|
||||
case MSG_END:
|
||||
running = false;
|
||||
send_msg(sock, -1, NULL, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
close(sock);
|
||||
}
|
||||
|
||||
int direct_ipc_open(int sock, const char *path) {
|
||||
struct msg msg = { .type = MSG_OPEN };
|
||||
snprintf(msg.path, sizeof(msg.path), "%s", path);
|
||||
|
||||
send_msg(sock, -1, &msg, sizeof(msg));
|
||||
|
||||
int fd, err, ret;
|
||||
int retry = 0;
|
||||
do {
|
||||
ret = recv_msg(sock, &fd, &err, sizeof(err));
|
||||
} while (ret == 0 && retry++ < 3);
|
||||
|
||||
return err ? -err : fd;
|
||||
}
|
||||
|
||||
void direct_ipc_setmaster(int sock, int fd) {
|
||||
struct msg msg = { .type = MSG_SETMASTER };
|
||||
|
||||
send_msg(sock, fd, &msg, sizeof(msg));
|
||||
recv_msg(sock, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
void direct_ipc_dropmaster(int sock, int fd) {
|
||||
struct msg msg = { .type = MSG_DROPMASTER };
|
||||
|
||||
send_msg(sock, fd, &msg, sizeof(msg));
|
||||
recv_msg(sock, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
void direct_ipc_finish(int sock, pid_t pid) {
|
||||
struct msg msg = { .type = MSG_END };
|
||||
|
||||
send_msg(sock, -1, &msg, sizeof(msg));
|
||||
recv_msg(sock, NULL, NULL, 0);
|
||||
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
|
||||
int direct_ipc_init(pid_t *pid_out) {
|
||||
int sock[2];
|
||||
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock) < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to create socket pair");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "Fork failed");
|
||||
close(sock[0]);
|
||||
close(sock[1]);
|
||||
return -1;
|
||||
} else if (pid == 0) {
|
||||
close(sock[0]);
|
||||
communicate(sock[1]);
|
||||
_Exit(0);
|
||||
}
|
||||
|
||||
close(sock[1]);
|
||||
*pid_out = pid;
|
||||
return sock[0];
|
||||
}
|
|
@ -1,291 +0,0 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/kd.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/vt.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend/session/interface.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "backend/session/direct-ipc.h"
|
||||
#include "backend/session/session.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
enum { DRM_MAJOR = 226 };
|
||||
|
||||
const struct session_impl session_direct;
|
||||
|
||||
struct direct_session {
|
||||
struct wlr_session base;
|
||||
int tty_fd;
|
||||
int old_kbmode;
|
||||
int sock;
|
||||
pid_t child;
|
||||
|
||||
struct wl_event_source *vt_source;
|
||||
};
|
||||
|
||||
static struct direct_session *direct_session_from_session(
|
||||
struct wlr_session *base) {
|
||||
assert(base->impl == &session_direct);
|
||||
return (struct direct_session *)base;
|
||||
}
|
||||
|
||||
static int direct_session_open(struct wlr_session *base, const char *path) {
|
||||
struct direct_session *session = direct_session_from_session(base);
|
||||
|
||||
int fd = direct_ipc_open(session->sock, path);
|
||||
if (fd < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to open %s: %s%s", path, strerror(-fd),
|
||||
fd == -EINVAL ? "; is another display server running?" : "");
|
||||
return fd;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) < 0) {
|
||||
close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (major(st.st_rdev) == DRM_MAJOR) {
|
||||
direct_ipc_setmaster(session->sock, fd);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void direct_session_close(struct wlr_session *base, int fd) {
|
||||
struct direct_session *session = direct_session_from_session(base);
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "Stat failed");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (major(st.st_rdev) == DRM_MAJOR) {
|
||||
direct_ipc_dropmaster(session->sock, fd);
|
||||
} else if (major(st.st_rdev) == INPUT_MAJOR) {
|
||||
ioctl(fd, EVIOCREVOKE, 0);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static bool direct_change_vt(struct wlr_session *base, unsigned vt) {
|
||||
struct direct_session *session = direct_session_from_session(base);
|
||||
|
||||
// Only seat0 has VTs associated with it
|
||||
if (strcmp(session->base.seat, "seat0") != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ioctl(session->tty_fd, VT_ACTIVATE, (int)vt) == 0;
|
||||
}
|
||||
|
||||
static void direct_session_destroy(struct wlr_session *base) {
|
||||
struct direct_session *session = direct_session_from_session(base);
|
||||
|
||||
if (strcmp(session->base.seat, "seat0") == 0) {
|
||||
struct vt_mode mode = {
|
||||
.mode = VT_AUTO,
|
||||
};
|
||||
errno = 0;
|
||||
|
||||
ioctl(session->tty_fd, KDSKBMODE, session->old_kbmode);
|
||||
ioctl(session->tty_fd, KDSETMODE, KD_TEXT);
|
||||
ioctl(session->tty_fd, VT_SETMODE, &mode);
|
||||
|
||||
if (errno) {
|
||||
wlr_log(WLR_ERROR, "Failed to restore tty");
|
||||
}
|
||||
|
||||
wl_event_source_remove(session->vt_source);
|
||||
close(session->tty_fd);
|
||||
}
|
||||
|
||||
direct_ipc_finish(session->sock, session->child);
|
||||
close(session->sock);
|
||||
|
||||
free(session);
|
||||
}
|
||||
|
||||
static int vt_handler(int signo, void *data) {
|
||||
struct direct_session *session = data;
|
||||
|
||||
if (session->base.active) {
|
||||
session->base.active = false;
|
||||
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||
|
||||
struct wlr_device *dev;
|
||||
wl_list_for_each(dev, &session->base.devices, link) {
|
||||
if (major(dev->dev) == DRM_MAJOR) {
|
||||
direct_ipc_dropmaster(session->sock,
|
||||
dev->fd);
|
||||
}
|
||||
}
|
||||
|
||||
ioctl(session->tty_fd, VT_RELDISP, 1);
|
||||
} else {
|
||||
ioctl(session->tty_fd, VT_RELDISP, VT_ACKACQ);
|
||||
|
||||
struct wlr_device *dev;
|
||||
wl_list_for_each(dev, &session->base.devices, link) {
|
||||
if (major(dev->dev) == DRM_MAJOR) {
|
||||
direct_ipc_setmaster(session->sock,
|
||||
dev->fd);
|
||||
}
|
||||
}
|
||||
|
||||
session->base.active = true;
|
||||
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool setup_tty(struct direct_session *session, struct wl_display *display) {
|
||||
|
||||
bool default_tty = false;
|
||||
|
||||
const char *tty_path = getenv("WLR_DIRECT_TTY");
|
||||
|
||||
if (!tty_path) {
|
||||
tty_path = "/dev/tty";
|
||||
default_tty = true;
|
||||
}
|
||||
|
||||
int fd = open(tty_path, O_RDWR | O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
wlr_log_errno(WLR_ERROR, "Cannot open %s", tty_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
struct vt_stat vt_stat;
|
||||
if (ioctl(fd, VT_GETSTATE, &vt_stat)) {
|
||||
wlr_log_errno(WLR_ERROR, "Could not get current tty number");
|
||||
goto error;
|
||||
}
|
||||
|
||||
int tty = vt_stat.v_active;
|
||||
int ret, kd_mode, old_kbmode;
|
||||
|
||||
ret = ioctl(fd, KDGETMODE, &kd_mode);
|
||||
if (ret) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to get tty mode");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (default_tty && kd_mode != KD_TEXT) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"tty already in graphics mode; is another display server running?");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ioctl(fd, VT_ACTIVATE, tty);
|
||||
ioctl(fd, VT_WAITACTIVE, tty);
|
||||
|
||||
if (ioctl(fd, KDGKBMODE, &old_kbmode)) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to read keyboard mode");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ioctl(fd, KDSKBMODE, K_OFF)) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to set keyboard mode");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ioctl(fd, KDSETMODE, KD_GRAPHICS)) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to set graphics mode on tty");
|
||||
goto error;
|
||||
}
|
||||
|
||||
struct vt_mode mode = {
|
||||
.mode = VT_PROCESS,
|
||||
.relsig = SIGUSR2,
|
||||
.acqsig = SIGUSR2,
|
||||
};
|
||||
|
||||
if (ioctl(fd, VT_SETMODE, &mode) < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to take control of tty");
|
||||
goto error;
|
||||
}
|
||||
|
||||
struct wl_event_loop *loop = wl_display_get_event_loop(display);
|
||||
session->vt_source = wl_event_loop_add_signal(loop, SIGUSR2,
|
||||
vt_handler, session);
|
||||
if (!session->vt_source) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
session->base.vtnr = tty;
|
||||
session->tty_fd = fd;
|
||||
session->old_kbmode = old_kbmode;
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct wlr_session *direct_session_create(struct wl_display *disp) {
|
||||
struct direct_session *session = calloc(1, sizeof(*session));
|
||||
if (!session) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
session_init(&session->base);
|
||||
session->sock = direct_ipc_init(&session->child);
|
||||
if (session->sock == -1) {
|
||||
goto error_session;
|
||||
}
|
||||
|
||||
const char *seat = getenv("XDG_SEAT");
|
||||
if (!seat) {
|
||||
seat = "seat0";
|
||||
}
|
||||
|
||||
if (strcmp(seat, "seat0") == 0) {
|
||||
if (!setup_tty(session, disp)) {
|
||||
goto error_ipc;
|
||||
}
|
||||
} else {
|
||||
session->base.vtnr = 0;
|
||||
session->tty_fd = -1;
|
||||
}
|
||||
|
||||
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat);
|
||||
session->base.impl = &session_direct;
|
||||
session->base.active = true;
|
||||
|
||||
wlr_log(WLR_INFO, "Successfully loaded direct session");
|
||||
return &session->base;
|
||||
|
||||
error_ipc:
|
||||
direct_ipc_finish(session->sock, session->child);
|
||||
close(session->sock);
|
||||
error_session:
|
||||
free(session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct session_impl session_direct = {
|
||||
.create = direct_session_create,
|
||||
.destroy = direct_session_destroy,
|
||||
.open = direct_session_open,
|
||||
.close = direct_session_close,
|
||||
.change_vt = direct_change_vt,
|
||||
};
|
|
@ -1,215 +0,0 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend/session/interface.h>
|
||||
#include <wlr/config.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "backend/session/session.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
#include <libseat.h>
|
||||
|
||||
const struct session_impl session_libseat;
|
||||
|
||||
struct libseat_device {
|
||||
struct wl_list link;
|
||||
int fd;
|
||||
int device_id;
|
||||
};
|
||||
|
||||
struct libseat_session {
|
||||
struct wlr_session base;
|
||||
|
||||
struct libseat *seat;
|
||||
struct wl_event_source *event;
|
||||
struct wl_list devices;
|
||||
};
|
||||
|
||||
static void handle_enable_seat(struct libseat *seat, void *data) {
|
||||
struct libseat_session *session = data;
|
||||
session->base.active = true;
|
||||
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||
}
|
||||
|
||||
static void handle_disable_seat(struct libseat *seat, void *data) {
|
||||
struct libseat_session *session = data;
|
||||
session->base.active = false;
|
||||
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||
libseat_disable_seat(session->seat);
|
||||
}
|
||||
|
||||
static int libseat_event(int fd, uint32_t mask, void *data) {
|
||||
struct libseat *seat = data;
|
||||
libseat_dispatch(seat, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct libseat_seat_listener seat_listener = {
|
||||
.enable_seat = handle_enable_seat,
|
||||
.disable_seat = handle_disable_seat,
|
||||
};
|
||||
|
||||
static struct libseat_session *libseat_session_from_session(
|
||||
struct wlr_session *base) {
|
||||
assert(base->impl == &session_libseat);
|
||||
return (struct libseat_session *)base;
|
||||
}
|
||||
|
||||
static enum wlr_log_importance libseat_log_level_to_wlr(
|
||||
enum libseat_log_level level) {
|
||||
switch (level) {
|
||||
case LIBSEAT_LOG_LEVEL_ERROR:
|
||||
return WLR_ERROR;
|
||||
case LIBSEAT_LOG_LEVEL_INFO:
|
||||
return WLR_INFO;
|
||||
default:
|
||||
return WLR_DEBUG;
|
||||
}
|
||||
}
|
||||
|
||||
static void log_libseat(enum libseat_log_level level,
|
||||
const char *fmt, va_list args) {
|
||||
enum wlr_log_importance importance = libseat_log_level_to_wlr(level);
|
||||
|
||||
static char wlr_fmt[1024];
|
||||
snprintf(wlr_fmt, sizeof(wlr_fmt), "[libseat] %s", fmt);
|
||||
|
||||
_wlr_vlog(importance, wlr_fmt, args);
|
||||
}
|
||||
|
||||
static struct wlr_session *libseat_session_create(struct wl_display *disp) {
|
||||
struct libseat_session *session = calloc(1, sizeof(*session));
|
||||
if (!session) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
session_init(&session->base);
|
||||
wl_list_init(&session->devices);
|
||||
|
||||
libseat_set_log_handler(log_libseat);
|
||||
libseat_set_log_level(LIBSEAT_LOG_LEVEL_ERROR);
|
||||
|
||||
session->seat = libseat_open_seat(&seat_listener, session);
|
||||
if (session->seat == NULL) {
|
||||
wlr_log_errno(WLR_ERROR, "Unable to create seat");
|
||||
goto error;
|
||||
}
|
||||
|
||||
const char *seat_name = libseat_seat_name(session->seat);
|
||||
if (seat_name == NULL) {
|
||||
wlr_log_errno(WLR_ERROR, "Unable to get seat info");
|
||||
goto error;
|
||||
}
|
||||
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat_name);
|
||||
|
||||
struct wl_event_loop *event_loop = wl_display_get_event_loop(disp);
|
||||
session->event = wl_event_loop_add_fd(event_loop, libseat_get_fd(session->seat),
|
||||
WL_EVENT_READABLE, libseat_event, session->seat);
|
||||
if (session->event == NULL) {
|
||||
wlr_log(WLR_ERROR, "Failed to create libseat event source");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// We may have received enable_seat immediately after the open_seat result,
|
||||
// so, dispatch once without timeout to speed up activation.
|
||||
if (libseat_dispatch(session->seat, 0) == -1) {
|
||||
wlr_log_errno(WLR_ERROR, "libseat dispatch failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
wlr_log(WLR_INFO, "Successfully loaded libseat session");
|
||||
session->base.impl = &session_libseat;
|
||||
return &session->base;
|
||||
|
||||
error:
|
||||
if (session->seat != NULL) {
|
||||
libseat_close_seat(session->seat);
|
||||
}
|
||||
if (session->event != NULL) {
|
||||
wl_event_source_remove(session->event);
|
||||
}
|
||||
free(session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void libseat_session_destroy(struct wlr_session *base) {
|
||||
struct libseat_session *session = libseat_session_from_session(base);
|
||||
|
||||
libseat_close_seat(session->seat);
|
||||
wl_event_source_remove(session->event);
|
||||
free(session);
|
||||
}
|
||||
|
||||
static struct libseat_device *find_device_by_fd(struct libseat_session *session, int fd) {
|
||||
struct libseat_device *dev;
|
||||
wl_list_for_each(dev, &session->devices, link) {
|
||||
if (dev->fd == fd) {
|
||||
return dev;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int libseat_session_open_device(struct wlr_session *base, const char *path) {
|
||||
struct libseat_session *session = libseat_session_from_session(base);
|
||||
|
||||
int fd;
|
||||
int device_id = libseat_open_device(session->seat, path, &fd);
|
||||
if (device_id == -1) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to open device '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct libseat_device *dev = calloc(1, sizeof(struct libseat_device));
|
||||
if (dev == NULL) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
libseat_close_device(session->seat, device_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev->fd = fd;
|
||||
dev->device_id = device_id;
|
||||
wl_list_insert(&session->devices, &dev->link);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void libseat_session_close_device(struct wlr_session *base, int fd) {
|
||||
struct libseat_session *session = libseat_session_from_session(base);
|
||||
|
||||
struct libseat_device *dev = find_device_by_fd(session, fd);
|
||||
if (dev == NULL) {
|
||||
wlr_log(WLR_ERROR, "No device with fd %d found", fd);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (libseat_close_device(session->seat, dev->device_id) == -1) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to close device %d", dev->device_id);
|
||||
}
|
||||
|
||||
wl_list_remove(&dev->link);
|
||||
free(dev);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static bool libseat_change_vt(struct wlr_session *base, unsigned vt) {
|
||||
struct libseat_session *session = libseat_session_from_session(base);
|
||||
return libseat_switch_session(session->seat, vt);
|
||||
}
|
||||
|
||||
const struct session_impl session_libseat = {
|
||||
.create = libseat_session_create,
|
||||
.destroy = libseat_session_destroy,
|
||||
.open = libseat_session_open_device,
|
||||
.close = libseat_session_close_device,
|
||||
.change_vt = libseat_change_vt,
|
||||
};
|
|
@ -1,876 +0,0 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend/session/interface.h>
|
||||
#include <wlr/config.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "backend/session/session.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
#if WLR_HAS_SYSTEMD
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <systemd/sd-login.h>
|
||||
#elif WLR_HAS_ELOGIND
|
||||
#include <elogind/sd-bus.h>
|
||||
#include <elogind/sd-login.h>
|
||||
#endif
|
||||
|
||||
enum { DRM_MAJOR = 226 };
|
||||
|
||||
const struct session_impl session_logind;
|
||||
|
||||
struct logind_session {
|
||||
struct wlr_session base;
|
||||
|
||||
sd_bus *bus;
|
||||
struct wl_event_source *event;
|
||||
|
||||
char *id;
|
||||
char *path;
|
||||
char *seat_path;
|
||||
|
||||
bool can_graphical;
|
||||
// specifies whether a drm device was taken
|
||||
// if so, the session will be (de)activated with the drm fd,
|
||||
// otherwise with the dbus PropertiesChanged on "active" signal
|
||||
bool has_drm;
|
||||
};
|
||||
|
||||
static struct logind_session *logind_session_from_session(
|
||||
struct wlr_session *base) {
|
||||
assert(base->impl == &session_logind);
|
||||
return (struct logind_session *)base;
|
||||
}
|
||||
|
||||
static int logind_take_device(struct wlr_session *base, const char *path) {
|
||||
struct logind_session *session = logind_session_from_session(base);
|
||||
|
||||
int fd = -1;
|
||||
int ret;
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
struct stat st;
|
||||
if (stat(path, &st) < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to stat '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (major(st.st_rdev) == DRM_MAJOR) {
|
||||
session->has_drm = true;
|
||||
}
|
||||
|
||||
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||
session->path, "org.freedesktop.login1.Session", "TakeDevice",
|
||||
&error, &msg, "uu", major(st.st_rdev), minor(st.st_rdev));
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to take device '%s': %s", path,
|
||||
error.message);
|
||||
goto out;
|
||||
}
|
||||
|
||||
int paused = 0;
|
||||
ret = sd_bus_message_read(msg, "hb", &fd, &paused);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to parse D-Bus response for '%s': %s",
|
||||
path, strerror(-ret));
|
||||
goto out;
|
||||
}
|
||||
|
||||
// The original fd seems to be closed when the message is freed
|
||||
// so we just clone it.
|
||||
fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
|
||||
if (fd < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to clone file descriptor for '%s': %s",
|
||||
path, strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void logind_release_device(struct wlr_session *base, int fd) {
|
||||
struct logind_session *session = logind_session_from_session(base);
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to stat device '%d': %s", fd,
|
||||
strerror(errno));
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
int ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||
session->path, "org.freedesktop.login1.Session", "ReleaseDevice",
|
||||
&error, &msg, "uu", major(st.st_rdev), minor(st.st_rdev));
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to release device '%d': %s", fd,
|
||||
error.message);
|
||||
}
|
||||
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
}
|
||||
|
||||
static bool logind_change_vt(struct wlr_session *base, unsigned vt) {
|
||||
struct logind_session *session = logind_session_from_session(base);
|
||||
|
||||
// Only if seat has VTs associated with it
|
||||
if (!sd_seat_can_tty(session->base.seat)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int ret;
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||
"/org/freedesktop/login1/seat/seat0", "org.freedesktop.login1.Seat", "SwitchTo",
|
||||
&error, &msg, "u", (uint32_t)vt);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to change to vt '%u'", vt);
|
||||
}
|
||||
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
static bool find_session_path(struct logind_session *session) {
|
||||
int ret;
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||
"/org/freedesktop/login1", "org.freedesktop.login1.Manager",
|
||||
"GetSession", &error, &msg, "s", session->id);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to get session path: %s", error.message);
|
||||
goto out;
|
||||
}
|
||||
|
||||
const char *path;
|
||||
ret = sd_bus_message_read(msg, "o", &path);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Could not parse session path: %s", error.message);
|
||||
goto out;
|
||||
}
|
||||
session->path = strdup(path);
|
||||
|
||||
out:
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
static bool find_seat_path(struct logind_session *session) {
|
||||
int ret;
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||
"/org/freedesktop/login1", "org.freedesktop.login1.Manager",
|
||||
"GetSeat", &error, &msg, "s", session->base.seat);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to get seat path: %s", error.message);
|
||||
goto out;
|
||||
}
|
||||
|
||||
const char *path;
|
||||
ret = sd_bus_message_read(msg, "o", &path);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Could not parse seat path: %s", error.message);
|
||||
goto out;
|
||||
}
|
||||
session->seat_path = strdup(path);
|
||||
|
||||
out:
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
static bool session_activate(struct logind_session *session) {
|
||||
int ret;
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||
session->path, "org.freedesktop.login1.Session", "Activate",
|
||||
&error, &msg, "");
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to activate session: %s", error.message);
|
||||
}
|
||||
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
static bool take_control(struct logind_session *session) {
|
||||
int ret;
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||
session->path, "org.freedesktop.login1.Session", "TakeControl",
|
||||
&error, &msg, "b", false);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to take control of session: %s",
|
||||
error.message);
|
||||
}
|
||||
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
static void set_type(struct logind_session *session) {
|
||||
int ret;
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||
session->path, "org.freedesktop.login1.Session", "SetType",
|
||||
&error, &msg, "s", "wayland");
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_DEBUG, "Failed to set logind session type for session: %s",
|
||||
error.message);
|
||||
}
|
||||
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
|
||||
if (ret < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ret = setenv("XDG_SESSION_TYPE", "wayland", 1);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to set XDG_SESSION_TYPE for session");
|
||||
}
|
||||
}
|
||||
|
||||
static void release_control(struct logind_session *session) {
|
||||
int ret;
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||
session->path, "org.freedesktop.login1.Session", "ReleaseControl",
|
||||
&error, &msg, "");
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to release control of session: %s",
|
||||
error.message);
|
||||
}
|
||||
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(msg);
|
||||
}
|
||||
|
||||
static void logind_session_destroy(struct wlr_session *base) {
|
||||
struct logind_session *session = logind_session_from_session(base);
|
||||
|
||||
release_control(session);
|
||||
|
||||
wl_event_source_remove(session->event);
|
||||
sd_bus_unref(session->bus);
|
||||
free(session->id);
|
||||
free(session->path);
|
||||
free(session->seat_path);
|
||||
free(session);
|
||||
}
|
||||
|
||||
static int session_removed(sd_bus_message *msg, void *userdata,
|
||||
sd_bus_error *ret_error) {
|
||||
wlr_log(WLR_INFO, "SessionRemoved signal received");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct wlr_device *find_device(struct wlr_session *session,
|
||||
dev_t devnum) {
|
||||
struct wlr_device *dev;
|
||||
|
||||
wl_list_for_each(dev, &session->devices, link) {
|
||||
if (dev->dev == devnum) {
|
||||
return dev;
|
||||
}
|
||||
}
|
||||
|
||||
wlr_log(WLR_ERROR, "Tried to use dev_t %lu not opened by session",
|
||||
(unsigned long)devnum);
|
||||
assert(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int pause_device(sd_bus_message *msg, void *userdata,
|
||||
sd_bus_error *ret_error) {
|
||||
struct logind_session *session = userdata;
|
||||
int ret;
|
||||
|
||||
uint32_t major, minor;
|
||||
const char *type;
|
||||
ret = sd_bus_message_read(msg, "uus", &major, &minor, &type);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to parse D-Bus response for PauseDevice: %s",
|
||||
strerror(-ret));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (major == DRM_MAJOR && strcmp(type, "gone") != 0) {
|
||||
assert(session->has_drm);
|
||||
session->base.active = false;
|
||||
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||
}
|
||||
|
||||
if (strcmp(type, "pause") == 0) {
|
||||
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||
session->path, "org.freedesktop.login1.Session", "PauseDeviceComplete",
|
||||
ret_error, &msg, "uu", major, minor);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to send PauseDeviceComplete signal: %s",
|
||||
strerror(-ret));
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resume_device(sd_bus_message *msg, void *userdata,
|
||||
sd_bus_error *ret_error) {
|
||||
struct logind_session *session = userdata;
|
||||
int ret;
|
||||
|
||||
int fd;
|
||||
uint32_t major, minor;
|
||||
ret = sd_bus_message_read(msg, "uuh", &major, &minor, &fd);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to parse D-Bus response for ResumeDevice: %s",
|
||||
strerror(-ret));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (major == DRM_MAJOR) {
|
||||
struct wlr_device *dev = find_device(&session->base, makedev(major, minor));
|
||||
|
||||
close(dev->fd);
|
||||
if (fcntl(fd, F_DUPFD_CLOEXEC, dev->fd) < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to duplicate file descriptor");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!session->base.active) {
|
||||
session->base.active = true;
|
||||
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_properties_changed(sd_bus_message *msg, void *userdata,
|
||||
sd_bus_error *ret_error) {
|
||||
struct logind_session *session = userdata;
|
||||
int ret = 0;
|
||||
|
||||
// if we have a drm fd we don't depend on this
|
||||
if (session->has_drm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// PropertiesChanged arg 1: interface
|
||||
const char *interface;
|
||||
ret = sd_bus_message_read_basic(msg, 's', &interface); // skip path
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strcmp(interface, "org.freedesktop.login1.Session") != 0) {
|
||||
// not interesting for us; ignore
|
||||
wlr_log(WLR_DEBUG, "ignoring PropertiesChanged from %s", interface);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// PropertiesChanged arg 2: changed properties with values
|
||||
ret = sd_bus_message_enter_container(msg, 'a', "{sv}");
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
const char *s;
|
||||
while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) {
|
||||
ret = sd_bus_message_read_basic(msg, 's', &s);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strcmp(s, "Active") == 0) {
|
||||
int ret;
|
||||
ret = sd_bus_message_enter_container(msg, 'v', "b");
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
bool active;
|
||||
ret = sd_bus_message_read_basic(msg, 'b', &active);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (session->base.active != active) {
|
||||
session->base.active = active;
|
||||
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
sd_bus_message_skip(msg, "{sv}");
|
||||
}
|
||||
|
||||
ret = sd_bus_message_exit_container(msg);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = sd_bus_message_exit_container(msg);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// PropertiesChanged arg 3: changed properties without values
|
||||
sd_bus_message_enter_container(msg, 'a', "s");
|
||||
while ((ret = sd_bus_message_read_basic(msg, 's', &s)) > 0) {
|
||||
if (strcmp(s, "Active") == 0) {
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
bool active;
|
||||
ret = sd_bus_get_property_trivial(session->bus,
|
||||
"org.freedesktop.login1", session->path,
|
||||
"org.freedesktop.login1.Session", "Active", &error,
|
||||
'b', &active);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to get 'Active' property: %s",
|
||||
error.message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (session->base.active != active) {
|
||||
session->base.active = active;
|
||||
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
wlr_log(WLR_ERROR, "Failed to parse D-Bus PropertiesChanged: %s",
|
||||
strerror(-ret));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int seat_properties_changed(sd_bus_message *msg, void *userdata,
|
||||
sd_bus_error *ret_error) {
|
||||
struct logind_session *session = userdata;
|
||||
int ret = 0;
|
||||
|
||||
// if we have a drm fd we don't depend on this
|
||||
if (session->has_drm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// PropertiesChanged arg 1: interface
|
||||
const char *interface;
|
||||
ret = sd_bus_message_read_basic(msg, 's', &interface); // skip path
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strcmp(interface, "org.freedesktop.login1.Seat") != 0) {
|
||||
// not interesting for us; ignore
|
||||
wlr_log(WLR_DEBUG, "ignoring PropertiesChanged from %s", interface);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// PropertiesChanged arg 2: changed properties with values
|
||||
ret = sd_bus_message_enter_container(msg, 'a', "{sv}");
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
const char *s;
|
||||
while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) {
|
||||
ret = sd_bus_message_read_basic(msg, 's', &s);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strcmp(s, "CanGraphical") == 0) {
|
||||
int ret;
|
||||
ret = sd_bus_message_enter_container(msg, 'v', "b");
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = sd_bus_message_read_basic(msg, 'b', &session->can_graphical);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
sd_bus_message_skip(msg, "{sv}");
|
||||
}
|
||||
|
||||
ret = sd_bus_message_exit_container(msg);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = sd_bus_message_exit_container(msg);
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// PropertiesChanged arg 3: changed properties without values
|
||||
sd_bus_message_enter_container(msg, 'a', "s");
|
||||
while ((ret = sd_bus_message_read_basic(msg, 's', &s)) > 0) {
|
||||
if (strcmp(s, "CanGraphical") == 0) {
|
||||
session->can_graphical = sd_seat_can_graphical(session->base.seat);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
wlr_log(WLR_ERROR, "Failed to parse D-Bus PropertiesChanged: %s",
|
||||
strerror(-ret));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool add_signal_matches(struct logind_session *session) {
|
||||
static const char *logind = "org.freedesktop.login1";
|
||||
static const char *logind_path = "/org/freedesktop/login1";
|
||||
static const char *manager_interface = "org.freedesktop.login1.Manager";
|
||||
static const char *session_interface = "org.freedesktop.login1.Session";
|
||||
static const char *property_interface = "org.freedesktop.DBus.Properties";
|
||||
int ret;
|
||||
|
||||
ret = sd_bus_match_signal(session->bus, NULL, logind, logind_path,
|
||||
manager_interface, "SessionRemoved", session_removed, session);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sd_bus_match_signal(session->bus, NULL, logind, session->path,
|
||||
session_interface, "PauseDevice", pause_device, session);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sd_bus_match_signal(session->bus, NULL, logind, session->path,
|
||||
session_interface, "ResumeDevice", resume_device, session);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sd_bus_match_signal(session->bus, NULL, logind, session->path,
|
||||
property_interface, "PropertiesChanged",
|
||||
session_properties_changed, session);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = sd_bus_match_signal(session->bus, NULL, logind, session->seat_path,
|
||||
property_interface, "PropertiesChanged",
|
||||
seat_properties_changed, session);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int dbus_event(int fd, uint32_t mask, void *data) {
|
||||
sd_bus *bus = data;
|
||||
while (sd_bus_process(bus, NULL) > 0) {
|
||||
// Do nothing.
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool contains_str(const char *needle, const char **haystack) {
|
||||
for (int i = 0; haystack[i]; i++) {
|
||||
if (strcmp(haystack[i], needle) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool get_greeter_session(char **session_id) {
|
||||
char *class = NULL;
|
||||
char **user_sessions = NULL;
|
||||
int user_session_count = sd_uid_get_sessions(getuid(), 1, &user_sessions);
|
||||
|
||||
if (user_session_count < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to get sessions");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (user_session_count == 0) {
|
||||
wlr_log(WLR_ERROR, "User has no sessions");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (int i = 0; i < user_session_count; ++i) {
|
||||
int ret = sd_session_get_class(user_sessions[i], &class);
|
||||
if (ret < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(class, "greeter") == 0) {
|
||||
*session_id = strdup(user_sessions[i]);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
free(class);
|
||||
for (int i = 0; i < user_session_count; ++i) {
|
||||
free(user_sessions[i]);
|
||||
}
|
||||
free(user_sessions);
|
||||
|
||||
return *session_id != NULL;
|
||||
}
|
||||
|
||||
static bool get_display_session(char **session_id) {
|
||||
assert(session_id != NULL);
|
||||
int ret;
|
||||
|
||||
char *type = NULL;
|
||||
char *state = NULL;
|
||||
char *xdg_session_id = getenv("XDG_SESSION_ID");
|
||||
|
||||
if (xdg_session_id) {
|
||||
// This just checks whether the supplied session ID is valid
|
||||
if (sd_session_is_active(xdg_session_id) < 0) {
|
||||
wlr_log(WLR_ERROR, "Invalid XDG_SESSION_ID: '%s'", xdg_session_id);
|
||||
goto error;
|
||||
}
|
||||
*session_id = strdup(xdg_session_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If there's a session active for the current process then just use that
|
||||
ret = sd_pid_get_session(getpid(), session_id);
|
||||
if (ret == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find any active sessions for the user if the process isn't part of an
|
||||
// active session itself
|
||||
ret = sd_uid_get_display(getuid(), session_id);
|
||||
if (ret < 0 && ret != -ENODATA) {
|
||||
wlr_log(WLR_ERROR, "Failed to get display: %s", strerror(-ret));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ret != 0 && !get_greeter_session(session_id)) {
|
||||
wlr_log(WLR_ERROR, "Couldn't find an active session or a greeter session");
|
||||
goto error;
|
||||
}
|
||||
|
||||
assert(*session_id != NULL);
|
||||
|
||||
// Check that the available session is graphical
|
||||
ret = sd_session_get_type(*session_id, &type);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Couldn't get a type for session '%s': %s",
|
||||
*session_id, strerror(-ret));
|
||||
goto error;
|
||||
}
|
||||
|
||||
const char *graphical_session_types[] = {"wayland", "x11", "mir", NULL};
|
||||
if (!contains_str(type, graphical_session_types)) {
|
||||
wlr_log(WLR_ERROR, "Session '%s' isn't a graphical session (type: '%s')",
|
||||
*session_id, type);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Check that the session is active
|
||||
ret = sd_session_get_state(*session_id, &state);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Couldn't get state for session '%s': %s",
|
||||
*session_id, strerror(-ret));
|
||||
goto error;
|
||||
}
|
||||
|
||||
const char *active_states[] = {"active", "online", NULL};
|
||||
if (!contains_str(state, active_states)) {
|
||||
wlr_log(WLR_ERROR, "Session '%s' is not active", *session_id);
|
||||
goto error;
|
||||
}
|
||||
|
||||
free(type);
|
||||
free(state);
|
||||
return true;
|
||||
|
||||
error:
|
||||
free(type);
|
||||
free(state);
|
||||
free(*session_id);
|
||||
*session_id = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct wlr_session *logind_session_create(struct wl_display *disp) {
|
||||
int ret;
|
||||
struct logind_session *session = calloc(1, sizeof(*session));
|
||||
if (!session) {
|
||||
wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
session_init(&session->base);
|
||||
|
||||
if (!get_display_session(&session->id)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
char *seat;
|
||||
ret = sd_session_get_seat(session->id, &seat);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to get seat id: %s", strerror(-ret));
|
||||
goto error;
|
||||
}
|
||||
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat);
|
||||
|
||||
if (sd_seat_can_tty(seat)) {
|
||||
ret = sd_session_get_vt(session->id, &session->base.vtnr);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Session not running in virtual terminal");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
free(seat);
|
||||
|
||||
ret = sd_bus_default_system(&session->bus);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s", strerror(-ret));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!find_session_path(session)) {
|
||||
sd_bus_unref(session->bus);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!find_seat_path(session)) {
|
||||
sd_bus_unref(session->bus);
|
||||
free(session->path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!add_signal_matches(session)) {
|
||||
goto error_bus;
|
||||
}
|
||||
|
||||
struct wl_event_loop *event_loop = wl_display_get_event_loop(disp);
|
||||
session->event = wl_event_loop_add_fd(event_loop, sd_bus_get_fd(session->bus),
|
||||
WL_EVENT_READABLE, dbus_event, session->bus);
|
||||
|
||||
if (!session_activate(session)) {
|
||||
goto error_bus;
|
||||
}
|
||||
|
||||
if (!take_control(session)) {
|
||||
goto error_bus;
|
||||
}
|
||||
|
||||
// Check for CanGraphical first
|
||||
session->can_graphical = sd_seat_can_graphical(session->base.seat);
|
||||
if (!session->can_graphical) {
|
||||
wlr_log(WLR_INFO, "Waiting for 'CanGraphical' on seat %s", session->base.seat);
|
||||
}
|
||||
|
||||
while (!session->can_graphical) {
|
||||
ret = wl_event_loop_dispatch(event_loop, -1);
|
||||
if (ret < 0) {
|
||||
wlr_log(WLR_ERROR, "Polling error waiting for 'CanGraphical' on seat %s",
|
||||
session->base.seat);
|
||||
goto error_bus;
|
||||
}
|
||||
}
|
||||
|
||||
set_type(session);
|
||||
|
||||
wlr_log(WLR_INFO, "Successfully loaded logind session");
|
||||
|
||||
session->base.impl = &session_logind;
|
||||
session->base.active = true;
|
||||
|
||||
return &session->base;
|
||||
|
||||
error_bus:
|
||||
sd_bus_unref(session->bus);
|
||||
free(session->path);
|
||||
free(session->seat_path);
|
||||
|
||||
error:
|
||||
free(session->id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct session_impl session_logind = {
|
||||
.create = logind_session_create,
|
||||
.destroy = logind_session_destroy,
|
||||
.open = logind_take_device,
|
||||
.close = logind_release_device,
|
||||
.change_vt = logind_change_vt,
|
||||
};
|
|
@ -1,73 +1,7 @@
|
|||
wlr_files += files(
|
||||
'direct-ipc.c',
|
||||
'noop.c',
|
||||
'session.c',
|
||||
libseat = dependency('libseat',
|
||||
version: '>=0.2.0',
|
||||
fallback: ['seatd', 'libseat'],
|
||||
default_options: ['server=disabled', 'man-pages=disabled'],
|
||||
)
|
||||
|
||||
if host_machine.system().startswith('freebsd')
|
||||
wlr_files += files('direct-freebsd.c')
|
||||
else
|
||||
wlr_files += files('direct.c')
|
||||
endif
|
||||
|
||||
# logind
|
||||
|
||||
msg = []
|
||||
if get_option('logind').enabled()
|
||||
msg += 'Install "lib@0@" or pass "-Dlogind=disabled".'
|
||||
endif
|
||||
if not get_option('logind').disabled()
|
||||
msg += 'Required for logind support.'
|
||||
msg += 'You may need to pass "-Dlogind-provider=elogind" or "-Dlogind-provider=systemd" to ensure the correct library is detected.'
|
||||
endif
|
||||
|
||||
logind_version = '>=237'
|
||||
logind_found = false
|
||||
|
||||
if get_option('logind-provider') == 'auto'
|
||||
if not get_option('logind').disabled()
|
||||
assert(get_option('auto_features').auto(), '-Dlogind-provider must be set to systemd or elogind since auto_features != auto')
|
||||
logind = dependency('libsystemd',
|
||||
required: get_option('logind'),
|
||||
not_found_message: 'libsystemd not found, trying libelogind\n' + '\n'.join(msg),
|
||||
version: logind_version,
|
||||
)
|
||||
if logind.found()
|
||||
conf_data.set10('WLR_HAS_SYSTEMD', true)
|
||||
else
|
||||
logind = dependency('libelogind',
|
||||
required: get_option('logind'),
|
||||
not_found_message: 'libelogind not found\n' + '\n'.join(msg),
|
||||
version: logind_version,
|
||||
)
|
||||
if logind.found()
|
||||
conf_data.set10('WLR_HAS_ELOGIND', true)
|
||||
endif
|
||||
endif
|
||||
logind_found = logind.found()
|
||||
endif
|
||||
else
|
||||
logind = dependency('lib' + get_option('logind-provider'),
|
||||
required: get_option('logind'),
|
||||
not_found_message: '\n'.join(msg).format(get_option('logind-provider')),
|
||||
version: logind_version,
|
||||
)
|
||||
if logind.found()
|
||||
conf_data.set10('WLR_HAS_' + get_option('logind-provider').to_upper(), true)
|
||||
logind_found = true
|
||||
endif
|
||||
endif
|
||||
|
||||
if logind_found
|
||||
wlr_files += files('logind.c')
|
||||
wlr_deps += logind
|
||||
endif
|
||||
|
||||
# libseat
|
||||
|
||||
libseat = dependency('libseat', required: get_option('libseat'), version: '>=0.2.0')
|
||||
if libseat.found()
|
||||
wlr_files += files('libseat.c')
|
||||
wlr_deps += libseat
|
||||
conf_data.set10('WLR_HAS_LIBSEAT', true)
|
||||
endif
|
||||
wlr_files += files('session.c')
|
||||
wlr_deps += libseat
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend/session/interface.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "backend/session/session.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
const struct session_impl session_noop;
|
||||
|
||||
static int noop_session_open(struct wlr_session *base, const char *path) {
|
||||
return open(path, O_RDWR | O_CLOEXEC);
|
||||
}
|
||||
|
||||
static void noop_session_close(struct wlr_session *base, int fd) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static bool noop_change_vt(struct wlr_session *base, unsigned vt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static void noop_session_destroy(struct wlr_session *base) {
|
||||
free(base);
|
||||
}
|
||||
|
||||
static struct wlr_session *noop_session_create(struct wl_display *disp) {
|
||||
struct wlr_session *session = calloc(1, sizeof(*session));
|
||||
if (!session) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
session_init(session);
|
||||
session->impl = &session_noop;
|
||||
session->active = true;
|
||||
|
||||
wlr_log(WLR_INFO, "Successfully initialized noop session");
|
||||
return session;
|
||||
}
|
||||
|
||||
const struct session_impl session_noop = {
|
||||
.create = noop_session_create,
|
||||
.destroy = noop_session_destroy,
|
||||
.open = noop_session_open,
|
||||
.close = noop_session_close,
|
||||
.change_vt = noop_change_vt,
|
||||
};
|
|
@ -1,14 +1,17 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <libudev.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend/session.h>
|
||||
#include <wlr/backend/session/interface.h>
|
||||
#include <wlr/config.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <xf86drm.h>
|
||||
|
@ -16,23 +19,156 @@
|
|||
#include "backend/session/session.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
extern const struct session_impl session_libseat;
|
||||
extern const struct session_impl session_logind;
|
||||
extern const struct session_impl session_direct;
|
||||
extern const struct session_impl session_noop;
|
||||
#include <libseat.h>
|
||||
|
||||
static const struct session_impl *impls[] = {
|
||||
#if WLR_HAS_LIBSEAT
|
||||
&session_libseat,
|
||||
#endif
|
||||
#if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND
|
||||
&session_logind,
|
||||
#endif
|
||||
&session_direct,
|
||||
NULL,
|
||||
#define WAIT_GPU_TIMEOUT 10000 // ms
|
||||
|
||||
static void handle_enable_seat(struct libseat *seat, void *data) {
|
||||
struct wlr_session *session = data;
|
||||
session->active = true;
|
||||
wlr_signal_emit_safe(&session->events.active, NULL);
|
||||
}
|
||||
|
||||
static void handle_disable_seat(struct libseat *seat, void *data) {
|
||||
struct wlr_session *session = data;
|
||||
session->active = false;
|
||||
wlr_signal_emit_safe(&session->events.active, NULL);
|
||||
libseat_disable_seat(session->seat_handle);
|
||||
}
|
||||
|
||||
static int libseat_event(int fd, uint32_t mask, void *data) {
|
||||
struct wlr_session *session = data;
|
||||
if (libseat_dispatch(session->seat_handle, 0) == -1) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to dispatch libseat");
|
||||
wl_display_terminate(session->display);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct libseat_seat_listener seat_listener = {
|
||||
.enable_seat = handle_enable_seat,
|
||||
.disable_seat = handle_disable_seat,
|
||||
};
|
||||
|
||||
static int udev_event(int fd, uint32_t mask, void *data) {
|
||||
static enum wlr_log_importance libseat_log_level_to_wlr(
|
||||
enum libseat_log_level level) {
|
||||
switch (level) {
|
||||
case LIBSEAT_LOG_LEVEL_ERROR:
|
||||
return WLR_ERROR;
|
||||
case LIBSEAT_LOG_LEVEL_INFO:
|
||||
return WLR_INFO;
|
||||
default:
|
||||
return WLR_DEBUG;
|
||||
}
|
||||
}
|
||||
|
||||
static void log_libseat(enum libseat_log_level level,
|
||||
const char *fmt, va_list args) {
|
||||
enum wlr_log_importance importance = libseat_log_level_to_wlr(level);
|
||||
|
||||
static char wlr_fmt[1024];
|
||||
snprintf(wlr_fmt, sizeof(wlr_fmt), "[libseat] %s", fmt);
|
||||
|
||||
_wlr_vlog(importance, wlr_fmt, args);
|
||||
}
|
||||
|
||||
static int libseat_session_init(struct wlr_session *session, struct wl_display *disp) {
|
||||
libseat_set_log_handler(log_libseat);
|
||||
libseat_set_log_level(LIBSEAT_LOG_LEVEL_INFO);
|
||||
|
||||
// libseat will take care of updating the logind state if necessary
|
||||
setenv("XDG_SESSION_TYPE", "wayland", 1);
|
||||
|
||||
session->seat_handle = libseat_open_seat(&seat_listener, session);
|
||||
if (session->seat_handle == NULL) {
|
||||
wlr_log_errno(WLR_ERROR, "Unable to create seat");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *seat_name = libseat_seat_name(session->seat_handle);
|
||||
if (seat_name == NULL) {
|
||||
wlr_log_errno(WLR_ERROR, "Unable to get seat info");
|
||||
goto error;
|
||||
}
|
||||
snprintf(session->seat, sizeof(session->seat), "%s", seat_name);
|
||||
|
||||
struct wl_event_loop *event_loop = wl_display_get_event_loop(disp);
|
||||
session->libseat_event = wl_event_loop_add_fd(event_loop, libseat_get_fd(session->seat_handle),
|
||||
WL_EVENT_READABLE, libseat_event, session);
|
||||
if (session->libseat_event == NULL) {
|
||||
wlr_log(WLR_ERROR, "Failed to create libseat event source");
|
||||
goto error;
|
||||
}
|
||||
|
||||
// We may have received enable_seat immediately after the open_seat result,
|
||||
// so, dispatch once without timeout to speed up activation.
|
||||
if (libseat_dispatch(session->seat_handle, 0) == -1) {
|
||||
wlr_log_errno(WLR_ERROR, "libseat dispatch failed");
|
||||
goto error_dispatch;
|
||||
}
|
||||
|
||||
wlr_log(WLR_INFO, "Successfully loaded libseat session");
|
||||
return 0;
|
||||
|
||||
error_dispatch:
|
||||
wl_event_source_remove(session->libseat_event);
|
||||
session->libseat_event = NULL;
|
||||
error:
|
||||
libseat_close_seat(session->seat_handle);
|
||||
session->seat_handle = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void libseat_session_finish(struct wlr_session *session) {
|
||||
libseat_close_seat(session->seat_handle);
|
||||
wl_event_source_remove(session->libseat_event);
|
||||
session->seat_handle = NULL;
|
||||
session->libseat_event = NULL;
|
||||
}
|
||||
|
||||
static bool is_drm_card(const char *sysname) {
|
||||
const char prefix[] = DRM_PRIMARY_MINOR_NAME;
|
||||
if (strncmp(sysname, prefix, strlen(prefix)) != 0) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = strlen(prefix); sysname[i] != '\0'; i++) {
|
||||
if (sysname[i] < '0' || sysname[i] > '9') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void read_udev_change_event(struct wlr_device_change_event *event,
|
||||
struct udev_device *udev_dev) {
|
||||
const char *hotplug = udev_device_get_property_value(udev_dev, "HOTPLUG");
|
||||
if (hotplug != NULL && strcmp(hotplug, "1") == 0) {
|
||||
event->type = WLR_DEVICE_HOTPLUG;
|
||||
struct wlr_device_hotplug_event *hotplug = &event->hotplug;
|
||||
|
||||
const char *connector =
|
||||
udev_device_get_property_value(udev_dev, "CONNECTOR");
|
||||
if (connector != NULL) {
|
||||
hotplug->connector_id = strtoul(connector, NULL, 10);
|
||||
}
|
||||
|
||||
const char *prop =
|
||||
udev_device_get_property_value(udev_dev, "PROPERTY");
|
||||
if (prop != NULL) {
|
||||
hotplug->prop_id = strtoul(prop, NULL, 10);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const char *lease = udev_device_get_property_value(udev_dev, "LEASE");
|
||||
if (lease != NULL && strcmp(lease, "1") == 0) {
|
||||
event->type = WLR_DEVICE_LEASE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int handle_udev_event(int fd, uint32_t mask, void *data) {
|
||||
struct wlr_session *session = data;
|
||||
|
||||
struct udev_device *udev_dev = udev_monitor_receive_device(session->mon);
|
||||
|
@ -40,21 +176,48 @@ static int udev_event(int fd, uint32_t mask, void *data) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
const char *sysname = udev_device_get_sysname(udev_dev);
|
||||
const char *devnode = udev_device_get_devnode(udev_dev);
|
||||
const char *action = udev_device_get_action(udev_dev);
|
||||
wlr_log(WLR_DEBUG, "udev event for %s (%s)", sysname, action);
|
||||
|
||||
wlr_log(WLR_DEBUG, "udev event for %s (%s)",
|
||||
udev_device_get_sysname(udev_dev), action);
|
||||
|
||||
if (!action || strcmp(action, "change") != 0) {
|
||||
if (!is_drm_card(sysname) || !action || !devnode) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_t devnum = udev_device_get_devnum(udev_dev);
|
||||
struct wlr_device *dev;
|
||||
const char *seat = udev_device_get_property_value(udev_dev, "ID_SEAT");
|
||||
if (!seat) {
|
||||
seat = "seat0";
|
||||
}
|
||||
if (session->seat[0] != '\0' && strcmp(session->seat, seat) != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
wl_list_for_each(dev, &session->devices, link) {
|
||||
if (dev->dev == devnum) {
|
||||
wlr_signal_emit_safe(&dev->signal, session);
|
||||
if (strcmp(action, "add") == 0) {
|
||||
wlr_log(WLR_DEBUG, "DRM device %s added", sysname);
|
||||
struct wlr_session_add_event event = {
|
||||
.path = devnode,
|
||||
};
|
||||
wlr_signal_emit_safe(&session->events.add_drm_card, &event);
|
||||
} else if (strcmp(action, "change") == 0 || strcmp(action, "remove") == 0) {
|
||||
dev_t devnum = udev_device_get_devnum(udev_dev);
|
||||
struct wlr_device *dev;
|
||||
wl_list_for_each(dev, &session->devices, link) {
|
||||
if (dev->dev != devnum) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(action, "change") == 0) {
|
||||
wlr_log(WLR_DEBUG, "DRM device %s changed", sysname);
|
||||
struct wlr_device_change_event event = {0};
|
||||
read_udev_change_event(&event, udev_dev);
|
||||
wlr_signal_emit_safe(&dev->events.change, &event);
|
||||
} else if (strcmp(action, "remove") == 0) {
|
||||
wlr_log(WLR_DEBUG, "DRM device %s removed", sysname);
|
||||
wlr_signal_emit_safe(&dev->events.remove, NULL);
|
||||
} else {
|
||||
assert(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -70,48 +233,21 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
|||
wlr_session_destroy(session);
|
||||
}
|
||||
|
||||
void session_init(struct wlr_session *session) {
|
||||
wl_signal_init(&session->session_signal);
|
||||
wl_signal_init(&session->events.destroy);
|
||||
wl_list_init(&session->devices);
|
||||
}
|
||||
|
||||
struct wlr_session *wlr_session_create(struct wl_display *disp) {
|
||||
struct wlr_session *session = NULL;
|
||||
|
||||
const char *env_wlr_session = getenv("WLR_SESSION");
|
||||
if (env_wlr_session) {
|
||||
if (strcmp(env_wlr_session, "libseat") == 0) {
|
||||
#if WLR_HAS_LIBSEAT
|
||||
session = session_libseat.create(disp);
|
||||
#else
|
||||
wlr_log(WLR_ERROR, "wlroots is not compiled with libseat support");
|
||||
#endif
|
||||
} else if (strcmp(env_wlr_session, "logind") == 0 ||
|
||||
strcmp(env_wlr_session, "systemd") == 0) {
|
||||
#if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND
|
||||
session = session_logind.create(disp);
|
||||
#else
|
||||
wlr_log(WLR_ERROR, "wlroots is not compiled with logind support");
|
||||
#endif
|
||||
} else if (strcmp(env_wlr_session, "direct") == 0) {
|
||||
session = session_direct.create(disp);
|
||||
} else if (strcmp(env_wlr_session, "noop") == 0) {
|
||||
session = session_noop.create(disp);
|
||||
} else {
|
||||
wlr_log(WLR_ERROR, "Unsupported WLR_SESSION: %s",
|
||||
env_wlr_session);
|
||||
}
|
||||
} else {
|
||||
const struct session_impl **iter;
|
||||
for (iter = impls; !session && *iter; ++iter) {
|
||||
session = (*iter)->create(disp);
|
||||
}
|
||||
struct wlr_session *session = calloc(1, sizeof(*session));
|
||||
if (!session) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
wl_signal_init(&session->events.active);
|
||||
wl_signal_init(&session->events.add_drm_card);
|
||||
wl_signal_init(&session->events.destroy);
|
||||
wl_list_init(&session->devices);
|
||||
|
||||
if (libseat_session_init(session, disp) == -1) {
|
||||
wlr_log(WLR_ERROR, "Failed to load session backend");
|
||||
return NULL;
|
||||
goto error_open;
|
||||
}
|
||||
|
||||
session->udev = udev_new();
|
||||
|
@ -133,12 +269,14 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) {
|
|||
int fd = udev_monitor_get_fd(session->mon);
|
||||
|
||||
session->udev_event = wl_event_loop_add_fd(event_loop, fd,
|
||||
WL_EVENT_READABLE, udev_event, session);
|
||||
WL_EVENT_READABLE, handle_udev_event, session);
|
||||
if (!session->udev_event) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to create udev event source");
|
||||
goto error_mon;
|
||||
}
|
||||
|
||||
session->display = disp;
|
||||
|
||||
session->display_destroy.notify = handle_display_destroy;
|
||||
wl_display_add_destroy_listener(disp, &session->display_destroy);
|
||||
|
||||
|
@ -149,7 +287,9 @@ error_mon:
|
|||
error_udev:
|
||||
udev_unref(session->udev);
|
||||
error_session:
|
||||
session->impl->destroy(session);
|
||||
libseat_session_finish(session);
|
||||
error_open:
|
||||
free(session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -165,13 +305,22 @@ void wlr_session_destroy(struct wlr_session *session) {
|
|||
udev_monitor_unref(session->mon);
|
||||
udev_unref(session->udev);
|
||||
|
||||
session->impl->destroy(session);
|
||||
struct wlr_device *dev, *tmp_dev;
|
||||
wl_list_for_each_safe(dev, tmp_dev, &session->devices, link) {
|
||||
wlr_session_close_file(session, dev);
|
||||
}
|
||||
|
||||
libseat_session_finish(session);
|
||||
free(session);
|
||||
}
|
||||
|
||||
int wlr_session_open_file(struct wlr_session *session, const char *path) {
|
||||
int fd = session->impl->open(session, path);
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
struct wlr_device *wlr_session_open_file(struct wlr_session *session,
|
||||
const char *path) {
|
||||
int fd;
|
||||
int device_id = libseat_open_device(session->seat_handle, path, &fd);
|
||||
if (device_id == -1) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to open device: '%s'", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_device *dev = malloc(sizeof(*dev));
|
||||
|
@ -188,86 +337,65 @@ int wlr_session_open_file(struct wlr_session *session, const char *path) {
|
|||
|
||||
dev->fd = fd;
|
||||
dev->dev = st.st_rdev;
|
||||
wl_signal_init(&dev->signal);
|
||||
dev->device_id = device_id;
|
||||
wl_signal_init(&dev->events.change);
|
||||
wl_signal_init(&dev->events.remove);
|
||||
wl_list_insert(&session->devices, &dev->link);
|
||||
|
||||
return fd;
|
||||
return dev;
|
||||
|
||||
error:
|
||||
libseat_close_device(session->seat_handle, device_id);
|
||||
free(dev);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static struct wlr_device *find_device(struct wlr_session *session, int fd) {
|
||||
struct wlr_device *dev;
|
||||
|
||||
wl_list_for_each(dev, &session->devices, link) {
|
||||
if (dev->fd == fd) {
|
||||
return dev;
|
||||
}
|
||||
}
|
||||
|
||||
wlr_log(WLR_ERROR, "Tried to use fd %d not opened by session", fd);
|
||||
assert(0);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void wlr_session_close_file(struct wlr_session *session, int fd) {
|
||||
struct wlr_device *dev = find_device(session, fd);
|
||||
|
||||
session->impl->close(session, fd);
|
||||
void wlr_session_close_file(struct wlr_session *session,
|
||||
struct wlr_device *dev) {
|
||||
if (libseat_close_device(session->seat_handle, dev->device_id) == -1) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to close device %d", dev->device_id);
|
||||
}
|
||||
close(dev->fd);
|
||||
wl_list_remove(&dev->link);
|
||||
free(dev);
|
||||
}
|
||||
|
||||
void wlr_session_signal_add(struct wlr_session *session, int fd,
|
||||
struct wl_listener *listener) {
|
||||
struct wlr_device *dev = find_device(session, fd);
|
||||
|
||||
wl_signal_add(&dev->signal, listener);
|
||||
}
|
||||
|
||||
bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) {
|
||||
if (!session) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return session->impl->change_vt(session, vt);
|
||||
return libseat_switch_session(session->seat_handle, vt) == 0;
|
||||
}
|
||||
|
||||
/* Tests if 'path' is KMS compatible by trying to open it.
|
||||
* It leaves the open device in *fd_out it it succeeds.
|
||||
*/
|
||||
static int open_if_kms(struct wlr_session *restrict session,
|
||||
/* Tests if 'path' is KMS compatible by trying to open it. Returns the opened
|
||||
* device on success. */
|
||||
struct wlr_device *session_open_if_kms(struct wlr_session *restrict session,
|
||||
const char *restrict path) {
|
||||
if (!path) {
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fd = wlr_session_open_file(session, path);
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
struct wlr_device *dev = wlr_session_open_file(session, path);
|
||||
if (!dev) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
drmVersion *ver = drmGetVersion(fd);
|
||||
if (!ver) {
|
||||
goto out_fd;
|
||||
if (!drmIsKMS(dev->fd)) {
|
||||
wlr_log(WLR_DEBUG, "Ignoring '%s': not a KMS device", path);
|
||||
wlr_session_close_file(session, dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
drmFreeVersion(ver);
|
||||
return fd;
|
||||
|
||||
out_fd:
|
||||
wlr_session_close_file(session, fd);
|
||||
return -1;
|
||||
return dev;
|
||||
}
|
||||
|
||||
static size_t explicit_find_gpus(struct wlr_session *session,
|
||||
size_t ret_len, int ret[static ret_len], const char *str) {
|
||||
static ssize_t explicit_find_gpus(struct wlr_session *session,
|
||||
size_t ret_len, struct wlr_device *ret[static ret_len], const char *str) {
|
||||
char *gpus = strdup(str);
|
||||
if (!gpus) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
|
@ -278,8 +406,8 @@ static size_t explicit_find_gpus(struct wlr_session *session,
|
|||
break;
|
||||
}
|
||||
|
||||
ret[i] = open_if_kms(session, ptr);
|
||||
if (ret[i] < 0) {
|
||||
ret[i] = session_open_if_kms(session, ptr);
|
||||
if (!ret[i]) {
|
||||
wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr);
|
||||
} else {
|
||||
++i;
|
||||
|
@ -290,25 +418,92 @@ static size_t explicit_find_gpus(struct wlr_session *session,
|
|||
return i;
|
||||
}
|
||||
|
||||
static struct udev_enumerate *enumerate_drm_cards(struct udev *udev) {
|
||||
struct udev_enumerate *en = udev_enumerate_new(udev);
|
||||
if (!en) {
|
||||
wlr_log(WLR_ERROR, "udev_enumerate_new failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
udev_enumerate_add_match_subsystem(en, "drm");
|
||||
udev_enumerate_add_match_sysname(en, DRM_PRIMARY_MINOR_NAME "[0-9]*");
|
||||
|
||||
if (udev_enumerate_scan_devices(en) != 0) {
|
||||
wlr_log(WLR_ERROR, "udev_enumerate_scan_devices failed");
|
||||
udev_enumerate_unref(en);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return en;
|
||||
}
|
||||
|
||||
static uint64_t get_current_time_ms(void) {
|
||||
struct timespec ts = {0};
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
struct find_gpus_add_handler {
|
||||
bool added;
|
||||
struct wl_listener listener;
|
||||
};
|
||||
|
||||
static void find_gpus_handle_add(struct wl_listener *listener, void *data) {
|
||||
struct find_gpus_add_handler *handler =
|
||||
wl_container_of(listener, handler, listener);
|
||||
handler->added = true;
|
||||
}
|
||||
|
||||
/* Tries to find the primary GPU by checking for the "boot_vga" attribute.
|
||||
* If it's not found, it returns the first valid GPU it finds.
|
||||
*/
|
||||
size_t wlr_session_find_gpus(struct wlr_session *session,
|
||||
size_t ret_len, int *ret) {
|
||||
ssize_t wlr_session_find_gpus(struct wlr_session *session,
|
||||
size_t ret_len, struct wlr_device **ret) {
|
||||
const char *explicit = getenv("WLR_DRM_DEVICES");
|
||||
if (explicit) {
|
||||
return explicit_find_gpus(session, ret_len, ret, explicit);
|
||||
}
|
||||
|
||||
struct udev_enumerate *en = udev_enumerate_new(session->udev);
|
||||
struct udev_enumerate *en = enumerate_drm_cards(session->udev);
|
||||
if (!en) {
|
||||
wlr_log(WLR_ERROR, "Failed to create udev enumeration");
|
||||
return -1;
|
||||
}
|
||||
|
||||
udev_enumerate_add_match_subsystem(en, "drm");
|
||||
udev_enumerate_add_match_sysname(en, "card[0-9]*");
|
||||
udev_enumerate_scan_devices(en);
|
||||
if (udev_enumerate_get_list_entry(en) == NULL) {
|
||||
udev_enumerate_unref(en);
|
||||
wlr_log(WLR_INFO, "Waiting for a DRM card device");
|
||||
|
||||
struct find_gpus_add_handler handler = {0};
|
||||
handler.listener.notify = find_gpus_handle_add;
|
||||
wl_signal_add(&session->events.add_drm_card, &handler.listener);
|
||||
|
||||
uint64_t started_at = get_current_time_ms();
|
||||
uint64_t timeout = WAIT_GPU_TIMEOUT;
|
||||
struct wl_event_loop *event_loop =
|
||||
wl_display_get_event_loop(session->display);
|
||||
while (!handler.added) {
|
||||
int ret = wl_event_loop_dispatch(event_loop, (int)timeout);
|
||||
if (ret < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to wait for DRM card device: "
|
||||
"wl_event_loop_dispatch failed");
|
||||
udev_enumerate_unref(en);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t now = get_current_time_ms();
|
||||
if (now >= started_at + WAIT_GPU_TIMEOUT) {
|
||||
break;
|
||||
}
|
||||
timeout = started_at + WAIT_GPU_TIMEOUT - now;
|
||||
}
|
||||
|
||||
wl_list_remove(&handler.listener.link);
|
||||
|
||||
en = enumerate_drm_cards(session->udev);
|
||||
if (!en) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
struct udev_list_entry *entry;
|
||||
size_t i = 0;
|
||||
|
@ -346,17 +541,18 @@ size_t wlr_session_find_gpus(struct wlr_session *session,
|
|||
}
|
||||
}
|
||||
|
||||
int fd = open_if_kms(session, udev_device_get_devnode(dev));
|
||||
if (fd < 0) {
|
||||
struct wlr_device *wlr_dev =
|
||||
session_open_if_kms(session, udev_device_get_devnode(dev));
|
||||
if (!wlr_dev) {
|
||||
udev_device_unref(dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
udev_device_unref(dev);
|
||||
|
||||
ret[i] = fd;
|
||||
ret[i] = wlr_dev;
|
||||
if (is_boot_vga) {
|
||||
int tmp = ret[0];
|
||||
struct wlr_device *tmp = ret[0];
|
||||
ret[0] = ret[i];
|
||||
ret[i] = tmp;
|
||||
}
|
||||
|
|
|
@ -1,30 +1,52 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <wlr/config.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <drm_fourcc.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <xf86drm.h>
|
||||
|
||||
#include <wlr/backend/interface.h>
|
||||
#include <wlr/interfaces/wlr_input_device.h>
|
||||
#include <wlr/interfaces/wlr_output.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include <wlr/render/gles2.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
||||
#include "backend/wayland.h"
|
||||
#include "render/drm_format_set.h"
|
||||
#include "render/pixel_format.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
#include "drm-client-protocol.h"
|
||||
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
||||
#include "pointer-gestures-unstable-v1-client-protocol.h"
|
||||
#include "presentation-time-client-protocol.h"
|
||||
#include "xdg-activation-v1-client-protocol.h"
|
||||
#include "xdg-decoration-unstable-v1-client-protocol.h"
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
#include "tablet-unstable-v2-client-protocol.h"
|
||||
#include "relative-pointer-unstable-v1-client-protocol.h"
|
||||
|
||||
struct wlr_wl_linux_dmabuf_feedback_v1 {
|
||||
struct wlr_wl_backend *backend;
|
||||
dev_t main_device_id;
|
||||
struct wlr_wl_linux_dmabuf_v1_table_entry *format_table;
|
||||
size_t format_table_size;
|
||||
|
||||
dev_t tranche_target_device_id;
|
||||
};
|
||||
|
||||
struct wlr_wl_linux_dmabuf_v1_table_entry {
|
||||
uint32_t format;
|
||||
uint32_t pad; /* unused */
|
||||
uint64_t modifier;
|
||||
};
|
||||
|
||||
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend) {
|
||||
assert(wlr_backend_is_wl(backend));
|
||||
return (struct wlr_wl_backend *)backend;
|
||||
|
@ -70,6 +92,16 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = {
|
|||
xdg_wm_base_handle_ping,
|
||||
};
|
||||
|
||||
static void presentation_handle_clock_id(void *data,
|
||||
struct wp_presentation *presentation, uint32_t clock) {
|
||||
struct wlr_wl_backend *wl = data;
|
||||
wl->presentation_clock = clock;
|
||||
}
|
||||
|
||||
static const struct wp_presentation_listener presentation_listener = {
|
||||
.clock_id = presentation_handle_clock_id,
|
||||
};
|
||||
|
||||
static void linux_dmabuf_v1_handle_format(void *data,
|
||||
struct zwp_linux_dmabuf_v1 *linux_dmabuf_v1, uint32_t format) {
|
||||
// Note, this event is deprecated
|
||||
|
@ -93,6 +125,218 @@ static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_v1_listener = {
|
|||
.modifier = linux_dmabuf_v1_handle_modifier,
|
||||
};
|
||||
|
||||
static void linux_dmabuf_feedback_v1_handle_done(void *data,
|
||||
struct zwp_linux_dmabuf_feedback_v1 *feedback) {
|
||||
// This space is intentionally left blank
|
||||
}
|
||||
|
||||
static void linux_dmabuf_feedback_v1_handle_format_table(void *data,
|
||||
struct zwp_linux_dmabuf_feedback_v1 *feedback, int fd, uint32_t size) {
|
||||
struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
|
||||
|
||||
feedback_data->format_table = NULL;
|
||||
|
||||
void *table_data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (table_data == MAP_FAILED) {
|
||||
wlr_log_errno(WLR_ERROR, "failed to mmap DMA-BUF format table");
|
||||
} else {
|
||||
feedback_data->format_table = table_data;
|
||||
feedback_data->format_table_size = size;
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void linux_dmabuf_feedback_v1_handle_main_device(void *data,
|
||||
struct zwp_linux_dmabuf_feedback_v1 *feedback,
|
||||
struct wl_array *dev_id_arr) {
|
||||
struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
|
||||
|
||||
dev_t dev_id;
|
||||
assert(dev_id_arr->size == sizeof(dev_id));
|
||||
memcpy(&dev_id, dev_id_arr->data, sizeof(dev_id));
|
||||
|
||||
feedback_data->main_device_id = dev_id;
|
||||
|
||||
drmDevice *device = NULL;
|
||||
if (drmGetDeviceFromDevId(dev_id, 0, &device) != 0) {
|
||||
wlr_log_errno(WLR_ERROR, "drmGetDeviceFromDevId failed");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *name = NULL;
|
||||
if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
|
||||
name = device->nodes[DRM_NODE_RENDER];
|
||||
} else {
|
||||
// Likely a split display/render setup. Pick the primary node and hope
|
||||
// Mesa will open the right render node under-the-hood.
|
||||
assert(device->available_nodes & (1 << DRM_NODE_PRIMARY));
|
||||
name = device->nodes[DRM_NODE_PRIMARY];
|
||||
wlr_log(WLR_DEBUG, "DRM device %s has no render node, "
|
||||
"falling back to primary node", name);
|
||||
}
|
||||
|
||||
feedback_data->backend->drm_render_name = strdup(name);
|
||||
|
||||
drmFreeDevice(&device);
|
||||
}
|
||||
|
||||
static void linux_dmabuf_feedback_v1_handle_tranche_done(void *data,
|
||||
struct zwp_linux_dmabuf_feedback_v1 *feedback) {
|
||||
struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
|
||||
feedback_data->tranche_target_device_id = 0;
|
||||
}
|
||||
|
||||
static void linux_dmabuf_feedback_v1_handle_tranche_target_device(void *data,
|
||||
struct zwp_linux_dmabuf_feedback_v1 *feedback,
|
||||
struct wl_array *dev_id_arr) {
|
||||
struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
|
||||
|
||||
dev_t dev_id;
|
||||
assert(dev_id_arr->size == sizeof(dev_id));
|
||||
memcpy(&dev_id, dev_id_arr->data, sizeof(dev_id));
|
||||
|
||||
feedback_data->tranche_target_device_id = dev_id;
|
||||
}
|
||||
|
||||
static void linux_dmabuf_feedback_v1_handle_tranche_formats(void *data,
|
||||
struct zwp_linux_dmabuf_feedback_v1 *feedback,
|
||||
struct wl_array *indices_arr) {
|
||||
struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
|
||||
|
||||
if (feedback_data->format_table == NULL) {
|
||||
return;
|
||||
}
|
||||
if (feedback_data->tranche_target_device_id != feedback_data->main_device_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t table_cap = feedback_data->format_table_size /
|
||||
sizeof(struct wlr_wl_linux_dmabuf_v1_table_entry);
|
||||
uint16_t *index_ptr;
|
||||
wl_array_for_each(index_ptr, indices_arr) {
|
||||
assert(*index_ptr < table_cap);
|
||||
const struct wlr_wl_linux_dmabuf_v1_table_entry *entry =
|
||||
&feedback_data->format_table[*index_ptr];
|
||||
wlr_drm_format_set_add(&feedback_data->backend->linux_dmabuf_v1_formats,
|
||||
entry->format, entry->modifier);
|
||||
}
|
||||
}
|
||||
|
||||
static void linux_dmabuf_feedback_v1_handle_tranche_flags(void *data,
|
||||
struct zwp_linux_dmabuf_feedback_v1 *feedback, uint32_t flags) {
|
||||
// TODO: handle SCANOUT flag
|
||||
}
|
||||
|
||||
static const struct zwp_linux_dmabuf_feedback_v1_listener
|
||||
linux_dmabuf_feedback_v1_listener = {
|
||||
.done = linux_dmabuf_feedback_v1_handle_done,
|
||||
.format_table = linux_dmabuf_feedback_v1_handle_format_table,
|
||||
.main_device = linux_dmabuf_feedback_v1_handle_main_device,
|
||||
.tranche_done = linux_dmabuf_feedback_v1_handle_tranche_done,
|
||||
.tranche_target_device = linux_dmabuf_feedback_v1_handle_tranche_target_device,
|
||||
.tranche_formats = linux_dmabuf_feedback_v1_handle_tranche_formats,
|
||||
.tranche_flags = linux_dmabuf_feedback_v1_handle_tranche_flags,
|
||||
};
|
||||
|
||||
static bool device_has_name(const drmDevice *device, const char *name) {
|
||||
for (size_t i = 0; i < DRM_NODE_MAX; i++) {
|
||||
if (!(device->available_nodes & (1 << i))) {
|
||||
continue;
|
||||
}
|
||||
if (strcmp(device->nodes[i], name) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static char *get_render_name(const char *name) {
|
||||
uint32_t flags = 0;
|
||||
int devices_len = drmGetDevices2(flags, NULL, 0);
|
||||
if (devices_len < 0) {
|
||||
wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len));
|
||||
return NULL;
|
||||
}
|
||||
drmDevice **devices = calloc(devices_len, sizeof(drmDevice *));
|
||||
if (devices == NULL) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
devices_len = drmGetDevices2(flags, devices, devices_len);
|
||||
if (devices_len < 0) {
|
||||
free(devices);
|
||||
wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const drmDevice *match = NULL;
|
||||
for (int i = 0; i < devices_len; i++) {
|
||||
if (device_has_name(devices[i], name)) {
|
||||
match = devices[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
char *render_name = NULL;
|
||||
if (match == NULL) {
|
||||
wlr_log(WLR_ERROR, "Cannot find DRM device %s", name);
|
||||
} else if (!(match->available_nodes & (1 << DRM_NODE_RENDER))) {
|
||||
// Likely a split display/render setup. Pick the primary node and hope
|
||||
// Mesa will open the right render node under-the-hood.
|
||||
wlr_log(WLR_DEBUG, "DRM device %s has no render node, "
|
||||
"falling back to primary node", name);
|
||||
assert(match->available_nodes & (1 << DRM_NODE_PRIMARY));
|
||||
render_name = strdup(match->nodes[DRM_NODE_PRIMARY]);
|
||||
} else {
|
||||
render_name = strdup(match->nodes[DRM_NODE_RENDER]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < devices_len; i++) {
|
||||
drmFreeDevice(&devices[i]);
|
||||
}
|
||||
free(devices);
|
||||
|
||||
return render_name;
|
||||
}
|
||||
|
||||
static void legacy_drm_handle_device(void *data, struct wl_drm *drm,
|
||||
const char *name) {
|
||||
struct wlr_wl_backend *wl = data;
|
||||
wl->drm_render_name = get_render_name(name);
|
||||
}
|
||||
|
||||
static void legacy_drm_handle_format(void *data, struct wl_drm *drm,
|
||||
uint32_t format) {
|
||||
// This space is intentionally left blank
|
||||
}
|
||||
|
||||
static void legacy_drm_handle_authenticated(void *data, struct wl_drm *drm) {
|
||||
// This space is intentionally left blank
|
||||
}
|
||||
|
||||
static void legacy_drm_handle_capabilities(void *data, struct wl_drm *drm,
|
||||
uint32_t caps) {
|
||||
// This space is intentionally left blank
|
||||
}
|
||||
|
||||
static const struct wl_drm_listener legacy_drm_listener = {
|
||||
.device = legacy_drm_handle_device,
|
||||
.format = legacy_drm_handle_format,
|
||||
.authenticated = legacy_drm_handle_authenticated,
|
||||
.capabilities = legacy_drm_handle_capabilities,
|
||||
};
|
||||
|
||||
static void shm_handle_format(void *data, struct wl_shm *shm,
|
||||
uint32_t shm_format) {
|
||||
struct wlr_wl_backend *wl = data;
|
||||
uint32_t drm_format = convert_wl_shm_format_to_drm(shm_format);
|
||||
wlr_drm_format_set_add(&wl->shm_formats, drm_format, DRM_FORMAT_MOD_INVALID);
|
||||
}
|
||||
|
||||
static const struct wl_shm_listener shm_listener = {
|
||||
.format = shm_handle_format,
|
||||
};
|
||||
|
||||
static void registry_global(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *iface, uint32_t version) {
|
||||
struct wlr_wl_backend *wl = data;
|
||||
|
@ -117,22 +361,33 @@ static void registry_global(void *data, struct wl_registry *registry,
|
|||
&zxdg_decoration_manager_v1_interface, 1);
|
||||
} else if (strcmp(iface, zwp_pointer_gestures_v1_interface.name) == 0) {
|
||||
wl->zwp_pointer_gestures_v1 = wl_registry_bind(registry, name,
|
||||
&zwp_pointer_gestures_v1_interface, 1);
|
||||
&zwp_pointer_gestures_v1_interface, version < 3 ? version : 3);
|
||||
} else if (strcmp(iface, wp_presentation_interface.name) == 0) {
|
||||
wl->presentation = wl_registry_bind(registry, name,
|
||||
&wp_presentation_interface, 1);
|
||||
wp_presentation_add_listener(wl->presentation,
|
||||
&presentation_listener, wl);
|
||||
} else if (strcmp(iface, zwp_tablet_manager_v2_interface.name) == 0) {
|
||||
wl->tablet_manager = wl_registry_bind(registry, name,
|
||||
&zwp_tablet_manager_v2_interface, 1);
|
||||
} else if (strcmp(iface, zwp_linux_dmabuf_v1_interface.name) == 0 &&
|
||||
version >= 3) {
|
||||
wl->zwp_linux_dmabuf_v1 = wl_registry_bind(registry, name,
|
||||
&zwp_linux_dmabuf_v1_interface, 3);
|
||||
&zwp_linux_dmabuf_v1_interface, version >= 4 ? 4 : version);
|
||||
zwp_linux_dmabuf_v1_add_listener(wl->zwp_linux_dmabuf_v1,
|
||||
&linux_dmabuf_v1_listener, wl);
|
||||
} else if (strcmp(iface, zwp_relative_pointer_manager_v1_interface.name) == 0) {
|
||||
wl->zwp_relative_pointer_manager_v1 = wl_registry_bind(registry, name,
|
||||
&zwp_relative_pointer_manager_v1_interface, 1);
|
||||
} else if (strcmp(iface, wl_drm_interface.name) == 0) {
|
||||
wl->legacy_drm = wl_registry_bind(registry, name, &wl_drm_interface, 1);
|
||||
wl_drm_add_listener(wl->legacy_drm, &legacy_drm_listener, wl);
|
||||
} else if (strcmp(iface, wl_shm_interface.name) == 0) {
|
||||
wl->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
|
||||
wl_shm_add_listener(wl->shm, &shm_listener, wl);
|
||||
} else if (strcmp(iface, xdg_activation_v1_interface.name) == 0) {
|
||||
wl->activation_v1 = wl_registry_bind(registry, name,
|
||||
&xdg_activation_v1_interface, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,19 +408,18 @@ static const struct wl_registry_listener registry_listener = {
|
|||
*/
|
||||
static bool backend_start(struct wlr_backend *backend) {
|
||||
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
||||
wlr_log(WLR_INFO, "Initializating wayland backend");
|
||||
wlr_log(WLR_INFO, "Starting Wayland backend");
|
||||
|
||||
wl->started = true;
|
||||
|
||||
struct wlr_wl_seat *seat = wl->seat;
|
||||
if (seat != NULL) {
|
||||
struct wlr_wl_seat *seat;
|
||||
wl_list_for_each(seat, &wl->seats, link) {
|
||||
if (seat->keyboard) {
|
||||
create_wl_keyboard(seat->keyboard, wl);
|
||||
create_wl_keyboard(seat);
|
||||
}
|
||||
|
||||
if (wl->tablet_manager) {
|
||||
wl_add_tablet_seat(wl->tablet_manager,
|
||||
seat->wl_seat, wl);
|
||||
wl_add_tablet_seat(wl->tablet_manager, seat);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,20 +442,25 @@ static void backend_destroy(struct wlr_backend *backend) {
|
|||
wlr_output_destroy(&output->wlr_output);
|
||||
}
|
||||
|
||||
struct wlr_input_device *input_device, *tmp_input_device;
|
||||
struct wlr_wl_input_device *input_device, *tmp_input_device;
|
||||
wl_list_for_each_safe(input_device, tmp_input_device, &wl->devices, link) {
|
||||
wlr_input_device_destroy(input_device);
|
||||
wlr_input_device_destroy(&input_device->wlr_input_device);
|
||||
}
|
||||
|
||||
wlr_signal_emit_safe(&wl->backend.events.destroy, &wl->backend);
|
||||
struct wlr_wl_buffer *buffer, *tmp_buffer;
|
||||
wl_list_for_each_safe(buffer, tmp_buffer, &wl->buffers, link) {
|
||||
destroy_wl_buffer(buffer);
|
||||
}
|
||||
|
||||
wlr_backend_finish(backend);
|
||||
|
||||
wl_list_remove(&wl->local_display_destroy.link);
|
||||
|
||||
wl_event_source_remove(wl->remote_display_src);
|
||||
|
||||
wlr_renderer_destroy(wl->renderer);
|
||||
wlr_egl_finish(&wl->egl);
|
||||
close(wl->drm_fd);
|
||||
|
||||
wlr_drm_format_set_finish(&wl->shm_formats);
|
||||
wlr_drm_format_set_finish(&wl->linux_dmabuf_v1_formats);
|
||||
|
||||
destroy_wl_seats(wl);
|
||||
|
@ -217,25 +476,44 @@ static void backend_destroy(struct wlr_backend *backend) {
|
|||
if (wl->zwp_linux_dmabuf_v1) {
|
||||
zwp_linux_dmabuf_v1_destroy(wl->zwp_linux_dmabuf_v1);
|
||||
}
|
||||
if (wl->shm) {
|
||||
wl_shm_destroy(wl->shm);
|
||||
}
|
||||
if (wl->zwp_relative_pointer_manager_v1) {
|
||||
zwp_relative_pointer_manager_v1_destroy(wl->zwp_relative_pointer_manager_v1);
|
||||
}
|
||||
free(wl->drm_render_name);
|
||||
free(wl->activation_token);
|
||||
xdg_wm_base_destroy(wl->xdg_wm_base);
|
||||
wl_compositor_destroy(wl->compositor);
|
||||
wl_registry_destroy(wl->registry);
|
||||
wl_display_flush(wl->remote_display);
|
||||
wl_display_disconnect(wl->remote_display);
|
||||
free(wl);
|
||||
}
|
||||
|
||||
static struct wlr_renderer *backend_get_renderer(struct wlr_backend *backend) {
|
||||
static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) {
|
||||
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
||||
return wl->renderer;
|
||||
return wl->presentation_clock;
|
||||
}
|
||||
|
||||
static struct wlr_backend_impl backend_impl = {
|
||||
static int backend_get_drm_fd(struct wlr_backend *backend) {
|
||||
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
||||
return wl->drm_fd;
|
||||
}
|
||||
|
||||
static uint32_t get_buffer_caps(struct wlr_backend *backend) {
|
||||
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
||||
return (wl->zwp_linux_dmabuf_v1 ? WLR_BUFFER_CAP_DMABUF : 0)
|
||||
| (wl->shm ? WLR_BUFFER_CAP_SHM : 0);
|
||||
}
|
||||
|
||||
static const struct wlr_backend_impl backend_impl = {
|
||||
.start = backend_start,
|
||||
.destroy = backend_destroy,
|
||||
.get_renderer = backend_get_renderer,
|
||||
.get_presentation_clock = backend_get_presentation_clock,
|
||||
.get_drm_fd = backend_get_drm_fd,
|
||||
.get_buffer_caps = get_buffer_caps,
|
||||
};
|
||||
|
||||
bool wlr_backend_is_wl(struct wlr_backend *b) {
|
||||
|
@ -249,7 +527,7 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
|||
}
|
||||
|
||||
struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
||||
const char *remote, wlr_renderer_create_func_t create_renderer_func) {
|
||||
const char *remote) {
|
||||
wlr_log(WLR_INFO, "Creating wayland backend");
|
||||
|
||||
struct wlr_wl_backend *wl = calloc(1, sizeof(*wl));
|
||||
|
@ -263,6 +541,9 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
|||
wl->local_display = display;
|
||||
wl_list_init(&wl->devices);
|
||||
wl_list_init(&wl->outputs);
|
||||
wl_list_init(&wl->seats);
|
||||
wl_list_init(&wl->buffers);
|
||||
wl->presentation_clock = CLOCK_MONOTONIC;
|
||||
|
||||
wl->remote_display = wl_display_connect(remote);
|
||||
if (!wl->remote_display) {
|
||||
|
@ -275,9 +556,9 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
|||
wlr_log_errno(WLR_ERROR, "Could not obtain reference to remote registry");
|
||||
goto error_display;
|
||||
}
|
||||
|
||||
wl_registry_add_listener(wl->registry, ®istry_listener, wl);
|
||||
wl_display_roundtrip(wl->remote_display);
|
||||
|
||||
wl_display_roundtrip(wl->remote_display); // get globals
|
||||
|
||||
if (!wl->compositor) {
|
||||
wlr_log(WLR_ERROR,
|
||||
|
@ -290,10 +571,38 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
|||
goto error_registry;
|
||||
}
|
||||
|
||||
struct zwp_linux_dmabuf_feedback_v1 *linux_dmabuf_feedback_v1 = NULL;
|
||||
struct wlr_wl_linux_dmabuf_feedback_v1 feedback_data = { .backend = wl };
|
||||
if (wl->zwp_linux_dmabuf_v1 != NULL &&
|
||||
zwp_linux_dmabuf_v1_get_version(wl->zwp_linux_dmabuf_v1) >=
|
||||
ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) {
|
||||
linux_dmabuf_feedback_v1 =
|
||||
zwp_linux_dmabuf_v1_get_default_feedback(wl->zwp_linux_dmabuf_v1);
|
||||
if (linux_dmabuf_feedback_v1 == NULL) {
|
||||
wlr_log(WLR_ERROR, "Allocation failed");
|
||||
goto error_registry;
|
||||
}
|
||||
zwp_linux_dmabuf_feedback_v1_add_listener(linux_dmabuf_feedback_v1,
|
||||
&linux_dmabuf_feedback_v1_listener, &feedback_data);
|
||||
|
||||
if (wl->legacy_drm != NULL) {
|
||||
wl_drm_destroy(wl->legacy_drm);
|
||||
wl->legacy_drm = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
wl_display_roundtrip(wl->remote_display); // get linux-dmabuf formats
|
||||
|
||||
if (feedback_data.format_table != NULL) {
|
||||
munmap(feedback_data.format_table, feedback_data.format_table_size);
|
||||
}
|
||||
if (linux_dmabuf_feedback_v1 != NULL) {
|
||||
zwp_linux_dmabuf_feedback_v1_destroy(linux_dmabuf_feedback_v1);
|
||||
}
|
||||
|
||||
struct wl_event_loop *loop = wl_display_get_event_loop(wl->local_display);
|
||||
int fd = wl_display_get_fd(wl->remote_display);
|
||||
int events = WL_EVENT_READABLE | WL_EVENT_ERROR | WL_EVENT_HANGUP;
|
||||
wl->remote_display_src = wl_event_loop_add_fd(loop, fd, events,
|
||||
wl->remote_display_src = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
|
||||
dispatch_events, wl);
|
||||
if (!wl->remote_display_src) {
|
||||
wlr_log(WLR_ERROR, "Failed to create event source");
|
||||
|
@ -301,36 +610,33 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
|||
}
|
||||
wl_event_source_check(wl->remote_display_src);
|
||||
|
||||
static EGLint config_attribs[] = {
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_RED_SIZE, 1,
|
||||
EGL_GREEN_SIZE, 1,
|
||||
EGL_BLUE_SIZE, 1,
|
||||
EGL_ALPHA_SIZE, 1,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
if (!create_renderer_func) {
|
||||
create_renderer_func = wlr_renderer_autocreate;
|
||||
}
|
||||
|
||||
wl->renderer = create_renderer_func(&wl->egl, EGL_PLATFORM_WAYLAND_EXT,
|
||||
wl->remote_display, config_attribs, WL_SHM_FORMAT_ARGB8888);
|
||||
|
||||
if (!wl->renderer) {
|
||||
wlr_log(WLR_ERROR, "Could not create renderer");
|
||||
goto error_event;
|
||||
if (wl->drm_render_name != NULL) {
|
||||
wlr_log(WLR_DEBUG, "Opening DRM render node %s", wl->drm_render_name);
|
||||
wl->drm_fd = open(wl->drm_render_name, O_RDWR | O_NONBLOCK | O_CLOEXEC);
|
||||
if (wl->drm_fd < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "Failed to open DRM render node %s",
|
||||
wl->drm_render_name);
|
||||
goto error_remote_display_src;
|
||||
}
|
||||
} else {
|
||||
wl->drm_fd = -1;
|
||||
}
|
||||
|
||||
wl->local_display_destroy.notify = handle_display_destroy;
|
||||
wl_display_add_destroy_listener(display, &wl->local_display_destroy);
|
||||
|
||||
const char *token = getenv("XDG_ACTIVATION_TOKEN");
|
||||
if (token != NULL) {
|
||||
wl->activation_token = strdup(token);
|
||||
unsetenv("XDG_ACTIVATION_TOKEN");
|
||||
}
|
||||
|
||||
return &wl->backend;
|
||||
|
||||
error_event:
|
||||
error_remote_display_src:
|
||||
wl_event_source_remove(wl->remote_display_src);
|
||||
error_registry:
|
||||
free(wl->drm_render_name);
|
||||
if (wl->compositor) {
|
||||
wl_compositor_destroy(wl->compositor);
|
||||
}
|
||||
|
@ -341,6 +647,7 @@ error_registry:
|
|||
error_display:
|
||||
wl_display_disconnect(wl->remote_display);
|
||||
error_wl:
|
||||
wlr_backend_finish(&wl->backend);
|
||||
free(wl);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
wayland_client = dependency('wayland-client',
|
||||
fallback: ['wayland', 'wayland_client_dep'],
|
||||
default_options: wayland_project_options,
|
||||
)
|
||||
wlr_deps += wayland_client
|
||||
|
||||
wlr_files += files(
|
||||
'backend.c',
|
||||
'output.c',
|
||||
|
@ -6,11 +12,13 @@ wlr_files += files(
|
|||
)
|
||||
|
||||
client_protos = [
|
||||
'drm',
|
||||
'linux-dmabuf-unstable-v1',
|
||||
'pointer-gestures-unstable-v1',
|
||||
'presentation-time',
|
||||
'relative-pointer-unstable-v1',
|
||||
'tablet-unstable-v2',
|
||||
'xdg-activation-v1',
|
||||
'xdg-decoration-unstable-v1',
|
||||
'xdg-shell',
|
||||
]
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <drm_fourcc.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include <wlr/interfaces/wlr_output.h>
|
||||
|
@ -15,12 +16,22 @@
|
|||
#include <wlr/util/log.h>
|
||||
|
||||
#include "backend/wayland.h"
|
||||
#include "render/pixel_format.h"
|
||||
#include "render/swapchain.h"
|
||||
#include "render/wlr_renderer.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
||||
#include "presentation-time-client-protocol.h"
|
||||
#include "xdg-activation-v1-client-protocol.h"
|
||||
#include "xdg-decoration-unstable-v1-client-protocol.h"
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
|
||||
static const uint32_t SUPPORTED_OUTPUT_STATE =
|
||||
WLR_OUTPUT_STATE_BACKEND_OPTIONAL |
|
||||
WLR_OUTPUT_STATE_BUFFER |
|
||||
WLR_OUTPUT_STATE_MODE;
|
||||
|
||||
static struct wlr_wl_output *get_wl_output_from_output(
|
||||
struct wlr_output *wlr_output) {
|
||||
assert(wlr_output_is_wl(wlr_output));
|
||||
|
@ -65,6 +76,7 @@ static void presentation_feedback_handle_presented(void *data,
|
|||
};
|
||||
struct wlr_output_event_present event = {
|
||||
.commit_seq = feedback->commit_seq,
|
||||
.presented = true,
|
||||
.when = &t,
|
||||
.seq = ((uint64_t)seq_hi << 32) | seq_lo,
|
||||
.refresh = refresh_ns,
|
||||
|
@ -79,7 +91,11 @@ static void presentation_feedback_handle_discarded(void *data,
|
|||
struct wp_presentation_feedback *wp_feedback) {
|
||||
struct wlr_wl_presentation_feedback *feedback = data;
|
||||
|
||||
wlr_output_send_present(&feedback->output->wlr_output, NULL);
|
||||
struct wlr_output_event_present event = {
|
||||
.commit_seq = feedback->commit_seq,
|
||||
.presented = false,
|
||||
};
|
||||
wlr_output_send_present(&feedback->output->wlr_output, &event);
|
||||
|
||||
presentation_feedback_destroy(feedback);
|
||||
}
|
||||
|
@ -93,51 +109,81 @@ static const struct wp_presentation_feedback_listener
|
|||
|
||||
static bool output_set_custom_mode(struct wlr_output *wlr_output,
|
||||
int32_t width, int32_t height, int32_t refresh) {
|
||||
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
||||
wl_egl_window_resize(output->egl_window, width, height, 0, 0);
|
||||
wlr_output_update_custom_mode(&output->wlr_output, width, height, 0);
|
||||
wlr_output_update_custom_mode(wlr_output, width, height, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool output_attach_render(struct wlr_output *wlr_output,
|
||||
int *buffer_age) {
|
||||
struct wlr_wl_output *output =
|
||||
get_wl_output_from_output(wlr_output);
|
||||
return wlr_egl_make_current(&output->backend->egl, output->egl_surface,
|
||||
buffer_age);
|
||||
}
|
||||
|
||||
static void destroy_wl_buffer(struct wlr_wl_buffer *buffer) {
|
||||
void destroy_wl_buffer(struct wlr_wl_buffer *buffer) {
|
||||
if (buffer == NULL) {
|
||||
return;
|
||||
}
|
||||
wl_list_remove(&buffer->buffer_destroy.link);
|
||||
wl_list_remove(&buffer->link);
|
||||
wl_buffer_destroy(buffer->wl_buffer);
|
||||
wlr_buffer_unlock(buffer->buffer);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) {
|
||||
struct wlr_wl_buffer *buffer = data;
|
||||
destroy_wl_buffer(buffer);
|
||||
buffer->released = true;
|
||||
wlr_buffer_unlock(buffer->buffer); // might free buffer
|
||||
}
|
||||
|
||||
static const struct wl_buffer_listener buffer_listener = {
|
||||
.release = buffer_handle_release,
|
||||
};
|
||||
|
||||
static void buffer_handle_buffer_destroy(struct wl_listener *listener,
|
||||
void *data) {
|
||||
struct wlr_wl_buffer *buffer =
|
||||
wl_container_of(listener, buffer, buffer_destroy);
|
||||
destroy_wl_buffer(buffer);
|
||||
}
|
||||
|
||||
static bool test_buffer(struct wlr_wl_backend *wl,
|
||||
struct wlr_buffer *wlr_buffer) {
|
||||
struct wlr_dmabuf_attributes attribs;
|
||||
if (!wlr_buffer_get_dmabuf(wlr_buffer, &attribs)) {
|
||||
struct wlr_dmabuf_attributes dmabuf;
|
||||
struct wlr_shm_attributes shm;
|
||||
if (wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) {
|
||||
return wlr_drm_format_set_has(&wl->linux_dmabuf_v1_formats,
|
||||
dmabuf.format, dmabuf.modifier);
|
||||
} else if (wlr_buffer_get_shm(wlr_buffer, &shm)) {
|
||||
return wlr_drm_format_set_has(&wl->shm_formats, shm.format,
|
||||
DRM_FORMAT_MOD_INVALID);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!wlr_drm_format_set_has(&wl->linux_dmabuf_v1_formats,
|
||||
attribs.format, attribs.modifier)) {
|
||||
return false;
|
||||
static struct wl_buffer *import_dmabuf(struct wlr_wl_backend *wl,
|
||||
struct wlr_dmabuf_attributes *dmabuf) {
|
||||
uint32_t modifier_hi = dmabuf->modifier >> 32;
|
||||
uint32_t modifier_lo = (uint32_t)dmabuf->modifier;
|
||||
struct zwp_linux_buffer_params_v1 *params =
|
||||
zwp_linux_dmabuf_v1_create_params(wl->zwp_linux_dmabuf_v1);
|
||||
for (int i = 0; i < dmabuf->n_planes; i++) {
|
||||
zwp_linux_buffer_params_v1_add(params, dmabuf->fd[i], i,
|
||||
dmabuf->offset[i], dmabuf->stride[i], modifier_hi, modifier_lo);
|
||||
}
|
||||
|
||||
return true;
|
||||
struct wl_buffer *wl_buffer = zwp_linux_buffer_params_v1_create_immed(
|
||||
params, dmabuf->width, dmabuf->height, dmabuf->format, 0);
|
||||
// TODO: handle create() errors
|
||||
return wl_buffer;
|
||||
}
|
||||
|
||||
static struct wl_buffer *import_shm(struct wlr_wl_backend *wl,
|
||||
struct wlr_shm_attributes *shm) {
|
||||
enum wl_shm_format wl_shm_format = convert_drm_format_to_wl_shm(shm->format);
|
||||
uint32_t size = shm->stride * shm->height;
|
||||
struct wl_shm_pool *pool = wl_shm_create_pool(wl->shm, shm->fd, size);
|
||||
if (pool == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
struct wl_buffer *wl_buffer = wl_shm_pool_create_buffer(pool, shm->offset,
|
||||
shm->width, shm->height, shm->stride, wl_shm_format);
|
||||
wl_shm_pool_destroy(pool);
|
||||
return wl_buffer;
|
||||
}
|
||||
|
||||
static struct wlr_wl_buffer *create_wl_buffer(struct wlr_wl_backend *wl,
|
||||
|
@ -146,34 +192,20 @@ static struct wlr_wl_buffer *create_wl_buffer(struct wlr_wl_backend *wl,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_dmabuf_attributes attribs;
|
||||
if (!wlr_buffer_get_dmabuf(wlr_buffer, &attribs)) {
|
||||
struct wlr_dmabuf_attributes dmabuf;
|
||||
struct wlr_shm_attributes shm;
|
||||
struct wl_buffer *wl_buffer;
|
||||
if (wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) {
|
||||
wl_buffer = import_dmabuf(wl, &dmabuf);
|
||||
} else if (wlr_buffer_get_shm(wlr_buffer, &shm)) {
|
||||
wl_buffer = import_shm(wl, &shm);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t modifier_hi = attribs.modifier >> 32;
|
||||
uint32_t modifier_lo = (uint32_t)attribs.modifier;
|
||||
struct zwp_linux_buffer_params_v1 *params =
|
||||
zwp_linux_dmabuf_v1_create_params(wl->zwp_linux_dmabuf_v1);
|
||||
for (int i = 0; i < attribs.n_planes; i++) {
|
||||
zwp_linux_buffer_params_v1_add(params, attribs.fd[i], i,
|
||||
attribs.offset[i], attribs.stride[i], modifier_hi, modifier_lo);
|
||||
if (wl_buffer == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t flags = 0;
|
||||
if (attribs.flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT) {
|
||||
flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT;
|
||||
}
|
||||
if (attribs.flags & WLR_DMABUF_ATTRIBUTES_FLAGS_INTERLACED) {
|
||||
flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED;
|
||||
}
|
||||
if (attribs.flags & WLR_DMABUF_ATTRIBUTES_FLAGS_BOTTOM_FIRST) {
|
||||
flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST;
|
||||
}
|
||||
struct wl_buffer *wl_buffer = zwp_linux_buffer_params_v1_create_immed(
|
||||
params, attribs.width, attribs.height, attribs.format, flags);
|
||||
// TODO: handle create() errors
|
||||
|
||||
struct wlr_wl_buffer *buffer = calloc(1, sizeof(struct wlr_wl_buffer));
|
||||
if (buffer == NULL) {
|
||||
wl_buffer_destroy(wl_buffer);
|
||||
|
@ -181,18 +213,42 @@ static struct wlr_wl_buffer *create_wl_buffer(struct wlr_wl_backend *wl,
|
|||
}
|
||||
buffer->wl_buffer = wl_buffer;
|
||||
buffer->buffer = wlr_buffer_lock(wlr_buffer);
|
||||
wl_list_insert(&wl->buffers, &buffer->link);
|
||||
|
||||
wl_buffer_add_listener(wl_buffer, &buffer_listener, buffer);
|
||||
|
||||
buffer->buffer_destroy.notify = buffer_handle_buffer_destroy;
|
||||
wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static struct wlr_wl_buffer *get_or_create_wl_buffer(struct wlr_wl_backend *wl,
|
||||
struct wlr_buffer *wlr_buffer) {
|
||||
struct wlr_wl_buffer *buffer;
|
||||
wl_list_for_each(buffer, &wl->buffers, link) {
|
||||
// We can only re-use a wlr_wl_buffer if the parent compositor has
|
||||
// released it, because wl_buffer.release is per-wl_buffer, not per
|
||||
// wl_surface.commit.
|
||||
if (buffer->buffer == wlr_buffer && buffer->released) {
|
||||
buffer->released = false;
|
||||
wlr_buffer_lock(buffer->buffer);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
return create_wl_buffer(wl, wlr_buffer);
|
||||
}
|
||||
|
||||
static bool output_test(struct wlr_output *wlr_output) {
|
||||
struct wlr_wl_output *output =
|
||||
get_wl_output_from_output(wlr_output);
|
||||
|
||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_ENABLED) {
|
||||
wlr_log(WLR_DEBUG, "Cannot disable a Wayland output");
|
||||
uint32_t unsupported =
|
||||
wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE;
|
||||
if (unsupported != 0) {
|
||||
wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32,
|
||||
unsupported);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -201,7 +257,6 @@ static bool output_test(struct wlr_output *wlr_output) {
|
|||
}
|
||||
|
||||
if ((wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) &&
|
||||
wlr_output->pending.buffer_type == WLR_OUTPUT_STATE_BUFFER_SCANOUT &&
|
||||
!test_buffer(output->backend, wlr_output->pending.buffer)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -246,39 +301,31 @@ static bool output_commit(struct wlr_output *wlr_output) {
|
|||
output->frame_callback = wl_surface_frame(output->surface);
|
||||
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
|
||||
|
||||
switch (wlr_output->pending.buffer_type) {
|
||||
case WLR_OUTPUT_STATE_BUFFER_RENDER:
|
||||
if (!wlr_egl_swap_buffers(&output->backend->egl,
|
||||
output->egl_surface, damage)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case WLR_OUTPUT_STATE_BUFFER_SCANOUT:;
|
||||
struct wlr_wl_buffer *buffer =
|
||||
create_wl_buffer(output->backend, wlr_output->pending.buffer);
|
||||
if (buffer == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wl_surface_attach(output->surface, buffer->wl_buffer, 0, 0);
|
||||
|
||||
if (damage == NULL) {
|
||||
wl_surface_damage_buffer(output->surface,
|
||||
0, 0, INT32_MAX, INT32_MAX);
|
||||
} else {
|
||||
int rects_len;
|
||||
pixman_box32_t *rects =
|
||||
pixman_region32_rectangles(damage, &rects_len);
|
||||
for (int i = 0; i < rects_len; i++) {
|
||||
pixman_box32_t *r = &rects[i];
|
||||
wl_surface_damage_buffer(output->surface, r->x1, r->y1,
|
||||
r->x2 - r->x1, r->y2 - r->y1);
|
||||
}
|
||||
}
|
||||
wl_surface_commit(output->surface);
|
||||
break;
|
||||
struct wlr_buffer *wlr_buffer = wlr_output->pending.buffer;
|
||||
struct wlr_wl_buffer *buffer =
|
||||
get_or_create_wl_buffer(output->backend, wlr_buffer);
|
||||
if (buffer == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wl_surface_attach(output->surface, buffer->wl_buffer, 0, 0);
|
||||
|
||||
if (damage == NULL) {
|
||||
wl_surface_damage_buffer(output->surface,
|
||||
0, 0, INT32_MAX, INT32_MAX);
|
||||
} else {
|
||||
int rects_len;
|
||||
pixman_box32_t *rects =
|
||||
pixman_region32_rectangles(damage, &rects_len);
|
||||
for (int i = 0; i < rects_len; i++) {
|
||||
pixman_box32_t *r = &rects[i];
|
||||
wl_surface_damage_buffer(output->surface, r->x1, r->y1,
|
||||
r->x2 - r->x1, r->y2 - r->y1);
|
||||
}
|
||||
}
|
||||
|
||||
wl_surface_commit(output->surface);
|
||||
|
||||
if (wp_feedback != NULL) {
|
||||
struct wlr_wl_presentation_feedback *feedback =
|
||||
calloc(1, sizeof(*feedback));
|
||||
|
@ -294,40 +341,26 @@ static bool output_commit(struct wlr_output *wlr_output) {
|
|||
wp_presentation_feedback_add_listener(wp_feedback,
|
||||
&presentation_feedback_listener, feedback);
|
||||
} else {
|
||||
wlr_output_send_present(wlr_output, NULL);
|
||||
struct wlr_output_event_present present_event = {
|
||||
.commit_seq = wlr_output->commit_seq + 1,
|
||||
.presented = true,
|
||||
};
|
||||
wlr_output_send_present(wlr_output, &present_event);
|
||||
}
|
||||
}
|
||||
|
||||
wl_display_flush(output->backend->remote_display);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void output_rollback_render(struct wlr_output *wlr_output) {
|
||||
struct wlr_wl_output *output =
|
||||
get_wl_output_from_output(wlr_output);
|
||||
wlr_egl_unset_current(&output->backend->egl);
|
||||
}
|
||||
|
||||
static bool output_set_cursor(struct wlr_output *wlr_output,
|
||||
struct wlr_texture *texture, float scale,
|
||||
enum wl_output_transform transform,
|
||||
int32_t hotspot_x, int32_t hotspot_y, bool update_texture) {
|
||||
struct wlr_buffer *wlr_buffer, int hotspot_x, int hotspot_y) {
|
||||
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
||||
struct wlr_wl_backend *backend = output->backend;
|
||||
|
||||
struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y };
|
||||
wlr_box_transform(&hotspot, &hotspot,
|
||||
wlr_output_transform_invert(wlr_output->transform),
|
||||
output->cursor.width, output->cursor.height);
|
||||
|
||||
// TODO: use output->wlr_output.transform to transform pixels and hotpot
|
||||
output->cursor.hotspot_x = hotspot.x;
|
||||
output->cursor.hotspot_y = hotspot.y;
|
||||
|
||||
if (!update_texture) {
|
||||
// Update hotspot without changing cursor image
|
||||
update_wl_output_cursor(output);
|
||||
return true;
|
||||
}
|
||||
output->cursor.hotspot_x = hotspot_x;
|
||||
output->cursor.hotspot_y = hotspot_y;
|
||||
|
||||
if (output->cursor.surface == NULL) {
|
||||
output->cursor.surface =
|
||||
|
@ -335,53 +368,37 @@ static bool output_set_cursor(struct wlr_output *wlr_output,
|
|||
}
|
||||
struct wl_surface *surface = output->cursor.surface;
|
||||
|
||||
if (texture != NULL) {
|
||||
int width, height;
|
||||
wlr_texture_get_size(texture, &width, &height);
|
||||
width = width * wlr_output->scale / scale;
|
||||
height = height * wlr_output->scale / scale;
|
||||
|
||||
output->cursor.width = width;
|
||||
output->cursor.height = height;
|
||||
|
||||
if (output->cursor.egl_window == NULL) {
|
||||
output->cursor.egl_window =
|
||||
wl_egl_window_create(surface, width, height);
|
||||
if (wlr_buffer != NULL) {
|
||||
struct wlr_wl_buffer *buffer =
|
||||
get_or_create_wl_buffer(output->backend, wlr_buffer);
|
||||
if (buffer == NULL) {
|
||||
return false;
|
||||
}
|
||||
wl_egl_window_resize(output->cursor.egl_window, width, height, 0, 0);
|
||||
|
||||
EGLSurface egl_surface =
|
||||
wlr_egl_create_surface(&backend->egl, output->cursor.egl_window);
|
||||
|
||||
wlr_egl_make_current(&backend->egl, egl_surface, NULL);
|
||||
|
||||
struct wlr_box cursor_box = {
|
||||
.width = width,
|
||||
.height = height,
|
||||
};
|
||||
|
||||
float projection[9];
|
||||
wlr_matrix_projection(projection, width, height, wlr_output->transform);
|
||||
|
||||
float matrix[9];
|
||||
wlr_matrix_project_box(matrix, &cursor_box, transform, 0, projection);
|
||||
|
||||
wlr_renderer_begin(backend->renderer, width, height);
|
||||
wlr_renderer_clear(backend->renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 });
|
||||
wlr_render_texture_with_matrix(backend->renderer, texture, matrix, 1.0);
|
||||
wlr_renderer_end(backend->renderer);
|
||||
|
||||
wlr_egl_swap_buffers(&backend->egl, egl_surface, NULL);
|
||||
wlr_egl_destroy_surface(&backend->egl, egl_surface);
|
||||
wl_surface_attach(surface, buffer->wl_buffer, 0, 0);
|
||||
wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX);
|
||||
wl_surface_commit(surface);
|
||||
} else {
|
||||
wl_surface_attach(surface, NULL, 0, 0);
|
||||
wl_surface_commit(surface);
|
||||
}
|
||||
|
||||
update_wl_output_cursor(output);
|
||||
wl_display_flush(backend->remote_display);
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct wlr_drm_format_set *output_get_formats(
|
||||
struct wlr_output *wlr_output, uint32_t buffer_caps) {
|
||||
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
||||
if (buffer_caps & WLR_BUFFER_CAP_DMABUF) {
|
||||
return &output->backend->linux_dmabuf_v1_formats;
|
||||
} else if (buffer_caps & WLR_BUFFER_CAP_SHM) {
|
||||
return &output->backend->shm_formats;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void output_destroy(struct wlr_output *wlr_output) {
|
||||
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
||||
if (output == NULL) {
|
||||
|
@ -390,9 +407,6 @@ static void output_destroy(struct wlr_output *wlr_output) {
|
|||
|
||||
wl_list_remove(&output->link);
|
||||
|
||||
if (output->cursor.egl_window != NULL) {
|
||||
wl_egl_window_destroy(output->cursor.egl_window);
|
||||
}
|
||||
if (output->cursor.surface) {
|
||||
wl_surface_destroy(output->cursor.surface);
|
||||
}
|
||||
|
@ -407,20 +421,21 @@ static void output_destroy(struct wlr_output *wlr_output) {
|
|||
presentation_feedback_destroy(feedback);
|
||||
}
|
||||
|
||||
wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface);
|
||||
wl_egl_window_destroy(output->egl_window);
|
||||
if (output->zxdg_toplevel_decoration_v1) {
|
||||
zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1);
|
||||
}
|
||||
xdg_toplevel_destroy(output->xdg_toplevel);
|
||||
xdg_surface_destroy(output->xdg_surface);
|
||||
wl_surface_destroy(output->surface);
|
||||
wl_display_flush(output->backend->remote_display);
|
||||
free(output);
|
||||
}
|
||||
|
||||
void update_wl_output_cursor(struct wlr_wl_output *output) {
|
||||
struct wlr_wl_pointer *pointer = output->backend->current_pointer;
|
||||
if (pointer && pointer->output == output && output->enter_serial) {
|
||||
struct wlr_wl_pointer *pointer = output->cursor.pointer;
|
||||
if (pointer) {
|
||||
assert(pointer->output == output);
|
||||
assert(output->enter_serial);
|
||||
wl_pointer_set_cursor(pointer->wl_pointer, output->enter_serial,
|
||||
output->cursor.surface, output->cursor.hotspot_x,
|
||||
output->cursor.hotspot_y);
|
||||
|
@ -434,12 +449,12 @@ static bool output_move_cursor(struct wlr_output *_output, int x, int y) {
|
|||
|
||||
static const struct wlr_output_impl output_impl = {
|
||||
.destroy = output_destroy,
|
||||
.attach_render = output_attach_render,
|
||||
.test = output_test,
|
||||
.commit = output_commit,
|
||||
.rollback_render = output_rollback_render,
|
||||
.set_cursor = output_set_cursor,
|
||||
.move_cursor = output_move_cursor,
|
||||
.get_cursor_formats = output_get_formats,
|
||||
.get_primary_formats = output_get_formats,
|
||||
};
|
||||
|
||||
bool wlr_output_is_wl(struct wlr_output *wlr_output) {
|
||||
|
@ -456,7 +471,7 @@ static void xdg_surface_handle_configure(void *data,
|
|||
// nothing else?
|
||||
}
|
||||
|
||||
static struct xdg_surface_listener xdg_surface_listener = {
|
||||
static const struct xdg_surface_listener xdg_surface_listener = {
|
||||
.configure = xdg_surface_handle_configure,
|
||||
};
|
||||
|
||||
|
@ -478,10 +493,10 @@ static void xdg_toplevel_handle_close(void *data,
|
|||
struct wlr_wl_output *output = data;
|
||||
assert(output && output->xdg_toplevel == xdg_toplevel);
|
||||
|
||||
wlr_output_destroy((struct wlr_output *)output);
|
||||
wlr_output_destroy(&output->wlr_output);
|
||||
}
|
||||
|
||||
static struct xdg_toplevel_listener xdg_toplevel_listener = {
|
||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
||||
.configure = xdg_toplevel_handle_configure,
|
||||
.close = xdg_toplevel_handle_close,
|
||||
};
|
||||
|
@ -505,12 +520,14 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) {
|
|||
wlr_output_update_custom_mode(wlr_output, 1280, 720, 0);
|
||||
strncpy(wlr_output->make, "wayland", sizeof(wlr_output->make));
|
||||
strncpy(wlr_output->model, "wayland", sizeof(wlr_output->model));
|
||||
snprintf(wlr_output->name, sizeof(wlr_output->name), "WL-%zd",
|
||||
++backend->last_output_num);
|
||||
|
||||
char name[64];
|
||||
snprintf(name, sizeof(name), "WL-%zu", ++backend->last_output_num);
|
||||
wlr_output_set_name(wlr_output, name);
|
||||
|
||||
char description[128];
|
||||
snprintf(description, sizeof(description),
|
||||
"Wayland output %zd", backend->last_output_num);
|
||||
"Wayland output %zu", backend->last_output_num);
|
||||
wlr_output_set_description(wlr_output, description);
|
||||
|
||||
output->backend = backend;
|
||||
|
@ -556,43 +573,29 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) {
|
|||
&xdg_toplevel_listener, output);
|
||||
wl_surface_commit(output->surface);
|
||||
|
||||
output->egl_window = wl_egl_window_create(output->surface,
|
||||
wlr_output->width, wlr_output->height);
|
||||
output->egl_surface = wlr_egl_create_surface(&backend->egl,
|
||||
output->egl_window);
|
||||
|
||||
wl_display_roundtrip(output->backend->remote_display);
|
||||
|
||||
// start rendering loop per callbacks by rendering first frame
|
||||
if (!wlr_egl_make_current(&output->backend->egl, output->egl_surface,
|
||||
NULL)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height);
|
||||
wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 });
|
||||
wlr_renderer_end(backend->renderer);
|
||||
|
||||
output->frame_callback = wl_surface_frame(output->surface);
|
||||
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
|
||||
|
||||
if (!wlr_egl_swap_buffers(&output->backend->egl, output->egl_surface,
|
||||
NULL)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
wl_list_insert(&backend->outputs, &output->link);
|
||||
wlr_output_update_enabled(wlr_output, true);
|
||||
|
||||
wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output);
|
||||
|
||||
struct wlr_wl_seat *seat = backend->seat;
|
||||
if (seat != NULL) {
|
||||
struct wlr_wl_seat *seat;
|
||||
wl_list_for_each(seat, &backend->seats, link) {
|
||||
if (seat->pointer) {
|
||||
create_wl_pointer(seat->pointer, output);
|
||||
create_wl_pointer(seat, output);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: let the compositor do this bit
|
||||
if (backend->activation_v1 && backend->activation_token) {
|
||||
xdg_activation_v1_activate(backend->activation_v1,
|
||||
backend->activation_token, output->surface);
|
||||
}
|
||||
|
||||
// Start the rendering loop by requesting the compositor to render a frame
|
||||
wlr_output_schedule_frame(wlr_output);
|
||||
|
||||
return wlr_output;
|
||||
|
||||
error:
|
||||
|
@ -612,6 +615,7 @@ void wlr_wl_output_set_title(struct wlr_output *output, const char *title) {
|
|||
}
|
||||
|
||||
xdg_toplevel_set_title(wl_output->xdg_toplevel, title);
|
||||
wl_display_flush(wl_output->backend->remote_display);
|
||||
}
|
||||
|
||||
struct wl_surface *wlr_wl_output_get_surface(struct wlr_output *output) {
|
||||
|
|
|
@ -22,14 +22,17 @@
|
|||
#include "util/signal.h"
|
||||
#include "util/time.h"
|
||||
|
||||
static struct wlr_wl_pointer *output_get_pointer(struct wlr_wl_output *output) {
|
||||
struct wlr_input_device *wlr_dev;
|
||||
wl_list_for_each(wlr_dev, &output->backend->devices, link) {
|
||||
if (wlr_dev->type != WLR_INPUT_DEVICE_POINTER) {
|
||||
static struct wlr_wl_pointer *output_get_pointer(
|
||||
struct wlr_wl_output *output,
|
||||
const struct wl_pointer *wl_pointer) {
|
||||
struct wlr_wl_input_device *dev;
|
||||
wl_list_for_each(dev, &output->backend->devices, link) {
|
||||
if (dev->wlr_input_device.type != WLR_INPUT_DEVICE_POINTER) {
|
||||
continue;
|
||||
}
|
||||
struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer);
|
||||
if (pointer->output == output) {
|
||||
struct wlr_wl_pointer *pointer =
|
||||
pointer_get_wl(dev->wlr_input_device.pointer);
|
||||
if (pointer->output == output && pointer->wl_pointer == wl_pointer) {
|
||||
return pointer;
|
||||
}
|
||||
}
|
||||
|
@ -40,44 +43,54 @@ static struct wlr_wl_pointer *output_get_pointer(struct wlr_wl_output *output) {
|
|||
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t serial, struct wl_surface *surface, wl_fixed_t sx,
|
||||
wl_fixed_t sy) {
|
||||
struct wlr_wl_backend *backend = data;
|
||||
struct wlr_wl_seat *seat = data;
|
||||
if (surface == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct wlr_wl_output *output = wl_surface_get_user_data(surface);
|
||||
assert(output);
|
||||
struct wlr_wl_pointer *pointer = output_get_pointer(output);
|
||||
assert(!backend->current_pointer || backend->current_pointer == pointer);
|
||||
struct wlr_wl_pointer *pointer = output_get_pointer(output, wl_pointer);
|
||||
seat->active_pointer = pointer;
|
||||
|
||||
// Manage cursor icon/rendering on output
|
||||
struct wlr_wl_pointer *current_pointer = output->cursor.pointer;
|
||||
if (current_pointer && current_pointer != pointer) {
|
||||
wlr_log(WLR_INFO, "Ignoring seat %s pointer cursor in favor of seat %s",
|
||||
seat->name, current_pointer->input_device->seat->name);
|
||||
return;
|
||||
}
|
||||
|
||||
output->enter_serial = serial;
|
||||
backend->current_pointer = pointer;
|
||||
output->cursor.pointer = pointer;
|
||||
update_wl_output_cursor(output);
|
||||
}
|
||||
|
||||
static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t serial, struct wl_surface *surface) {
|
||||
struct wlr_wl_backend *backend = data;
|
||||
struct wlr_wl_seat *seat = data;
|
||||
if (surface == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct wlr_wl_output *output = wl_surface_get_user_data(surface);
|
||||
assert(output);
|
||||
output->enter_serial = 0;
|
||||
|
||||
if (backend->current_pointer == NULL ||
|
||||
backend->current_pointer->output != output) {
|
||||
return;
|
||||
if (seat->active_pointer != NULL &&
|
||||
seat->active_pointer->output == output) {
|
||||
seat->active_pointer = NULL;
|
||||
}
|
||||
|
||||
backend->current_pointer = NULL;
|
||||
if (output->cursor.pointer == seat->active_pointer) {
|
||||
output->enter_serial = 0;
|
||||
output->cursor.pointer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t time, wl_fixed_t sx, wl_fixed_t sy) {
|
||||
struct wlr_wl_backend *backend = data;
|
||||
struct wlr_wl_pointer *pointer = backend->current_pointer;
|
||||
struct wlr_wl_seat *seat = data;
|
||||
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
||||
if (pointer == NULL) {
|
||||
return;
|
||||
}
|
||||
|
@ -94,8 +107,8 @@ static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
|
|||
|
||||
static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
|
||||
struct wlr_wl_backend *backend = data;
|
||||
struct wlr_wl_pointer *pointer = backend->current_pointer;
|
||||
struct wlr_wl_seat *seat = data;
|
||||
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
||||
if (pointer == NULL) {
|
||||
return;
|
||||
}
|
||||
|
@ -111,8 +124,8 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
|
|||
|
||||
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t time, uint32_t axis, wl_fixed_t value) {
|
||||
struct wlr_wl_backend *backend = data;
|
||||
struct wlr_wl_pointer *pointer = backend->current_pointer;
|
||||
struct wlr_wl_seat *seat = data;
|
||||
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
||||
if (pointer == NULL) {
|
||||
return;
|
||||
}
|
||||
|
@ -131,8 +144,8 @@ static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
|
|||
}
|
||||
|
||||
static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
|
||||
struct wlr_wl_backend *backend = data;
|
||||
struct wlr_wl_pointer *pointer = backend->current_pointer;
|
||||
struct wlr_wl_seat *seat = data;
|
||||
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
||||
if (pointer == NULL) {
|
||||
return;
|
||||
}
|
||||
|
@ -143,8 +156,8 @@ static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
|
|||
|
||||
static void pointer_handle_axis_source(void *data,
|
||||
struct wl_pointer *wl_pointer, uint32_t axis_source) {
|
||||
struct wlr_wl_backend *backend = data;
|
||||
struct wlr_wl_pointer *pointer = backend->current_pointer;
|
||||
struct wlr_wl_seat *seat = data;
|
||||
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
||||
if (pointer == NULL) {
|
||||
return;
|
||||
}
|
||||
|
@ -154,8 +167,8 @@ static void pointer_handle_axis_source(void *data,
|
|||
|
||||
static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t time, uint32_t axis) {
|
||||
struct wlr_wl_backend *backend = data;
|
||||
struct wlr_wl_pointer *pointer = backend->current_pointer;
|
||||
struct wlr_wl_seat *seat = data;
|
||||
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
||||
if (pointer == NULL) {
|
||||
return;
|
||||
}
|
||||
|
@ -173,8 +186,8 @@ static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer,
|
|||
|
||||
static void pointer_handle_axis_discrete(void *data,
|
||||
struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
|
||||
struct wlr_wl_backend *backend = data;
|
||||
struct wlr_wl_pointer *pointer = backend->current_pointer;
|
||||
struct wlr_wl_seat *seat = data;
|
||||
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
||||
if (pointer == NULL) {
|
||||
return;
|
||||
}
|
||||
|
@ -209,7 +222,7 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
|
|||
wl_array_for_each(keycode_ptr, keys) {
|
||||
struct wlr_event_keyboard_key event = {
|
||||
.keycode = *keycode_ptr,
|
||||
.state = WLR_KEY_PRESSED,
|
||||
.state = WL_KEYBOARD_KEY_STATE_PRESSED,
|
||||
.time_msec = time,
|
||||
.update_state = false,
|
||||
};
|
||||
|
@ -223,16 +236,17 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
|
|||
|
||||
uint32_t time = get_current_time_msec();
|
||||
|
||||
uint32_t pressed[dev->keyboard->num_keycodes + 1];
|
||||
size_t num_keycodes = dev->keyboard->num_keycodes;
|
||||
uint32_t pressed[num_keycodes + 1];
|
||||
memcpy(pressed, dev->keyboard->keycodes,
|
||||
dev->keyboard->num_keycodes * sizeof(uint32_t));
|
||||
num_keycodes * sizeof(uint32_t));
|
||||
|
||||
for (size_t i = 0; i < sizeof(pressed)/sizeof(pressed[0]); ++i) {
|
||||
for (size_t i = 0; i < num_keycodes; ++i) {
|
||||
uint32_t keycode = pressed[i];
|
||||
|
||||
struct wlr_event_keyboard_key event = {
|
||||
.keycode = keycode,
|
||||
.state = WLR_KEY_RELEASED,
|
||||
.state = WL_KEYBOARD_KEY_STATE_RELEASED,
|
||||
.time_msec = time,
|
||||
.update_state = false,
|
||||
};
|
||||
|
@ -268,7 +282,7 @@ static void keyboard_handle_repeat_info(void *data,
|
|||
// This space is intentionally left blank
|
||||
}
|
||||
|
||||
static struct wl_keyboard_listener keyboard_listener = {
|
||||
static const struct wl_keyboard_listener keyboard_listener = {
|
||||
.keymap = keyboard_handle_keymap,
|
||||
.enter = keyboard_handle_enter,
|
||||
.leave = keyboard_handle_leave,
|
||||
|
@ -339,7 +353,10 @@ static void touch_handle_motion(void *data, struct wl_touch *wl_touch,
|
|||
}
|
||||
|
||||
static void touch_handle_frame(void *data, struct wl_touch *wl_touch) {
|
||||
// no-op
|
||||
struct wlr_wl_input_device *device = data;
|
||||
assert(device && device->wlr_input_device.touch);
|
||||
|
||||
wlr_signal_emit_safe(&device->wlr_input_device.touch->events.frame, NULL);
|
||||
}
|
||||
|
||||
static void touch_handle_cancel(void *data, struct wl_touch *wl_touch) {
|
||||
|
@ -356,7 +373,7 @@ static void touch_handle_orientation(void *data, struct wl_touch *wl_touch,
|
|||
// no-op
|
||||
}
|
||||
|
||||
static struct wl_touch_listener touch_listener = {
|
||||
static const struct wl_touch_listener touch_listener = {
|
||||
.down = touch_handle_down,
|
||||
.up = touch_handle_up,
|
||||
.motion = touch_handle_motion,
|
||||
|
@ -373,51 +390,45 @@ static struct wlr_wl_input_device *get_wl_input_device_from_input_device(
|
|||
}
|
||||
|
||||
bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl) {
|
||||
assert(!wl->seat); // only one seat supported at the moment
|
||||
struct wlr_wl_seat *seat = calloc(1, sizeof(struct wlr_wl_seat));
|
||||
if (!seat) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return false;
|
||||
}
|
||||
seat->wl_seat = wl_seat;
|
||||
wl->seat = seat;
|
||||
wl_seat_add_listener(wl_seat, &seat_listener, wl);
|
||||
seat->backend = wl;
|
||||
wl_list_insert(&wl->seats, &seat->link);
|
||||
wl_seat_add_listener(wl_seat, &seat_listener, seat);
|
||||
return true;
|
||||
}
|
||||
|
||||
void destroy_wl_seats(struct wlr_wl_backend *wl) {
|
||||
struct wlr_wl_seat *seat = wl->seat;
|
||||
if (!seat) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (seat->touch) {
|
||||
wl_touch_destroy(seat->touch);
|
||||
}
|
||||
if (seat->pointer) {
|
||||
wl_pointer_destroy(seat->pointer);
|
||||
}
|
||||
if (seat->keyboard && !wl->started) {
|
||||
// early termination will not be handled by input_device_destroy
|
||||
wl_keyboard_destroy(seat->keyboard);
|
||||
}
|
||||
free(seat->name);
|
||||
if (seat->wl_seat) {
|
||||
struct wlr_wl_seat *seat, *tmp_seat;
|
||||
wl_list_for_each_safe(seat, tmp_seat, &wl->seats, link) {
|
||||
if (seat->touch) {
|
||||
wl_touch_destroy(seat->touch);
|
||||
}
|
||||
if (seat->pointer) {
|
||||
wl_pointer_destroy(seat->pointer);
|
||||
}
|
||||
if (seat->keyboard && !wl->started) {
|
||||
// early termination will not be handled by input_device_destroy
|
||||
wl_keyboard_destroy(seat->keyboard);
|
||||
}
|
||||
free(seat->name);
|
||||
assert(seat->wl_seat);
|
||||
wl_seat_destroy(seat->wl_seat);
|
||||
}
|
||||
free(seat);
|
||||
}
|
||||
|
||||
static struct wlr_wl_seat *backend_get_seat(struct wlr_wl_backend *backend, struct wl_seat *wl_seat) {
|
||||
assert(backend->seat && backend->seat->wl_seat == wl_seat);
|
||||
return backend->seat;
|
||||
wl_list_remove(&seat->link);
|
||||
free(seat);
|
||||
}
|
||||
}
|
||||
|
||||
static struct wlr_wl_seat *input_device_get_seat(struct wlr_input_device *wlr_dev) {
|
||||
struct wlr_wl_input_device *dev =
|
||||
get_wl_input_device_from_input_device(wlr_dev);
|
||||
assert(dev->backend->seat);
|
||||
return dev->backend->seat;
|
||||
assert(dev->seat);
|
||||
return dev->seat;
|
||||
}
|
||||
|
||||
static void input_device_destroy(struct wlr_input_device *wlr_dev) {
|
||||
|
@ -428,11 +439,13 @@ static void input_device_destroy(struct wlr_input_device *wlr_dev) {
|
|||
wl_keyboard_release(seat->keyboard);
|
||||
seat->keyboard = NULL;
|
||||
}
|
||||
wl_list_remove(&dev->wlr_input_device.link);
|
||||
// We can't destroy pointer here because we might have multiple devices
|
||||
// exposing it to compositor.
|
||||
wl_list_remove(&dev->link);
|
||||
free(dev);
|
||||
}
|
||||
|
||||
static struct wlr_input_device_impl input_device_impl = {
|
||||
static const struct wlr_input_device_impl input_device_impl = {
|
||||
.destroy = input_device_destroy,
|
||||
};
|
||||
|
||||
|
@ -441,26 +454,54 @@ bool wlr_input_device_is_wl(struct wlr_input_device *dev) {
|
|||
}
|
||||
|
||||
struct wlr_wl_input_device *create_wl_input_device(
|
||||
struct wlr_wl_backend *backend, enum wlr_input_device_type type) {
|
||||
struct wlr_wl_seat *seat, enum wlr_input_device_type type) {
|
||||
struct wlr_wl_input_device *dev =
|
||||
calloc(1, sizeof(struct wlr_wl_input_device));
|
||||
if (dev == NULL) {
|
||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
dev->backend = backend;
|
||||
dev->backend = seat->backend;
|
||||
dev->seat = seat;
|
||||
|
||||
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
||||
|
||||
unsigned int vendor = 0, product = 0;
|
||||
const char *name = "wayland";
|
||||
|
||||
const char *type_name = "unknown";
|
||||
|
||||
switch (type) {
|
||||
case WLR_INPUT_DEVICE_KEYBOARD:
|
||||
type_name = "keyboard";
|
||||
break;
|
||||
case WLR_INPUT_DEVICE_POINTER:
|
||||
type_name = "pointer";
|
||||
break;
|
||||
case WLR_INPUT_DEVICE_TOUCH:
|
||||
type_name = "touch";
|
||||
break;
|
||||
case WLR_INPUT_DEVICE_TABLET_TOOL:
|
||||
type_name = "tablet-tool";
|
||||
break;
|
||||
case WLR_INPUT_DEVICE_TABLET_PAD:
|
||||
type_name = "tablet-pad";
|
||||
break;
|
||||
case WLR_INPUT_DEVICE_SWITCH:
|
||||
type_name = "switch";
|
||||
break;
|
||||
}
|
||||
|
||||
size_t name_size = 8 + strlen(type_name) + strlen(seat->name) + 1;
|
||||
char name[name_size];
|
||||
(void) snprintf(name, name_size, "wayland-%s-%s", type_name, seat->name);
|
||||
|
||||
wlr_input_device_init(wlr_dev, type, &input_device_impl, name, vendor,
|
||||
product);
|
||||
wl_list_insert(&backend->devices, &wlr_dev->link);
|
||||
wl_list_insert(&seat->backend->devices, &dev->link);
|
||||
return dev;
|
||||
}
|
||||
|
||||
static struct wlr_pointer_impl pointer_impl;
|
||||
static const struct wlr_pointer_impl pointer_impl;
|
||||
|
||||
struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer) {
|
||||
assert(wlr_pointer->impl == &pointer_impl);
|
||||
|
@ -470,15 +511,35 @@ struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer) {
|
|||
static void pointer_destroy(struct wlr_pointer *wlr_pointer) {
|
||||
struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_pointer);
|
||||
|
||||
if (pointer->output->backend->current_pointer == pointer) {
|
||||
pointer->output->backend->current_pointer = NULL;
|
||||
if (pointer->output->cursor.pointer == pointer) {
|
||||
pointer->output->cursor.pointer = NULL;
|
||||
}
|
||||
|
||||
struct wlr_wl_seat *seat = pointer->input_device->seat;
|
||||
if (seat->active_pointer == pointer) {
|
||||
seat->active_pointer = NULL;
|
||||
}
|
||||
|
||||
// pointer->wl_pointer belongs to the wlr_wl_seat
|
||||
|
||||
if (pointer->gesture_swipe != NULL) {
|
||||
zwp_pointer_gesture_swipe_v1_destroy(pointer->gesture_swipe);
|
||||
}
|
||||
if (pointer->gesture_pinch != NULL) {
|
||||
zwp_pointer_gesture_pinch_v1_destroy(pointer->gesture_pinch);
|
||||
}
|
||||
if (pointer->gesture_hold != NULL) {
|
||||
zwp_pointer_gesture_hold_v1_destroy(pointer->gesture_hold);
|
||||
}
|
||||
if (pointer->relative_pointer != NULL) {
|
||||
zwp_relative_pointer_v1_destroy(pointer->relative_pointer);
|
||||
}
|
||||
|
||||
wl_list_remove(&pointer->output_destroy.link);
|
||||
free(pointer);
|
||||
}
|
||||
|
||||
static struct wlr_pointer_impl pointer_impl = {
|
||||
static const struct wlr_pointer_impl pointer_impl = {
|
||||
.destroy = pointer_destroy,
|
||||
};
|
||||
|
||||
|
@ -525,7 +586,7 @@ static void gesture_swipe_end(void *data,
|
|||
wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_end, &wlr_event);
|
||||
}
|
||||
|
||||
static struct zwp_pointer_gesture_swipe_v1_listener gesture_swipe_impl = {
|
||||
static const struct zwp_pointer_gesture_swipe_v1_listener gesture_swipe_impl = {
|
||||
.begin = gesture_swipe_begin,
|
||||
.update = gesture_swipe_update,
|
||||
.end = gesture_swipe_end,
|
||||
|
@ -576,12 +637,44 @@ static void gesture_pinch_end(void *data,
|
|||
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_end, &wlr_event);
|
||||
}
|
||||
|
||||
static struct zwp_pointer_gesture_pinch_v1_listener gesture_pinch_impl = {
|
||||
static const struct zwp_pointer_gesture_pinch_v1_listener gesture_pinch_impl = {
|
||||
.begin = gesture_pinch_begin,
|
||||
.update = gesture_pinch_update,
|
||||
.end = gesture_pinch_end,
|
||||
};
|
||||
|
||||
static void gesture_hold_begin(void *data,
|
||||
struct zwp_pointer_gesture_hold_v1 *zwp_pointer_gesture_hold_v1,
|
||||
uint32_t serial, uint32_t time,
|
||||
struct wl_surface *surface, uint32_t fingers) {
|
||||
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
|
||||
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
|
||||
struct wlr_event_pointer_hold_begin wlr_event = {
|
||||
.device = wlr_dev,
|
||||
.time_msec = time,
|
||||
.fingers = fingers,
|
||||
};
|
||||
input_device->fingers = fingers;
|
||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.hold_begin, &wlr_event);
|
||||
}
|
||||
|
||||
static void gesture_hold_end(void *data,
|
||||
struct zwp_pointer_gesture_hold_v1 *zwp_pointer_gesture_hold_v1,
|
||||
uint32_t serial, uint32_t time, int32_t cancelled) {
|
||||
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
|
||||
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
|
||||
struct wlr_event_pointer_hold_end wlr_event = {
|
||||
.device = wlr_dev,
|
||||
.time_msec = time,
|
||||
.cancelled = cancelled,
|
||||
};
|
||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.hold_end, &wlr_event);
|
||||
}
|
||||
|
||||
static const struct zwp_pointer_gesture_hold_v1_listener gesture_hold_impl = {
|
||||
.begin = gesture_hold_begin,
|
||||
.end = gesture_hold_end,
|
||||
};
|
||||
|
||||
static void relative_pointer_handle_relative_motion(void *data,
|
||||
struct zwp_relative_pointer_v1 *relative_pointer, uint32_t utime_hi,
|
||||
|
@ -589,7 +682,7 @@ static void relative_pointer_handle_relative_motion(void *data,
|
|||
wl_fixed_t dy_unaccel) {
|
||||
struct wlr_wl_input_device *input_device = data;
|
||||
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
|
||||
if (pointer_get_wl(wlr_dev->pointer) != input_device->backend->current_pointer) {
|
||||
if (pointer_get_wl(wlr_dev->pointer) != input_device->seat->active_pointer) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -615,24 +708,19 @@ static void pointer_handle_output_destroy(struct wl_listener *listener,
|
|||
void *data) {
|
||||
struct wlr_wl_pointer *pointer =
|
||||
wl_container_of(listener, pointer, output_destroy);
|
||||
if (pointer->relative_pointer) {
|
||||
zwp_relative_pointer_v1_destroy(pointer->relative_pointer);
|
||||
}
|
||||
wlr_input_device_destroy(&pointer->input_device->wlr_input_device);
|
||||
}
|
||||
|
||||
void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *output) {
|
||||
void create_wl_pointer(struct wlr_wl_seat *seat, struct wlr_wl_output *output) {
|
||||
assert(seat->pointer);
|
||||
struct wl_pointer *wl_pointer = seat->pointer;
|
||||
struct wlr_wl_backend *backend = output->backend;
|
||||
|
||||
struct wlr_input_device *wlr_dev;
|
||||
wl_list_for_each(wlr_dev, &output->backend->devices, link) {
|
||||
if (wlr_dev->type != WLR_INPUT_DEVICE_POINTER) {
|
||||
continue;
|
||||
}
|
||||
struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer);
|
||||
if (pointer->output == output) {
|
||||
return;
|
||||
}
|
||||
if (output_get_pointer(output, wl_pointer)) {
|
||||
wlr_log(WLR_DEBUG,
|
||||
"Pointer for seat %s and output %s already exists (ignoring)",
|
||||
seat->name, output->wlr_output.name);
|
||||
return;
|
||||
}
|
||||
|
||||
struct wlr_wl_pointer *pointer = calloc(1, sizeof(struct wlr_wl_pointer));
|
||||
|
@ -641,10 +729,10 @@ void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *outp
|
|||
return;
|
||||
}
|
||||
pointer->wl_pointer = wl_pointer;
|
||||
pointer->output = output;
|
||||
pointer->output = output; // we need output to map absolute coordinates onto
|
||||
|
||||
struct wlr_wl_input_device *dev =
|
||||
create_wl_input_device(backend, WLR_INPUT_DEVICE_POINTER);
|
||||
create_wl_input_device(seat, WLR_INPUT_DEVICE_POINTER);
|
||||
if (dev == NULL) {
|
||||
free(pointer);
|
||||
wlr_log(WLR_ERROR, "Allocation failed");
|
||||
|
@ -655,18 +743,27 @@ void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *outp
|
|||
wl_signal_add(&output->wlr_output.events.destroy, &pointer->output_destroy);
|
||||
pointer->output_destroy.notify = pointer_handle_output_destroy;
|
||||
|
||||
wlr_dev = &dev->wlr_input_device;
|
||||
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
||||
wlr_dev->pointer = &pointer->wlr_pointer;
|
||||
wlr_dev->output_name = strdup(output->wlr_output.name);
|
||||
wlr_pointer_init(wlr_dev->pointer, &pointer_impl);
|
||||
|
||||
if (backend->zwp_pointer_gestures_v1) {
|
||||
uint32_t version = zwp_pointer_gestures_v1_get_version(
|
||||
backend->zwp_pointer_gestures_v1);
|
||||
|
||||
pointer->gesture_swipe = zwp_pointer_gestures_v1_get_swipe_gesture(
|
||||
backend->zwp_pointer_gestures_v1, wl_pointer);
|
||||
zwp_pointer_gesture_swipe_v1_add_listener(pointer->gesture_swipe, &gesture_swipe_impl, dev);
|
||||
pointer->gesture_pinch = zwp_pointer_gestures_v1_get_pinch_gesture(
|
||||
backend->zwp_pointer_gestures_v1, wl_pointer);
|
||||
zwp_pointer_gesture_pinch_v1_add_listener(pointer->gesture_pinch, &gesture_pinch_impl, dev);
|
||||
|
||||
if (version >= ZWP_POINTER_GESTURES_V1_GET_HOLD_GESTURE) {
|
||||
pointer->gesture_hold = zwp_pointer_gestures_v1_get_hold_gesture(
|
||||
backend->zwp_pointer_gestures_v1, wl_pointer);
|
||||
zwp_pointer_gesture_hold_v1_add_listener(pointer->gesture_hold, &gesture_hold_impl, dev);
|
||||
}
|
||||
}
|
||||
|
||||
if (backend->zwp_relative_pointer_manager_v1) {
|
||||
|
@ -677,13 +774,15 @@ void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *outp
|
|||
&relative_pointer_listener, dev);
|
||||
}
|
||||
|
||||
wl_pointer_add_listener(wl_pointer, &pointer_listener, backend);
|
||||
wl_pointer_add_listener(wl_pointer, &pointer_listener, seat);
|
||||
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_dev);
|
||||
}
|
||||
|
||||
void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *wl) {
|
||||
void create_wl_keyboard(struct wlr_wl_seat *seat) {
|
||||
assert(seat->keyboard);
|
||||
struct wl_keyboard *wl_keyboard = seat->keyboard;
|
||||
struct wlr_wl_input_device *dev =
|
||||
create_wl_input_device(wl, WLR_INPUT_DEVICE_KEYBOARD);
|
||||
create_wl_input_device(seat, WLR_INPUT_DEVICE_KEYBOARD);
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
|
@ -699,12 +798,14 @@ void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *
|
|||
wlr_keyboard_init(wlr_dev->keyboard, NULL);
|
||||
|
||||
wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, wlr_dev);
|
||||
wlr_signal_emit_safe(&wl->backend.events.new_input, wlr_dev);
|
||||
wlr_signal_emit_safe(&seat->backend->backend.events.new_input, wlr_dev);
|
||||
}
|
||||
|
||||
void create_wl_touch(struct wl_touch *wl_touch, struct wlr_wl_backend *wl) {
|
||||
void create_wl_touch(struct wlr_wl_seat *seat) {
|
||||
assert(seat->touch);
|
||||
struct wl_touch *wl_touch = seat->touch;
|
||||
struct wlr_wl_input_device *dev =
|
||||
create_wl_input_device(wl, WLR_INPUT_DEVICE_TOUCH);
|
||||
create_wl_input_device(seat, WLR_INPUT_DEVICE_TOUCH);
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
|
@ -720,14 +821,14 @@ void create_wl_touch(struct wl_touch *wl_touch, struct wlr_wl_backend *wl) {
|
|||
wlr_touch_init(wlr_dev->touch, NULL);
|
||||
|
||||
wl_touch_add_listener(wl_touch, &touch_listener, dev);
|
||||
wlr_signal_emit_safe(&wl->backend.events.new_input, wlr_dev);
|
||||
wlr_signal_emit_safe(&seat->backend->backend.events.new_input, wlr_dev);
|
||||
}
|
||||
|
||||
|
||||
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
||||
enum wl_seat_capability caps) {
|
||||
struct wlr_wl_backend *backend = data;
|
||||
struct wlr_wl_seat *seat = backend_get_seat(backend, wl_seat);
|
||||
struct wlr_wl_seat *seat = data;
|
||||
struct wlr_wl_backend *backend = seat->backend;
|
||||
|
||||
if ((caps & WL_SEAT_CAPABILITY_POINTER) && seat->pointer == NULL) {
|
||||
wlr_log(WLR_DEBUG, "seat %p offered pointer", (void *)wl_seat);
|
||||
|
@ -737,17 +838,30 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
|||
|
||||
struct wlr_wl_output *output;
|
||||
wl_list_for_each(output, &backend->outputs, link) {
|
||||
create_wl_pointer(wl_pointer, output);
|
||||
create_wl_pointer(seat, output);
|
||||
}
|
||||
}
|
||||
if (!(caps & WL_SEAT_CAPABILITY_POINTER) && seat->pointer != NULL) {
|
||||
wlr_log(WLR_DEBUG, "seat %p dropped pointer", (void *)wl_seat);
|
||||
|
||||
struct wlr_input_device *device, *tmp;
|
||||
struct wl_pointer *wl_pointer = seat->pointer;
|
||||
|
||||
struct wlr_wl_input_device *device, *tmp;
|
||||
wl_list_for_each_safe(device, tmp, &backend->devices, link) {
|
||||
if (device->type == WLR_INPUT_DEVICE_POINTER) {
|
||||
wlr_input_device_destroy(device);
|
||||
if (device->wlr_input_device.type != WLR_INPUT_DEVICE_POINTER) {
|
||||
continue;
|
||||
}
|
||||
struct wlr_wl_pointer *pointer =
|
||||
pointer_get_wl(device->wlr_input_device.pointer);
|
||||
if (pointer->wl_pointer != wl_pointer) {
|
||||
continue;
|
||||
}
|
||||
wlr_log(WLR_DEBUG, "dropping pointer %s",
|
||||
pointer->input_device->wlr_input_device.name);
|
||||
struct wlr_wl_output *output = pointer->output;
|
||||
wlr_input_device_destroy(&device->wlr_input_device);
|
||||
assert(seat->active_pointer != pointer);
|
||||
assert(output->cursor.pointer != pointer);
|
||||
}
|
||||
|
||||
wl_pointer_release(seat->pointer);
|
||||
|
@ -761,17 +875,22 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
|||
seat->keyboard = wl_keyboard;
|
||||
|
||||
if (backend->started) {
|
||||
create_wl_keyboard(wl_keyboard, backend);
|
||||
create_wl_keyboard(seat);
|
||||
}
|
||||
}
|
||||
if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->keyboard != NULL) {
|
||||
wlr_log(WLR_DEBUG, "seat %p dropped keyboard", (void *)wl_seat);
|
||||
|
||||
struct wlr_input_device *device, *tmp;
|
||||
struct wlr_wl_input_device *device, *tmp;
|
||||
wl_list_for_each_safe(device, tmp, &backend->devices, link) {
|
||||
if (device->type == WLR_INPUT_DEVICE_KEYBOARD) {
|
||||
wlr_input_device_destroy(device);
|
||||
if (device->wlr_input_device.type != WLR_INPUT_DEVICE_KEYBOARD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (device->seat != seat) {
|
||||
continue;
|
||||
}
|
||||
wlr_input_device_destroy(&device->wlr_input_device);
|
||||
}
|
||||
assert(seat->keyboard == NULL); // free'ed by input_device_destroy
|
||||
}
|
||||
|
@ -781,16 +900,16 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
|||
|
||||
seat->touch = wl_seat_get_touch(wl_seat);
|
||||
if (backend->started) {
|
||||
create_wl_touch(seat->touch, backend);
|
||||
create_wl_touch(seat);
|
||||
}
|
||||
}
|
||||
if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->touch != NULL) {
|
||||
wlr_log(WLR_DEBUG, "seat %p dropped touch", (void *)wl_seat);
|
||||
|
||||
struct wlr_input_device *device, *tmp;
|
||||
struct wlr_wl_input_device *device, *tmp;
|
||||
wl_list_for_each_safe(device, tmp, &backend->devices, link) {
|
||||
if (device->type == WLR_INPUT_DEVICE_TOUCH) {
|
||||
wlr_input_device_destroy(device);
|
||||
if (device->wlr_input_device.type == WLR_INPUT_DEVICE_TOUCH) {
|
||||
wlr_input_device_destroy(&device->wlr_input_device);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -801,8 +920,7 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
|||
|
||||
static void seat_handle_name(void *data, struct wl_seat *wl_seat,
|
||||
const char *name) {
|
||||
struct wlr_wl_backend *backend = data;
|
||||
struct wlr_wl_seat *seat = backend_get_seat(backend, wl_seat);
|
||||
struct wlr_wl_seat *seat = data;
|
||||
free(seat->name);
|
||||
seat->name = strdup(name);
|
||||
}
|
||||
|
|
|
@ -336,7 +336,8 @@ static void handle_tablet_pad_path(void *data,
|
|||
struct wlr_wl_input_device *dev = data;
|
||||
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
|
||||
|
||||
wlr_list_push(&tablet_pad->paths, strdup(path));
|
||||
char **dst = wl_array_add(&tablet_pad->paths, sizeof(char *));
|
||||
*dst = strdup(path);
|
||||
}
|
||||
|
||||
static void handle_tablet_pad_buttons(void *data,
|
||||
|
@ -401,7 +402,7 @@ static void handle_tablet_pad_removed(void *data,
|
|||
/* This doesn't free anything, but emits the destroy signal */
|
||||
wlr_input_device_destroy(&dev->wlr_input_device);
|
||||
/* This is a bit ugly, but we need to remove it from our list */
|
||||
wl_list_remove(&dev->wlr_input_device.link);
|
||||
wl_list_remove(&dev->link);
|
||||
|
||||
struct wlr_wl_tablet_pad_group *group, *it;
|
||||
wl_list_for_each_safe(group, it, &tablet_pad->groups, group.link) {
|
||||
|
@ -429,9 +430,9 @@ static void handle_pad_added(void *data,
|
|||
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
|
||||
struct zwp_tablet_pad_v2 *id) {
|
||||
wlr_log(WLR_DEBUG, "New tablet pad");
|
||||
struct wlr_wl_backend *backend = data;
|
||||
struct wlr_wl_seat *seat = data;
|
||||
struct wlr_wl_input_device *dev = create_wl_input_device(
|
||||
backend, WLR_INPUT_DEVICE_TABLET_PAD);
|
||||
seat, WLR_INPUT_DEVICE_TABLET_PAD);
|
||||
if (!dev) {
|
||||
/* This leaks a couple of server-sent resource ids. iirc this
|
||||
* shouldn't ever be a problem, but it isn't exactly nice
|
||||
|
@ -854,7 +855,8 @@ static void handle_tablet_path(void *data, struct zwp_tablet_v2 *zwp_tablet_v2,
|
|||
struct wlr_wl_input_device *dev = data;
|
||||
struct wlr_tablet *tablet = dev->wlr_input_device.tablet;
|
||||
|
||||
wlr_list_push(&tablet->paths, strdup(path));
|
||||
char **dst = wl_array_add(&tablet->paths, sizeof(char *));
|
||||
*dst = strdup(path);
|
||||
}
|
||||
|
||||
static void handle_tablet_done(void *data, struct zwp_tablet_v2 *zwp_tablet_v2) {
|
||||
|
@ -871,7 +873,7 @@ static void handle_tablet_removed(void *data,
|
|||
/* This doesn't free anything, but emits the destroy signal */
|
||||
wlr_input_device_destroy(&dev->wlr_input_device);
|
||||
/* This is a bit ugly, but we need to remove it from our list */
|
||||
wl_list_remove(&dev->wlr_input_device.link);
|
||||
wl_list_remove(&dev->link);
|
||||
|
||||
zwp_tablet_v2_destroy(dev->resource);
|
||||
free(dev);
|
||||
|
@ -889,9 +891,9 @@ static void handle_tab_added(void *data,
|
|||
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
|
||||
struct zwp_tablet_v2 *id) {
|
||||
wlr_log(WLR_DEBUG, "New tablet");
|
||||
struct wlr_wl_backend *backend = data;
|
||||
struct wlr_wl_seat *seat = data;
|
||||
struct wlr_wl_input_device *dev = create_wl_input_device(
|
||||
backend, WLR_INPUT_DEVICE_TABLET_TOOL);
|
||||
seat, WLR_INPUT_DEVICE_TABLET_TOOL);
|
||||
|
||||
if (!dev) {
|
||||
zwp_tablet_v2_destroy(id);
|
||||
|
@ -919,18 +921,18 @@ static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
|
|||
|
||||
struct wlr_wl_tablet_seat *wl_add_tablet_seat(
|
||||
struct zwp_tablet_manager_v2 *manager,
|
||||
struct wl_seat *seat, struct wlr_wl_backend *backend) {
|
||||
struct wlr_wl_seat *seat) {
|
||||
struct wlr_wl_tablet_seat *ret =
|
||||
calloc(1, sizeof(struct wlr_wl_tablet_seat));
|
||||
|
||||
if (!(ret->tablet_seat =
|
||||
zwp_tablet_manager_v2_get_tablet_seat(manager, seat))) {
|
||||
zwp_tablet_manager_v2_get_tablet_seat(manager, seat->wl_seat))) {
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
zwp_tablet_seat_v2_add_listener(ret->tablet_seat,
|
||||
&tablet_seat_listener, backend);
|
||||
&tablet_seat_listener, seat);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
#define _POSIX_C_SOURCE 200112L
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <xf86drm.h>
|
||||
|
||||
#include <wlr/config.h>
|
||||
|
||||
#include <X11/Xlib-xcb.h>
|
||||
#include <drm_fourcc.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/dri3.h>
|
||||
#include <xcb/present.h>
|
||||
#include <xcb/render.h>
|
||||
#include <xcb/shm.h>
|
||||
#include <xcb/xcb_renderutil.h>
|
||||
#include <xcb/xfixes.h>
|
||||
#include <xcb/xinput.h>
|
||||
|
||||
|
@ -20,13 +27,27 @@
|
|||
#include <wlr/interfaces/wlr_input_device.h>
|
||||
#include <wlr/interfaces/wlr_keyboard.h>
|
||||
#include <wlr/interfaces/wlr_pointer.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
||||
#include "backend/x11.h"
|
||||
#include "render/drm_format_set.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
// See dri2_format_for_depth in mesa
|
||||
const struct wlr_x11_format formats[] = {
|
||||
{ .drm = DRM_FORMAT_XRGB8888, .depth = 24, .bpp = 32 },
|
||||
{ .drm = DRM_FORMAT_ARGB8888, .depth = 32, .bpp = 32 },
|
||||
};
|
||||
|
||||
static const struct wlr_x11_format *x11_format_from_depth(uint8_t depth) {
|
||||
for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
|
||||
if (formats[i].depth == depth) {
|
||||
return &formats[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_x11_output *get_x11_output_from_window_id(
|
||||
struct wlr_x11_backend *x11, xcb_window_t window) {
|
||||
struct wlr_x11_output *output;
|
||||
|
@ -38,6 +59,10 @@ struct wlr_x11_output *get_x11_output_from_window_id(
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void handle_x11_error(struct wlr_x11_backend *x11, xcb_value_error_t *ev);
|
||||
static void handle_x11_unknown_event(struct wlr_x11_backend *x11,
|
||||
xcb_generic_event_t *ev);
|
||||
|
||||
static void handle_x11_event(struct wlr_x11_backend *x11,
|
||||
xcb_generic_event_t *event) {
|
||||
switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) {
|
||||
|
@ -46,6 +71,9 @@ static void handle_x11_event(struct wlr_x11_backend *x11,
|
|||
struct wlr_x11_output *output =
|
||||
get_x11_output_from_window_id(x11, ev->window);
|
||||
if (output != NULL) {
|
||||
pixman_region32_union_rect(
|
||||
&output->exposed, &output->exposed,
|
||||
ev->x, ev->y, ev->width, ev->height);
|
||||
wlr_output_update_needs_frame(&output->wlr_output);
|
||||
}
|
||||
break;
|
||||
|
@ -68,6 +96,9 @@ static void handle_x11_event(struct wlr_x11_backend *x11,
|
|||
if (output != NULL) {
|
||||
wlr_output_destroy(&output->wlr_output);
|
||||
}
|
||||
} else {
|
||||
wlr_log(WLR_DEBUG, "Unhandled client message %"PRIu32,
|
||||
ev->data.data32[0]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -75,8 +106,24 @@ static void handle_x11_event(struct wlr_x11_backend *x11,
|
|||
xcb_ge_generic_event_t *ev = (xcb_ge_generic_event_t *)event;
|
||||
if (ev->extension == x11->xinput_opcode) {
|
||||
handle_x11_xinput_event(x11, ev);
|
||||
} else if (ev->extension == x11->present_opcode) {
|
||||
handle_x11_present_event(x11, ev);
|
||||
} else {
|
||||
handle_x11_unknown_event(x11, event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0: {
|
||||
xcb_value_error_t *ev = (xcb_value_error_t *)event;
|
||||
handle_x11_error(x11, ev);
|
||||
break;
|
||||
}
|
||||
case XCB_UNMAP_NOTIFY:
|
||||
case XCB_MAP_NOTIFY:
|
||||
break;
|
||||
default:
|
||||
handle_x11_unknown_event(x11, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,6 +131,9 @@ static int x11_event(int fd, uint32_t mask, void *data) {
|
|||
struct wlr_x11_backend *x11 = data;
|
||||
|
||||
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
|
||||
if (mask & WL_EVENT_ERROR) {
|
||||
wlr_log(WLR_ERROR, "Failed to read from X11 server");
|
||||
}
|
||||
wl_display_terminate(x11->wl_display);
|
||||
return 0;
|
||||
}
|
||||
|
@ -94,6 +144,12 @@ static int x11_event(int fd, uint32_t mask, void *data) {
|
|||
free(e);
|
||||
}
|
||||
|
||||
int ret = xcb_connection_has_error(x11->xcb);
|
||||
if (ret != 0) {
|
||||
wlr_log(WLR_ERROR, "X11 connection error (%d)", ret);
|
||||
wl_display_terminate(x11->wl_display);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -107,6 +163,8 @@ static bool backend_start(struct wlr_backend *backend) {
|
|||
struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend);
|
||||
x11->started = true;
|
||||
|
||||
wlr_log(WLR_INFO, "Starting X11 backend");
|
||||
|
||||
wlr_signal_emit_safe(&x11->backend.events.new_input, &x11->keyboard_dev);
|
||||
|
||||
for (size_t i = 0; i < x11->requested_outputs; ++i) {
|
||||
|
@ -130,32 +188,43 @@ static void backend_destroy(struct wlr_backend *backend) {
|
|||
|
||||
wlr_input_device_destroy(&x11->keyboard_dev);
|
||||
|
||||
wlr_signal_emit_safe(&backend->events.destroy, backend);
|
||||
wlr_backend_finish(backend);
|
||||
|
||||
if (x11->event_source) {
|
||||
wl_event_source_remove(x11->event_source);
|
||||
}
|
||||
wl_list_remove(&x11->display_destroy.link);
|
||||
|
||||
wlr_renderer_destroy(x11->renderer);
|
||||
wlr_egl_finish(&x11->egl);
|
||||
wlr_drm_format_set_finish(&x11->primary_dri3_formats);
|
||||
wlr_drm_format_set_finish(&x11->primary_shm_formats);
|
||||
wlr_drm_format_set_finish(&x11->dri3_formats);
|
||||
wlr_drm_format_set_finish(&x11->shm_formats);
|
||||
|
||||
if (x11->xlib_conn) {
|
||||
XCloseDisplay(x11->xlib_conn);
|
||||
}
|
||||
#if HAS_XCB_ERRORS
|
||||
xcb_errors_context_free(x11->errors_context);
|
||||
#endif
|
||||
|
||||
close(x11->drm_fd);
|
||||
xcb_disconnect(x11->xcb);
|
||||
free(x11);
|
||||
}
|
||||
|
||||
static struct wlr_renderer *backend_get_renderer(
|
||||
struct wlr_backend *backend) {
|
||||
static int backend_get_drm_fd(struct wlr_backend *backend) {
|
||||
struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend);
|
||||
return x11->renderer;
|
||||
return x11->drm_fd;
|
||||
}
|
||||
|
||||
static uint32_t get_buffer_caps(struct wlr_backend *backend) {
|
||||
struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend);
|
||||
return (x11->have_dri3 ? WLR_BUFFER_CAP_DMABUF : 0)
|
||||
| (x11->have_shm ? WLR_BUFFER_CAP_SHM : 0);
|
||||
}
|
||||
|
||||
static const struct wlr_backend_impl backend_impl = {
|
||||
.start = backend_start,
|
||||
.destroy = backend_destroy,
|
||||
.get_renderer = backend_get_renderer,
|
||||
.get_drm_fd = backend_get_drm_fd,
|
||||
.get_buffer_caps = get_buffer_caps,
|
||||
};
|
||||
|
||||
bool wlr_backend_is_x11(struct wlr_backend *backend) {
|
||||
|
@ -168,9 +237,165 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
|||
backend_destroy(&x11->backend);
|
||||
}
|
||||
|
||||
static xcb_depth_t *get_depth(xcb_screen_t *screen, uint8_t depth) {
|
||||
xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(screen);
|
||||
while (iter.rem > 0) {
|
||||
if (iter.data->depth == depth) {
|
||||
return iter.data;
|
||||
}
|
||||
xcb_depth_next(&iter);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static xcb_visualid_t pick_visualid(xcb_depth_t *depth) {
|
||||
xcb_visualtype_t *visuals = xcb_depth_visuals(depth);
|
||||
for (int i = 0; i < xcb_depth_visuals_length(depth); i++) {
|
||||
if (visuals[i]._class == XCB_VISUAL_CLASS_TRUE_COLOR) {
|
||||
return visuals[i].visual_id;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int query_dri3_drm_fd(struct wlr_x11_backend *x11) {
|
||||
xcb_dri3_open_cookie_t open_cookie =
|
||||
xcb_dri3_open(x11->xcb, x11->screen->root, 0);
|
||||
xcb_dri3_open_reply_t *open_reply =
|
||||
xcb_dri3_open_reply(x11->xcb, open_cookie, NULL);
|
||||
if (open_reply == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int *open_fds = xcb_dri3_open_reply_fds(x11->xcb, open_reply);
|
||||
if (open_fds == NULL) {
|
||||
free(open_reply);
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(open_reply->nfd == 1);
|
||||
int drm_fd = open_fds[0];
|
||||
|
||||
free(open_reply);
|
||||
|
||||
int flags = fcntl(drm_fd, F_GETFD);
|
||||
if (flags < 0) {
|
||||
close(drm_fd);
|
||||
return -1;
|
||||
}
|
||||
if (fcntl(drm_fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
|
||||
close(drm_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (drmGetNodeTypeFromFd(drm_fd) != DRM_NODE_RENDER) {
|
||||
char *render_name = drmGetRenderDeviceNameFromFd(drm_fd);
|
||||
if (render_name == NULL) {
|
||||
close(drm_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(drm_fd);
|
||||
drm_fd = open(render_name, O_RDWR | O_CLOEXEC);
|
||||
if (drm_fd < 0) {
|
||||
free(render_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(render_name);
|
||||
}
|
||||
|
||||
return drm_fd;
|
||||
}
|
||||
|
||||
static bool query_dri3_modifiers(struct wlr_x11_backend *x11,
|
||||
const struct wlr_x11_format *format) {
|
||||
if (x11->dri3_major_version == 1 && x11->dri3_minor_version < 2) {
|
||||
return true; // GetSupportedModifiers requires DRI3 1.2
|
||||
}
|
||||
|
||||
// Query the root window's supported modifiers, because we only care about
|
||||
// screen_modifiers for now
|
||||
xcb_dri3_get_supported_modifiers_cookie_t modifiers_cookie =
|
||||
xcb_dri3_get_supported_modifiers(x11->xcb, x11->screen->root,
|
||||
format->depth, format->bpp);
|
||||
xcb_dri3_get_supported_modifiers_reply_t *modifiers_reply =
|
||||
xcb_dri3_get_supported_modifiers_reply(x11->xcb, modifiers_cookie,
|
||||
NULL);
|
||||
if (!modifiers_reply) {
|
||||
wlr_log(WLR_ERROR, "Failed to get DMA-BUF modifiers supported by "
|
||||
"the X11 server for the format 0x%"PRIX32, format->drm);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If modifiers aren't supported, DRI3 will return an empty list
|
||||
const uint64_t *modifiers =
|
||||
xcb_dri3_get_supported_modifiers_screen_modifiers(modifiers_reply);
|
||||
int modifiers_len =
|
||||
xcb_dri3_get_supported_modifiers_screen_modifiers_length(modifiers_reply);
|
||||
for (int i = 0; i < modifiers_len; i++) {
|
||||
wlr_drm_format_set_add(&x11->dri3_formats, format->drm, modifiers[i]);
|
||||
}
|
||||
|
||||
free(modifiers_reply);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool query_formats(struct wlr_x11_backend *x11) {
|
||||
xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(x11->screen);
|
||||
while (iter.rem > 0) {
|
||||
uint8_t depth = iter.data->depth;
|
||||
|
||||
const struct wlr_x11_format *format = x11_format_from_depth(depth);
|
||||
if (format != NULL) {
|
||||
if (x11->have_shm) {
|
||||
wlr_drm_format_set_add(&x11->shm_formats, format->drm,
|
||||
DRM_FORMAT_MOD_INVALID);
|
||||
}
|
||||
|
||||
if (x11->have_dri3) {
|
||||
// X11 always supports implicit modifiers
|
||||
wlr_drm_format_set_add(&x11->dri3_formats, format->drm,
|
||||
DRM_FORMAT_MOD_INVALID);
|
||||
if (!query_dri3_modifiers(x11, format)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xcb_depth_next(&iter);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void x11_get_argb32(struct wlr_x11_backend *x11) {
|
||||
xcb_render_query_pict_formats_cookie_t cookie =
|
||||
xcb_render_query_pict_formats(x11->xcb);
|
||||
xcb_render_query_pict_formats_reply_t *reply =
|
||||
xcb_render_query_pict_formats_reply(x11->xcb, cookie, NULL);
|
||||
if (!reply) {
|
||||
wlr_log(WLR_ERROR, "Did not get any reply from xcb_render_query_pict_formats");
|
||||
return;
|
||||
}
|
||||
|
||||
xcb_render_pictforminfo_t *format =
|
||||
xcb_render_util_find_standard_format(reply, XCB_PICT_STANDARD_ARGB_32);
|
||||
|
||||
if (format == NULL) {
|
||||
wlr_log(WLR_DEBUG, "No ARGB_32 render format");
|
||||
free(reply);
|
||||
return;
|
||||
}
|
||||
|
||||
x11->argb32 = format->id;
|
||||
free(reply);
|
||||
}
|
||||
|
||||
struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
||||
const char *x11_display,
|
||||
wlr_renderer_create_func_t create_renderer_func) {
|
||||
const char *x11_display) {
|
||||
wlr_log(WLR_INFO, "Creating X11 backend");
|
||||
|
||||
struct wlr_x11_backend *x11 = calloc(1, sizeof(*x11));
|
||||
if (!x11) {
|
||||
return NULL;
|
||||
|
@ -180,20 +405,12 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
|||
x11->wl_display = display;
|
||||
wl_list_init(&x11->outputs);
|
||||
|
||||
x11->xlib_conn = XOpenDisplay(x11_display);
|
||||
if (!x11->xlib_conn) {
|
||||
wlr_log(WLR_ERROR, "Failed to open X connection");
|
||||
goto error_x11;
|
||||
}
|
||||
|
||||
x11->xcb = XGetXCBConnection(x11->xlib_conn);
|
||||
x11->xcb = xcb_connect(x11_display, NULL);
|
||||
if (!x11->xcb || xcb_connection_has_error(x11->xcb)) {
|
||||
wlr_log(WLR_ERROR, "Failed to open xcb connection");
|
||||
goto error_display;
|
||||
goto error_x11;
|
||||
}
|
||||
|
||||
XSetEventQueueOwner(x11->xlib_conn, XCBOwnsEventQueue);
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
xcb_intern_atom_cookie_t cookie;
|
||||
|
@ -225,6 +442,80 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
|||
|
||||
const xcb_query_extension_reply_t *ext;
|
||||
|
||||
// DRI3 extension
|
||||
|
||||
ext = xcb_get_extension_data(x11->xcb, &xcb_dri3_id);
|
||||
if (ext && ext->present) {
|
||||
xcb_dri3_query_version_cookie_t dri3_cookie =
|
||||
xcb_dri3_query_version(x11->xcb, 1, 2);
|
||||
xcb_dri3_query_version_reply_t *dri3_reply =
|
||||
xcb_dri3_query_version_reply(x11->xcb, dri3_cookie, NULL);
|
||||
if (dri3_reply && dri3_reply->major_version >= 1) {
|
||||
x11->have_dri3 = true;
|
||||
x11->dri3_major_version = dri3_reply->major_version;
|
||||
x11->dri3_minor_version = dri3_reply->minor_version;
|
||||
} else {
|
||||
wlr_log(WLR_INFO, "X11 does not support required DRI3 version "
|
||||
"(has %"PRIu32".%"PRIu32", want 1.0)",
|
||||
dri3_reply->major_version, dri3_reply->minor_version);
|
||||
}
|
||||
free(dri3_reply);
|
||||
} else {
|
||||
wlr_log(WLR_INFO, "X11 does not support DRI3 extension");
|
||||
}
|
||||
|
||||
// SHM extension
|
||||
|
||||
ext = xcb_get_extension_data(x11->xcb, &xcb_shm_id);
|
||||
if (ext && ext->present) {
|
||||
xcb_shm_query_version_cookie_t shm_cookie =
|
||||
xcb_shm_query_version(x11->xcb);
|
||||
xcb_shm_query_version_reply_t *shm_reply =
|
||||
xcb_shm_query_version_reply(x11->xcb, shm_cookie, NULL);
|
||||
if (shm_reply) {
|
||||
if (shm_reply->major_version >= 1 || shm_reply->minor_version >= 2) {
|
||||
if (shm_reply->shared_pixmaps) {
|
||||
x11->have_shm = true;
|
||||
} else {
|
||||
wlr_log(WLR_INFO, "X11 does not support shared pixmaps");
|
||||
}
|
||||
} else {
|
||||
wlr_log(WLR_INFO, "X11 does not support required SHM version "
|
||||
"(has %"PRIu32".%"PRIu32", want 1.2)",
|
||||
shm_reply->major_version, shm_reply->minor_version);
|
||||
}
|
||||
} else {
|
||||
wlr_log(WLR_INFO, "X11 does not support required SHM version");
|
||||
}
|
||||
free(shm_reply);
|
||||
} else {
|
||||
wlr_log(WLR_INFO, "X11 does not support SHM extension");
|
||||
}
|
||||
|
||||
// Present extension
|
||||
|
||||
ext = xcb_get_extension_data(x11->xcb, &xcb_present_id);
|
||||
if (!ext || !ext->present) {
|
||||
wlr_log(WLR_ERROR, "X11 does not support Present extension");
|
||||
goto error_display;
|
||||
}
|
||||
x11->present_opcode = ext->major_opcode;
|
||||
|
||||
xcb_present_query_version_cookie_t present_cookie =
|
||||
xcb_present_query_version(x11->xcb, 1, 2);
|
||||
xcb_present_query_version_reply_t *present_reply =
|
||||
xcb_present_query_version_reply(x11->xcb, present_cookie, NULL);
|
||||
if (!present_reply || present_reply->major_version < 1) {
|
||||
wlr_log(WLR_ERROR, "X11 does not support required Present version "
|
||||
"(has %"PRIu32".%"PRIu32", want 1.0)",
|
||||
present_reply->major_version, present_reply->minor_version);
|
||||
free(present_reply);
|
||||
goto error_display;
|
||||
}
|
||||
free(present_reply);
|
||||
|
||||
// Xfixes extension
|
||||
|
||||
ext = xcb_get_extension_data(x11->xcb, &xcb_xfixes_id);
|
||||
if (!ext || !ext->present) {
|
||||
wlr_log(WLR_ERROR, "X11 does not support Xfixes extension");
|
||||
|
@ -235,14 +526,17 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
|||
xcb_xfixes_query_version(x11->xcb, 4, 0);
|
||||
xcb_xfixes_query_version_reply_t *fixes_reply =
|
||||
xcb_xfixes_query_version_reply(x11->xcb, fixes_cookie, NULL);
|
||||
|
||||
if (!fixes_reply || fixes_reply->major_version < 4) {
|
||||
wlr_log(WLR_ERROR, "X11 does not support required Xfixes version");
|
||||
wlr_log(WLR_ERROR, "X11 does not support required Xfixes version "
|
||||
"(has %"PRIu32".%"PRIu32", want 4.0)",
|
||||
fixes_reply->major_version, fixes_reply->minor_version);
|
||||
free(fixes_reply);
|
||||
goto error_display;
|
||||
}
|
||||
free(fixes_reply);
|
||||
|
||||
// Xinput extension
|
||||
|
||||
ext = xcb_get_extension_data(x11->xcb, &xcb_input_id);
|
||||
if (!ext || !ext->present) {
|
||||
wlr_log(WLR_ERROR, "X11 does not support Xinput extension");
|
||||
|
@ -254,9 +548,10 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
|||
xcb_input_xi_query_version(x11->xcb, 2, 0);
|
||||
xcb_input_xi_query_version_reply_t *xi_reply =
|
||||
xcb_input_xi_query_version_reply(x11->xcb, xi_cookie, NULL);
|
||||
|
||||
if (!xi_reply || xi_reply->major_version < 2) {
|
||||
wlr_log(WLR_ERROR, "X11 does not support required Xinput version");
|
||||
wlr_log(WLR_ERROR, "X11 does not support required Xinput version "
|
||||
"(has %"PRIu32".%"PRIu32", want 2.0)",
|
||||
xi_reply->major_version, xi_reply->minor_version);
|
||||
free(xi_reply);
|
||||
goto error_display;
|
||||
}
|
||||
|
@ -273,29 +568,76 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
|||
wl_event_source_check(x11->event_source);
|
||||
|
||||
x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb)).data;
|
||||
|
||||
if (!create_renderer_func) {
|
||||
create_renderer_func = wlr_renderer_autocreate;
|
||||
}
|
||||
|
||||
static EGLint config_attribs[] = {
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_RED_SIZE, 1,
|
||||
EGL_GREEN_SIZE, 1,
|
||||
EGL_BLUE_SIZE, 1,
|
||||
EGL_ALPHA_SIZE, 0,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
x11->renderer = create_renderer_func(&x11->egl, EGL_PLATFORM_X11_KHR,
|
||||
x11->xlib_conn, config_attribs, x11->screen->root_visual);
|
||||
|
||||
if (x11->renderer == NULL) {
|
||||
wlr_log(WLR_ERROR, "Failed to create renderer");
|
||||
if (!x11->screen) {
|
||||
wlr_log(WLR_ERROR, "Failed to get X11 screen");
|
||||
goto error_event;
|
||||
}
|
||||
|
||||
x11->depth = get_depth(x11->screen, 24);
|
||||
if (!x11->depth) {
|
||||
wlr_log(WLR_ERROR, "Failed to get 24-bit depth for X11 screen");
|
||||
goto error_event;
|
||||
}
|
||||
|
||||
x11->visualid = pick_visualid(x11->depth);
|
||||
if (!x11->visualid) {
|
||||
wlr_log(WLR_ERROR, "Failed to pick X11 visual");
|
||||
goto error_event;
|
||||
}
|
||||
|
||||
x11->x11_format = x11_format_from_depth(x11->depth->depth);
|
||||
if (!x11->x11_format) {
|
||||
wlr_log(WLR_ERROR, "Unsupported depth %"PRIu8, x11->depth->depth);
|
||||
goto error_event;
|
||||
}
|
||||
|
||||
x11->colormap = xcb_generate_id(x11->xcb);
|
||||
xcb_create_colormap(x11->xcb, XCB_COLORMAP_ALLOC_NONE, x11->colormap,
|
||||
x11->screen->root, x11->visualid);
|
||||
|
||||
if (!query_formats(x11)) {
|
||||
wlr_log(WLR_ERROR, "Failed to query supported DRM formats");
|
||||
return false;
|
||||
}
|
||||
|
||||
x11->drm_fd = -1;
|
||||
if (x11->have_dri3) {
|
||||
// DRI3 may return a render node (Xwayland) or an authenticated primary
|
||||
// node (plain Glamor).
|
||||
x11->drm_fd = query_dri3_drm_fd(x11);
|
||||
if (x11->drm_fd < 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to query DRI3 DRM FD");
|
||||
goto error_event;
|
||||
}
|
||||
}
|
||||
|
||||
// Windows can only display buffers with the depth they were created with
|
||||
// TODO: look into changing the window's depth at runtime
|
||||
const struct wlr_drm_format *dri3_format =
|
||||
wlr_drm_format_set_get(&x11->dri3_formats, x11->x11_format->drm);
|
||||
if (x11->have_dri3 && dri3_format != NULL) {
|
||||
wlr_drm_format_set_add(&x11->primary_dri3_formats,
|
||||
dri3_format->format, DRM_FORMAT_MOD_INVALID);
|
||||
for (size_t i = 0; i < dri3_format->len; i++) {
|
||||
wlr_drm_format_set_add(&x11->primary_dri3_formats,
|
||||
dri3_format->format, dri3_format->modifiers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
const struct wlr_drm_format *shm_format =
|
||||
wlr_drm_format_set_get(&x11->shm_formats, x11->x11_format->drm);
|
||||
if (x11->have_shm && shm_format != NULL) {
|
||||
wlr_drm_format_set_add(&x11->primary_shm_formats,
|
||||
shm_format->format, DRM_FORMAT_MOD_INVALID);
|
||||
}
|
||||
|
||||
#if HAS_XCB_ERRORS
|
||||
if (xcb_errors_context_new(x11->xcb, &x11->errors_context) != 0) {
|
||||
wlr_log(WLR_ERROR, "Failed to create error context");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
wlr_input_device_init(&x11->keyboard_dev, WLR_INPUT_DEVICE_KEYBOARD,
|
||||
&input_device_impl, "X11 keyboard", 0, 0);
|
||||
wlr_keyboard_init(&x11->keyboard, &keyboard_impl);
|
||||
|
@ -304,13 +646,87 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
|||
x11->display_destroy.notify = handle_display_destroy;
|
||||
wl_display_add_destroy_listener(display, &x11->display_destroy);
|
||||
|
||||
// Create an empty pixmap to be used as the cursor. The
|
||||
// default GC foreground is 0, and that is what it will be
|
||||
// filled with.
|
||||
xcb_pixmap_t blank = xcb_generate_id(x11->xcb);
|
||||
xcb_create_pixmap(x11->xcb, 1, blank, x11->screen->root, 1, 1);
|
||||
xcb_gcontext_t gc = xcb_generate_id(x11->xcb);
|
||||
xcb_create_gc(x11->xcb, gc, blank, 0, NULL);
|
||||
xcb_rectangle_t rect = { .x = 0, .y = 0, .width = 1, .height = 1 };
|
||||
xcb_poly_fill_rectangle(x11->xcb, blank, gc, 1, &rect);
|
||||
|
||||
x11->transparent_cursor = xcb_generate_id(x11->xcb);
|
||||
xcb_create_cursor(x11->xcb, x11->transparent_cursor, blank, blank,
|
||||
0, 0, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
xcb_free_gc(x11->xcb, gc);
|
||||
xcb_free_pixmap(x11->xcb, blank);
|
||||
|
||||
x11_get_argb32(x11);
|
||||
|
||||
return &x11->backend;
|
||||
|
||||
error_event:
|
||||
wl_event_source_remove(x11->event_source);
|
||||
error_display:
|
||||
XCloseDisplay(x11->xlib_conn);
|
||||
xcb_disconnect(x11->xcb);
|
||||
error_x11:
|
||||
free(x11);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void handle_x11_error(struct wlr_x11_backend *x11, xcb_value_error_t *ev) {
|
||||
#if HAS_XCB_ERRORS
|
||||
const char *major_name = xcb_errors_get_name_for_major_code(
|
||||
x11->errors_context, ev->major_opcode);
|
||||
if (!major_name) {
|
||||
wlr_log(WLR_DEBUG, "X11 error happened, but could not get major name");
|
||||
goto log_raw;
|
||||
}
|
||||
|
||||
const char *minor_name = xcb_errors_get_name_for_minor_code(
|
||||
x11->errors_context, ev->major_opcode, ev->minor_opcode);
|
||||
|
||||
const char *extension;
|
||||
const char *error_name = xcb_errors_get_name_for_error(x11->errors_context,
|
||||
ev->error_code, &extension);
|
||||
if (!error_name) {
|
||||
wlr_log(WLR_DEBUG, "X11 error happened, but could not get error name");
|
||||
goto log_raw;
|
||||
}
|
||||
|
||||
wlr_log(WLR_ERROR, "X11 error: op %s (%s), code %s (%s), "
|
||||
"sequence %"PRIu16", value %"PRIu32,
|
||||
major_name, minor_name ? minor_name : "no minor",
|
||||
error_name, extension ? extension : "no extension",
|
||||
ev->sequence, ev->bad_value);
|
||||
|
||||
return;
|
||||
|
||||
log_raw:
|
||||
#endif
|
||||
|
||||
wlr_log(WLR_ERROR, "X11 error: op %"PRIu8":%"PRIu16", code %"PRIu8", "
|
||||
"sequence %"PRIu16", value %"PRIu32,
|
||||
ev->major_opcode, ev->minor_opcode, ev->error_code,
|
||||
ev->sequence, ev->bad_value);
|
||||
}
|
||||
|
||||
static void handle_x11_unknown_event(struct wlr_x11_backend *x11,
|
||||
xcb_generic_event_t *ev) {
|
||||
#if HAS_XCB_ERRORS
|
||||
const char *extension;
|
||||
const char *event_name = xcb_errors_get_name_for_xcb_event(
|
||||
x11->errors_context, ev, &extension);
|
||||
if (!event_name) {
|
||||
wlr_log(WLR_DEBUG, "No name for unhandled event: %u",
|
||||
ev->response_type);
|
||||
return;
|
||||
}
|
||||
|
||||
wlr_log(WLR_DEBUG, "Unhandled X11 event: %s (%u)", event_name, ev->response_type);
|
||||
#else
|
||||
wlr_log(WLR_DEBUG, "Unhandled X11 event: %u", ev->response_type);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include <linux/input-event-codes.h>
|
||||
|
||||
#include <wayland-server-protocol.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xfixes.h>
|
||||
#include <xcb/xinput.h>
|
||||
|
@ -18,7 +20,7 @@
|
|||
#include "util/signal.h"
|
||||
|
||||
static void send_key_event(struct wlr_x11_backend *x11, uint32_t key,
|
||||
enum wlr_key_state st, xcb_timestamp_t time) {
|
||||
enum wl_keyboard_key_state st, xcb_timestamp_t time) {
|
||||
struct wlr_event_keyboard_key ev = {
|
||||
.time_msec = time,
|
||||
.keycode = key,
|
||||
|
@ -77,6 +79,7 @@ static void send_touch_down_event(struct wlr_x11_output *output,
|
|||
.touch_id = touch_id,
|
||||
};
|
||||
wlr_signal_emit_safe(&output->touch.events.down, &ev);
|
||||
wlr_signal_emit_safe(&output->touch.events.frame, NULL);
|
||||
}
|
||||
|
||||
static void send_touch_motion_event(struct wlr_x11_output *output,
|
||||
|
@ -89,6 +92,7 @@ static void send_touch_motion_event(struct wlr_x11_output *output,
|
|||
.touch_id = touch_id,
|
||||
};
|
||||
wlr_signal_emit_safe(&output->touch.events.motion, &ev);
|
||||
wlr_signal_emit_safe(&output->touch.events.frame, NULL);
|
||||
}
|
||||
|
||||
static void send_touch_up_event(struct wlr_x11_output *output,
|
||||
|
@ -99,10 +103,11 @@ static void send_touch_up_event(struct wlr_x11_output *output,
|
|||
.touch_id = touch_id,
|
||||
};
|
||||
wlr_signal_emit_safe(&output->touch.events.up, &ev);
|
||||
wlr_signal_emit_safe(&output->touch.events.frame, NULL);
|
||||
}
|
||||
|
||||
static struct wlr_x11_touchpoint* get_touchpoint_from_x11_touch_id(struct wlr_x11_output *output,
|
||||
uint32_t id) {
|
||||
static struct wlr_x11_touchpoint *get_touchpoint_from_x11_touch_id(
|
||||
struct wlr_x11_output *output, uint32_t id) {
|
||||
struct wlr_x11_touchpoint *touchpoint;
|
||||
wl_list_for_each(touchpoint, &output->touchpoints, link) {
|
||||
if (touchpoint->x11_id == id) {
|
||||
|
@ -121,9 +126,13 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
|
|||
xcb_input_key_press_event_t *ev =
|
||||
(xcb_input_key_press_event_t *)event;
|
||||
|
||||
if (ev->flags & XCB_INPUT_KEY_EVENT_FLAGS_KEY_REPEAT) {
|
||||
return;
|
||||
}
|
||||
|
||||
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
|
||||
ev->mods.latched, ev->mods.locked, ev->mods.effective);
|
||||
send_key_event(x11, ev->detail - 8, WLR_KEY_PRESSED, ev->time);
|
||||
send_key_event(x11, ev->detail - 8, WL_KEYBOARD_KEY_STATE_PRESSED, ev->time);
|
||||
x11->time = ev->time;
|
||||
break;
|
||||
}
|
||||
|
@ -133,7 +142,7 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
|
|||
|
||||
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
|
||||
ev->mods.latched, ev->mods.locked, ev->mods.effective);
|
||||
send_key_event(x11, ev->detail - 8, WLR_KEY_RELEASED, ev->time);
|
||||
send_key_event(x11, ev->detail - 8, WL_KEYBOARD_KEY_STATE_RELEASED, ev->time);
|
||||
x11->time = ev->time;
|
||||
break;
|
||||
}
|
||||
|
@ -210,36 +219,6 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
|
|||
x11->time = ev->time;
|
||||
break;
|
||||
}
|
||||
case XCB_INPUT_ENTER: {
|
||||
xcb_input_enter_event_t *ev = (xcb_input_enter_event_t *)event;
|
||||
|
||||
output = get_x11_output_from_window_id(x11, ev->event);
|
||||
if (!output) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!output->cursor_hidden) {
|
||||
xcb_xfixes_hide_cursor(x11->xcb, output->win);
|
||||
xcb_flush(x11->xcb);
|
||||
output->cursor_hidden = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case XCB_INPUT_LEAVE: {
|
||||
xcb_input_leave_event_t *ev = (xcb_input_leave_event_t *)event;
|
||||
|
||||
output = get_x11_output_from_window_id(x11, ev->event);
|
||||
if (!output) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (output->cursor_hidden) {
|
||||
xcb_xfixes_show_cursor(x11->xcb, output->win);
|
||||
xcb_flush(x11->xcb);
|
||||
output->cursor_hidden = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case XCB_INPUT_TOUCH_BEGIN: {
|
||||
xcb_input_touch_begin_event_t *ev = (xcb_input_touch_begin_event_t *)event;
|
||||
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
x11_libs = []
|
||||
x11_required = [
|
||||
'x11-xcb',
|
||||
'xcb',
|
||||
'xcb-xinput',
|
||||
'xcb-dri3',
|
||||
'xcb-present',
|
||||
'xcb-render',
|
||||
'xcb-renderutil',
|
||||
'xcb-shm',
|
||||
'xcb-xfixes',
|
||||
'xcb-xinput',
|
||||
]
|
||||
|
||||
msg = []
|
||||
if get_option('x11-backend').enabled()
|
||||
msg += 'Install "@0@" or pass "-Dx11-backend=disabled" to disable it.'
|
||||
endif
|
||||
if not get_option('x11-backend').disabled()
|
||||
msg += 'Required for X11 backend support.'
|
||||
msg = ['Required for X11 backend support.']
|
||||
if 'x11' in backends
|
||||
msg += 'Install "@0@" or disable the X11 backend.'
|
||||
endif
|
||||
|
||||
foreach lib : x11_required
|
||||
dep = dependency(lib,
|
||||
required: get_option('x11-backend'),
|
||||
required: 'x11' in backends,
|
||||
not_found_message: '\n'.join(msg).format(lib),
|
||||
)
|
||||
if not dep.found()
|
||||
|
@ -32,4 +33,4 @@ wlr_files += files(
|
|||
'output.c',
|
||||
)
|
||||
wlr_deps += x11_libs
|
||||
conf_data.set10('WLR_HAS_X11_BACKEND', true)
|
||||
features += { 'x11-backend': true }
|
||||
|
|
|
@ -3,24 +3,31 @@
|
|||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <drm_fourcc.h>
|
||||
#include <xcb/dri3.h>
|
||||
#include <xcb/present.h>
|
||||
#include <xcb/render.h>
|
||||
#include <xcb/shm.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xinput.h>
|
||||
|
||||
#include <wlr/interfaces/wlr_output.h>
|
||||
#include <wlr/interfaces/wlr_pointer.h>
|
||||
#include <wlr/interfaces/wlr_touch.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_matrix.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
||||
#include "backend/x11.h"
|
||||
#include "util/signal.h"
|
||||
#include "util/time.h"
|
||||
|
||||
static int signal_frame(void *data) {
|
||||
struct wlr_x11_output *output = data;
|
||||
wlr_output_send_frame(&output->wlr_output);
|
||||
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
|
||||
return 0;
|
||||
}
|
||||
static const uint32_t SUPPORTED_OUTPUT_STATE =
|
||||
WLR_OUTPUT_STATE_BACKEND_OPTIONAL |
|
||||
WLR_OUTPUT_STATE_BUFFER |
|
||||
WLR_OUTPUT_STATE_MODE;
|
||||
|
||||
static void parse_xcb_setup(struct wlr_output *output,
|
||||
xcb_connection_t *xcb) {
|
||||
|
@ -40,26 +47,11 @@ static struct wlr_x11_output *get_x11_output_from_output(
|
|||
return (struct wlr_x11_output *)wlr_output;
|
||||
}
|
||||
|
||||
static void output_set_refresh(struct wlr_output *wlr_output, int32_t refresh) {
|
||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||
|
||||
if (refresh <= 0) {
|
||||
refresh = X11_DEFAULT_REFRESH;
|
||||
}
|
||||
|
||||
wlr_output_update_custom_mode(&output->wlr_output, wlr_output->width,
|
||||
wlr_output->height, refresh);
|
||||
|
||||
output->frame_delay = 1000000 / refresh;
|
||||
}
|
||||
|
||||
static bool output_set_custom_mode(struct wlr_output *wlr_output,
|
||||
int32_t width, int32_t height, int32_t refresh) {
|
||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||
struct wlr_x11_backend *x11 = output->x11;
|
||||
|
||||
output_set_refresh(&output->wlr_output, refresh);
|
||||
|
||||
const uint32_t values[] = { width, height };
|
||||
xcb_void_cookie_t cookie = xcb_configure_window_checked(
|
||||
x11->xcb, output->win,
|
||||
|
@ -76,32 +68,41 @@ static bool output_set_custom_mode(struct wlr_output *wlr_output,
|
|||
return true;
|
||||
}
|
||||
|
||||
static void destroy_x11_buffer(struct wlr_x11_buffer *buffer);
|
||||
|
||||
static void output_destroy(struct wlr_output *wlr_output) {
|
||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||
struct wlr_x11_backend *x11 = output->x11;
|
||||
|
||||
pixman_region32_fini(&output->exposed);
|
||||
|
||||
wlr_input_device_destroy(&output->pointer_dev);
|
||||
wlr_input_device_destroy(&output->touch_dev);
|
||||
|
||||
struct wlr_x11_buffer *buffer, *buffer_tmp;
|
||||
wl_list_for_each_safe(buffer, buffer_tmp, &output->buffers, link) {
|
||||
destroy_x11_buffer(buffer);
|
||||
}
|
||||
|
||||
wl_list_remove(&output->link);
|
||||
wl_event_source_remove(output->frame_timer);
|
||||
wlr_egl_destroy_surface(&x11->egl, output->surf);
|
||||
|
||||
if (output->cursor.pic != XCB_NONE) {
|
||||
xcb_render_free_picture(x11->xcb, output->cursor.pic);
|
||||
}
|
||||
|
||||
// A zero event mask deletes the event context
|
||||
xcb_present_select_input(x11->xcb, output->present_event_id, output->win, 0);
|
||||
xcb_destroy_window(x11->xcb, output->win);
|
||||
xcb_flush(x11->xcb);
|
||||
free(output);
|
||||
}
|
||||
|
||||
static bool output_attach_render(struct wlr_output *wlr_output,
|
||||
int *buffer_age) {
|
||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||
struct wlr_x11_backend *x11 = output->x11;
|
||||
|
||||
return wlr_egl_make_current(&x11->egl, output->surf, buffer_age);
|
||||
}
|
||||
|
||||
static bool output_test(struct wlr_output *wlr_output) {
|
||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_ENABLED) {
|
||||
wlr_log(WLR_DEBUG, "Cannot disable an X11 output");
|
||||
uint32_t unsupported =
|
||||
wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE;
|
||||
if (unsupported != 0) {
|
||||
wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32,
|
||||
unsupported);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -112,6 +113,201 @@ static bool output_test(struct wlr_output *wlr_output) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static void destroy_x11_buffer(struct wlr_x11_buffer *buffer) {
|
||||
if (!buffer) {
|
||||
return;
|
||||
}
|
||||
wl_list_remove(&buffer->buffer_destroy.link);
|
||||
wl_list_remove(&buffer->link);
|
||||
xcb_free_pixmap(buffer->x11->xcb, buffer->pixmap);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
static void buffer_handle_buffer_destroy(struct wl_listener *listener,
|
||||
void *data) {
|
||||
struct wlr_x11_buffer *buffer =
|
||||
wl_container_of(listener, buffer, buffer_destroy);
|
||||
destroy_x11_buffer(buffer);
|
||||
}
|
||||
|
||||
static xcb_pixmap_t import_dmabuf(struct wlr_x11_output *output,
|
||||
struct wlr_dmabuf_attributes *dmabuf) {
|
||||
struct wlr_x11_backend *x11 = output->x11;
|
||||
|
||||
if (dmabuf->format != x11->x11_format->drm) {
|
||||
// The pixmap's depth must match the window's depth, otherwise Present
|
||||
// will throw a Match error
|
||||
return XCB_PIXMAP_NONE;
|
||||
}
|
||||
|
||||
// xcb closes the FDs after sending them, so we need to dup them here
|
||||
struct wlr_dmabuf_attributes dup_attrs = {0};
|
||||
if (!wlr_dmabuf_attributes_copy(&dup_attrs, dmabuf)) {
|
||||
return XCB_PIXMAP_NONE;
|
||||
}
|
||||
|
||||
const struct wlr_x11_format *x11_fmt = x11->x11_format;
|
||||
xcb_pixmap_t pixmap = xcb_generate_id(x11->xcb);
|
||||
|
||||
if (x11->dri3_major_version > 1 || x11->dri3_minor_version >= 2) {
|
||||
if (dmabuf->n_planes > 4) {
|
||||
wlr_dmabuf_attributes_finish(&dup_attrs);
|
||||
return XCB_PIXMAP_NONE;
|
||||
}
|
||||
xcb_dri3_pixmap_from_buffers(x11->xcb, pixmap, output->win,
|
||||
dmabuf->n_planes, dmabuf->width, dmabuf->height, dmabuf->stride[0],
|
||||
dmabuf->offset[0], dmabuf->stride[1], dmabuf->offset[1],
|
||||
dmabuf->stride[2], dmabuf->offset[2], dmabuf->stride[3],
|
||||
dmabuf->offset[3], x11_fmt->depth, x11_fmt->bpp, dmabuf->modifier,
|
||||
dup_attrs.fd);
|
||||
} else {
|
||||
// PixmapFromBuffers requires DRI3 1.2
|
||||
if (dmabuf->n_planes != 1
|
||||
|| dmabuf->modifier != DRM_FORMAT_MOD_INVALID) {
|
||||
wlr_dmabuf_attributes_finish(&dup_attrs);
|
||||
return XCB_PIXMAP_NONE;
|
||||
}
|
||||
xcb_dri3_pixmap_from_buffer(x11->xcb, pixmap, output->win,
|
||||
dmabuf->height * dmabuf->stride[0], dmabuf->width, dmabuf->height,
|
||||
dmabuf->stride[0], x11_fmt->depth, x11_fmt->bpp, dup_attrs.fd[0]);
|
||||
}
|
||||
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
static xcb_pixmap_t import_shm(struct wlr_x11_output *output,
|
||||
struct wlr_shm_attributes *shm) {
|
||||
struct wlr_x11_backend *x11 = output->x11;
|
||||
|
||||
if (shm->format != x11->x11_format->drm) {
|
||||
// The pixmap's depth must match the window's depth, otherwise Present
|
||||
// will throw a Match error
|
||||
return XCB_PIXMAP_NONE;
|
||||
}
|
||||
|
||||
// xcb closes the FD after sending it
|
||||
int fd = fcntl(shm->fd, F_DUPFD_CLOEXEC, 0);
|
||||
if (fd < 0) {
|
||||
wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed");
|
||||
return XCB_PIXMAP_NONE;
|
||||
}
|
||||
|
||||
xcb_shm_seg_t seg = xcb_generate_id(x11->xcb);
|
||||
xcb_shm_attach_fd(x11->xcb, seg, fd, false);
|
||||
|
||||
xcb_pixmap_t pixmap = xcb_generate_id(x11->xcb);
|
||||
xcb_shm_create_pixmap(x11->xcb, pixmap, output->win, shm->width,
|
||||
shm->height, x11->x11_format->depth, seg, shm->offset);
|
||||
|
||||
xcb_shm_detach(x11->xcb, seg);
|
||||
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
static struct wlr_x11_buffer *create_x11_buffer(struct wlr_x11_output *output,
|
||||
struct wlr_buffer *wlr_buffer) {
|
||||
struct wlr_x11_backend *x11 = output->x11;
|
||||
xcb_pixmap_t pixmap = XCB_PIXMAP_NONE;
|
||||
|
||||
struct wlr_dmabuf_attributes dmabuf_attrs;
|
||||
struct wlr_shm_attributes shm_attrs;
|
||||
if (wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf_attrs)) {
|
||||
pixmap = import_dmabuf(output, &dmabuf_attrs);
|
||||
} else if (wlr_buffer_get_shm(wlr_buffer, &shm_attrs)) {
|
||||
pixmap = import_shm(output, &shm_attrs);
|
||||
}
|
||||
|
||||
if (pixmap == XCB_PIXMAP_NONE) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct wlr_x11_buffer *buffer = calloc(1, sizeof(struct wlr_x11_buffer));
|
||||
if (!buffer) {
|
||||
xcb_free_pixmap(x11->xcb, pixmap);
|
||||
return NULL;
|
||||
}
|
||||
buffer->buffer = wlr_buffer_lock(wlr_buffer);
|
||||
buffer->pixmap = pixmap;
|
||||
buffer->x11 = x11;
|
||||
wl_list_insert(&output->buffers, &buffer->link);
|
||||
|
||||
buffer->buffer_destroy.notify = buffer_handle_buffer_destroy;
|
||||
wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static struct wlr_x11_buffer *get_or_create_x11_buffer(
|
||||
struct wlr_x11_output *output, struct wlr_buffer *wlr_buffer) {
|
||||
struct wlr_x11_buffer *buffer;
|
||||
wl_list_for_each(buffer, &output->buffers, link) {
|
||||
if (buffer->buffer == wlr_buffer) {
|
||||
wlr_buffer_lock(buffer->buffer);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
return create_x11_buffer(output, wlr_buffer);
|
||||
}
|
||||
|
||||
static bool output_commit_buffer(struct wlr_x11_output *output) {
|
||||
struct wlr_x11_backend *x11 = output->x11;
|
||||
|
||||
struct wlr_buffer *buffer = output->wlr_output.pending.buffer;
|
||||
struct wlr_x11_buffer *x11_buffer =
|
||||
get_or_create_x11_buffer(output, buffer);
|
||||
if (!x11_buffer) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
xcb_xfixes_region_t region = XCB_NONE;
|
||||
if (output->wlr_output.pending.committed & WLR_OUTPUT_STATE_DAMAGE) {
|
||||
pixman_region32_union(&output->exposed, &output->exposed, &output->wlr_output.pending.damage);
|
||||
|
||||
int rects_len = 0;
|
||||
pixman_box32_t *rects = pixman_region32_rectangles(&output->exposed, &rects_len);
|
||||
|
||||
xcb_rectangle_t *xcb_rects = calloc(rects_len, sizeof(xcb_rectangle_t));
|
||||
if (!xcb_rects) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rects_len; i++) {
|
||||
pixman_box32_t *box = &rects[i];
|
||||
xcb_rects[i] = (struct xcb_rectangle_t){
|
||||
.x = box->x1,
|
||||
.y = box->y1,
|
||||
.width = box->x2 - box->x1,
|
||||
.height = box->y2 - box->y1,
|
||||
};
|
||||
}
|
||||
|
||||
region = xcb_generate_id(x11->xcb);
|
||||
xcb_xfixes_create_region(x11->xcb, region, rects_len, xcb_rects);
|
||||
|
||||
free(xcb_rects);
|
||||
}
|
||||
|
||||
pixman_region32_clear(&output->exposed);
|
||||
|
||||
uint32_t serial = output->wlr_output.commit_seq;
|
||||
uint32_t options = 0;
|
||||
uint64_t target_msc = output->last_msc ? output->last_msc + 1 : 0;
|
||||
xcb_present_pixmap(x11->xcb, output->win, x11_buffer->pixmap, serial,
|
||||
0, region, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, options, target_msc,
|
||||
0, 0, 0, NULL);
|
||||
|
||||
if (region != XCB_NONE) {
|
||||
xcb_xfixes_destroy_region(x11->xcb, region);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
destroy_x11_buffer(x11_buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool output_commit(struct wlr_output *wlr_output) {
|
||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||
struct wlr_x11_backend *x11 = output->x11;
|
||||
|
@ -145,32 +341,154 @@ static bool output_commit(struct wlr_output *wlr_output) {
|
|||
}
|
||||
|
||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
|
||||
pixman_region32_t *damage = NULL;
|
||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) {
|
||||
damage = &wlr_output->pending.damage;
|
||||
}
|
||||
|
||||
if (!wlr_egl_swap_buffers(&x11->egl, output->surf, damage)) {
|
||||
if (!output_commit_buffer(output)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
wlr_output_send_present(wlr_output, NULL);
|
||||
}
|
||||
|
||||
xcb_flush(x11->xcb);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void output_rollback_render(struct wlr_output *wlr_output) {
|
||||
static void update_x11_output_cursor(struct wlr_x11_output *output,
|
||||
int32_t hotspot_x, int32_t hotspot_y) {
|
||||
struct wlr_x11_backend *x11 = output->x11;
|
||||
|
||||
xcb_cursor_t cursor = x11->transparent_cursor;
|
||||
|
||||
if (output->cursor.pic != XCB_NONE) {
|
||||
cursor = xcb_generate_id(x11->xcb);
|
||||
xcb_render_create_cursor(x11->xcb, cursor, output->cursor.pic,
|
||||
hotspot_x, hotspot_y);
|
||||
}
|
||||
|
||||
uint32_t values[] = {cursor};
|
||||
xcb_change_window_attributes(x11->xcb, output->win,
|
||||
XCB_CW_CURSOR, values);
|
||||
xcb_flush(x11->xcb);
|
||||
|
||||
if (cursor != x11->transparent_cursor) {
|
||||
xcb_free_cursor(x11->xcb, cursor);
|
||||
}
|
||||
}
|
||||
|
||||
static bool output_cursor_to_picture(struct wlr_x11_output *output,
|
||||
struct wlr_buffer *buffer) {
|
||||
struct wlr_x11_backend *x11 = output->x11;
|
||||
struct wlr_renderer *renderer = output->wlr_output.renderer;
|
||||
|
||||
if (output->cursor.pic != XCB_NONE) {
|
||||
xcb_render_free_picture(x11->xcb, output->cursor.pic);
|
||||
}
|
||||
output->cursor.pic = XCB_NONE;
|
||||
|
||||
if (buffer == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int depth = 32;
|
||||
int stride = buffer->width * 4;
|
||||
|
||||
uint8_t *data = malloc(buffer->height * stride);
|
||||
if (data == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!wlr_renderer_begin_with_buffer(renderer, buffer)) {
|
||||
free(data);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = wlr_renderer_read_pixels(
|
||||
renderer, DRM_FORMAT_ARGB8888, NULL,
|
||||
stride, buffer->width, buffer->height, 0, 0, 0, 0,
|
||||
data);
|
||||
|
||||
wlr_renderer_end(renderer);
|
||||
|
||||
if (!result) {
|
||||
free(data);
|
||||
return false;
|
||||
}
|
||||
|
||||
xcb_pixmap_t pix = xcb_generate_id(x11->xcb);
|
||||
xcb_create_pixmap(x11->xcb, depth, pix, output->win,
|
||||
buffer->width, buffer->height);
|
||||
|
||||
output->cursor.pic = xcb_generate_id(x11->xcb);
|
||||
xcb_render_create_picture(x11->xcb, output->cursor.pic,
|
||||
pix, x11->argb32, 0, 0);
|
||||
|
||||
xcb_gcontext_t gc = xcb_generate_id(x11->xcb);
|
||||
xcb_create_gc(x11->xcb, gc, pix, 0, NULL);
|
||||
|
||||
xcb_put_image(x11->xcb, XCB_IMAGE_FORMAT_Z_PIXMAP,
|
||||
pix, gc, buffer->width, buffer->height, 0, 0, 0, depth,
|
||||
stride * buffer->height * sizeof(uint8_t), data);
|
||||
free(data);
|
||||
xcb_free_gc(x11->xcb, gc);
|
||||
xcb_free_pixmap(x11->xcb, pix);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool output_set_cursor(struct wlr_output *wlr_output,
|
||||
struct wlr_buffer *buffer, int32_t hotspot_x, int32_t hotspot_y) {
|
||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||
wlr_egl_unset_current(&output->x11->egl);
|
||||
struct wlr_x11_backend *x11 = output->x11;
|
||||
|
||||
if (x11->argb32 == XCB_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buffer != NULL) {
|
||||
if (hotspot_x < 0) {
|
||||
hotspot_x = 0;
|
||||
}
|
||||
if (hotspot_x > buffer->width) {
|
||||
hotspot_x = buffer->width;
|
||||
}
|
||||
if (hotspot_y < 0) {
|
||||
hotspot_y = 0;
|
||||
}
|
||||
if (hotspot_y > buffer->height) {
|
||||
hotspot_y = buffer->height;
|
||||
}
|
||||
}
|
||||
|
||||
bool success = output_cursor_to_picture(output, buffer);
|
||||
|
||||
update_x11_output_cursor(output, hotspot_x, hotspot_y);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool output_move_cursor(struct wlr_output *_output, int x, int y) {
|
||||
// TODO: only return true if x == current x and y == current y
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct wlr_drm_format_set *output_get_primary_formats(
|
||||
struct wlr_output *wlr_output, uint32_t buffer_caps) {
|
||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||
struct wlr_x11_backend *x11 = output->x11;
|
||||
|
||||
if (x11->have_dri3 && (buffer_caps & WLR_BUFFER_CAP_DMABUF)) {
|
||||
return &output->x11->primary_dri3_formats;
|
||||
} else if (x11->have_shm && (buffer_caps & WLR_BUFFER_CAP_SHM)) {
|
||||
return &output->x11->primary_shm_formats;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct wlr_output_impl output_impl = {
|
||||
.destroy = output_destroy,
|
||||
.attach_render = output_attach_render,
|
||||
.test = output_test,
|
||||
.commit = output_commit,
|
||||
.rollback_render = output_rollback_render,
|
||||
.set_cursor = output_set_cursor,
|
||||
.move_cursor = output_move_cursor,
|
||||
.get_primary_formats = output_get_primary_formats,
|
||||
};
|
||||
|
||||
struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
||||
|
@ -186,32 +504,39 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
|||
return NULL;
|
||||
}
|
||||
output->x11 = x11;
|
||||
wl_list_init(&output->buffers);
|
||||
pixman_region32_init(&output->exposed);
|
||||
|
||||
struct wlr_output *wlr_output = &output->wlr_output;
|
||||
wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display);
|
||||
|
||||
wlr_output->width = 1024;
|
||||
wlr_output->height = 768;
|
||||
wlr_output_update_custom_mode(wlr_output, 1024, 768, 0);
|
||||
|
||||
output_set_refresh(&output->wlr_output, 0);
|
||||
char name[64];
|
||||
snprintf(name, sizeof(name), "X11-%zu", ++x11->last_output_num);
|
||||
wlr_output_set_name(wlr_output, name);
|
||||
|
||||
snprintf(wlr_output->name, sizeof(wlr_output->name), "X11-%zd",
|
||||
++x11->last_output_num);
|
||||
parse_xcb_setup(wlr_output, x11->xcb);
|
||||
|
||||
char description[128];
|
||||
snprintf(description, sizeof(description),
|
||||
"X11 output %zd", x11->last_output_num);
|
||||
"X11 output %zu", x11->last_output_num);
|
||||
wlr_output_set_description(wlr_output, description);
|
||||
|
||||
uint32_t mask = XCB_CW_EVENT_MASK;
|
||||
// The X11 protocol requires us to set a colormap and border pixel if the
|
||||
// depth doesn't match the root window's
|
||||
uint32_t mask = XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK |
|
||||
XCB_CW_COLORMAP | XCB_CW_CURSOR;
|
||||
uint32_t values[] = {
|
||||
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY
|
||||
0,
|
||||
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY,
|
||||
x11->colormap,
|
||||
x11->transparent_cursor,
|
||||
};
|
||||
output->win = xcb_generate_id(x11->xcb);
|
||||
xcb_create_window(x11->xcb, XCB_COPY_FROM_PARENT, output->win,
|
||||
x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 1,
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->screen->root_visual, mask, values);
|
||||
xcb_create_window(x11->xcb, x11->depth->depth, output->win,
|
||||
x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 0,
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->visualid, mask, values);
|
||||
|
||||
struct {
|
||||
xcb_input_event_mask_t head;
|
||||
|
@ -223,20 +548,17 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
|||
XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS |
|
||||
XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE |
|
||||
XCB_INPUT_XI_EVENT_MASK_MOTION |
|
||||
XCB_INPUT_XI_EVENT_MASK_ENTER |
|
||||
XCB_INPUT_XI_EVENT_MASK_LEAVE |
|
||||
XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN |
|
||||
XCB_INPUT_XI_EVENT_MASK_TOUCH_END |
|
||||
XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE,
|
||||
};
|
||||
xcb_input_xi_select_events(x11->xcb, output->win, 1, &xinput_mask.head);
|
||||
|
||||
output->surf = wlr_egl_create_surface(&x11->egl, &output->win);
|
||||
if (!output->surf) {
|
||||
wlr_log(WLR_ERROR, "Failed to create EGL surface");
|
||||
free(output);
|
||||
return NULL;
|
||||
}
|
||||
uint32_t present_mask = XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY |
|
||||
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY;
|
||||
output->present_event_id = xcb_generate_id(x11->xcb);
|
||||
xcb_present_select_input(x11->xcb, output->present_event_id, output->win,
|
||||
present_mask);
|
||||
|
||||
xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win,
|
||||
x11->atoms.wm_protocols, XCB_ATOM_ATOM, 32, 1,
|
||||
|
@ -247,12 +569,8 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
|||
xcb_map_window(x11->xcb, output->win);
|
||||
xcb_flush(x11->xcb);
|
||||
|
||||
struct wl_event_loop *ev = wl_display_get_event_loop(x11->wl_display);
|
||||
output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output);
|
||||
|
||||
wl_list_insert(&x11->outputs, &output->link);
|
||||
|
||||
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
|
||||
wlr_output_update_enabled(wlr_output, true);
|
||||
|
||||
wlr_input_device_init(&output->pointer_dev, WLR_INPUT_DEVICE_POINTER,
|
||||
|
@ -272,23 +590,27 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
|||
wlr_signal_emit_safe(&x11->backend.events.new_input, &output->pointer_dev);
|
||||
wlr_signal_emit_safe(&x11->backend.events.new_input, &output->touch_dev);
|
||||
|
||||
// Start the rendering loop by requesting the compositor to render a frame
|
||||
wlr_output_schedule_frame(wlr_output);
|
||||
|
||||
return wlr_output;
|
||||
}
|
||||
|
||||
void handle_x11_configure_notify(struct wlr_x11_output *output,
|
||||
xcb_configure_notify_event_t *ev) {
|
||||
// ignore events that set an invalid size:
|
||||
if (ev->width > 0 && ev->height > 0) {
|
||||
wlr_output_update_custom_mode(&output->wlr_output, ev->width,
|
||||
ev->height, output->wlr_output.refresh);
|
||||
|
||||
// Move the pointer to its new location
|
||||
update_x11_pointer_position(output, output->x11->time);
|
||||
} else {
|
||||
if (ev->width == 0 || ev->height == 0) {
|
||||
wlr_log(WLR_DEBUG,
|
||||
"Ignoring X11 configure event for height=%d, width=%d",
|
||||
ev->width, ev->height);
|
||||
return;
|
||||
}
|
||||
|
||||
wlr_output_update_custom_mode(&output->wlr_output, ev->width,
|
||||
ev->height, 0);
|
||||
|
||||
// Move the pointer to its new location
|
||||
update_x11_pointer_position(output, output->x11->time);
|
||||
}
|
||||
|
||||
bool wlr_output_is_x11(struct wlr_output *wlr_output) {
|
||||
|
@ -310,3 +632,76 @@ void wlr_x11_output_set_title(struct wlr_output *output, const char *title) {
|
|||
x11_output->x11->atoms.net_wm_name, x11_output->x11->atoms.utf8_string, 8,
|
||||
strlen(title), title);
|
||||
}
|
||||
|
||||
static struct wlr_x11_buffer *get_x11_buffer(struct wlr_x11_output *output,
|
||||
xcb_pixmap_t pixmap) {
|
||||
struct wlr_x11_buffer *buffer;
|
||||
wl_list_for_each(buffer, &output->buffers, link) {
|
||||
if (buffer->pixmap == pixmap) {
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void handle_x11_present_event(struct wlr_x11_backend *x11,
|
||||
xcb_ge_generic_event_t *event) {
|
||||
struct wlr_x11_output *output;
|
||||
|
||||
switch (event->event_type) {
|
||||
case XCB_PRESENT_EVENT_IDLE_NOTIFY:;
|
||||
xcb_present_idle_notify_event_t *idle_notify =
|
||||
(xcb_present_idle_notify_event_t *)event;
|
||||
|
||||
output = get_x11_output_from_window_id(x11, idle_notify->window);
|
||||
if (!output) {
|
||||
wlr_log(WLR_DEBUG, "Got PresentIdleNotify event for unknown window");
|
||||
return;
|
||||
}
|
||||
|
||||
struct wlr_x11_buffer *buffer =
|
||||
get_x11_buffer(output, idle_notify->pixmap);
|
||||
if (!buffer) {
|
||||
wlr_log(WLR_DEBUG, "Got PresentIdleNotify event for unknown buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
wlr_buffer_unlock(buffer->buffer); // may destroy buffer
|
||||
break;
|
||||
case XCB_PRESENT_COMPLETE_NOTIFY:;
|
||||
xcb_present_complete_notify_event_t *complete_notify =
|
||||
(xcb_present_complete_notify_event_t *)event;
|
||||
|
||||
output = get_x11_output_from_window_id(x11, complete_notify->window);
|
||||
if (!output) {
|
||||
wlr_log(WLR_DEBUG, "Got PresentCompleteNotify event for unknown window");
|
||||
return;
|
||||
}
|
||||
|
||||
output->last_msc = complete_notify->msc;
|
||||
|
||||
struct timespec t;
|
||||
timespec_from_nsec(&t, complete_notify->ust * 1000);
|
||||
|
||||
uint32_t flags = 0;
|
||||
if (complete_notify->mode == XCB_PRESENT_COMPLETE_MODE_FLIP) {
|
||||
flags |= WLR_OUTPUT_PRESENT_ZERO_COPY;
|
||||
}
|
||||
|
||||
bool presented = complete_notify->mode != XCB_PRESENT_COMPLETE_MODE_SKIP;
|
||||
struct wlr_output_event_present present_event = {
|
||||
.output = &output->wlr_output,
|
||||
.commit_seq = complete_notify->serial,
|
||||
.presented = presented,
|
||||
.when = &t,
|
||||
.seq = complete_notify->msc,
|
||||
.flags = flags,
|
||||
};
|
||||
wlr_output_send_present(&output->wlr_output, &present_event);
|
||||
|
||||
wlr_output_send_frame(&output->wlr_output);
|
||||
break;
|
||||
default:
|
||||
wlr_log(WLR_DEBUG, "Unhandled Present event %"PRIu16, event->event_type);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
#!/bin/sh -eu
|
||||
old_version="$1"
|
||||
new_version="$2"
|
||||
sed -i meson.build -e "s/version: '$old_version'/version: '$new_version'/g"
|
||||
|
||||
bugfix='n'
|
||||
printf "Breaking API changes? (y/n) "
|
||||
read breaking
|
||||
if [ "$breaking" = 'n' ]
|
||||
then
|
||||
printf "Bugfix release (no backwards-incompatible ABI changes)? (y/n) "
|
||||
read bugfix
|
||||
fi
|
||||
|
||||
soversion=$(egrep '^soversion =' meson.build | cut -d'=' -f2-)
|
||||
soversion=$((soversion))
|
||||
|
||||
if [ "$bugfix" != 'y' ]
|
||||
then
|
||||
soversion=$((soversion+1))
|
||||
fi
|
||||
|
||||
sed -i meson.build \
|
||||
-e "s/soversion = .*/soversion = $soversion/g"
|
||||
|
||||
exit 1
|
||||
git add meson.build
|
||||
git commit -m "Update version to $new_version"
|
|
@ -3,14 +3,15 @@ wlroots reads these environment variables
|
|||
# wlroots specific
|
||||
|
||||
* *WLR_BACKENDS*: comma-separated list of backends to use (available backends:
|
||||
libinput, drm, wayland, x11, headless, noop)
|
||||
libinput, drm, wayland, x11, headless)
|
||||
* *WLR_NO_HARDWARE_CURSORS*: set to 1 to use software cursors instead of
|
||||
hardware cursors
|
||||
* *WLR_SESSION*: specifies the wlr\_session to be used (available sessions:
|
||||
logind/systemd, direct)
|
||||
* *WLR_DIRECT_TTY*: specifies the tty to be used (instead of using /dev/tty)
|
||||
* *WLR_XWAYLAND*: specifies the path to an Xwayland binary to be used (instead
|
||||
of following shell search semantics for "Xwayland")
|
||||
* *WLR_RENDERER*: forces the creation of a specified renderer (available
|
||||
renderers: gles2, pixman, vulkan)
|
||||
* *WLR_RENDER_DRM_DEVICE*: specifies the DRM node to use for
|
||||
hardware-accelerated renderers.
|
||||
|
||||
## DRM backend
|
||||
|
||||
|
@ -39,10 +40,15 @@ wlroots reads these environment variables
|
|||
|
||||
* *WLR_X11_OUTPUTS*: when using the X11 backend specifies the number of outputs
|
||||
|
||||
## gles2 renderer
|
||||
|
||||
* *WLR_RENDERER_ALLOW_SOFTWARE*: allows the gles2 renderer to use software
|
||||
rendering
|
||||
|
||||
# Generic
|
||||
|
||||
* *DISPLAY*: if set probe X11 backend in *wlr_backend_autocreate*
|
||||
* *WAYLAND_DISPLAY*, *_WAYLAND_DISPLAY*, *WAYLAND_SOCKET*: if set probe Wayland
|
||||
backend in *wlr_backend_autocreate*
|
||||
* *DISPLAY*: if set probe X11 backend in `wlr_backend_autocreate`
|
||||
* *WAYLAND_DISPLAY*, *WAYLAND_SOCKET*: if set probe Wayland backend in
|
||||
`wlr_backend_autocreate`
|
||||
* *XCURSOR_PATH*: directory where xcursors are located
|
||||
* *XDG_SESSION_ID*: if set, session ID used by the logind session
|
||||
|
|
|
@ -490,26 +490,27 @@ static void *vid_encode_thread(void *arg) {
|
|||
}
|
||||
|
||||
while (1) {
|
||||
AVPacket pkt;
|
||||
av_init_packet(&pkt);
|
||||
|
||||
int ret = avcodec_receive_packet(ctx->avctx, &pkt);
|
||||
AVPacket *pkt = av_packet_alloc();
|
||||
int ret = avcodec_receive_packet(ctx->avctx, pkt);
|
||||
if (ret == AVERROR(EAGAIN)) {
|
||||
av_packet_free(&pkt);
|
||||
break;
|
||||
} else if (ret == AVERROR_EOF) {
|
||||
av_log(ctx, AV_LOG_INFO, "Encoder flushed!\n");
|
||||
av_packet_free(&pkt);
|
||||
goto end;
|
||||
} else if (ret) {
|
||||
av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n",
|
||||
av_err2str(ret));
|
||||
av_packet_free(&pkt);
|
||||
err = ret;
|
||||
goto end;
|
||||
}
|
||||
|
||||
pkt.stream_index = 0;
|
||||
err = av_interleaved_write_frame(ctx->avf, &pkt);
|
||||
pkt->stream_index = 0;
|
||||
err = av_interleaved_write_frame(ctx->avf, pkt);
|
||||
|
||||
av_packet_unref(&pkt);
|
||||
av_packet_free(&pkt);
|
||||
|
||||
if (err) {
|
||||
av_log(ctx, AV_LOG_ERROR, "Writing packet fail: %s!\n",
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include "egl_common.h"
|
||||
|
||||
EGLDisplay egl_display;
|
||||
EGLConfig egl_config;
|
||||
EGLContext egl_context;
|
||||
|
||||
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT;
|
||||
PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT;
|
||||
|
||||
const EGLint config_attribs[] = {
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_RED_SIZE, 1,
|
||||
EGL_GREEN_SIZE, 1,
|
||||
EGL_BLUE_SIZE, 1,
|
||||
EGL_ALPHA_SIZE, 1,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
const EGLint context_attribs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
bool egl_init(struct wl_display *display) {
|
||||
const char *client_exts_str =
|
||||
eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
||||
if (client_exts_str == NULL) {
|
||||
if (eglGetError() == EGL_BAD_DISPLAY) {
|
||||
fprintf(stderr, "EGL_EXT_client_extensions not supported\n");
|
||||
} else {
|
||||
fprintf(stderr, "Failed to query EGL client extensions\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!strstr(client_exts_str, "EGL_EXT_platform_base")) {
|
||||
fprintf(stderr, "EGL_EXT_platform_base not supported\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!strstr(client_exts_str, "EGL_EXT_platform_wayland")) {
|
||||
fprintf(stderr, "EGL_EXT_platform_wayland not supported\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
eglGetPlatformDisplayEXT =
|
||||
(void *)eglGetProcAddress("eglGetPlatformDisplayEXT");
|
||||
if (eglGetPlatformDisplayEXT == NULL) {
|
||||
fprintf(stderr, "Failed to get eglGetPlatformDisplayEXT\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
eglCreatePlatformWindowSurfaceEXT =
|
||||
(void *)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
|
||||
if (eglCreatePlatformWindowSurfaceEXT == NULL) {
|
||||
fprintf(stderr, "Failed to get eglCreatePlatformWindowSurfaceEXT\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
egl_display =
|
||||
eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT,
|
||||
display, NULL);
|
||||
if (egl_display == EGL_NO_DISPLAY) {
|
||||
fprintf(stderr, "Failed to create EGL display\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (eglInitialize(egl_display, NULL, NULL) == EGL_FALSE) {
|
||||
fprintf(stderr, "Failed to initialize EGL\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
EGLint matched = 0;
|
||||
if (!eglChooseConfig(egl_display, config_attribs,
|
||||
&egl_config, 1, &matched)) {
|
||||
fprintf(stderr, "eglChooseConfig failed\n");
|
||||
goto error;
|
||||
}
|
||||
if (matched == 0) {
|
||||
fprintf(stderr, "Failed to match an EGL config\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
egl_context =
|
||||
eglCreateContext(egl_display, egl_config,
|
||||
EGL_NO_CONTEXT, context_attribs);
|
||||
if (egl_context == EGL_NO_CONTEXT) {
|
||||
fprintf(stderr, "Failed to create EGL context\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE,
|
||||
EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
if (egl_display) {
|
||||
eglTerminate(egl_display);
|
||||
}
|
||||
eglReleaseThread();
|
||||
return false;
|
||||
}
|
||||
|
||||
void egl_finish(void) {
|
||||
eglMakeCurrent(egl_display, EGL_NO_SURFACE,
|
||||
EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
eglDestroyContext(egl_display, egl_context);
|
||||
eglTerminate(egl_display);
|
||||
eglReleaseThread();
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef _EGL_COMMON_H
|
||||
#define _EGL_COMMON_H
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
extern EGLDisplay egl_display;
|
||||
extern EGLConfig egl_config;
|
||||
extern EGLContext egl_context;
|
||||
|
||||
extern PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT;
|
||||
|
||||
bool egl_init(struct wl_display *display);
|
||||
|
||||
void egl_finish(void);
|
|
@ -9,26 +9,27 @@
|
|||
|
||||
#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION 3
|
||||
|
||||
/**
|
||||
* Usage:
|
||||
* 1. foreign-toplevel
|
||||
* Prints a list of opened toplevels
|
||||
* 2. foreign-toplevel -f <id>
|
||||
* Focus the toplevel with the given id
|
||||
* 3. foreign-toplevel -a <id>
|
||||
* Maximize the toplevel with the given id
|
||||
* 4. foreign-toplevel -u <id>
|
||||
* Unmaximize the toplevel with the given id
|
||||
* 5. foreign-toplevel -i <id>
|
||||
* Minimize the toplevel with the given id
|
||||
* 6. foreign-toplevel -r <id>
|
||||
* Restore(unminimize) the toplevel with the given id
|
||||
* 7. foreign-toplevel -c <id>
|
||||
* Close the toplevel with the given id
|
||||
* 8. foreign-toplevel -m
|
||||
* Continuously print changes to the list of opened toplevels.
|
||||
* Can be used together with some of the previous options.
|
||||
*/
|
||||
|
||||
static void print_help(void) {
|
||||
static const char usage[] =
|
||||
"Usage: foreign-toplevel [OPTIONS] ...\n"
|
||||
"Manage and view information about toplevel windows.\n"
|
||||
"\n"
|
||||
" -f <id> focus\n"
|
||||
" -s <id> fullscreen\n"
|
||||
" -o <output_id> select output for fullscreen toplevel to appear on. Use this\n"
|
||||
" option with -s. View available outputs with wayland-info.\n"
|
||||
" -S <id> unfullscreen\n"
|
||||
" -a <id> maximize\n"
|
||||
" -u <id> unmaximize\n"
|
||||
" -i <id> minimize\n"
|
||||
" -r <id> restore(unminimize)\n"
|
||||
" -c <id> close\n"
|
||||
" -m continuously print changes to the list of opened toplevels\n"
|
||||
" Can be used together with some of the previous options.\n"
|
||||
" -h print help message and quit\n";
|
||||
fprintf(stderr, "%s", usage);
|
||||
}
|
||||
|
||||
enum toplevel_state_field {
|
||||
TOPLEVEL_STATE_MAXIMIZED = (1 << 0),
|
||||
|
@ -39,6 +40,8 @@ enum toplevel_state_field {
|
|||
};
|
||||
|
||||
static const uint32_t no_parent = (uint32_t)-1;
|
||||
static struct wl_output *pref_output = NULL;
|
||||
static uint32_t pref_output_id = UINT32_MAX;
|
||||
|
||||
struct toplevel_state {
|
||||
char *title;
|
||||
|
@ -94,7 +97,7 @@ static void print_toplevel(struct toplevel_v1 *toplevel, bool print_endl) {
|
|||
} else {
|
||||
printf(" no parent");
|
||||
}
|
||||
|
||||
|
||||
if (print_endl) {
|
||||
printf("\n");
|
||||
}
|
||||
|
@ -125,6 +128,11 @@ static void print_toplevel_state(struct toplevel_v1 *toplevel, bool print_endl)
|
|||
}
|
||||
}
|
||||
|
||||
static void finish_toplevel_state(struct toplevel_state *state) {
|
||||
free(state->title);
|
||||
free(state->app_id);
|
||||
}
|
||||
|
||||
static void toplevel_handle_title(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
|
||||
const char *title) {
|
||||
|
@ -225,6 +233,9 @@ static void toplevel_handle_closed(void *data,
|
|||
printf(" closed\n");
|
||||
|
||||
zwlr_foreign_toplevel_handle_v1_destroy(zwlr_toplevel);
|
||||
finish_toplevel_state(&toplevel->current);
|
||||
finish_toplevel_state(&toplevel->pending);
|
||||
free(toplevel);
|
||||
}
|
||||
|
||||
static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = {
|
||||
|
@ -271,9 +282,11 @@ struct wl_seat *seat = NULL;
|
|||
static void handle_global(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface, uint32_t version) {
|
||||
if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||
struct wl_output *output = wl_registry_bind(registry, name,
|
||||
&wl_output_interface, version);
|
||||
wl_output_set_user_data(output, (void*)(size_t)name); // assign some ID to the output
|
||||
if (name == pref_output_id) {
|
||||
pref_output = wl_registry_bind(registry, name,
|
||||
&wl_output_interface, version);
|
||||
|
||||
}
|
||||
} else if (strcmp(interface,
|
||||
zwlr_foreign_toplevel_manager_v1_interface.name) == 0) {
|
||||
toplevel_manager = wl_registry_bind(registry, name,
|
||||
|
@ -324,8 +337,7 @@ int main(int argc, char **argv) {
|
|||
int one_shot = 1;
|
||||
int c;
|
||||
|
||||
// TODO maybe print usage with -h?
|
||||
while ((c = getopt(argc, argv, "f:a:u:i:r:c:s:S:m")) != -1) {
|
||||
while ((c = getopt(argc, argv, "f:a:u:i:r:c:s:S:mo:h")) != -1) {
|
||||
switch (c) {
|
||||
case 'f':
|
||||
focus_id = atoi(optarg);
|
||||
|
@ -354,6 +366,17 @@ int main(int argc, char **argv) {
|
|||
case 'm':
|
||||
one_shot = 0;
|
||||
break;
|
||||
case 'o':
|
||||
pref_output_id = atoi(optarg);
|
||||
break;
|
||||
case '?':
|
||||
print_help();
|
||||
return EXIT_FAILURE;
|
||||
break;
|
||||
case 'h':
|
||||
print_help();
|
||||
return EXIT_SUCCESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -391,7 +414,11 @@ int main(int argc, char **argv) {
|
|||
zwlr_foreign_toplevel_handle_v1_unset_minimized(toplevel->zwlr_toplevel);
|
||||
}
|
||||
if ((toplevel = toplevel_by_id_or_bail(fullscreen_id))) {
|
||||
zwlr_foreign_toplevel_handle_v1_set_fullscreen(toplevel->zwlr_toplevel, NULL);
|
||||
if (pref_output_id != UINT32_MAX && pref_output == NULL) {
|
||||
fprintf(stderr, "Could not find output %i\n", pref_output_id);
|
||||
}
|
||||
|
||||
zwlr_foreign_toplevel_handle_v1_set_fullscreen(toplevel->zwlr_toplevel, pref_output);
|
||||
}
|
||||
if ((toplevel = toplevel_by_id_or_bail(unfullscreen_id))) {
|
||||
zwlr_foreign_toplevel_handle_v1_unset_fullscreen(toplevel->zwlr_toplevel);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <unistd.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend.h>
|
||||
#include <wlr/render/allocator.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_compositor.h>
|
||||
#include <wlr/types/wlr_fullscreen_shell_v1.h>
|
||||
|
@ -14,6 +15,7 @@
|
|||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_surface.h>
|
||||
#include <wlr/util/box.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
||||
/**
|
||||
|
@ -24,6 +26,7 @@ struct fullscreen_server {
|
|||
struct wl_display *wl_display;
|
||||
struct wlr_backend *backend;
|
||||
struct wlr_renderer *renderer;
|
||||
struct wlr_allocator *allocator;
|
||||
|
||||
struct wlr_fullscreen_shell_v1 *fullscreen_shell;
|
||||
struct wl_listener present_surface;
|
||||
|
@ -46,7 +49,6 @@ struct fullscreen_output {
|
|||
struct render_data {
|
||||
struct wlr_output *output;
|
||||
struct wlr_renderer *renderer;
|
||||
struct tinywl_view *view;
|
||||
struct timespec *when;
|
||||
};
|
||||
|
||||
|
@ -146,11 +148,7 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) {
|
|||
wl_container_of(listener, server, new_output);
|
||||
struct wlr_output *wlr_output = data;
|
||||
|
||||
if (!wl_list_empty(&wlr_output->modes)) {
|
||||
struct wlr_output_mode *mode =
|
||||
wl_container_of(wlr_output->modes.prev, mode, link);
|
||||
wlr_output_set_mode(wlr_output, mode);
|
||||
}
|
||||
wlr_output_init_render(wlr_output, server->allocator, server->renderer);
|
||||
|
||||
struct fullscreen_output *output =
|
||||
calloc(1, sizeof(struct fullscreen_output));
|
||||
|
@ -163,6 +161,11 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) {
|
|||
wlr_output_layout_add_auto(server->output_layout, wlr_output);
|
||||
wlr_output_create_global(wlr_output);
|
||||
|
||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
|
||||
if (mode != NULL) {
|
||||
wlr_output_set_mode(wlr_output, mode);
|
||||
}
|
||||
|
||||
wlr_output_commit(wlr_output);
|
||||
}
|
||||
|
||||
|
@ -203,9 +206,11 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
struct fullscreen_server server = {0};
|
||||
server.wl_display = wl_display_create();
|
||||
server.backend = wlr_backend_autocreate(server.wl_display, NULL);
|
||||
server.renderer = wlr_backend_get_renderer(server.backend);
|
||||
server.backend = wlr_backend_autocreate(server.wl_display);
|
||||
server.renderer = wlr_renderer_autocreate(server.backend);
|
||||
wlr_renderer_init_wl_display(server.renderer, server.wl_display);
|
||||
server.allocator = wlr_allocator_autocreate(server.backend,
|
||||
server.renderer);
|
||||
|
||||
wlr_compositor_create(server.wl_display, server.renderer);
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <string.h>
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-egl.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include "egl_common.h"
|
||||
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
|
||||
|
@ -27,12 +27,11 @@ static struct xdg_wm_base *wm_base = NULL;
|
|||
static struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = NULL;
|
||||
static struct zwp_idle_inhibitor_v1 *idle_inhibitor = NULL;
|
||||
|
||||
struct wlr_egl egl;
|
||||
struct wl_egl_window *egl_window;
|
||||
struct wlr_egl_surface *egl_surface;
|
||||
|
||||
static void draw(void) {
|
||||
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
|
||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
||||
|
||||
float color[] = {1.0, 1.0, 0.0, 1.0};
|
||||
if (idle_inhibitor) {
|
||||
|
@ -43,7 +42,7 @@ static void draw(void) {
|
|||
glClearColor(color[0], color[1], color[2], 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
eglSwapBuffers(egl.display, egl_surface);
|
||||
eglSwapBuffers(egl_display, egl_surface);
|
||||
}
|
||||
|
||||
static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
|
||||
|
@ -192,8 +191,7 @@ int main(int argc, char **argv) {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
||||
WL_SHM_FORMAT_ARGB8888);
|
||||
egl_init(display);
|
||||
|
||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||
struct xdg_surface *xdg_surface =
|
||||
|
@ -214,7 +212,8 @@ int main(int argc, char **argv) {
|
|||
wl_surface_commit(surface);
|
||||
|
||||
egl_window = wl_egl_window_create(surface, width, height);
|
||||
egl_surface = wlr_egl_create_surface(&egl, egl_window);
|
||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
||||
egl_display, egl_config, egl_window, NULL);
|
||||
|
||||
wl_display_roundtrip(display);
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <string.h>
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-egl.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include "egl_common.h"
|
||||
#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
|
||||
|
@ -18,12 +18,11 @@ static struct xdg_wm_base *wm_base = NULL;
|
|||
static struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager = NULL;
|
||||
static struct zwlr_input_inhibitor_v1 *input_inhibitor = NULL;
|
||||
|
||||
struct wlr_egl egl;
|
||||
struct wl_egl_window *egl_window;
|
||||
struct wlr_egl_surface *egl_surface;
|
||||
|
||||
static void render_frame(void) {
|
||||
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
|
||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
||||
|
||||
glViewport(0, 0, width, height);
|
||||
if (keys) {
|
||||
|
@ -33,7 +32,7 @@ static void render_frame(void) {
|
|||
}
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
eglSwapBuffers(egl.display, egl_surface);
|
||||
eglSwapBuffers(egl_display, egl_surface);
|
||||
}
|
||||
|
||||
static void xdg_surface_handle_configure(void *data,
|
||||
|
@ -157,8 +156,7 @@ int main(int argc, char **argv) {
|
|||
input_inhibit_manager);
|
||||
assert(input_inhibitor);
|
||||
|
||||
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
||||
WL_SHM_FORMAT_ARGB8888);
|
||||
egl_init(display);
|
||||
|
||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||
assert(surface);
|
||||
|
@ -174,7 +172,8 @@ int main(int argc, char **argv) {
|
|||
wl_surface_commit(surface);
|
||||
|
||||
egl_window = wl_egl_window_create(surface, width, height);
|
||||
egl_surface = wlr_egl_create_surface(&egl, egl_window);
|
||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
||||
egl_display, egl_config, egl_window, NULL);
|
||||
|
||||
wl_display_roundtrip(display);
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <string.h>
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-egl.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include "egl_common.h"
|
||||
#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
|
||||
|
@ -33,12 +33,11 @@ static struct zwp_keyboard_shortcuts_inhibitor_v1 *
|
|||
keyboard_shortcuts_inhibitor = NULL;
|
||||
static bool active = false;
|
||||
|
||||
struct wlr_egl egl;
|
||||
struct wl_egl_window *egl_window;
|
||||
struct wlr_egl_surface *egl_surface;
|
||||
|
||||
static void draw(void) {
|
||||
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
|
||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
||||
|
||||
float color[] = {1.0, 1.0, 0.0, 1.0};
|
||||
if (keyboard_shortcuts_inhibitor) {
|
||||
|
@ -49,7 +48,7 @@ static void draw(void) {
|
|||
glClearColor(color[0], color[1], color[2], 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
eglSwapBuffers(egl.display, egl_surface);
|
||||
eglSwapBuffers(egl_display, egl_surface);
|
||||
}
|
||||
|
||||
static void keyboard_shortcuts_inhibit_handle_active(void *data,
|
||||
|
@ -224,8 +223,7 @@ int main(int argc, char **argv) {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
||||
WL_SHM_FORMAT_ARGB8888);
|
||||
egl_init(display);
|
||||
|
||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||
struct xdg_surface *xdg_surface =
|
||||
|
@ -241,7 +239,8 @@ int main(int argc, char **argv) {
|
|||
wl_surface_commit(surface);
|
||||
|
||||
egl_window = wl_egl_window_create(surface, width, height);
|
||||
egl_surface = wlr_egl_create_surface(&egl, egl_window);
|
||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
||||
egl_display, egl_config, egl_window, NULL);
|
||||
|
||||
wl_display_roundtrip(display);
|
||||
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
#include <wayland-client.h>
|
||||
#include <wayland-cursor.h>
|
||||
#include <wayland-egl.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "egl_common.h"
|
||||
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
|
||||
|
@ -29,7 +28,6 @@ struct zwlr_layer_surface_v1 *layer_surface;
|
|||
static struct wl_output *wl_output;
|
||||
|
||||
struct wl_surface *wl_surface;
|
||||
struct wlr_egl egl;
|
||||
struct wl_egl_window *egl_window;
|
||||
struct wlr_egl_surface *egl_surface;
|
||||
struct wl_callback *frame_callback;
|
||||
|
@ -50,7 +48,8 @@ static int32_t margin_top = 0;
|
|||
static double alpha = 1.0;
|
||||
static bool run_display = true;
|
||||
static bool animate = false;
|
||||
static bool keyboard_interactive = false;
|
||||
static enum zwlr_layer_surface_v1_keyboard_interactivity keyboard_interactive =
|
||||
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
|
||||
static double frame = 0;
|
||||
static int cur_x = -1, cur_y = -1;
|
||||
static int buttons = 0;
|
||||
|
@ -93,7 +92,7 @@ static struct wl_callback_listener popup_frame_listener = {
|
|||
};
|
||||
|
||||
static void draw(void) {
|
||||
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
|
||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
|
||||
|
@ -142,7 +141,7 @@ static void draw(void) {
|
|||
frame_callback = wl_surface_frame(wl_surface);
|
||||
wl_callback_add_listener(frame_callback, &frame_listener, NULL);
|
||||
|
||||
eglSwapBuffers(egl.display, egl_surface);
|
||||
eglSwapBuffers(egl_display, egl_surface);
|
||||
|
||||
demo.last_frame = ts;
|
||||
}
|
||||
|
@ -150,7 +149,7 @@ static void draw(void) {
|
|||
static void draw_popup(void) {
|
||||
static float alpha_mod = -0.01;
|
||||
|
||||
eglMakeCurrent(egl.display, popup_egl_surface, popup_egl_surface, egl.context);
|
||||
eglMakeCurrent(egl_display, popup_egl_surface, popup_egl_surface, egl_context);
|
||||
glViewport(0, 0, popup_width, popup_height);
|
||||
glClearColor(popup_red, 0.5f, 0.5f, popup_alpha);
|
||||
popup_alpha += alpha_mod;
|
||||
|
@ -162,7 +161,7 @@ static void draw_popup(void) {
|
|||
popup_frame_callback = wl_surface_frame(popup_wl_surface);
|
||||
assert(popup_frame_callback);
|
||||
wl_callback_add_listener(popup_frame_callback, &popup_frame_listener, NULL);
|
||||
eglSwapBuffers(egl.display, popup_egl_surface);
|
||||
eglSwapBuffers(egl_display, popup_egl_surface);
|
||||
wl_surface_commit(popup_wl_surface);
|
||||
}
|
||||
|
||||
|
@ -177,8 +176,7 @@ static const struct xdg_surface_listener xdg_surface_listener = {
|
|||
|
||||
static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup,
|
||||
int32_t x, int32_t y, int32_t width, int32_t height) {
|
||||
wlr_log(WLR_DEBUG, "Popup configured %dx%d@%d,%d",
|
||||
width, height, x, y);
|
||||
fprintf(stderr, "Popup configured %dx%d@%d,%d\n", width, height, x, y);
|
||||
popup_width = width;
|
||||
popup_height = height;
|
||||
if (popup_egl_window) {
|
||||
|
@ -187,7 +185,7 @@ static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup,
|
|||
}
|
||||
|
||||
static void popup_destroy(void) {
|
||||
wlr_egl_destroy_surface(&egl, popup_egl_surface);
|
||||
eglDestroySurface(egl_display, popup_egl_surface);
|
||||
wl_egl_window_destroy(popup_egl_window);
|
||||
xdg_popup_destroy(popup);
|
||||
wl_surface_destroy(popup_wl_surface);
|
||||
|
@ -197,7 +195,7 @@ static void popup_destroy(void) {
|
|||
}
|
||||
|
||||
static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) {
|
||||
wlr_log(WLR_DEBUG, "Popup done");
|
||||
fprintf(stderr, "Popup done\n");
|
||||
popup_destroy();
|
||||
}
|
||||
|
||||
|
@ -241,8 +239,9 @@ static void create_popup(uint32_t serial) {
|
|||
popup_wl_surface = surface;
|
||||
popup_egl_window = wl_egl_window_create(surface, popup_width, popup_height);
|
||||
assert(popup_egl_window);
|
||||
popup_egl_surface = wlr_egl_create_surface(&egl, popup_egl_window);
|
||||
assert(popup_egl_surface);
|
||||
popup_egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
||||
egl_display, egl_config, popup_egl_window, NULL);
|
||||
assert(popup_egl_surface != EGL_NO_SURFACE);
|
||||
draw_popup();
|
||||
}
|
||||
|
||||
|
@ -259,7 +258,7 @@ static void layer_surface_configure(void *data,
|
|||
|
||||
static void layer_surface_closed(void *data,
|
||||
struct zwlr_layer_surface_v1 *surface) {
|
||||
wlr_egl_destroy_surface(&egl, egl_surface);
|
||||
eglDestroySurface(egl_display, egl_surface);
|
||||
wl_egl_window_destroy(egl_window);
|
||||
zwlr_layer_surface_v1_destroy(surface);
|
||||
wl_surface_destroy(wl_surface);
|
||||
|
@ -376,17 +375,17 @@ static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
|||
|
||||
static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
|
||||
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
|
||||
wlr_log(WLR_DEBUG, "Keyboard enter");
|
||||
fprintf(stderr, "Keyboard enter\n");
|
||||
}
|
||||
|
||||
static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
|
||||
uint32_t serial, struct wl_surface *surface) {
|
||||
wlr_log(WLR_DEBUG, "Keyboard leave");
|
||||
fprintf(stderr, "Keyboard leave\n");
|
||||
}
|
||||
|
||||
static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
|
||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
|
||||
wlr_log(WLR_DEBUG, "Key event: %d %d", key, state);
|
||||
fprintf(stderr, "Key event: %d %d\n", key, state);
|
||||
}
|
||||
|
||||
static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
|
||||
|
@ -453,8 +452,8 @@ static void handle_global(void *data, struct wl_registry *registry,
|
|||
&wl_seat_interface, 1);
|
||||
wl_seat_add_listener(seat, &seat_listener, NULL);
|
||||
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
||||
layer_shell = wl_registry_bind(
|
||||
registry, name, &zwlr_layer_shell_v1_interface, 1);
|
||||
layer_shell = wl_registry_bind(registry, name,
|
||||
&zwlr_layer_shell_v1_interface, version < 4 ? version : 4);
|
||||
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
||||
xdg_wm_base = wl_registry_bind(
|
||||
registry, name, &xdg_wm_base_interface, 1);
|
||||
|
@ -472,13 +471,12 @@ static const struct wl_registry_listener registry_listener = {
|
|||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
wlr_log_init(WLR_DEBUG, NULL);
|
||||
char *namespace = "wlroots";
|
||||
int exclusive_zone = 0;
|
||||
int32_t margin_right = 0, margin_bottom = 0, margin_left = 0;
|
||||
bool found;
|
||||
int c;
|
||||
while ((c = getopt(argc, argv, "knw:h:o:l:a:x:m:t:")) != -1) {
|
||||
while ((c = getopt(argc, argv, "k:nw:h:o:l:a:x:m:t:")) != -1) {
|
||||
switch (c) {
|
||||
case 'o':
|
||||
output = atoi(optarg);
|
||||
|
@ -558,9 +556,29 @@ int main(int argc, char **argv) {
|
|||
case 'n':
|
||||
animate = true;
|
||||
break;
|
||||
case 'k':
|
||||
keyboard_interactive = true;
|
||||
case 'k': {
|
||||
const struct {
|
||||
const char *name;
|
||||
enum zwlr_layer_surface_v1_keyboard_interactivity value;
|
||||
} kb_int[] = {
|
||||
{ "none", ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE },
|
||||
{ "exclusive", ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE },
|
||||
{ "on_demand", ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND }
|
||||
};
|
||||
found = false;
|
||||
for (size_t i = 0; i < sizeof(kb_int) / sizeof(kb_int[0]); ++i) {
|
||||
if (strcmp(optarg, kb_int[i].name) == 0) {
|
||||
keyboard_interactive = kb_int[i].value;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
fprintf(stderr, "invalid keyboard interactivity setting %s\n", optarg);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -610,9 +628,7 @@ int main(int argc, char **argv) {
|
|||
cursor_surface = wl_compositor_create_surface(compositor);
|
||||
assert(cursor_surface);
|
||||
|
||||
EGLint attribs[] = { EGL_ALPHA_SIZE, 8, EGL_NONE };
|
||||
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display,
|
||||
attribs, WL_SHM_FORMAT_ARGB8888);
|
||||
egl_init(display);
|
||||
|
||||
wl_surface = wl_compositor_create_surface(compositor);
|
||||
assert(wl_surface);
|
||||
|
@ -634,8 +650,9 @@ int main(int argc, char **argv) {
|
|||
|
||||
egl_window = wl_egl_window_create(wl_surface, width, height);
|
||||
assert(egl_window);
|
||||
egl_surface = wlr_egl_create_surface(&egl, egl_window);
|
||||
assert(egl_surface);
|
||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
||||
egl_display, egl_config, egl_window, NULL);
|
||||
assert(egl_surface != EGL_NO_SURFACE);
|
||||
|
||||
wl_display_roundtrip(display);
|
||||
draw();
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
threads = dependency('threads')
|
||||
wayland_egl = dependency('wayland-egl')
|
||||
wayland_cursor = dependency('wayland-cursor')
|
||||
wayland_client = dependency('wayland-client')
|
||||
libpng = dependency('libpng', required: false, disabler: true)
|
||||
egl = dependency('egl', required: false, disabler: true)
|
||||
glesv2 = dependency('glesv2', required: false, disabler: true)
|
||||
# These versions correspond to ffmpeg 4.0
|
||||
libavutil = dependency('libavutil', version: '>=56.14.100', required: false, disabler: true)
|
||||
libavcodec = dependency('libavcodec', version: '>=58.18.100', required: false, disabler: true)
|
||||
libavformat = dependency('libavformat', version: '>=58.12.100', required: false, disabler: true)
|
||||
# Only needed for drm_fourcc.h
|
||||
libdrm = dependency('libdrm').partial_dependency(compile_args: true, includes: true)
|
||||
|
||||
# epoll is a separate library in FreeBSD
|
||||
if host_machine.system() == 'freebsd'
|
||||
|
@ -13,8 +19,7 @@ else
|
|||
libepoll = dependency('', required: false)
|
||||
endif
|
||||
|
||||
# Check if libavutil is found because of https://github.com/mesonbuild/meson/issues/6010
|
||||
if libavutil.found() and not cc.has_header('libavutil/hwcontext_drm.h', dependencies: libavutil)
|
||||
if not cc.has_header('libavutil/hwcontext_drm.h', dependencies: libavutil)
|
||||
libavutil = disabler()
|
||||
endif
|
||||
|
||||
|
@ -44,6 +49,13 @@ compositors = {
|
|||
'src': 'fullscreen-shell.c',
|
||||
'proto': ['fullscreen-shell-unstable-v1'],
|
||||
},
|
||||
'quads': {
|
||||
'src': 'quads.c',
|
||||
},
|
||||
'scene-graph': {
|
||||
'src': 'scene-graph.c',
|
||||
'proto': ['xdg-shell'],
|
||||
},
|
||||
}
|
||||
|
||||
clients = {
|
||||
|
@ -53,32 +65,32 @@ clients = {
|
|||
'proto': ['kde-idle'],
|
||||
},
|
||||
'idle-inhibit': {
|
||||
'src': 'idle-inhibit.c',
|
||||
'dep': wlroots,
|
||||
'src': ['idle-inhibit.c', 'egl_common.c'],
|
||||
'dep': [wayland_egl, egl, glesv2],
|
||||
'proto': [
|
||||
'idle-inhibit-unstable-v1',
|
||||
'xdg-shell',
|
||||
],
|
||||
},
|
||||
'keyboard-shortcuts-inhibit': {
|
||||
'src': 'keyboard-shortcuts-inhibit.c',
|
||||
'dep': [wayland_cursor, wlroots],
|
||||
'src': ['keyboard-shortcuts-inhibit.c', 'egl_common.c'],
|
||||
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
|
||||
'proto': [
|
||||
'keyboard-shortcuts-inhibit-unstable-v1',
|
||||
'xdg-shell',
|
||||
],
|
||||
},
|
||||
'layer-shell': {
|
||||
'src': 'layer-shell.c',
|
||||
'dep': [wayland_cursor, wlroots],
|
||||
'src': ['layer-shell.c', 'egl_common.c'],
|
||||
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
|
||||
'proto': [
|
||||
'wlr-layer-shell-unstable-v1',
|
||||
'xdg-shell',
|
||||
],
|
||||
},
|
||||
'input-inhibitor': {
|
||||
'src': 'input-inhibitor.c',
|
||||
'dep': [wayland_cursor, wlroots],
|
||||
'src': ['input-inhibitor.c', 'egl_common.c'],
|
||||
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
|
||||
'proto': [
|
||||
'wlr-input-inhibitor-unstable-v1',
|
||||
'xdg-shell',
|
||||
|
@ -91,20 +103,20 @@ clients = {
|
|||
},
|
||||
'output-power-management': {
|
||||
'src': 'output-power-management.c',
|
||||
'dep': [wayland_client, wlroots],
|
||||
'dep': [wayland_client],
|
||||
'proto': ['wlr-output-power-management-unstable-v1'],
|
||||
},
|
||||
'pointer-constraints': {
|
||||
'src': 'pointer-constraints.c',
|
||||
'dep': wlroots,
|
||||
'src': ['pointer-constraints.c', 'egl_common.c'],
|
||||
'dep': [wayland_egl, egl, glesv2],
|
||||
'proto': [
|
||||
'pointer-constraints-unstable-v1',
|
||||
'xdg-shell',
|
||||
],
|
||||
},
|
||||
'relative-pointer': {
|
||||
'src': 'relative-pointer-unstable-v1.c',
|
||||
'dep': wlroots,
|
||||
'src': ['relative-pointer-unstable-v1.c', 'egl_common.c'],
|
||||
'dep': [wayland_egl, egl, glesv2],
|
||||
'proto': [
|
||||
'pointer-constraints-unstable-v1',
|
||||
'relative-pointer-unstable-v1',
|
||||
|
@ -117,7 +129,7 @@ clients = {
|
|||
libavcodec,
|
||||
libavformat,
|
||||
libavutil,
|
||||
drm.partial_dependency(compile_args: true), # <drm_fourcc.h>
|
||||
drm,
|
||||
threads,
|
||||
],
|
||||
'proto': ['wlr-export-dmabuf-unstable-v1'],
|
||||
|
@ -136,8 +148,8 @@ clients = {
|
|||
],
|
||||
},
|
||||
'toplevel-decoration': {
|
||||
'src': 'toplevel-decoration.c',
|
||||
'dep': wlroots,
|
||||
'src': ['toplevel-decoration.c', 'egl_common.c'],
|
||||
'dep': [wayland_egl, egl, glesv2],
|
||||
'proto': [
|
||||
'xdg-decoration-unstable-v1',
|
||||
'xdg-shell',
|
||||
|
@ -145,7 +157,7 @@ clients = {
|
|||
},
|
||||
'input-method': {
|
||||
'src': 'input-method.c',
|
||||
'dep': libepoll,
|
||||
'dep': [wayland_egl, libepoll],
|
||||
'proto': [
|
||||
'input-method-unstable-v2',
|
||||
'text-input-unstable-v3',
|
||||
|
@ -153,8 +165,8 @@ clients = {
|
|||
],
|
||||
},
|
||||
'text-input': {
|
||||
'src': 'text-input.c',
|
||||
'dep': [wayland_cursor, wlroots],
|
||||
'src': ['text-input.c', 'egl_common.c'],
|
||||
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
|
||||
'proto': [
|
||||
'text-input-unstable-v3',
|
||||
'xdg-shell',
|
||||
|
@ -162,12 +174,10 @@ clients = {
|
|||
},
|
||||
'foreign-toplevel': {
|
||||
'src': 'foreign-toplevel.c',
|
||||
'dep': [wlroots],
|
||||
'proto': ['wlr-foreign-toplevel-management-unstable-v1'],
|
||||
},
|
||||
'virtual-pointer': {
|
||||
'src': 'virtual-pointer.c',
|
||||
'dep': wlroots,
|
||||
'proto': ['wlr-virtual-pointer-unstable-v1'],
|
||||
},
|
||||
'input-method-keyboard-grab': {
|
||||
|
@ -188,7 +198,7 @@ foreach name, info : compositors
|
|||
executable(
|
||||
name,
|
||||
[info.get('src'), extra_src],
|
||||
dependencies: wlroots,
|
||||
dependencies: [wlroots, libdrm],
|
||||
include_directories: [wlr_inc, proto_inc],
|
||||
build_by_default: get_option('examples'),
|
||||
)
|
||||
|
@ -204,7 +214,7 @@ foreach name, info : clients
|
|||
executable(
|
||||
name,
|
||||
[info.get('src'), extra_src],
|
||||
dependencies: [wayland_client, info.get('dep')],
|
||||
dependencies: [wayland_client, info.get('dep', [])],
|
||||
build_by_default: get_option('examples'),
|
||||
)
|
||||
endforeach
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#define _POSIX_C_SOURCE 200112L
|
||||
#include <assert.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -10,12 +9,13 @@
|
|||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend.h>
|
||||
#include <wlr/backend/session.h>
|
||||
#include <wlr/render/allocator.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_cursor.h>
|
||||
#include <wlr/types/wlr_keyboard.h>
|
||||
#include <wlr/types/wlr_list.h>
|
||||
#include <wlr/types/wlr_matrix.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_pointer.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <wlr/xcursor.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
@ -23,6 +23,8 @@
|
|||
struct sample_state {
|
||||
struct wl_display *display;
|
||||
struct wlr_xcursor *xcursor;
|
||||
struct wlr_renderer *renderer;
|
||||
struct wlr_allocator *allocator;
|
||||
float default_color[4];
|
||||
float clear_color[4];
|
||||
struct wlr_output_layout *layout;
|
||||
|
@ -70,7 +72,7 @@ struct sample_keyboard {
|
|||
static void configure_cursor(struct wlr_cursor *cursor, struct wlr_input_device *device,
|
||||
struct sample_state *sample) {
|
||||
struct sample_output *output;
|
||||
wlr_log(WLR_ERROR, "Configuring cursor %p for device %p", cursor, device);
|
||||
wlr_log(WLR_INFO, "Configuring cursor %p for device %p", cursor, device);
|
||||
|
||||
// reset mappings
|
||||
wlr_cursor_map_to_output(cursor, NULL);
|
||||
|
@ -91,14 +93,16 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
|||
struct sample_output *output = wl_container_of(listener, output, frame);
|
||||
struct sample_state *sample = output->sample;
|
||||
struct wlr_output *wlr_output = output->output;
|
||||
struct wlr_renderer *renderer = sample->renderer;
|
||||
|
||||
wlr_output_attach_render(wlr_output, NULL);
|
||||
|
||||
glClearColor(sample->clear_color[0], sample->clear_color[1],
|
||||
sample->clear_color[2], sample->clear_color[3]);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
|
||||
|
||||
wlr_renderer_clear(renderer, sample->clear_color);
|
||||
|
||||
wlr_output_render_software_cursors(wlr_output, NULL);
|
||||
wlr_renderer_end(renderer);
|
||||
wlr_output_commit(wlr_output);
|
||||
}
|
||||
|
||||
|
@ -143,11 +147,10 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
|
|||
static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||
struct wlr_output *output = data;
|
||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
||||
|
||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
||||
|
||||
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
||||
if (!wl_list_empty(&output->modes)) {
|
||||
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
|
||||
wlr_output_set_mode(output, mode);
|
||||
}
|
||||
sample_output->output = output;
|
||||
sample_output->sample = sample;
|
||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||
|
@ -157,7 +160,6 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
|
|||
|
||||
wlr_output_layout_add_auto(sample->layout, output);
|
||||
|
||||
|
||||
struct sample_cursor *cursor;
|
||||
wl_list_for_each(cursor, &sample->cursors, link) {
|
||||
configure_cursor(cursor->cursor, cursor->device, sample);
|
||||
|
@ -171,6 +173,11 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
|
|||
}
|
||||
wl_list_insert(&sample->outputs, &sample_output->link);
|
||||
|
||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
||||
if (mode != NULL) {
|
||||
wlr_output_set_mode(output, mode);
|
||||
}
|
||||
|
||||
wlr_output_commit(output);
|
||||
}
|
||||
|
||||
|
@ -209,18 +216,12 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
|
|||
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||
keyboard->key.notify = keyboard_key_notify;
|
||||
struct xkb_rule_names rules = { 0 };
|
||||
rules.rules = getenv("XKB_DEFAULT_RULES");
|
||||
rules.model = getenv("XKB_DEFAULT_MODEL");
|
||||
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
|
||||
rules.variant = getenv("XKB_DEFAULT_VARIANT");
|
||||
rules.options = getenv("XKB_DEFAULT_OPTIONS");
|
||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!context) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||
exit(1);
|
||||
}
|
||||
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
|
||||
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!keymap) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||
|
@ -270,10 +271,14 @@ int main(int argc, char *argv[]) {
|
|||
.clear_color = { 0.25f, 0.25f, 0.25f, 1 },
|
||||
.display = display,
|
||||
};
|
||||
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
||||
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
||||
if (!wlr) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
state.renderer = wlr_renderer_autocreate(wlr);
|
||||
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
||||
|
||||
wl_list_init(&state.cursors);
|
||||
wl_list_init(&state.pointers);
|
||||
wl_list_init(&state.outputs);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#define _POSIX_C_SOURCE 200112L
|
||||
#include <GLES2/gl2.h>
|
||||
#include <drm_fourcc.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
@ -11,12 +11,14 @@
|
|||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend.h>
|
||||
#include <wlr/backend/session.h>
|
||||
#include <wlr/render/allocator.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_keyboard.h>
|
||||
#include <wlr/types/wlr_matrix.h>
|
||||
#include <wlr/types/wlr_input_device.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/util/box.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include "cat.h"
|
||||
|
@ -26,6 +28,7 @@ struct sample_state {
|
|||
struct wl_listener new_output;
|
||||
struct wl_listener new_input;
|
||||
struct wlr_renderer *renderer;
|
||||
struct wlr_allocator *allocator;
|
||||
struct wlr_texture *cat_texture;
|
||||
struct wlr_output_layout *layout;
|
||||
float x_offs, y_offs;
|
||||
|
@ -157,11 +160,10 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
|
|||
static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||
struct wlr_output *output = data;
|
||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
||||
|
||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
||||
|
||||
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
||||
if (!wl_list_empty(&output->modes)) {
|
||||
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
|
||||
wlr_output_set_mode(output, mode);
|
||||
}
|
||||
wlr_output_layout_add_auto(sample->layout, output);
|
||||
sample_output->output = output;
|
||||
sample_output->sample = sample;
|
||||
|
@ -170,6 +172,11 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
|
|||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
||||
sample_output->destroy.notify = output_remove_notify;
|
||||
|
||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
||||
if (mode != NULL) {
|
||||
wlr_output_set_mode(output, mode);
|
||||
}
|
||||
|
||||
wlr_output_commit(output);
|
||||
}
|
||||
|
||||
|
@ -190,7 +197,7 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
|||
// and make this change in pixels/sec^2
|
||||
// Also, key repeat
|
||||
int delta = 75;
|
||||
if (event->state == WLR_KEY_PRESSED) {
|
||||
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||
switch (sym) {
|
||||
case XKB_KEY_Left:
|
||||
update_velocities(sample, -delta, 0);
|
||||
|
@ -228,18 +235,12 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
|
|||
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||
keyboard->key.notify = keyboard_key_notify;
|
||||
struct xkb_rule_names rules = { 0 };
|
||||
rules.rules = getenv("XKB_DEFAULT_RULES");
|
||||
rules.model = getenv("XKB_DEFAULT_MODEL");
|
||||
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
|
||||
rules.variant = getenv("XKB_DEFAULT_VARIANT");
|
||||
rules.options = getenv("XKB_DEFAULT_OPTIONS");
|
||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!context) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||
exit(1);
|
||||
}
|
||||
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
|
||||
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!keymap) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||
|
@ -267,7 +268,7 @@ int main(int argc, char *argv[]) {
|
|||
state.layout = wlr_output_layout_create();
|
||||
clock_gettime(CLOCK_MONOTONIC, &state.ts_last);
|
||||
|
||||
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
||||
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
||||
if (!wlr) {
|
||||
exit(1);
|
||||
}
|
||||
|
@ -277,11 +278,13 @@ int main(int argc, char *argv[]) {
|
|||
wl_signal_add(&wlr->events.new_input, &state.new_input);
|
||||
state.new_input.notify = new_input_notify;
|
||||
|
||||
state.renderer = wlr_backend_get_renderer(wlr);
|
||||
state.renderer = wlr_renderer_autocreate(wlr);
|
||||
state.cat_texture = wlr_texture_from_pixels(state.renderer,
|
||||
WL_SHM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
|
||||
DRM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
|
||||
cat_tex.pixel_data);
|
||||
|
||||
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
||||
|
||||
if (!wlr_backend_start(wlr)) {
|
||||
wlr_log(WLR_ERROR, "Failed to start backend");
|
||||
wlr_backend_destroy(wlr);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <string.h>
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-egl.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include "egl_common.h"
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
#include "pointer-constraints-unstable-v1-client-protocol.h"
|
||||
|
||||
|
@ -16,7 +16,6 @@ static struct wl_seat *seat = NULL;
|
|||
static struct xdg_wm_base *wm_base = NULL;
|
||||
static struct zwp_pointer_constraints_v1 *pointer_constraints = NULL;
|
||||
|
||||
struct wlr_egl egl;
|
||||
struct wl_egl_window *egl_window;
|
||||
struct wlr_egl_surface *egl_surface;
|
||||
struct zwp_locked_pointer_v1* locked_pointer;
|
||||
|
@ -32,7 +31,7 @@ enum {
|
|||
struct wl_region *regions[3];
|
||||
|
||||
static void draw(void) {
|
||||
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
|
||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
||||
|
||||
float color[] = {1.0, 1.0, 0.0, 1.0};
|
||||
|
||||
|
@ -40,7 +39,7 @@ static void draw(void) {
|
|||
glClearColor(color[0], color[1], color[2], 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
eglSwapBuffers(egl.display, egl_surface);
|
||||
eglSwapBuffers(egl_display, egl_surface);
|
||||
}
|
||||
|
||||
static void pointer_handle_button(void *data, struct wl_pointer *pointer,
|
||||
|
@ -211,8 +210,7 @@ int main(int argc, char **argv) {
|
|||
wl_region_add(joint_region, 256, 256, 256, 256);
|
||||
regions[REGION_TYPE_JOINT] = joint_region;
|
||||
|
||||
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
||||
WL_SHM_FORMAT_ARGB8888);
|
||||
egl_init(display);
|
||||
|
||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||
struct xdg_surface *xdg_surface =
|
||||
|
@ -241,7 +239,8 @@ int main(int argc, char **argv) {
|
|||
wl_surface_commit(surface);
|
||||
|
||||
egl_window = wl_egl_window_create(surface, width, height);
|
||||
egl_surface = wlr_egl_create_surface(&egl, egl_window);
|
||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
||||
egl_display, egl_config, egl_window, NULL);
|
||||
|
||||
wl_display_roundtrip(display);
|
||||
|
||||
|
|
|
@ -9,12 +9,15 @@
|
|||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend.h>
|
||||
#include <wlr/backend/session.h>
|
||||
#include <wlr/render/allocator.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_cursor.h>
|
||||
#include <wlr/types/wlr_keyboard.h>
|
||||
#include <wlr/types/wlr_list.h>
|
||||
#include <wlr/types/wlr_matrix.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_pointer.h>
|
||||
#include <wlr/types/wlr_tablet_tool.h>
|
||||
#include <wlr/types/wlr_touch.h>
|
||||
#include <wlr/types/wlr_xcursor_manager.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
@ -22,6 +25,8 @@
|
|||
struct sample_state {
|
||||
struct wl_display *display;
|
||||
struct compositor_state *compositor;
|
||||
struct wlr_renderer *renderer;
|
||||
struct wlr_allocator *allocator;
|
||||
struct wlr_xcursor_manager *xcursor_manager;
|
||||
struct wlr_cursor *cursor;
|
||||
double cur_x, cur_y;
|
||||
|
@ -93,7 +98,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
|||
struct sample_output *sample_output = wl_container_of(listener, sample_output, frame);
|
||||
struct sample_state *state = sample_output->state;
|
||||
struct wlr_output *wlr_output = sample_output->output;
|
||||
struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
|
||||
struct wlr_renderer *renderer = state->renderer;
|
||||
assert(renderer);
|
||||
|
||||
wlr_output_attach_render(wlr_output, NULL);
|
||||
|
@ -248,11 +253,10 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
|
|||
static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||
struct wlr_output *output = data;
|
||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
||||
|
||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
||||
|
||||
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
||||
if (!wl_list_empty(&output->modes)) {
|
||||
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
|
||||
wlr_output_set_mode(output, mode);
|
||||
}
|
||||
sample_output->output = output;
|
||||
sample_output->state = sample;
|
||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||
|
@ -265,6 +269,11 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
|
|||
wlr_xcursor_manager_set_cursor_image(sample->xcursor_manager, "left_ptr",
|
||||
sample->cursor);
|
||||
|
||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
||||
if (mode != NULL) {
|
||||
wlr_output_set_mode(output, mode);
|
||||
}
|
||||
|
||||
wlr_output_commit(output);
|
||||
}
|
||||
|
||||
|
@ -294,18 +303,12 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
|
|||
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||
keyboard->key.notify = keyboard_key_notify;
|
||||
struct xkb_rule_names rules = { 0 };
|
||||
rules.rules = getenv("XKB_DEFAULT_RULES");
|
||||
rules.model = getenv("XKB_DEFAULT_MODEL");
|
||||
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
|
||||
rules.variant = getenv("XKB_DEFAULT_VARIANT");
|
||||
rules.options = getenv("XKB_DEFAULT_OPTIONS");
|
||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!context) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||
exit(1);
|
||||
}
|
||||
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
|
||||
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!keymap) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||
|
@ -330,10 +333,14 @@ int main(int argc, char *argv[]) {
|
|||
.display = display
|
||||
};
|
||||
|
||||
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
||||
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
||||
if (!wlr) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
state.renderer = wlr_renderer_autocreate(wlr);
|
||||
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
||||
|
||||
state.cursor = wlr_cursor_create();
|
||||
state.layout = wlr_output_layout_create();
|
||||
wlr_cursor_attach_output_layout(state.cursor, state.layout);
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
#define _XOPEN_SOURCE 600
|
||||
#include <getopt.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend.h>
|
||||
#include <wlr/render/allocator.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_keyboard.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_input_device.h>
|
||||
#include <wlr/types/wlr_matrix.h>
|
||||
#include <wlr/util/box.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
struct sample_state {
|
||||
struct wl_display *display;
|
||||
struct wl_listener new_output;
|
||||
struct wl_listener new_input;
|
||||
struct timespec last_frame;
|
||||
struct wlr_renderer *renderer;
|
||||
struct wlr_allocator *allocator;
|
||||
struct wl_list outputs;
|
||||
};
|
||||
|
||||
struct sample_output {
|
||||
struct sample_state *sample;
|
||||
struct wlr_output *output;
|
||||
struct wl_listener frame;
|
||||
struct wl_listener destroy;
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
struct sample_keyboard {
|
||||
struct sample_state *sample;
|
||||
struct wlr_input_device *device;
|
||||
struct wl_listener key;
|
||||
struct wl_listener destroy;
|
||||
};
|
||||
|
||||
static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, frame);
|
||||
struct sample_state *sample = sample_output->sample;
|
||||
struct wlr_output *wlr_output = sample_output->output;
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
int32_t width, height;
|
||||
wlr_output_effective_resolution(wlr_output, &width, &height);
|
||||
|
||||
wlr_output_attach_render(wlr_output, NULL);
|
||||
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
|
||||
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
|
||||
|
||||
static float rotation = 0.f;
|
||||
for (int y = -128; y < height; y += 128) {
|
||||
for (int x = -128; x < width; x += 128) {
|
||||
struct wlr_box box = {
|
||||
.x = x,
|
||||
.y = y,
|
||||
.width = 128,
|
||||
.height = 128,
|
||||
};
|
||||
|
||||
float color[] = {
|
||||
255.f / x,
|
||||
255.f / y,
|
||||
255.f / (x + y),
|
||||
1.f
|
||||
};
|
||||
|
||||
float matrix[9];
|
||||
wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL,
|
||||
rotation, wlr_output->transform_matrix);
|
||||
|
||||
wlr_render_quad_with_matrix(sample->renderer, color, matrix);
|
||||
}
|
||||
}
|
||||
|
||||
wlr_renderer_end(sample->renderer);
|
||||
wlr_output_commit(wlr_output);
|
||||
|
||||
//TODO rotate with a delta time
|
||||
rotation += 0.05;
|
||||
if (rotation > 2 * M_PI) {
|
||||
rotation = 0.f;
|
||||
}
|
||||
|
||||
sample->last_frame = now;
|
||||
}
|
||||
|
||||
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
||||
wl_list_remove(&sample_output->frame.link);
|
||||
wl_list_remove(&sample_output->destroy.link);
|
||||
free(sample_output);
|
||||
}
|
||||
|
||||
static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||
struct wlr_output *output = data;
|
||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
||||
|
||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
||||
|
||||
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
||||
|
||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
||||
if (mode != NULL) {
|
||||
wlr_output_set_mode(output, mode);
|
||||
}
|
||||
|
||||
sample_output->output = output;
|
||||
sample_output->sample = sample;
|
||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||
sample_output->frame.notify = output_frame_notify;
|
||||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
||||
sample_output->destroy.notify = output_remove_notify;
|
||||
wl_list_insert(&sample->outputs, &sample_output->link);
|
||||
|
||||
wlr_output_commit(output);
|
||||
}
|
||||
|
||||
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
||||
struct sample_state *sample = keyboard->sample;
|
||||
struct wlr_event_keyboard_key *event = data;
|
||||
uint32_t keycode = event->keycode + 8;
|
||||
const xkb_keysym_t *syms;
|
||||
int nsyms = xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state,
|
||||
keycode, &syms);
|
||||
for (int i = 0; i < nsyms; i++) {
|
||||
xkb_keysym_t sym = syms[i];
|
||||
if (sym == XKB_KEY_Escape) {
|
||||
wl_display_terminate(sample->display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
||||
wl_list_remove(&keyboard->destroy.link);
|
||||
wl_list_remove(&keyboard->key.link);
|
||||
free(keyboard);
|
||||
}
|
||||
|
||||
static void new_input_notify(struct wl_listener *listener, void *data) {
|
||||
struct wlr_input_device *device = data;
|
||||
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
||||
switch (device->type) {
|
||||
case WLR_INPUT_DEVICE_KEYBOARD:;
|
||||
struct sample_keyboard *keyboard = calloc(1, sizeof(struct sample_keyboard));
|
||||
keyboard->device = device;
|
||||
keyboard->sample = sample;
|
||||
wl_signal_add(&device->events.destroy, &keyboard->destroy);
|
||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||
keyboard->key.notify = keyboard_key_notify;
|
||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!context) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||
exit(1);
|
||||
}
|
||||
struct xkb_keymap *keymap = xkb_map_new_from_names(context, NULL,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!keymap) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||
exit(1);
|
||||
}
|
||||
wlr_keyboard_set_keymap(device->keyboard, keymap);
|
||||
xkb_keymap_unref(keymap);
|
||||
xkb_context_unref(context);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
wlr_log_init(WLR_DEBUG, NULL);
|
||||
struct wl_display *display = wl_display_create();
|
||||
struct sample_state state = {
|
||||
.display = display,
|
||||
};
|
||||
wl_list_init(&state.outputs);
|
||||
|
||||
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
||||
if (!wlr) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
wl_signal_add(&wlr->events.new_output, &state.new_output);
|
||||
state.new_output.notify = new_output_notify;
|
||||
wl_signal_add(&wlr->events.new_input, &state.new_input);
|
||||
state.new_input.notify = new_input_notify;
|
||||
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
||||
|
||||
state.renderer = wlr_renderer_autocreate(wlr);
|
||||
if (!state.renderer) {
|
||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||
wlr_backend_destroy(wlr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
||||
|
||||
if (!wlr_backend_start(wlr)) {
|
||||
wlr_log(WLR_ERROR, "Failed to start backend");
|
||||
wlr_backend_destroy(wlr);
|
||||
exit(1);
|
||||
}
|
||||
wl_display_run(display);
|
||||
|
||||
wl_display_destroy(display);
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
#include <GLES2/gl2.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <wayland-egl.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include "egl_common.h"
|
||||
#include "pointer-constraints-unstable-v1-client-protocol.h"
|
||||
#include "relative-pointer-unstable-v1-client-protocol.h"
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
|
@ -17,7 +17,6 @@
|
|||
*/
|
||||
|
||||
struct egl_info {
|
||||
struct wlr_egl *egl;
|
||||
struct wl_egl_window *egl_window;
|
||||
struct wlr_egl_surface *egl_surface;
|
||||
uint32_t width;
|
||||
|
@ -58,8 +57,8 @@ static struct wl_callback_listener surface_callback_listener = {
|
|||
};
|
||||
|
||||
static void draw_init(struct egl_info *e) {
|
||||
eglMakeCurrent(e->egl->display, e->egl_surface,
|
||||
e->egl_surface, e->egl->context);
|
||||
eglMakeCurrent(egl_display, e->egl_surface,
|
||||
e->egl_surface, egl_context);
|
||||
glViewport(0, 0, e->width, e->height);
|
||||
}
|
||||
|
||||
|
@ -88,7 +87,7 @@ static void draw_end(struct egl_info *e) {
|
|||
e->frame_callback = wl_surface_frame(e->surface);
|
||||
wl_callback_add_listener(e->frame_callback, &surface_callback_listener, e);
|
||||
|
||||
eglSwapBuffers(e->egl->display, e->egl_surface);
|
||||
eglSwapBuffers(egl_display, e->egl_surface);
|
||||
}
|
||||
|
||||
|
||||
|
@ -170,8 +169,7 @@ static void xdg_toplevel_handle_configure(void *data,
|
|||
|
||||
static void xdg_toplevel_handle_close(void *data,
|
||||
struct xdg_toplevel *xdg_toplevel) {
|
||||
struct egl_info *e = data;
|
||||
wlr_egl_finish(e->egl);
|
||||
egl_finish();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
@ -440,11 +438,9 @@ int main(int argc, char **argv) {
|
|||
/* Initialize EGL context */
|
||||
|
||||
struct egl_info *e = calloc(1, sizeof(struct egl_info));
|
||||
e->egl = calloc(1, sizeof(struct wlr_egl));
|
||||
e->width = e->height = 512;
|
||||
|
||||
wlr_egl_init(e->egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
||||
WL_SHM_FORMAT_ARGB8888);
|
||||
egl_init(display);
|
||||
|
||||
/* Create the surface and xdg_toplevels, and set listeners */
|
||||
|
||||
|
@ -461,7 +457,8 @@ int main(int argc, char **argv) {
|
|||
wl_surface_commit(surface);
|
||||
|
||||
e->egl_window = wl_egl_window_create(surface, e->width, e->height);
|
||||
e->egl_surface = wlr_egl_create_surface(e->egl, e->egl_window);
|
||||
e->egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
||||
egl_display, egl_config, e->egl_window, NULL);
|
||||
e->surface = surface;
|
||||
|
||||
wl_display_roundtrip(display);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#define _POSIX_C_SOURCE 200112L
|
||||
#include <GLES2/gl2.h>
|
||||
#include <drm_fourcc.h>
|
||||
#include <getopt.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
@ -10,11 +10,10 @@
|
|||
#include <unistd.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend.h>
|
||||
#include <wlr/backend/session.h>
|
||||
#include <wlr/render/allocator.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_keyboard.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_input_device.h>
|
||||
#include <wlr/types/wlr_matrix.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
@ -27,6 +26,7 @@ struct sample_state {
|
|||
struct wl_listener new_input;
|
||||
struct timespec last_frame;
|
||||
struct wlr_renderer *renderer;
|
||||
struct wlr_allocator *allocator;
|
||||
struct wlr_texture *cat_texture;
|
||||
struct wl_list outputs;
|
||||
enum wl_output_transform transform;
|
||||
|
@ -107,11 +107,10 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
|
|||
static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||
struct wlr_output *output = data;
|
||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
||||
|
||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
||||
|
||||
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
||||
if (!wl_list_empty(&output->modes)) {
|
||||
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
|
||||
wlr_output_set_mode(output, mode);
|
||||
}
|
||||
sample_output->x_offs = sample_output->y_offs = 0;
|
||||
sample_output->x_vel = sample_output->y_vel = 128;
|
||||
|
||||
|
@ -124,6 +123,11 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
|
|||
sample_output->destroy.notify = output_remove_notify;
|
||||
wl_list_insert(&sample->outputs, &sample_output->link);
|
||||
|
||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
||||
if (mode != NULL) {
|
||||
wlr_output_set_mode(output, mode);
|
||||
}
|
||||
|
||||
wlr_output_commit(output);
|
||||
}
|
||||
|
||||
|
@ -140,7 +144,7 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
|||
if (sym == XKB_KEY_Escape) {
|
||||
wl_display_terminate(sample->display);
|
||||
}
|
||||
if (event->state == WLR_KEY_PRESSED) {
|
||||
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||
switch (sym) {
|
||||
case XKB_KEY_Left:
|
||||
update_velocities(sample, -16, 0);
|
||||
|
@ -178,18 +182,12 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
|
|||
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||
keyboard->key.notify = keyboard_key_notify;
|
||||
struct xkb_rule_names rules = { 0 };
|
||||
rules.rules = getenv("XKB_DEFAULT_RULES");
|
||||
rules.model = getenv("XKB_DEFAULT_MODEL");
|
||||
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
|
||||
rules.variant = getenv("XKB_DEFAULT_VARIANT");
|
||||
rules.options = getenv("XKB_DEFAULT_OPTIONS");
|
||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!context) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||
exit(1);
|
||||
}
|
||||
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
|
||||
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!keymap) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||
|
@ -230,7 +228,7 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
wlr_log_init(WLR_DEBUG, NULL);
|
||||
|
@ -241,7 +239,7 @@ int main(int argc, char *argv[]) {
|
|||
};
|
||||
wl_list_init(&state.outputs);
|
||||
|
||||
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
||||
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
||||
if (!wlr) {
|
||||
exit(1);
|
||||
}
|
||||
|
@ -252,20 +250,22 @@ int main(int argc, char *argv[]) {
|
|||
state.new_input.notify = new_input_notify;
|
||||
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
||||
|
||||
state.renderer = wlr_backend_get_renderer(wlr);
|
||||
state.renderer = wlr_renderer_autocreate(wlr);
|
||||
if (!state.renderer) {
|
||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||
wlr_backend_destroy(wlr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
state.cat_texture = wlr_texture_from_pixels(state.renderer,
|
||||
WL_SHM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
|
||||
DRM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
|
||||
cat_tex.pixel_data);
|
||||
if (!state.cat_texture) {
|
||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
||||
|
||||
if (!wlr_backend_start(wlr)) {
|
||||
wlr_log(WLR_ERROR, "Failed to start backend");
|
||||
wlr_backend_destroy(wlr);
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
#define _POSIX_C_SOURCE 200112L
|
||||
#include <assert.h>
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend.h>
|
||||
#include <wlr/render/allocator.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_compositor.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_scene.h>
|
||||
#include <wlr/types/wlr_surface.h>
|
||||
#include <wlr/types/wlr_xdg_shell.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
||||
/* Simple compositor making use of the scene-graph API. Input is unimplemented.
|
||||
*
|
||||
* New surfaces are stacked on top of the existing ones as they appear. */
|
||||
|
||||
static const int border_width = 3;
|
||||
|
||||
struct server {
|
||||
struct wl_display *display;
|
||||
struct wlr_backend *backend;
|
||||
struct wlr_renderer *renderer;
|
||||
struct wlr_allocator *allocator;
|
||||
struct wlr_scene *scene;
|
||||
|
||||
struct wl_list outputs;
|
||||
struct wl_list surfaces;
|
||||
|
||||
struct wl_listener new_output;
|
||||
struct wl_listener new_surface;
|
||||
};
|
||||
|
||||
struct surface {
|
||||
struct wlr_surface *wlr;
|
||||
struct wlr_scene_surface *scene_surface;
|
||||
struct wlr_scene_rect *border;
|
||||
struct wl_list link;
|
||||
|
||||
struct wl_listener commit;
|
||||
struct wl_listener destroy;
|
||||
};
|
||||
|
||||
struct output {
|
||||
struct wl_list link;
|
||||
struct server *server;
|
||||
struct wlr_output *wlr;
|
||||
struct wlr_scene_output *scene_output;
|
||||
|
||||
struct wl_listener frame;
|
||||
};
|
||||
|
||||
static void output_handle_frame(struct wl_listener *listener, void *data) {
|
||||
struct output *output = wl_container_of(listener, output, frame);
|
||||
|
||||
if (!wlr_scene_output_commit(output->scene_output)) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
struct surface *surface;
|
||||
wl_list_for_each(surface, &output->server->surfaces, link) {
|
||||
wlr_surface_send_frame_done(surface->wlr, &now);
|
||||
}
|
||||
}
|
||||
|
||||
static void server_handle_new_output(struct wl_listener *listener, void *data) {
|
||||
struct server *server = wl_container_of(listener, server, new_output);
|
||||
struct wlr_output *wlr_output = data;
|
||||
|
||||
wlr_output_init_render(wlr_output, server->allocator, server->renderer);
|
||||
|
||||
struct output *output =
|
||||
calloc(1, sizeof(struct output));
|
||||
output->wlr = wlr_output;
|
||||
output->server = server;
|
||||
output->frame.notify = output_handle_frame;
|
||||
wl_signal_add(&wlr_output->events.frame, &output->frame);
|
||||
wl_list_insert(&server->outputs, &output->link);
|
||||
|
||||
output->scene_output = wlr_scene_output_create(server->scene, wlr_output);
|
||||
|
||||
if (!wl_list_empty(&wlr_output->modes)) {
|
||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
|
||||
wlr_output_set_mode(wlr_output, mode);
|
||||
wlr_output_commit(wlr_output);
|
||||
}
|
||||
|
||||
wlr_output_create_global(wlr_output);
|
||||
}
|
||||
|
||||
static void surface_handle_commit(struct wl_listener *listener, void *data) {
|
||||
struct surface *surface = wl_container_of(listener, surface, commit);
|
||||
wlr_scene_rect_set_size(surface->border,
|
||||
surface->wlr->current.width + 2 * border_width,
|
||||
surface->wlr->current.height + 2 * border_width);
|
||||
}
|
||||
|
||||
static void surface_handle_destroy(struct wl_listener *listener, void *data) {
|
||||
struct surface *surface = wl_container_of(listener, surface, destroy);
|
||||
wlr_scene_node_destroy(&surface->scene_surface->node);
|
||||
wlr_scene_node_destroy(&surface->border->node);
|
||||
wl_list_remove(&surface->destroy.link);
|
||||
wl_list_remove(&surface->link);
|
||||
free(surface);
|
||||
}
|
||||
|
||||
static void server_handle_new_surface(struct wl_listener *listener,
|
||||
void *data) {
|
||||
struct server *server = wl_container_of(listener, server, new_surface);
|
||||
struct wlr_surface *wlr_surface = data;
|
||||
|
||||
int pos = 50 * wl_list_length(&server->surfaces);
|
||||
|
||||
struct surface *surface = calloc(1, sizeof(struct surface));
|
||||
surface->wlr = wlr_surface;
|
||||
surface->commit.notify = surface_handle_commit;
|
||||
wl_signal_add(&wlr_surface->events.commit, &surface->commit);
|
||||
surface->destroy.notify = surface_handle_destroy;
|
||||
wl_signal_add(&wlr_surface->events.destroy, &surface->destroy);
|
||||
|
||||
/* Border dimensions will be set in surface.commit handler */
|
||||
surface->border = wlr_scene_rect_create(&server->scene->node,
|
||||
0, 0, (float[4]){ 0.5f, 0.5f, 0.5f, 1 });
|
||||
wlr_scene_node_set_position(&surface->border->node, pos, pos);
|
||||
|
||||
surface->scene_surface =
|
||||
wlr_scene_surface_create(&server->scene->node, wlr_surface);
|
||||
wl_list_insert(server->surfaces.prev, &surface->link);
|
||||
|
||||
wlr_scene_node_set_position(&surface->scene_surface->node,
|
||||
pos + border_width, pos + border_width);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
wlr_log_init(WLR_DEBUG, NULL);
|
||||
|
||||
char *startup_cmd = NULL;
|
||||
|
||||
int c;
|
||||
while ((c = getopt(argc, argv, "s:")) != -1) {
|
||||
switch (c) {
|
||||
case 's':
|
||||
startup_cmd = optarg;
|
||||
break;
|
||||
default:
|
||||
printf("usage: %s [-s startup-command]\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
if (optind < argc) {
|
||||
printf("usage: %s [-s startup-command]\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
struct server server = {0};
|
||||
server.display = wl_display_create();
|
||||
server.backend = wlr_backend_autocreate(server.display);
|
||||
server.scene = wlr_scene_create();
|
||||
|
||||
server.renderer = wlr_renderer_autocreate(server.backend);
|
||||
wlr_renderer_init_wl_display(server.renderer, server.display);
|
||||
|
||||
server.allocator = wlr_allocator_autocreate(server.backend,
|
||||
server.renderer);
|
||||
|
||||
struct wlr_compositor *compositor =
|
||||
wlr_compositor_create(server.display, server.renderer);
|
||||
|
||||
wlr_xdg_shell_create(server.display);
|
||||
|
||||
wl_list_init(&server.outputs);
|
||||
wl_list_init(&server.surfaces);
|
||||
|
||||
server.new_output.notify = server_handle_new_output;
|
||||
wl_signal_add(&server.backend->events.new_output, &server.new_output);
|
||||
|
||||
server.new_surface.notify = server_handle_new_surface;
|
||||
wl_signal_add(&compositor->events.new_surface, &server.new_surface);
|
||||
|
||||
const char *socket = wl_display_add_socket_auto(server.display);
|
||||
if (!socket) {
|
||||
wl_display_destroy(server.display);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!wlr_backend_start(server.backend)) {
|
||||
wl_display_destroy(server.display);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
setenv("WAYLAND_DISPLAY", socket, true);
|
||||
if (startup_cmd != NULL) {
|
||||
if (fork() == 0) {
|
||||
execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL);
|
||||
}
|
||||
}
|
||||
|
||||
wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s",
|
||||
socket);
|
||||
wl_display_run(server.display);
|
||||
|
||||
wl_display_destroy_clients(server.display);
|
||||
wl_display_destroy(server.display);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -7,9 +7,11 @@
|
|||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend.h>
|
||||
#include <wlr/backend/session.h>
|
||||
#include <wlr/render/allocator.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_input_device.h>
|
||||
#include <wlr/types/wlr_keyboard.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
|
@ -17,6 +19,8 @@ struct sample_state {
|
|||
struct wl_display *display;
|
||||
struct wl_listener new_output;
|
||||
struct wl_listener new_input;
|
||||
struct wlr_renderer *renderer;
|
||||
struct wlr_allocator *allocator;
|
||||
struct timespec last_frame;
|
||||
float color[4];
|
||||
int dec;
|
||||
|
@ -60,8 +64,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
|||
|
||||
wlr_output_attach_render(wlr_output, NULL);
|
||||
|
||||
struct wlr_renderer *renderer =
|
||||
wlr_backend_get_renderer(wlr_output->backend);
|
||||
struct wlr_renderer *renderer = sample->renderer;
|
||||
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
|
||||
wlr_renderer_clear(renderer, sample->color);
|
||||
wlr_renderer_end(renderer);
|
||||
|
@ -83,13 +86,11 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
|
|||
struct wlr_output *output = data;
|
||||
struct sample_state *sample =
|
||||
wl_container_of(listener, sample, new_output);
|
||||
|
||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
||||
|
||||
struct sample_output *sample_output =
|
||||
calloc(1, sizeof(struct sample_output));
|
||||
|
||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
||||
if (mode != NULL) {
|
||||
wlr_output_set_mode(output, mode);
|
||||
}
|
||||
sample_output->output = output;
|
||||
sample_output->sample = sample;
|
||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||
|
@ -97,6 +98,11 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
|
|||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
||||
sample_output->destroy.notify = output_remove_notify;
|
||||
|
||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
||||
if (mode != NULL) {
|
||||
wlr_output_set_mode(output, mode);
|
||||
}
|
||||
|
||||
wlr_output_commit(sample_output->output);
|
||||
}
|
||||
|
||||
|
@ -137,18 +143,12 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
|
|||
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||
keyboard->key.notify = keyboard_key_notify;
|
||||
struct xkb_rule_names rules = { 0 };
|
||||
rules.rules = getenv("XKB_DEFAULT_RULES");
|
||||
rules.model = getenv("XKB_DEFAULT_MODEL");
|
||||
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
|
||||
rules.variant = getenv("XKB_DEFAULT_VARIANT");
|
||||
rules.options = getenv("XKB_DEFAULT_OPTIONS");
|
||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!context) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||
exit(1);
|
||||
}
|
||||
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
|
||||
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!keymap) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||
|
@ -172,10 +172,14 @@ int main(void) {
|
|||
.last_frame = { 0 },
|
||||
.display = display
|
||||
};
|
||||
struct wlr_backend *backend = wlr_backend_autocreate(display, NULL);
|
||||
struct wlr_backend *backend = wlr_backend_autocreate(display);
|
||||
if (!backend) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
state.renderer = wlr_renderer_autocreate(backend);
|
||||
state.allocator = wlr_allocator_autocreate(backend, state.renderer);
|
||||
|
||||
wl_signal_add(&backend->events.new_output, &state.new_output);
|
||||
state.new_output.notify = new_output_notify;
|
||||
wl_signal_add(&backend->events.new_input, &state.new_input);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#define _XOPEN_SOURCE 600
|
||||
#include <GLES2/gl2.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -9,19 +8,22 @@
|
|||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend.h>
|
||||
#include <wlr/backend/session.h>
|
||||
#include <wlr/render/allocator.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_box.h>
|
||||
#include <wlr/types/wlr_matrix.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_input_device.h>
|
||||
#include <wlr/types/wlr_keyboard.h>
|
||||
#include <wlr/types/wlr_tablet_pad.h>
|
||||
#include <wlr/types/wlr_tablet_tool.h>
|
||||
#include <wlr/util/box.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
struct sample_state {
|
||||
struct wl_display *display;
|
||||
struct wlr_renderer *renderer;
|
||||
struct wlr_allocator *allocator;
|
||||
bool proximity, tap, button;
|
||||
double distance;
|
||||
double pressure;
|
||||
|
@ -237,11 +239,10 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
|
|||
static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||
struct wlr_output *output = data;
|
||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
||||
|
||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
||||
|
||||
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
||||
if (!wl_list_empty(&output->modes)) {
|
||||
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
|
||||
wlr_output_set_mode(output, mode);
|
||||
}
|
||||
sample_output->output = output;
|
||||
sample_output->sample = sample;
|
||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||
|
@ -249,6 +250,11 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
|
|||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
||||
sample_output->destroy.notify = output_remove_notify;
|
||||
|
||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
||||
if (mode != NULL) {
|
||||
wlr_output_set_mode(output, mode);
|
||||
}
|
||||
|
||||
wlr_output_commit(output);
|
||||
}
|
||||
|
||||
|
@ -287,18 +293,12 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
|
|||
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||
keyboard->key.notify = keyboard_key_notify;
|
||||
struct xkb_rule_names rules = { 0 };
|
||||
rules.rules = getenv("XKB_DEFAULT_RULES");
|
||||
rules.model = getenv("XKB_DEFAULT_MODEL");
|
||||
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
|
||||
rules.variant = getenv("XKB_DEFAULT_VARIANT");
|
||||
rules.options = getenv("XKB_DEFAULT_OPTIONS");
|
||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!context) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||
exit(1);
|
||||
}
|
||||
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
|
||||
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!keymap) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||
|
@ -355,7 +355,7 @@ int main(int argc, char *argv[]) {
|
|||
};
|
||||
wl_list_init(&state.tablet_pads);
|
||||
wl_list_init(&state.tablet_tools);
|
||||
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
||||
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
||||
if (!wlr) {
|
||||
exit(1);
|
||||
}
|
||||
|
@ -366,11 +366,14 @@ int main(int argc, char *argv[]) {
|
|||
state.new_input.notify = new_input_notify;
|
||||
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
||||
|
||||
state.renderer = wlr_backend_get_renderer(wlr);
|
||||
state.renderer = wlr_renderer_autocreate(wlr);
|
||||
if (!state.renderer) {
|
||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
||||
|
||||
if (!wlr_backend_start(wlr)) {
|
||||
wlr_log(WLR_ERROR, "Failed to start backend");
|
||||
wlr_backend_destroy(wlr);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <unistd.h>
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-egl.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include "egl_common.h"
|
||||
#include "text-input-unstable-v3-client-protocol.h"
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
|
||||
|
@ -63,12 +63,11 @@ static struct xdg_wm_base *wm_base = NULL;
|
|||
static struct zwp_text_input_manager_v3 *text_input_manager = NULL;
|
||||
static struct zwp_text_input_v3 *text_input = NULL;
|
||||
|
||||
struct wlr_egl egl;
|
||||
struct wl_egl_window *egl_window;
|
||||
struct wlr_egl_surface *egl_surface;
|
||||
|
||||
static void draw(void) {
|
||||
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
|
||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
||||
|
||||
float color[] = {1.0, 1.0, 0.0, 1.0};
|
||||
color[0] = enabled * 1.0;
|
||||
|
@ -78,7 +77,7 @@ static void draw(void) {
|
|||
glClearColor(color[0], color[1], color[2], 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
eglSwapBuffers(egl.display, egl_surface);
|
||||
eglSwapBuffers(egl_display, egl_surface);
|
||||
}
|
||||
|
||||
static size_t utf8_strlen(char *str) {
|
||||
|
@ -363,9 +362,7 @@ int main(int argc, char **argv) {
|
|||
|
||||
zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL);
|
||||
|
||||
|
||||
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
||||
WL_SHM_FORMAT_ARGB8888);
|
||||
egl_init(display);
|
||||
|
||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||
struct xdg_surface *xdg_surface =
|
||||
|
@ -378,7 +375,8 @@ int main(int argc, char **argv) {
|
|||
wl_surface_commit(surface);
|
||||
|
||||
egl_window = wl_egl_window_create(surface, width, height);
|
||||
egl_surface = wlr_egl_create_surface(&egl, egl_window);
|
||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
||||
egl_display, egl_config, egl_window, NULL);
|
||||
|
||||
wl_display_roundtrip(display);
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <string.h>
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-egl.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include "egl_common.h"
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
#include "xdg-decoration-unstable-v1-client-protocol.h"
|
||||
|
||||
|
@ -20,7 +20,6 @@ static struct wl_compositor *compositor = NULL;
|
|||
static struct xdg_wm_base *wm_base = NULL;
|
||||
static struct zxdg_decoration_manager_v1 *decoration_manager = NULL;
|
||||
|
||||
struct wlr_egl egl;
|
||||
struct wl_egl_window *egl_window;
|
||||
struct wlr_egl_surface *egl_surface;
|
||||
|
||||
|
@ -53,7 +52,7 @@ static void request_preferred_mode(void) {
|
|||
}
|
||||
|
||||
static void draw(void) {
|
||||
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
|
||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
||||
|
||||
float color[] = {1.0, 1.0, 0.0, 1.0};
|
||||
if (current_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE) {
|
||||
|
@ -64,7 +63,7 @@ static void draw(void) {
|
|||
glClearColor(color[0], color[1], color[2], 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
eglSwapBuffers(egl.display, egl_surface);
|
||||
eglSwapBuffers(egl_display, egl_surface);
|
||||
}
|
||||
|
||||
static void xdg_surface_handle_configure(void *data,
|
||||
|
@ -218,8 +217,7 @@ int main(int argc, char **argv) {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
||||
WL_SHM_FORMAT_ARGB8888);
|
||||
egl_init(display);
|
||||
|
||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||
struct xdg_surface *xdg_surface =
|
||||
|
@ -238,7 +236,8 @@ int main(int argc, char **argv) {
|
|||
wl_surface_commit(surface);
|
||||
|
||||
egl_window = wl_egl_window_create(surface, width, height);
|
||||
egl_surface = wlr_egl_create_surface(&egl, egl_window);
|
||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
||||
egl_display, egl_config, egl_window, NULL);
|
||||
|
||||
wl_display_roundtrip(display);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#define _POSIX_C_SOURCE 200112L
|
||||
#include <GLES2/gl2.h>
|
||||
#include <drm_fourcc.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
@ -10,11 +10,13 @@
|
|||
#include <wayland-server-core.h>
|
||||
#include <wlr/backend.h>
|
||||
#include <wlr/backend/session.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/render/allocator.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_list.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_input_device.h>
|
||||
#include <wlr/types/wlr_keyboard.h>
|
||||
#include <wlr/types/wlr_matrix.h>
|
||||
#include <wlr/types/wlr_touch.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include "cat.h"
|
||||
|
@ -22,6 +24,7 @@
|
|||
struct sample_state {
|
||||
struct wl_display *display;
|
||||
struct wlr_renderer *renderer;
|
||||
struct wlr_allocator *allocator;
|
||||
struct wlr_texture *cat_texture;
|
||||
struct wl_list touch_points;
|
||||
struct timespec last_frame;
|
||||
|
@ -76,13 +79,10 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
|||
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
|
||||
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
|
||||
|
||||
int tex_width, tex_height;
|
||||
wlr_texture_get_size(sample->cat_texture, &tex_width, &tex_height);
|
||||
|
||||
struct touch_point *p;
|
||||
wl_list_for_each(p, &sample->touch_points, link) {
|
||||
int x = (int)(p->x * width) - tex_width / 2;
|
||||
int y = (int)(p->y * height) - tex_height / 2;
|
||||
int x = (int)(p->x * width) - sample->cat_texture->width / 2;
|
||||
int y = (int)(p->y * height) - sample->cat_texture->height / 2;
|
||||
wlr_render_texture(sample->renderer, sample->cat_texture,
|
||||
wlr_output->transform_matrix, x, y, 1.0f);
|
||||
}
|
||||
|
@ -150,11 +150,10 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
|
|||
static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||
struct wlr_output *output = data;
|
||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
||||
|
||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
||||
|
||||
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
||||
if (!wl_list_empty(&output->modes)) {
|
||||
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
|
||||
wlr_output_set_mode(output, mode);
|
||||
}
|
||||
sample_output->output = output;
|
||||
sample_output->sample = sample;
|
||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||
|
@ -162,6 +161,11 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
|
|||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
||||
sample_output->destroy.notify = output_remove_notify;
|
||||
|
||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
||||
if (mode != NULL) {
|
||||
wlr_output_set_mode(output, mode);
|
||||
}
|
||||
|
||||
wlr_output_commit(output);
|
||||
}
|
||||
|
||||
|
@ -200,18 +204,12 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
|
|||
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||
keyboard->key.notify = keyboard_key_notify;
|
||||
struct xkb_rule_names rules = { 0 };
|
||||
rules.rules = getenv("XKB_DEFAULT_RULES");
|
||||
rules.model = getenv("XKB_DEFAULT_MODEL");
|
||||
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
|
||||
rules.variant = getenv("XKB_DEFAULT_VARIANT");
|
||||
rules.options = getenv("XKB_DEFAULT_OPTIONS");
|
||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!context) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||
exit(1);
|
||||
}
|
||||
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
|
||||
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
if (!keymap) {
|
||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||
|
@ -250,7 +248,7 @@ int main(int argc, char *argv[]) {
|
|||
wl_list_init(&state.touch_points);
|
||||
wl_list_init(&state.touch);
|
||||
|
||||
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
||||
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
||||
if (!wlr) {
|
||||
exit(1);
|
||||
}
|
||||
|
@ -261,20 +259,21 @@ int main(int argc, char *argv[]) {
|
|||
state.new_input.notify = new_input_notify;
|
||||
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
||||
|
||||
|
||||
state.renderer = wlr_backend_get_renderer(wlr);
|
||||
state.renderer = wlr_renderer_autocreate(wlr);
|
||||
if (!state.renderer) {
|
||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
state.cat_texture = wlr_texture_from_pixels(state.renderer,
|
||||
WL_SHM_FORMAT_ARGB8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
|
||||
DRM_FORMAT_ARGB8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
|
||||
cat_tex.pixel_data);
|
||||
if (!state.cat_texture) {
|
||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
||||
|
||||
if (!wlr_backend_start(wlr)) {
|
||||
wlr_log(WLR_ERROR, "Failed to start backend");
|
||||
wlr_backend_destroy(wlr);
|
||||
|
|
|
@ -132,5 +132,7 @@ int main(int argc, char *argv[]) {
|
|||
zwlr_virtual_pointer_v1_frame(pointer);
|
||||
zwlr_virtual_pointer_v1_destroy(pointer);
|
||||
|
||||
wl_display_flush(display);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef BACKEND_WLR_BACKEND_H
|
||||
#define BACKEND_WLR_BACKEND_H
|
||||
|
||||
#include <wlr/backend.h>
|
||||
|
||||
/**
|
||||
* Get the supported buffer capabilities.
|
||||
*
|
||||
* This functions returns a bitfield of supported wlr_buffer_cap.
|
||||
*/
|
||||
uint32_t backend_get_buffer_caps(struct wlr_backend *backend);
|
||||
|
||||
#endif
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef BACKEND_DRM_DRM_H
|
||||
#define BACKEND_DRM_DRM_H
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <gbm.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
@ -12,46 +10,33 @@
|
|||
#include <wlr/backend/drm.h>
|
||||
#include <wlr/backend/session.h>
|
||||
#include <wlr/render/drm_format_set.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include <xf86drmMode.h>
|
||||
#include "iface.h"
|
||||
#include "properties.h"
|
||||
#include "renderer.h"
|
||||
#include "backend/drm/iface.h"
|
||||
#include "backend/drm/properties.h"
|
||||
#include "backend/drm/renderer.h"
|
||||
|
||||
struct wlr_drm_plane {
|
||||
uint32_t type;
|
||||
uint32_t id;
|
||||
|
||||
struct wlr_drm_surface surf;
|
||||
/* Only initialized on multi-GPU setups */
|
||||
struct wlr_drm_surface mgpu_surf;
|
||||
|
||||
/* Buffer to be submitted to the kernel on the next page-flip */
|
||||
struct wlr_drm_fb pending_fb;
|
||||
struct wlr_drm_fb *pending_fb;
|
||||
/* Buffer submitted to the kernel, will be presented on next vblank */
|
||||
struct wlr_drm_fb queued_fb;
|
||||
struct wlr_drm_fb *queued_fb;
|
||||
/* Buffer currently displayed on screen */
|
||||
struct wlr_drm_fb current_fb;
|
||||
struct wlr_drm_fb *current_fb;
|
||||
|
||||
uint32_t drm_format; // ARGB8888 or XRGB8888
|
||||
struct wlr_drm_format_set formats;
|
||||
|
||||
// Only used by cursor
|
||||
bool cursor_enabled;
|
||||
int32_t cursor_hotspot_x, cursor_hotspot_y;
|
||||
|
||||
union wlr_drm_plane_props props;
|
||||
};
|
||||
|
||||
struct wlr_drm_crtc_state {
|
||||
bool active;
|
||||
struct wlr_drm_mode *mode;
|
||||
};
|
||||
|
||||
struct wlr_drm_crtc {
|
||||
uint32_t id;
|
||||
|
||||
bool pending_modeset;
|
||||
struct wlr_drm_crtc_state pending, current;
|
||||
struct wlr_drm_lease *lease;
|
||||
|
||||
// Atomic modesetting only
|
||||
uint32_t mode_id;
|
||||
|
@ -63,13 +48,6 @@ struct wlr_drm_crtc {
|
|||
struct wlr_drm_plane *primary;
|
||||
struct wlr_drm_plane *cursor;
|
||||
|
||||
/*
|
||||
* We don't support overlay planes yet, but we keep track of them to
|
||||
* give to DRM lease clients.
|
||||
*/
|
||||
size_t num_overlays;
|
||||
uint32_t *overlays;
|
||||
|
||||
union wlr_drm_crtc_props props;
|
||||
};
|
||||
|
||||
|
@ -82,6 +60,8 @@ struct wlr_drm_backend {
|
|||
bool addfb2_modifiers;
|
||||
|
||||
int fd;
|
||||
char *name;
|
||||
struct wlr_device *dev;
|
||||
|
||||
size_t num_crtcs;
|
||||
struct wlr_drm_crtc *crtcs;
|
||||
|
@ -91,16 +71,25 @@ struct wlr_drm_backend {
|
|||
|
||||
struct wl_listener display_destroy;
|
||||
struct wl_listener session_destroy;
|
||||
struct wl_listener session_signal;
|
||||
struct wl_listener drm_invalidated;
|
||||
struct wl_listener session_active;
|
||||
struct wl_listener parent_destroy;
|
||||
struct wl_listener dev_change;
|
||||
struct wl_listener dev_remove;
|
||||
|
||||
struct wl_list fbs; // wlr_drm_fb.link
|
||||
struct wl_list outputs;
|
||||
|
||||
struct wlr_drm_renderer renderer;
|
||||
/* Only initialized on multi-GPU setups */
|
||||
struct wlr_drm_renderer mgpu_renderer;
|
||||
|
||||
struct wlr_session *session;
|
||||
|
||||
uint64_t cursor_width, cursor_height;
|
||||
|
||||
struct wlr_drm_format_set mgpu_formats;
|
||||
};
|
||||
|
||||
enum wlr_drm_connector_state {
|
||||
enum wlr_drm_connector_status {
|
||||
// Connector is available but no output is plugged in
|
||||
WLR_DRM_CONN_DISCONNECTED,
|
||||
// An output just has been plugged in and is waiting for a modeset
|
||||
|
@ -114,32 +103,43 @@ struct wlr_drm_mode {
|
|||
drmModeModeInfo drm_mode;
|
||||
};
|
||||
|
||||
struct wlr_drm_connector {
|
||||
struct wlr_output output;
|
||||
struct wlr_drm_connector_state {
|
||||
const struct wlr_output_state *base;
|
||||
bool modeset;
|
||||
bool active;
|
||||
drmModeModeInfo mode;
|
||||
};
|
||||
|
||||
enum wlr_drm_connector_state state;
|
||||
struct wlr_output_mode *desired_mode;
|
||||
struct wlr_drm_connector {
|
||||
struct wlr_output output; // only valid if status != DISCONNECTED
|
||||
|
||||
struct wlr_drm_backend *backend;
|
||||
char name[24];
|
||||
enum wlr_drm_connector_status status;
|
||||
bool desired_enabled;
|
||||
uint32_t id;
|
||||
struct wlr_drm_lease *lease;
|
||||
|
||||
struct wlr_drm_crtc *crtc;
|
||||
uint32_t possible_crtc;
|
||||
uint32_t possible_crtcs;
|
||||
|
||||
union wlr_drm_connector_props props;
|
||||
|
||||
int32_t cursor_x, cursor_y;
|
||||
|
||||
drmModeCrtc *old_crtc;
|
||||
bool cursor_enabled;
|
||||
int cursor_x, cursor_y;
|
||||
int cursor_width, cursor_height;
|
||||
int cursor_hotspot_x, cursor_hotspot_y;
|
||||
|
||||
struct wl_list link;
|
||||
|
||||
/*
|
||||
/* CRTC ID if a page-flip is pending, zero otherwise.
|
||||
*
|
||||
* We've asked for a state change in the kernel, and yet to receive a
|
||||
* notification for its completion. Currently, the kernel only has a
|
||||
* queue length of 1, and no way to modify your submissions after
|
||||
* they're sent.
|
||||
*/
|
||||
bool pageflip_pending;
|
||||
uint32_t pending_page_flip_crtc;
|
||||
};
|
||||
|
||||
struct wlr_drm_backend *get_drm_backend_from_backend(
|
||||
|
@ -147,16 +147,24 @@ struct wlr_drm_backend *get_drm_backend_from_backend(
|
|||
bool check_drm_features(struct wlr_drm_backend *drm);
|
||||
bool init_drm_resources(struct wlr_drm_backend *drm);
|
||||
void finish_drm_resources(struct wlr_drm_backend *drm);
|
||||
void restore_drm_outputs(struct wlr_drm_backend *drm);
|
||||
void scan_drm_connectors(struct wlr_drm_backend *state);
|
||||
void scan_drm_connectors(struct wlr_drm_backend *state,
|
||||
struct wlr_device_hotplug_event *event);
|
||||
void scan_drm_leases(struct wlr_drm_backend *drm);
|
||||
int handle_drm_event(int fd, uint32_t mask, void *data);
|
||||
bool drm_connector_set_mode(struct wlr_drm_connector *conn,
|
||||
struct wlr_output_mode *mode);
|
||||
void destroy_drm_connector(struct wlr_drm_connector *conn);
|
||||
bool drm_connector_commit_state(struct wlr_drm_connector *conn,
|
||||
const struct wlr_output_state *state);
|
||||
bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn);
|
||||
bool drm_connector_supports_vrr(struct wlr_drm_connector *conn);
|
||||
size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm,
|
||||
struct wlr_drm_crtc *crtc);
|
||||
void drm_lease_destroy(struct wlr_drm_lease *lease);
|
||||
|
||||
struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane);
|
||||
|
||||
#define wlr_drm_conn_log(conn, verb, fmt, ...) \
|
||||
wlr_log(verb, "connector %s: " fmt, conn->name, ##__VA_ARGS__)
|
||||
#define wlr_drm_conn_log_errno(conn, verb, fmt, ...) \
|
||||
wlr_log_errno(verb, "connector %s: " fmt, conn->name, ##__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#ifndef BACKEND_DRM_IFACE_H
|
||||
#define BACKEND_DRM_IFACE_H
|
||||
|
||||
#include <gbm.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <xf86drm.h>
|
||||
|
@ -10,12 +9,14 @@
|
|||
struct wlr_drm_backend;
|
||||
struct wlr_drm_connector;
|
||||
struct wlr_drm_crtc;
|
||||
struct wlr_drm_connector_state;
|
||||
|
||||
// Used to provide atomic or legacy DRM functions
|
||||
struct wlr_drm_interface {
|
||||
// Commit al pending changes on a CRTC.
|
||||
bool (*crtc_commit)(struct wlr_drm_backend *drm,
|
||||
struct wlr_drm_connector *conn, uint32_t flags);
|
||||
// Commit all pending changes on a CRTC.
|
||||
bool (*crtc_commit)(struct wlr_drm_connector *conn,
|
||||
const struct wlr_drm_connector_state *state, uint32_t flags,
|
||||
bool test_only);
|
||||
};
|
||||
|
||||
extern const struct wlr_drm_interface atomic_iface;
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef BACKEND_DRM_MONITOR_H
|
||||
#define BACKEND_DRM_MONITOR_H
|
||||
|
||||
#include <wlr/backend/drm.h>
|
||||
|
||||
/**
|
||||
* Helper to create new DRM sub-backends on GPU hotplug.
|
||||
*/
|
||||
struct wlr_drm_backend_monitor {
|
||||
struct wlr_backend *multi;
|
||||
struct wlr_backend *primary_drm;
|
||||
struct wlr_session *session;
|
||||
|
||||
struct wl_listener multi_destroy;
|
||||
struct wl_listener primary_drm_destroy;
|
||||
struct wl_listener session_destroy;
|
||||
struct wl_listener session_add_drm_card;
|
||||
};
|
||||
|
||||
struct wlr_drm_backend_monitor *drm_backend_monitor_create(
|
||||
struct wlr_backend *multi, struct wlr_backend *primary_drm,
|
||||
struct wlr_session *session);
|
||||
|
||||
#endif
|
|
@ -17,6 +17,9 @@ union wlr_drm_connector_props {
|
|||
uint32_t link_status; // not guaranteed to exist
|
||||
uint32_t path;
|
||||
uint32_t vrr_capable; // not guaranteed to exist
|
||||
uint32_t subconnector; // not guaranteed to exist
|
||||
uint32_t non_desktop;
|
||||
uint32_t panel_orientation; // not guaranteed to exist
|
||||
|
||||
// atomic-modesetting only
|
||||
|
||||
|
@ -28,8 +31,6 @@ union wlr_drm_connector_props {
|
|||
union wlr_drm_crtc_props {
|
||||
struct {
|
||||
// Neither of these are guaranteed to exist
|
||||
uint32_t rotation;
|
||||
uint32_t scaling_mode;
|
||||
uint32_t vrr_enabled;
|
||||
uint32_t gamma_lut;
|
||||
uint32_t gamma_lut_size;
|
||||
|
@ -60,8 +61,9 @@ union wlr_drm_plane_props {
|
|||
uint32_t crtc_h;
|
||||
uint32_t fb_id;
|
||||
uint32_t crtc_id;
|
||||
uint32_t fb_damage_clips;
|
||||
};
|
||||
uint32_t props[13];
|
||||
uint32_t props[14];
|
||||
};
|
||||
|
||||
bool get_drm_connector_props(int fd, uint32_t id,
|
||||
|
@ -71,5 +73,6 @@ bool get_drm_plane_props(int fd, uint32_t id, union wlr_drm_plane_props *out);
|
|||
|
||||
bool get_drm_prop(int fd, uint32_t obj, uint32_t prop, uint64_t *ret);
|
||||
void *get_drm_prop_blob(int fd, uint32_t obj, uint32_t prop, size_t *ret_len);
|
||||
char *get_drm_prop_enum(int fd, uint32_t obj, uint32_t prop);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#ifndef BACKEND_DRM_RENDERER_H
|
||||
#define BACKEND_DRM_RENDERER_H
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <gbm.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <wlr/backend.h>
|
||||
|
@ -13,13 +11,10 @@ struct wlr_drm_plane;
|
|||
struct wlr_buffer;
|
||||
|
||||
struct wlr_drm_renderer {
|
||||
int fd;
|
||||
struct gbm_device *gbm;
|
||||
struct wlr_egl egl;
|
||||
|
||||
uint32_t gbm_format;
|
||||
struct wlr_drm_backend *backend;
|
||||
|
||||
struct wlr_renderer *wlr_rend;
|
||||
struct wlr_allocator *allocator;
|
||||
};
|
||||
|
||||
struct wlr_drm_surface {
|
||||
|
@ -28,50 +23,38 @@ struct wlr_drm_surface {
|
|||
uint32_t width;
|
||||
uint32_t height;
|
||||
|
||||
struct gbm_surface *gbm;
|
||||
EGLSurface egl;
|
||||
};
|
||||
|
||||
enum wlr_drm_fb_type {
|
||||
WLR_DRM_FB_TYPE_NONE,
|
||||
WLR_DRM_FB_TYPE_SURFACE,
|
||||
WLR_DRM_FB_TYPE_WLR_BUFFER
|
||||
struct wlr_swapchain *swapchain;
|
||||
};
|
||||
|
||||
struct wlr_drm_fb {
|
||||
enum wlr_drm_fb_type type;
|
||||
struct gbm_bo *bo;
|
||||
struct wlr_buffer *wlr_buf;
|
||||
struct wlr_addon addon;
|
||||
struct wlr_drm_backend *backend;
|
||||
struct wl_list link; // wlr_drm_backend.fbs
|
||||
|
||||
struct wlr_drm_surface *mgpu_surf;
|
||||
struct gbm_bo *mgpu_bo;
|
||||
|
||||
union {
|
||||
struct wlr_drm_surface *surf;
|
||||
struct wlr_buffer *wlr_buf;
|
||||
};
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
bool init_drm_renderer(struct wlr_drm_backend *drm,
|
||||
struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_render);
|
||||
struct wlr_drm_renderer *renderer);
|
||||
void finish_drm_renderer(struct wlr_drm_renderer *renderer);
|
||||
|
||||
bool drm_surface_make_current(struct wlr_drm_surface *surf, int *buffer_age);
|
||||
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs);
|
||||
bool init_drm_surface(struct wlr_drm_surface *surf,
|
||||
struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height,
|
||||
const struct wlr_drm_format *drm_format);
|
||||
|
||||
void drm_fb_clear(struct wlr_drm_fb *fb);
|
||||
bool drm_fb_lock_surface(struct wlr_drm_fb *fb, struct wlr_drm_surface *surf);
|
||||
bool drm_fb_import_wlr(struct wlr_drm_fb *fb, struct wlr_drm_renderer *renderer,
|
||||
struct wlr_buffer *buf, struct wlr_drm_format_set *set);
|
||||
bool drm_fb_import(struct wlr_drm_fb **fb, struct wlr_drm_backend *drm,
|
||||
struct wlr_buffer *buf, const struct wlr_drm_format_set *formats);
|
||||
void drm_fb_destroy(struct wlr_drm_fb *fb);
|
||||
|
||||
void drm_fb_move(struct wlr_drm_fb *new, struct wlr_drm_fb *old);
|
||||
void drm_fb_clear(struct wlr_drm_fb **fb);
|
||||
void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old);
|
||||
|
||||
bool drm_surface_render_black_frame(struct wlr_drm_surface *surf);
|
||||
struct gbm_bo *drm_fb_acquire(struct wlr_drm_fb *fb, struct wlr_drm_backend *drm,
|
||||
struct wlr_drm_surface *mgpu);
|
||||
struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf,
|
||||
struct wlr_buffer *buffer);
|
||||
|
||||
bool drm_plane_init_surface(struct wlr_drm_plane *plane,
|
||||
struct wlr_drm_backend *drm, int32_t width, uint32_t height,
|
||||
uint32_t format, uint32_t flags, bool with_modifiers);
|
||||
struct wlr_drm_format *drm_plane_pick_render_format(
|
||||
struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer);
|
||||
void drm_plane_finish_surface(struct wlr_drm_plane *plane);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,8 +13,6 @@ void parse_edid(struct wlr_output *restrict output, size_t len,
|
|||
const uint8_t *data);
|
||||
// Returns the string representation of a DRM output type
|
||||
const char *conn_get_name(uint32_t type_id);
|
||||
// Returns the DRM framebuffer id for a gbm_bo
|
||||
uint32_t get_fb_for_bo(struct gbm_bo *bo, bool with_modifiers);
|
||||
|
||||
// Part of match_obj
|
||||
enum {
|
||||
|
|
|
@ -3,23 +3,17 @@
|
|||
|
||||
#include <wlr/backend/headless.h>
|
||||
#include <wlr/backend/interface.h>
|
||||
#include <wlr/render/gles2.h>
|
||||
|
||||
#define HEADLESS_DEFAULT_REFRESH (60 * 1000) // 60 Hz
|
||||
|
||||
struct wlr_headless_backend {
|
||||
struct wlr_backend backend;
|
||||
struct wlr_egl priv_egl; // may be uninitialized
|
||||
struct wlr_egl *egl;
|
||||
struct wlr_renderer *renderer;
|
||||
struct wl_display *display;
|
||||
struct wl_list outputs;
|
||||
size_t last_output_num;
|
||||
struct wl_list input_devices;
|
||||
struct wl_listener display_destroy;
|
||||
struct wl_listener renderer_destroy;
|
||||
bool started;
|
||||
GLenum internal_format;
|
||||
};
|
||||
|
||||
struct wlr_headless_output {
|
||||
|
@ -28,15 +22,13 @@ struct wlr_headless_output {
|
|||
struct wlr_headless_backend *backend;
|
||||
struct wl_list link;
|
||||
|
||||
GLuint fbo, rbo;
|
||||
|
||||
struct wl_event_source *frame_timer;
|
||||
int frame_delay; // ms
|
||||
};
|
||||
|
||||
struct wlr_headless_input_device {
|
||||
struct wlr_input_device wlr_input_device;
|
||||
|
||||
struct wl_list link;
|
||||
struct wlr_headless_backend *backend;
|
||||
};
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <wlr/backend/libinput.h>
|
||||
#include <wlr/interfaces/wlr_input_device.h>
|
||||
#include <wlr/types/wlr_input_device.h>
|
||||
#include <wlr/types/wlr_list.h>
|
||||
|
||||
struct wlr_libinput_backend {
|
||||
struct wlr_backend backend;
|
||||
|
@ -22,12 +21,12 @@ struct wlr_libinput_backend {
|
|||
struct wl_listener session_destroy;
|
||||
struct wl_listener session_signal;
|
||||
|
||||
struct wlr_list wlr_device_lists; // list of struct wl_list
|
||||
struct wl_array wlr_device_lists; // struct wl_list *
|
||||
};
|
||||
|
||||
struct wlr_libinput_input_device {
|
||||
struct wlr_input_device wlr_input_device;
|
||||
|
||||
struct wl_list link;
|
||||
struct libinput_device *handle;
|
||||
};
|
||||
|
||||
|
@ -67,6 +66,10 @@ void handle_pointer_pinch_update(struct libinput_event *event,
|
|||
struct libinput_device *device);
|
||||
void handle_pointer_pinch_end(struct libinput_event *event,
|
||||
struct libinput_device *device);
|
||||
void handle_pointer_hold_begin(struct libinput_event *event,
|
||||
struct libinput_device *device);
|
||||
void handle_pointer_hold_end(struct libinput_event *event,
|
||||
struct libinput_device *device);
|
||||
|
||||
struct wlr_switch *create_libinput_switch(
|
||||
struct libinput_device *device);
|
||||
|
@ -83,6 +86,8 @@ void handle_touch_motion(struct libinput_event *event,
|
|||
struct libinput_device *device);
|
||||
void handle_touch_cancel(struct libinput_event *event,
|
||||
struct libinput_device *device);
|
||||
void handle_touch_frame(struct libinput_event *event,
|
||||
struct libinput_device *device);
|
||||
|
||||
struct wlr_tablet *create_libinput_tablet(
|
||||
struct libinput_device *device);
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
#ifndef BACKEND_NOOP_H
|
||||
#define BACKEND_NOOP_H
|
||||
|
||||
#include <wlr/backend/noop.h>
|
||||
#include <wlr/backend/interface.h>
|
||||
|
||||
struct wlr_noop_backend {
|
||||
struct wlr_backend backend;
|
||||
struct wl_display *display;
|
||||
struct wl_list outputs;
|
||||
size_t last_output_num;
|
||||
bool started;
|
||||
};
|
||||
|
||||
struct wlr_noop_output {
|
||||
struct wlr_output wlr_output;
|
||||
|
||||
struct wlr_noop_backend *backend;
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
struct wlr_noop_backend *noop_backend_from_backend(
|
||||
struct wlr_backend *wlr_backend);
|
||||
|
||||
#endif
|
|
@ -1,12 +0,0 @@
|
|||
#ifndef BACKEND_SESSION_DIRECT_IPC_H
|
||||
#define BACKEND_SESSION_DIRECT_IPC_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
int direct_ipc_open(int sock, const char *path);
|
||||
void direct_ipc_setmaster(int sock, int fd);
|
||||
void direct_ipc_dropmaster(int sock, int fd);
|
||||
void direct_ipc_finish(int sock, pid_t pid);
|
||||
int direct_ipc_init(pid_t *pid_out);
|
||||
|
||||
#endif
|
|
@ -3,6 +3,15 @@
|
|||
|
||||
struct wlr_session;
|
||||
|
||||
struct wlr_session *libseat_session_create(struct wl_display *disp);
|
||||
void libseat_session_destroy(struct wlr_session *base);
|
||||
int libseat_session_open_device(struct wlr_session *base, const char *path);
|
||||
void libseat_session_close_device(struct wlr_session *base, int fd);
|
||||
bool libseat_change_vt(struct wlr_session *base, unsigned vt);
|
||||
|
||||
void session_init(struct wlr_session *session);
|
||||
|
||||
struct wlr_device *session_open_if_kms(struct wlr_session *restrict session,
|
||||
const char *restrict path);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,14 +4,11 @@
|
|||
#include <stdbool.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-egl.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wayland-util.h>
|
||||
|
||||
#include <wlr/backend/wayland.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/types/wlr_box.h>
|
||||
#include <wlr/types/wlr_pointer.h>
|
||||
#include <wlr/render/drm_format_set.h>
|
||||
|
||||
struct wlr_wl_backend {
|
||||
|
@ -22,11 +19,13 @@ struct wlr_wl_backend {
|
|||
struct wl_display *local_display;
|
||||
struct wl_list devices;
|
||||
struct wl_list outputs;
|
||||
struct wlr_egl egl;
|
||||
struct wlr_renderer *renderer;
|
||||
int drm_fd;
|
||||
struct wl_list buffers; // wlr_wl_buffer.link
|
||||
size_t requested_outputs;
|
||||
size_t last_output_num;
|
||||
struct wl_listener local_display_destroy;
|
||||
char *activation_token;
|
||||
|
||||
/* remote state */
|
||||
struct wl_display *remote_display;
|
||||
struct wl_event_source *remote_display_src;
|
||||
|
@ -36,17 +35,25 @@ struct wlr_wl_backend {
|
|||
struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1;
|
||||
struct zwp_pointer_gestures_v1 *zwp_pointer_gestures_v1;
|
||||
struct wp_presentation *presentation;
|
||||
struct wl_shm *shm;
|
||||
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1;
|
||||
struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1;
|
||||
struct wlr_wl_seat *seat;
|
||||
struct wlr_wl_pointer *current_pointer;
|
||||
struct wl_list seats; // wlr_wl_seat.link
|
||||
struct zwp_tablet_manager_v2 *tablet_manager;
|
||||
clockid_t presentation_clock;
|
||||
struct wlr_drm_format_set shm_formats;
|
||||
struct wlr_drm_format_set linux_dmabuf_v1_formats;
|
||||
struct wl_drm *legacy_drm;
|
||||
struct xdg_activation_v1 *activation_v1;
|
||||
char *drm_render_name;
|
||||
};
|
||||
|
||||
struct wlr_wl_buffer {
|
||||
struct wlr_buffer *buffer;
|
||||
struct wl_buffer *wl_buffer;
|
||||
bool released;
|
||||
struct wl_list link; // wlr_wl_backend.buffers
|
||||
struct wl_listener buffer_destroy;
|
||||
};
|
||||
|
||||
struct wlr_wl_presentation_feedback {
|
||||
|
@ -67,25 +74,24 @@ struct wlr_wl_output {
|
|||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1;
|
||||
struct wl_egl_window *egl_window;
|
||||
EGLSurface egl_surface;
|
||||
struct wl_list presentation_feedbacks;
|
||||
|
||||
uint32_t enter_serial;
|
||||
|
||||
struct {
|
||||
struct wlr_wl_pointer *pointer;
|
||||
struct wl_surface *surface;
|
||||
struct wl_egl_window *egl_window;
|
||||
int32_t hotspot_x, hotspot_y;
|
||||
int32_t width, height;
|
||||
} cursor;
|
||||
};
|
||||
|
||||
struct wlr_wl_input_device {
|
||||
struct wlr_input_device wlr_input_device;
|
||||
struct wl_list link;
|
||||
uint32_t fingers;
|
||||
|
||||
struct wlr_wl_backend *backend;
|
||||
struct wlr_wl_seat *seat;
|
||||
void *resource;
|
||||
};
|
||||
|
||||
|
@ -96,6 +102,7 @@ struct wlr_wl_pointer {
|
|||
struct wl_pointer *wl_pointer;
|
||||
struct zwp_pointer_gesture_swipe_v1 *gesture_swipe;
|
||||
struct zwp_pointer_gesture_pinch_v1 *gesture_pinch;
|
||||
struct zwp_pointer_gesture_hold_v1 *gesture_hold;
|
||||
struct zwp_relative_pointer_v1 *relative_pointer;
|
||||
enum wlr_axis_source axis_source;
|
||||
int32_t axis_discrete;
|
||||
|
@ -107,27 +114,32 @@ struct wlr_wl_pointer {
|
|||
struct wlr_wl_seat {
|
||||
struct wl_seat *wl_seat;
|
||||
|
||||
struct wl_list link; // wlr_wl_backend.seats
|
||||
char *name;
|
||||
struct wl_touch *touch;
|
||||
struct wl_pointer *pointer;
|
||||
struct wl_keyboard *keyboard;
|
||||
|
||||
struct wlr_wl_backend *backend;
|
||||
struct wlr_wl_pointer *active_pointer;
|
||||
};
|
||||
|
||||
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend);
|
||||
void update_wl_output_cursor(struct wlr_wl_output *output);
|
||||
struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer);
|
||||
void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *output);
|
||||
void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *wl);
|
||||
void create_wl_touch(struct wl_touch *wl_touch, struct wlr_wl_backend *wl);
|
||||
void create_wl_pointer(struct wlr_wl_seat *seat, struct wlr_wl_output *output);
|
||||
void create_wl_keyboard(struct wlr_wl_seat *seat);
|
||||
void create_wl_touch(struct wlr_wl_seat *seat);
|
||||
struct wlr_wl_input_device *create_wl_input_device(
|
||||
struct wlr_wl_backend *backend, enum wlr_input_device_type type);
|
||||
struct wlr_wl_seat *seat, enum wlr_input_device_type type);
|
||||
bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl);
|
||||
void destroy_wl_seats(struct wlr_wl_backend *wl);
|
||||
void destroy_wl_buffer(struct wlr_wl_buffer *buffer);
|
||||
|
||||
extern const struct wl_seat_listener seat_listener;
|
||||
|
||||
struct wlr_wl_tablet_seat *wl_add_tablet_seat(
|
||||
struct zwp_tablet_manager_v2 *manager,
|
||||
struct wl_seat *seat, struct wlr_wl_backend *backend);
|
||||
struct wlr_wl_seat *seat);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,23 +1,29 @@
|
|||
#ifndef BACKEND_X11_H
|
||||
#define BACKEND_X11_H
|
||||
|
||||
#include <wlr/config.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <X11/Xlib-xcb.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/present.h>
|
||||
|
||||
#if HAS_XCB_ERRORS
|
||||
#include <xcb/xcb_errors.h>
|
||||
#endif
|
||||
|
||||
#include <pixman.h>
|
||||
#include <wlr/backend/x11.h>
|
||||
#include <wlr/config.h>
|
||||
#include <wlr/interfaces/wlr_input_device.h>
|
||||
#include <wlr/interfaces/wlr_keyboard.h>
|
||||
#include <wlr/interfaces/wlr_output.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/interfaces/wlr_pointer.h>
|
||||
#include <wlr/interfaces/wlr_touch.h>
|
||||
#include <wlr/render/drm_format_set.h>
|
||||
|
||||
#define XCB_EVENT_RESPONSE_TYPE_MASK 0x7f
|
||||
|
||||
#define X11_DEFAULT_REFRESH (60 * 1000) // 60 Hz
|
||||
|
||||
struct wlr_x11_backend;
|
||||
|
||||
struct wlr_x11_output {
|
||||
|
@ -26,7 +32,7 @@ struct wlr_x11_output {
|
|||
struct wl_list link; // wlr_x11_backend::outputs
|
||||
|
||||
xcb_window_t win;
|
||||
EGLSurface surf;
|
||||
xcb_present_event_t present_event_id;
|
||||
|
||||
struct wlr_pointer pointer;
|
||||
struct wlr_input_device pointer_dev;
|
||||
|
@ -35,10 +41,16 @@ struct wlr_x11_output {
|
|||
struct wlr_input_device touch_dev;
|
||||
struct wl_list touchpoints; // wlr_x11_touchpoint::link
|
||||
|
||||
struct wl_event_source *frame_timer;
|
||||
int frame_delay;
|
||||
struct wl_list buffers; // wlr_x11_buffer::link
|
||||
|
||||
bool cursor_hidden;
|
||||
pixman_region32_t exposed;
|
||||
|
||||
uint64_t last_msc;
|
||||
|
||||
struct {
|
||||
struct wlr_swapchain *swapchain;
|
||||
xcb_render_picture_t pic;
|
||||
} cursor;
|
||||
};
|
||||
|
||||
struct wlr_x11_touchpoint {
|
||||
|
@ -52,9 +64,17 @@ struct wlr_x11_backend {
|
|||
struct wl_display *wl_display;
|
||||
bool started;
|
||||
|
||||
Display *xlib_conn;
|
||||
xcb_connection_t *xcb;
|
||||
xcb_screen_t *screen;
|
||||
xcb_depth_t *depth;
|
||||
xcb_visualid_t visualid;
|
||||
xcb_colormap_t colormap;
|
||||
xcb_cursor_t transparent_cursor;
|
||||
xcb_render_pictformat_t argb32;
|
||||
|
||||
bool have_shm;
|
||||
bool have_dri3;
|
||||
uint32_t dri3_major_version, dri3_minor_version;
|
||||
|
||||
size_t requested_outputs;
|
||||
size_t last_output_num;
|
||||
|
@ -63,8 +83,12 @@ struct wlr_x11_backend {
|
|||
struct wlr_keyboard keyboard;
|
||||
struct wlr_input_device keyboard_dev;
|
||||
|
||||
struct wlr_egl egl;
|
||||
struct wlr_renderer *renderer;
|
||||
int drm_fd;
|
||||
struct wlr_drm_format_set dri3_formats;
|
||||
struct wlr_drm_format_set shm_formats;
|
||||
const struct wlr_x11_format *x11_format;
|
||||
struct wlr_drm_format_set primary_dri3_formats;
|
||||
struct wlr_drm_format_set primary_shm_formats;
|
||||
struct wl_event_source *event_source;
|
||||
|
||||
struct {
|
||||
|
@ -78,11 +102,29 @@ struct wlr_x11_backend {
|
|||
// The time we last received an event
|
||||
xcb_timestamp_t time;
|
||||
|
||||
#if HAS_XCB_ERRORS
|
||||
xcb_errors_context_t *errors_context;
|
||||
#endif
|
||||
|
||||
uint8_t present_opcode;
|
||||
uint8_t xinput_opcode;
|
||||
|
||||
struct wl_listener display_destroy;
|
||||
};
|
||||
|
||||
struct wlr_x11_buffer {
|
||||
struct wlr_x11_backend *x11;
|
||||
struct wlr_buffer *buffer;
|
||||
xcb_pixmap_t pixmap;
|
||||
struct wl_list link; // wlr_x11_output::buffers
|
||||
struct wl_listener buffer_destroy;
|
||||
};
|
||||
|
||||
struct wlr_x11_format {
|
||||
uint32_t drm;
|
||||
uint8_t depth, bpp;
|
||||
};
|
||||
|
||||
struct wlr_x11_backend *get_x11_backend_from_backend(
|
||||
struct wlr_backend *wlr_backend);
|
||||
struct wlr_x11_output *get_x11_output_from_window_id(
|
||||
|
@ -100,5 +142,7 @@ void update_x11_pointer_position(struct wlr_x11_output *output,
|
|||
|
||||
void handle_x11_configure_notify(struct wlr_x11_output *output,
|
||||
xcb_configure_notify_event_t *event);
|
||||
void handle_x11_present_event(struct wlr_x11_backend *x11,
|
||||
xcb_ge_generic_event_t *event);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,11 +1,25 @@
|
|||
subdir('wlr')
|
||||
|
||||
exclude_files = ['meson.build', 'config.h.in', 'version.h.in']
|
||||
if conf_data.get('WLR_HAS_X11_BACKEND', 0) != 1
|
||||
if not features.get('drm-backend')
|
||||
exclude_files += 'backend/drm.h'
|
||||
endif
|
||||
if not features.get('libinput-backend')
|
||||
exclude_files += 'backend/libinput.h'
|
||||
endif
|
||||
if not features.get('x11-backend')
|
||||
exclude_files += 'backend/x11.h'
|
||||
endif
|
||||
if conf_data.get('WLR_HAS_XWAYLAND', 0) != 1
|
||||
if not features.get('xwayland')
|
||||
exclude_files += 'xwayland.h'
|
||||
else
|
||||
subdir('xwayland')
|
||||
endif
|
||||
if not features.get('gles2-renderer')
|
||||
exclude_files += ['render/egl.h', 'render/gles2.h']
|
||||
endif
|
||||
if not features.get('vulkan-renderer')
|
||||
exclude_files += 'render/vulkan.h'
|
||||
endif
|
||||
|
||||
install_subdir('wlr',
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef RENDER_ALLOCATOR_ALLOCATOR_H
|
||||
#define RENDER_ALLOCATOR_ALLOCATOR_H
|
||||
|
||||
#include <wlr/render/allocator.h>
|
||||
|
||||
struct wlr_allocator *allocator_autocreate_with_drm_fd(
|
||||
struct wlr_backend *backend, struct wlr_renderer *renderer, int drm_fd);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef RENDER_ALLOCATOR_DRM_DUMB_H
|
||||
#define RENDER_ALLOCATOR_DRM_DUMB_H
|
||||
|
||||
#include <wlr/render/dmabuf.h>
|
||||
#include <wlr/types/wlr_buffer.h>
|
||||
#include "render/allocator/allocator.h"
|
||||
|
||||
struct wlr_drm_dumb_buffer {
|
||||
struct wlr_buffer base;
|
||||
struct wl_list link; // wlr_drm_dumb_allocator::buffers
|
||||
|
||||
int drm_fd; // -1 if the allocator has been destroyed
|
||||
struct wlr_dmabuf_attributes dmabuf;
|
||||
|
||||
uint32_t format;
|
||||
uint32_t handle;
|
||||
uint32_t stride;
|
||||
uint32_t width, height;
|
||||
|
||||
uint64_t size;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct wlr_drm_dumb_allocator {
|
||||
struct wlr_allocator base;
|
||||
struct wl_list buffers; // wlr_drm_dumb_buffer::link
|
||||
int drm_fd;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new drm dumb allocator from a DRM FD.
|
||||
*
|
||||
* Does not take ownership over the FD.
|
||||
*/
|
||||
struct wlr_allocator *wlr_drm_dumb_allocator_create(int fd);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef RENDER_ALLOCATOR_GBM_H
|
||||
#define RENDER_ALLOCATOR_GBM_H
|
||||
|
||||
#include <gbm.h>
|
||||
#include <wlr/render/dmabuf.h>
|
||||
#include <wlr/types/wlr_buffer.h>
|
||||
#include "render/allocator/allocator.h"
|
||||
|
||||
struct wlr_gbm_buffer {
|
||||
struct wlr_buffer base;
|
||||
|
||||
struct wl_list link; // wlr_gbm_allocator.buffers
|
||||
|
||||
struct gbm_bo *gbm_bo; // NULL if the gbm_device has been destroyed
|
||||
struct wlr_dmabuf_attributes dmabuf;
|
||||
};
|
||||
|
||||
struct wlr_gbm_allocator {
|
||||
struct wlr_allocator base;
|
||||
|
||||
int fd;
|
||||
struct gbm_device *gbm_device;
|
||||
|
||||
struct wl_list buffers; // wlr_gbm_buffer.link
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new GBM allocator from a DRM FD.
|
||||
*
|
||||
* Takes ownership over the FD.
|
||||
*/
|
||||
struct wlr_allocator *wlr_gbm_allocator_create(int drm_fd);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef RENDER_ALLOCATOR_SHM_H
|
||||
#define RENDER_ALLOCATOR_SHM_H
|
||||
|
||||
#include <wlr/types/wlr_buffer.h>
|
||||
#include "render/allocator/allocator.h"
|
||||
|
||||
struct wlr_shm_buffer {
|
||||
struct wlr_buffer base;
|
||||
struct wlr_shm_attributes shm;
|
||||
void *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct wlr_shm_allocator {
|
||||
struct wlr_allocator base;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new shared memory allocator.
|
||||
*/
|
||||
struct wlr_allocator *wlr_shm_allocator_create(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef RENDER_DRM_FORMAT_SET_H
|
||||
#define RENDER_DRM_FORMAT_SET_H
|
||||
|
||||
#include <wlr/render/drm_format_set.h>
|
||||
|
||||
struct wlr_drm_format *wlr_drm_format_create(uint32_t format);
|
||||
bool wlr_drm_format_has(const struct wlr_drm_format *fmt, uint64_t modifier);
|
||||
bool wlr_drm_format_add(struct wlr_drm_format **fmt_ptr, uint64_t modifier);
|
||||
struct wlr_drm_format *wlr_drm_format_dup(const struct wlr_drm_format *format);
|
||||
/**
|
||||
* Intersect modifiers for two DRM formats.
|
||||
*
|
||||
* Both arguments must have the same format field. If the formats aren't
|
||||
* compatible, NULL is returned. If either format doesn't support any modifier,
|
||||
* a format that doesn't support any modifier is returned.
|
||||
*/
|
||||
struct wlr_drm_format *wlr_drm_format_intersect(
|
||||
const struct wlr_drm_format *a, const struct wlr_drm_format *b);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,63 @@
|
|||
#ifndef RENDER_EGL_H
|
||||
#define RENDER_EGL_H
|
||||
|
||||
#include <wlr/render/egl.h>
|
||||
|
||||
struct wlr_egl_context {
|
||||
EGLDisplay display;
|
||||
EGLContext context;
|
||||
EGLSurface draw_surface;
|
||||
EGLSurface read_surface;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes an EGL context for the given DRM FD.
|
||||
*
|
||||
* Will attempt to load all possibly required API functions.
|
||||
*/
|
||||
struct wlr_egl *wlr_egl_create_with_drm_fd(int drm_fd);
|
||||
|
||||
/**
|
||||
* Frees all related EGL resources, makes the context not-current and
|
||||
* unbinds a bound wayland display.
|
||||
*/
|
||||
void wlr_egl_destroy(struct wlr_egl *egl);
|
||||
|
||||
/**
|
||||
* Creates an EGL image from the given dmabuf attributes. Check usability
|
||||
* of the dmabuf with wlr_egl_check_import_dmabuf once first.
|
||||
*/
|
||||
EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl,
|
||||
struct wlr_dmabuf_attributes *attributes, bool *external_only);
|
||||
|
||||
/**
|
||||
* Get DMA-BUF formats suitable for sampling usage.
|
||||
*/
|
||||
const struct wlr_drm_format_set *wlr_egl_get_dmabuf_texture_formats(
|
||||
struct wlr_egl *egl);
|
||||
/**
|
||||
* Get DMA-BUF formats suitable for rendering usage.
|
||||
*/
|
||||
const struct wlr_drm_format_set *wlr_egl_get_dmabuf_render_formats(
|
||||
struct wlr_egl *egl);
|
||||
|
||||
/**
|
||||
* Destroys an EGL image created with the given wlr_egl.
|
||||
*/
|
||||
bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImageKHR image);
|
||||
|
||||
int wlr_egl_dup_drm_fd(struct wlr_egl *egl);
|
||||
|
||||
/**
|
||||
* Save the current EGL context to the structure provided in the argument.
|
||||
*
|
||||
* This includes display, context, draw surface and read surface.
|
||||
*/
|
||||
void wlr_egl_save_context(struct wlr_egl_context *context);
|
||||
|
||||
/**
|
||||
* Restore EGL context that was previously saved using wlr_egl_save_current().
|
||||
*/
|
||||
bool wlr_egl_restore_context(struct wlr_egl_context *context);
|
||||
|
||||
#endif
|
|
@ -12,19 +12,18 @@
|
|||
#include <wlr/render/interface.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/render/wlr_texture.h>
|
||||
#include <wlr/util/addon.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
||||
struct wlr_gles2_pixel_format {
|
||||
enum wl_shm_format wl_format;
|
||||
uint32_t drm_format;
|
||||
GLint gl_format, gl_type;
|
||||
int depth, bpp;
|
||||
bool has_alpha;
|
||||
};
|
||||
|
||||
struct wlr_gles2_tex_shader {
|
||||
GLuint program;
|
||||
GLint proj;
|
||||
GLint invert_y;
|
||||
GLint tex;
|
||||
GLint alpha;
|
||||
GLint pos_attrib;
|
||||
|
@ -34,14 +33,18 @@ struct wlr_gles2_tex_shader {
|
|||
struct wlr_gles2_renderer {
|
||||
struct wlr_renderer wlr_renderer;
|
||||
|
||||
float projection[9];
|
||||
struct wlr_egl *egl;
|
||||
int drm_fd;
|
||||
|
||||
const char *exts_str;
|
||||
struct {
|
||||
bool read_format_bgra_ext;
|
||||
bool debug_khr;
|
||||
bool egl_image_external_oes;
|
||||
bool egl_image_oes;
|
||||
bool EXT_read_format_bgra;
|
||||
bool KHR_debug;
|
||||
bool OES_egl_image_external;
|
||||
bool OES_egl_image;
|
||||
bool EXT_texture_type_2_10_10_10_REV;
|
||||
bool OES_texture_half_float_linear;
|
||||
} exts;
|
||||
|
||||
struct {
|
||||
|
@ -60,24 +63,34 @@ struct wlr_gles2_renderer {
|
|||
GLint color;
|
||||
GLint pos_attrib;
|
||||
} quad;
|
||||
struct {
|
||||
GLuint program;
|
||||
GLint proj;
|
||||
GLint color;
|
||||
GLint pos_attrib;
|
||||
GLint tex_attrib;
|
||||
} ellipse;
|
||||
struct wlr_gles2_tex_shader tex_rgba;
|
||||
struct wlr_gles2_tex_shader tex_rgbx;
|
||||
struct wlr_gles2_tex_shader tex_ext;
|
||||
} shaders;
|
||||
|
||||
struct wl_list buffers; // wlr_gles2_buffer.link
|
||||
struct wl_list textures; // wlr_gles2_texture.link
|
||||
|
||||
struct wlr_gles2_buffer *current_buffer;
|
||||
uint32_t viewport_width, viewport_height;
|
||||
};
|
||||
|
||||
struct wlr_gles2_buffer {
|
||||
struct wlr_buffer *buffer;
|
||||
struct wlr_gles2_renderer *renderer;
|
||||
struct wl_list link; // wlr_gles2_renderer.buffers
|
||||
|
||||
EGLImageKHR image;
|
||||
GLuint rbo;
|
||||
GLuint fbo;
|
||||
|
||||
struct wlr_addon addon;
|
||||
};
|
||||
|
||||
struct wlr_gles2_texture {
|
||||
struct wlr_texture wlr_texture;
|
||||
struct wlr_gles2_renderer *renderer;
|
||||
struct wl_list link; // wlr_gles2_renderer.textures
|
||||
|
||||
// Basically:
|
||||
// GL_TEXTURE_2D == mutable
|
||||
|
@ -87,31 +100,34 @@ struct wlr_gles2_texture {
|
|||
|
||||
EGLImageKHR image;
|
||||
|
||||
bool inverted_y;
|
||||
bool has_alpha;
|
||||
|
||||
// Only affects target == GL_TEXTURE_2D
|
||||
enum wl_shm_format wl_format; // used to interpret upload data
|
||||
uint32_t drm_format; // used to interpret upload data
|
||||
// If imported from a wlr_buffer
|
||||
struct wlr_buffer *buffer;
|
||||
struct wlr_addon buffer_addon;
|
||||
};
|
||||
|
||||
const struct wlr_gles2_pixel_format *get_gles2_format_from_wl(
|
||||
enum wl_shm_format fmt);
|
||||
|
||||
bool is_gles2_pixel_format_supported(const struct wlr_gles2_renderer *renderer,
|
||||
const struct wlr_gles2_pixel_format *format);
|
||||
const struct wlr_gles2_pixel_format *get_gles2_format_from_drm(uint32_t fmt);
|
||||
const struct wlr_gles2_pixel_format *get_gles2_format_from_gl(
|
||||
GLint gl_format, GLint gl_type, bool alpha);
|
||||
const enum wl_shm_format *get_gles2_wl_formats(size_t *len);
|
||||
const uint32_t *get_gles2_shm_formats(const struct wlr_gles2_renderer *renderer,
|
||||
size_t *len);
|
||||
|
||||
struct wlr_gles2_renderer *gles2_get_renderer(
|
||||
struct wlr_renderer *wlr_renderer);
|
||||
struct wlr_gles2_texture *gles2_get_texture(
|
||||
struct wlr_texture *wlr_texture);
|
||||
|
||||
struct wlr_texture *gles2_texture_from_pixels(struct wlr_renderer *wlr_renderer,
|
||||
enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, uint32_t height,
|
||||
const void *data);
|
||||
struct wlr_texture *gles2_texture_from_wl_drm(struct wlr_renderer *wlr_renderer,
|
||||
struct wl_resource *data);
|
||||
struct wlr_texture *gles2_texture_from_dmabuf(struct wlr_renderer *wlr_renderer,
|
||||
struct wlr_dmabuf_attributes *attribs);
|
||||
struct wlr_texture *gles2_texture_from_buffer(struct wlr_renderer *wlr_renderer,
|
||||
struct wlr_buffer *buffer);
|
||||
void gles2_texture_destroy(struct wlr_gles2_texture *texture);
|
||||
|
||||
void push_gles2_debug_(struct wlr_gles2_renderer *renderer,
|
||||
const char *file, const char *func);
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef RENDER_PIXEL_FORMAT_H
|
||||
#define RENDER_PIXEL_FORMAT_H
|
||||
|
||||
#include <wayland-server-protocol.h>
|
||||
|
||||
struct wlr_pixel_format_info {
|
||||
uint32_t drm_format;
|
||||
|
||||
/* Equivalent of the format if it has an alpha channel,
|
||||
* DRM_FORMAT_INVALID (0) if NA
|
||||
*/
|
||||
uint32_t opaque_substitute;
|
||||
|
||||
/* Bits per pixels */
|
||||
uint32_t bpp;
|
||||
|
||||
/* True if the format has an alpha channel */
|
||||
bool has_alpha;
|
||||
};
|
||||
|
||||
const struct wlr_pixel_format_info *drm_get_pixel_format_info(uint32_t fmt);
|
||||
|
||||
uint32_t convert_wl_shm_format_to_drm(enum wl_shm_format fmt);
|
||||
enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef RENDER_PIXMAN_H
|
||||
#define RENDER_PIXMAN_H
|
||||
|
||||
#include <wlr/render/pixman.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/render/drm_format_set.h>
|
||||
#include "render/pixel_format.h"
|
||||
|
||||
struct wlr_pixman_pixel_format {
|
||||
uint32_t drm_format;
|
||||
pixman_format_code_t pixman_format;
|
||||
};
|
||||
|
||||
struct wlr_pixman_buffer;
|
||||
|
||||
struct wlr_pixman_renderer {
|
||||
struct wlr_renderer wlr_renderer;
|
||||
|
||||
struct wl_list buffers; // wlr_pixman_buffer.link
|
||||
struct wl_list textures; // wlr_pixman_texture.link
|
||||
|
||||
struct wlr_pixman_buffer *current_buffer;
|
||||
int32_t width, height;
|
||||
|
||||
struct wlr_drm_format_set drm_formats;
|
||||
};
|
||||
|
||||
struct wlr_pixman_buffer {
|
||||
struct wlr_buffer *buffer;
|
||||
struct wlr_pixman_renderer *renderer;
|
||||
|
||||
pixman_image_t *image;
|
||||
|
||||
struct wl_listener buffer_destroy;
|
||||
struct wl_list link; // wlr_pixman_renderer.buffers
|
||||
};
|
||||
|
||||
struct wlr_pixman_texture {
|
||||
struct wlr_texture wlr_texture;
|
||||
struct wlr_pixman_renderer *renderer;
|
||||
struct wl_list link; // wlr_pixman_renderer.textures
|
||||
|
||||
pixman_image_t *image;
|
||||
pixman_format_code_t format;
|
||||
const struct wlr_pixel_format_info *format_info;
|
||||
|
||||
void *data; // if created via texture_from_pixels
|
||||
struct wlr_buffer *buffer; // if created via texture_from_buffer
|
||||
};
|
||||
|
||||
pixman_format_code_t get_pixman_format_from_drm(uint32_t fmt);
|
||||
uint32_t get_drm_format_from_pixman(pixman_format_code_t fmt);
|
||||
const uint32_t *get_pixman_drm_formats(size_t *len);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef RENDER_SWAPCHAIN_H
|
||||
#define RENDER_SWAPCHAIN_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/render/drm_format_set.h>
|
||||
|
||||
#define WLR_SWAPCHAIN_CAP 4
|
||||
|
||||
struct wlr_swapchain_slot {
|
||||
struct wlr_buffer *buffer;
|
||||
bool acquired; // waiting for release
|
||||
int age;
|
||||
|
||||
struct wl_listener release;
|
||||
};
|
||||
|
||||
struct wlr_swapchain {
|
||||
struct wlr_allocator *allocator; // NULL if destroyed
|
||||
|
||||
int width, height;
|
||||
struct wlr_drm_format *format;
|
||||
|
||||
struct wlr_swapchain_slot slots[WLR_SWAPCHAIN_CAP];
|
||||
|
||||
struct wl_listener allocator_destroy;
|
||||
};
|
||||
|
||||
struct wlr_swapchain *wlr_swapchain_create(
|
||||
struct wlr_allocator *alloc, int width, int height,
|
||||
const struct wlr_drm_format *format);
|
||||
void wlr_swapchain_destroy(struct wlr_swapchain *swapchain);
|
||||
/**
|
||||
* Acquire a buffer from the swap chain.
|
||||
*
|
||||
* The returned buffer is locked. When the caller is done with it, they must
|
||||
* unlock it by calling wlr_buffer_unlock.
|
||||
*/
|
||||
struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain,
|
||||
int *age);
|
||||
/**
|
||||
* Mark the buffer as submitted for presentation. This needs to be called by
|
||||
* swap chain users on frame boundaries.
|
||||
*
|
||||
* If the buffer hasn't been created via the swap chain, the call is ignored.
|
||||
*/
|
||||
void wlr_swapchain_set_buffer_submitted(struct wlr_swapchain *swapchain,
|
||||
struct wlr_buffer *buffer);
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue