Compare commits
1519 Commits
0.8.1
...
dkondor-up
Author | SHA1 | Date |
---|---|---|
|
c5e3ca6249 | |
|
29938b7425 | |
|
eb1a451803 | |
|
a819c512ec | |
|
c4824b680a | |
|
ec3780e6ea | |
|
68c5fa340d | |
|
304c61307a | |
|
b0fee56974 | |
|
7dde2b66d6 | |
|
ea77cc5cb2 | |
|
a59a957f2b | |
|
df945b665c | |
|
4b358c2f91 | |
|
7864f26d73 | |
|
b309e8fc39 | |
|
80d10b9c98 | |
|
9f41627aa1 | |
|
8e566f716c | |
|
07ccc6e0b3 | |
|
c0b120a30c | |
|
bedfec94bb | |
|
a15c327718 | |
|
4377b55292 | |
|
4c59f7d46a | |
|
31914928d2 | |
|
1c3e0816f3 | |
|
ad01cdf0b2 | |
|
fecde72be3 | |
|
fb1f613510 | |
|
0215dffba5 | |
|
d8ca494558 | |
|
f6d3efbf4b | |
|
e3fefda023 | |
|
0fcc842291 | |
|
7964bdae76 | |
|
df7d280343 | |
|
f463ca669a | |
|
818fc4a87b | |
|
36a2b19485 | |
|
1fbd13ec79 | |
|
90e9d327dd | |
|
83bdb3ad07 | |
|
ad28490cf4 | |
|
c50c4fc5cc | |
|
1d8340754b | |
|
77d811a21b | |
|
c9f3c2b4f7 | |
|
ca1af8119c | |
|
efeb8346cf | |
|
45069fb623 | |
|
60b7267e18 | |
|
f016eca97c | |
|
7201aae3d6 | |
|
92080b3a01 | |
|
0d32118a80 | |
|
1bf9676e87 | |
|
de0bc78319 | |
|
051d1ce90e | |
|
ffd4a27714 | |
|
812ab2e716 | |
|
611b9ca843 | |
|
a44b2af672 | |
|
ba974a4e9f | |
|
dd84c5a1cc | |
|
697a1cd0f5 | |
|
e93435016e | |
|
2540de494e | |
|
456b971099 | |
|
6bfb930aa7 | |
|
fbaefd90fc | |
|
bff5b2c559 | |
|
0fb479ca61 | |
|
d37eb5c2ea | |
|
254ab890e7 | |
|
83d78f9fd4 | |
|
ef1669d33e | |
|
98f2efde98 | |
|
d5df8d5cbf | |
|
e163a7cf48 | |
|
affe9eda57 | |
|
d78cb808b1 | |
|
585a908a01 | |
|
1d3dd7fc08 | |
|
b234edcf58 | |
|
2e33139ef7 | |
|
f29abe4c77 | |
|
e4f748c6e9 | |
|
bf57825560 | |
|
bcefb71cf6 | |
|
f132d66816 | |
|
5332935afc | |
|
1d9c1bcea6 | |
|
c9ba9e82b6 | |
|
3b93da70a0 | |
|
3d73b899ff | |
|
d70d74ad4f | |
|
52c34e8253 | |
|
e656697a7d | |
|
6bb8973309 | |
|
86f5ecf468 | |
|
a37f538ca0 | |
|
d0bb7df630 | |
|
e879d566bb | |
|
3d7d6ec06f | |
|
7508f87fcb | |
|
ee210758fc | |
|
c0fd60be63 | |
|
25bb92faee | |
|
33eba9080c | |
|
e736ebc63c | |
|
fdf3169b41 | |
|
d1ebd52ab2 | |
|
42549a1c9a | |
|
a143093339 | |
|
5f11198605 | |
|
5a98eae0dc | |
|
d07c87f668 | |
|
6dc6af1534 | |
|
0c76aef202 | |
|
a6538ced35 | |
|
6d6e70b9e0 | |
|
142d10e591 | |
|
b5d4bc3c62 | |
|
a04cfca4da | |
|
9a4e1095ca | |
|
8274c85d21 | |
|
4a8e681a5f | |
|
3a685b10b6 | |
|
02a1ae169e | |
|
ab16861e86 | |
|
76bab68e70 | |
|
fa77aeb80e | |
|
f20c49d78a | |
|
e326b76959 | |
|
eb5f23d6d0 | |
|
fc1ed72bdc | |
|
8634dd3e6a | |
|
e13f3f8608 | |
|
2ff4e113e2 | |
|
3e801d68f2 | |
|
760e166578 | |
|
8bc1086cac | |
|
d1b75674d4 | |
|
cc2ebd9fc0 | |
|
8e225261f0 | |
|
e2aff8a9b0 | |
|
6ad0f819e2 | |
|
83090de034 | |
|
b2f6ff45c2 | |
|
0817c52a21 | |
|
3b96aa04db | |
|
a80f2b2816 | |
|
a92293a15a | |
|
6666604f17 | |
|
4fb652c27f | |
|
ebe3cfaceb | |
|
cbedbd0fc0 | |
|
5619cf368b | |
|
c43130cb89 | |
|
bf42630d32 | |
|
3d6ca9942d | |
|
fb393ddf84 | |
|
a4ccca0834 | |
|
db4c93028d | |
|
70e8277175 | |
|
7c10a77e0a | |
|
6c3a71d9f6 | |
|
3dc99ed281 | |
|
36cf387427 | |
|
f7ea33da0a | |
|
b62ce3c3c8 | |
|
e22a386319 | |
|
8e34692250 | |
|
2edf468aeb | |
|
2af8cc769a | |
|
1089b7b8d6 | |
|
1b65a80e9d | |
|
4fae8f7be3 | |
|
2a8d385386 | |
|
dc22a06184 | |
|
c3e54021f8 | |
|
cdaab82020 | |
|
28248dd83b | |
|
31af2b67b0 | |
|
13cdb84ee8 | |
|
ce66244fd2 | |
|
893434b2d4 | |
|
dc3d1530bf | |
|
323b8498ad | |
|
1d7e438d8a | |
|
61b83441a1 | |
|
62be833aef | |
|
42138a073b | |
|
6d281d96cb | |
|
780052d4da | |
|
d6be1d68b7 | |
|
ea7357b703 | |
|
833437d592 | |
|
744a5c2fef | |
|
665a164f27 | |
|
0e34208344 | |
|
db4afc2408 | |
|
3d0848daae | |
|
20d9448257 | |
|
5f3e490c80 | |
|
4c3e307ec8 | |
|
62e62b6942 | |
|
52d2491931 | |
|
95970b3619 | |
|
d069a783bc | |
|
fb15538247 | |
|
d96d2f5f23 | |
|
754f40f9cb | |
|
59fa3637c3 | |
|
3c26244340 | |
|
43833fba64 | |
|
3d4afbe945 | |
|
27b529f8a0 | |
|
63040d6744 | |
|
fdc22449d6 | |
|
7939bf8cc6 | |
|
f6f0e010d1 | |
|
b25759cd20 | |
|
30d3c76817 | |
|
e0daa65aa6 | |
|
2e12de96ca | |
|
0c5ff5efab | |
|
2e590026e9 | |
|
597ba2b932 | |
|
211b3b760e | |
|
ccc84f11a4 | |
|
0e2d369106 | |
|
b72a217fcc | |
|
9579d62a16 | |
|
6cb25ebad7 | |
|
52da68b591 | |
|
e479dc1ef0 | |
|
4e7a8707cc | |
|
105fdecd0c | |
|
04d234bac1 | |
|
a181a37b12 | |
|
7832005a1f | |
|
679f5ed966 | |
|
e05c884891 | |
|
44f0f7a0a7 | |
|
9195b77e14 | |
|
04d105760d | |
|
968c1df7e9 | |
|
872993f95d | |
|
3984c81faa | |
|
c67e3fe3b7 | |
|
94ed8f9496 | |
|
e5a949a955 | |
|
42dba9dc90 | |
|
b01d97a38f | |
|
04304c322e | |
|
35f0a0d570 | |
|
0c8fba1a2f | |
|
de1c73021c | |
|
274c8189d4 | |
|
3c74bd0c91 | |
|
3fbf6e02a3 | |
|
88919464ef | |
|
ba0525c5c0 | |
|
0978a702d7 | |
|
0fe3b45361 | |
|
24c397dbf8 | |
|
bb82b6dada | |
|
cbe099dcc7 | |
|
610f0c0805 | |
|
cf56596565 | |
|
ba55c7c4ff | |
|
90e62390d9 | |
|
3ac99fa4dc | |
|
56b6b80b9a | |
|
242c23743f | |
|
d290b13871 | |
|
62924cc523 | |
|
55ca93469c | |
|
38cd1b4f4f | |
|
5aa5137fae | |
|
7df2ae88fa | |
|
00c2bae1d3 | |
|
e2e68ff680 | |
|
9ed16e39fa | |
|
b7cd06e8fa | |
|
526652a554 | |
|
b0972a94c3 | |
|
267eb02c31 | |
|
d9523faa76 | |
|
ee6c841d47 | |
|
d9d8fc1ab9 | |
|
c8d97e2791 | |
|
7ec9523ea3 | |
|
d5263be355 | |
|
0f534e32e4 | |
|
a1d462fa81 | |
|
b18c254e5f | |
|
1ad3cd7f36 | |
|
ea800b7418 | |
|
2ddd8e8036 | |
|
bfc69decdd | |
|
e4d0ec9ee1 | |
|
501b29db03 | |
|
97954154bc | |
|
86e9309808 | |
|
c41bd320be | |
|
c7d489b5b6 | |
|
5f645598d8 | |
|
5dfaf5ea9c | |
|
749b3c00f0 | |
|
3ce2ea9e16 | |
|
b37731cdbb | |
|
65c0ab00b6 | |
|
72a156b18a | |
|
664307f968 | |
|
f2f3df9fb1 | |
|
de1522aeee | |
|
9b7803a9b3 | |
|
18c2dce65e | |
|
46c42e55c6 | |
|
109405729b | |
|
cdd9a60f72 | |
|
aa78c50bf1 | |
|
59b292b691 | |
|
7544b7abf9 | |
|
cae7b98136 | |
|
0c19a28266 | |
|
3364eec07e | |
|
ad7651a370 | |
|
ee1156b62b | |
|
93964012e6 | |
|
20404ed8bb | |
|
3f9e4f7a44 | |
|
111d4eafd7 | |
|
debd6c5f0b | |
|
11f799e88e | |
|
a6a80850b7 | |
|
604674dc54 | |
|
eb0ce659cf | |
|
88f65db87f | |
|
033c9cab74 | |
|
c27263c105 | |
|
d48ffac56b | |
|
ca0b19fc9c | |
|
1936e136df | |
|
df0e75ba05 | |
|
8a3cd28973 | |
|
b913e64f95 | |
|
923258b0be | |
|
f12bacf4b4 | |
|
456c6e2279 | |
|
f5df956c18 | |
|
44e8451cd9 | |
|
4dc52bcb6c | |
|
f76960388f | |
|
6973361d60 | |
|
3132c0ab10 | |
|
f211bc983a | |
|
4ddde1a7bd | |
|
d17a009062 | |
|
55ac7e335a | |
|
c55f70c8b7 | |
|
c74dc45bb6 | |
|
9b99570869 | |
|
ebb661532c | |
|
f5900c1f00 | |
|
85d7ad2eef | |
|
6aadf811aa | |
|
0fb55c76d0 | |
|
1a5b6722a8 | |
|
d6f0fc251e | |
|
d1c931cbe8 | |
|
e5063ef3a3 | |
|
6f19295647 | |
|
7667ab73bd | |
|
770a561bce | |
|
4b316a3823 | |
|
3cf2535c23 | |
|
ace2eda073 | |
|
04d4fb536d | |
|
0778151f94 | |
|
646a25667e | |
|
f09c88c1b7 | |
|
2fa47c1837 | |
|
70fb21c35b | |
|
66c42f4fcb | |
|
cc8bc0db20 | |
|
8afb4d8bf0 | |
|
f94eb174c7 | |
|
a93b18dbd5 | |
|
a47f89cf7c | |
|
bcd5d8504c | |
|
709190c4c8 | |
|
aec062d0d3 | |
|
87e8c60faf | |
|
28aa803916 | |
|
5544973814 | |
|
9dba176e8d | |
|
9b70eab194 | |
|
4c51a0f6eb | |
|
4554f17377 | |
|
d7c68ce632 | |
|
a0baba4fa0 | |
|
0abb67c478 | |
|
7b25b0ff88 | |
|
60f4d8f409 | |
|
d086501fba | |
|
6d8029b07e | |
|
f67cfb2ce2 | |
|
fde56c20b4 | |
|
017555651b | |
|
a362d21d6b | |
|
c1b27cc499 | |
|
d71ed635b9 | |
|
e035f2b9c4 | |
|
b934fbaf04 | |
|
22fd411bc3 | |
|
c1902cdb3f | |
|
8eef6a8843 | |
|
2d36d7fb67 | |
|
84906a832f | |
|
a48e569d38 | |
|
c2bd63c186 | |
|
4c7657ee62 | |
|
e192d87731 | |
|
d975f35bba | |
|
3fdf8cf07e | |
|
9a8097682b | |
|
d3d1c69aca | |
|
8a4957570f | |
|
e5b5592a95 | |
|
4e07d4cbf9 | |
|
d7b19fb294 | |
|
c868e509b7 | |
|
f7e3d325fe | |
|
78121ad2d8 | |
|
4dee7a2f6f | |
|
5f26360bd8 | |
|
e8c408b31b | |
|
82af6e7208 | |
|
475d9701e2 | |
|
c7018a45b7 | |
|
0a522cb798 | |
|
a38baec1f8 | |
|
29be2d47e4 | |
|
7ad44051a2 | |
|
08e5b909f9 | |
|
18adb43a44 | |
|
29c8df7e0a | |
|
7ec66a9990 | |
|
ea585dba0f | |
|
57b70a478c | |
|
5888c96da8 | |
|
e6cb11d882 | |
|
a6ed4ae308 | |
|
dbb0e2f75b | |
|
1db976cecb | |
|
1c4b5bcab3 | |
|
1b4fb4b537 | |
|
bcbdee43f7 | |
|
7cbcc65ad0 | |
|
ddc98bf593 | |
|
634a20d89c | |
|
f6ae028e99 | |
|
787842c459 | |
|
31db232704 | |
|
d2b6b570ea | |
|
a2419eb4ea | |
|
b69db15da6 | |
|
264d4e2bce | |
|
0467a7523a | |
|
8810e95082 | |
|
2f615468b6 | |
|
15c8453ba1 | |
|
3c03639cd5 | |
|
dc17ecd236 | |
|
fdc40e071e | |
|
103edde481 | |
|
f6e680ef94 | |
|
8d2a94b0df | |
|
cb6db86a28 | |
|
72ee196efa | |
|
fb933d3204 | |
|
6c3d080e25 | |
|
6259fd23fb | |
|
3345eaca89 | |
|
eca5d2f37f | |
|
758f117442 | |
|
f55b43ddd6 | |
|
cb378600e4 | |
|
b180d3482f | |
|
63f891e393 | |
|
e89cf5f047 | |
|
2806154900 | |
|
b2f6db3533 | |
|
c87c849ec6 | |
|
fbadadf36f | |
|
a667175ec7 | |
|
69477051cc | |
|
543f5b35d0 | |
|
5f8092b045 | |
|
a670ee7940 | |
|
68c4f15958 | |
|
44feb832f9 | |
|
1a06ea7750 | |
|
233a2617cf | |
|
4d603826c8 | |
|
534615cd55 | |
|
7c26345826 | |
|
625c66ef75 | |
|
6e43d642b2 | |
|
38ba5881a0 | |
|
9e58301df7 | |
|
11040d4942 | |
|
76f51a949f | |
|
8ff435831f | |
|
b86a0c8d8f | |
|
2b0a1aeed5 | |
|
01e0f51fad | |
|
e06ea4e84a | |
|
9e9be83a58 | |
|
de51df2770 | |
|
91ee33e956 | |
|
6430230d1f | |
|
b732f094c6 | |
|
ce3e819b33 | |
|
766a24fa77 | |
|
5c30cf3d94 | |
|
6605d7c390 | |
|
ae2f3ecb68 | |
|
699d724000 | |
|
abf527b075 | |
|
d0560e2597 | |
|
b3ff6db730 | |
|
8fa4a6b303 | |
|
e6c00f7eea | |
|
2fd20b17b6 | |
|
fd2b1f018e | |
|
c82f37542d | |
|
349553d011 | |
|
4dae12890f | |
|
bcabe34a2e | |
|
7ec5bf6b10 | |
|
beae3018cb | |
|
f73c04b801 | |
|
66e100ffbf | |
|
9ca743f9fd | |
|
a8d7c2d4ea | |
|
68758e8c21 | |
|
f6ba26ff58 | |
|
9d55f712e3 | |
|
9221ed7b4c | |
|
6f39574ff5 | |
|
6f69e2f12e | |
|
6369f70931 | |
|
9f211b5dd4 | |
|
101b9a193d | |
|
8008d21f5b | |
|
ed7f2651b6 | |
|
2c90e0f521 | |
|
d0c1f0c0b6 | |
|
66d5805594 | |
|
2603a5dee7 | |
|
c85789a3a9 | |
|
a1e8a639b3 | |
|
e543e26206 | |
|
e06c62af77 | |
|
218955ce95 | |
|
7aba881c47 | |
|
31082a0554 | |
|
dfea0ff31d | |
|
485ecc11a6 | |
|
c9c2d7539c | |
|
1a9701cd7c | |
|
8f90d7f8f5 | |
|
69d4cf19b5 | |
|
e7f68ba081 | |
|
5be76bb047 | |
|
619a975025 | |
|
6bf2406dbf | |
|
1c1ef69326 | |
|
cc1b66364c | |
|
318e3ac92c | |
|
982498fab3 | |
|
144189674e | |
|
6ec6527855 | |
|
a8c91fbac9 | |
|
c75aa71816 | |
|
3a04fb4560 | |
|
24fde77c62 | |
|
ccbce0f0a6 | |
|
144b41a45c | |
|
30706b71fb | |
|
0411dc0663 | |
|
4839664a92 | |
|
9b0e0970f9 | |
|
5597776914 | |
|
c49ea9ef4f | |
|
e804de923d | |
|
f9f90b4173 | |
|
1a5530d14d | |
|
5c699f09cb | |
|
af78ecb86b | |
|
8a27050b4e | |
|
ce30a22159 | |
|
b5cfaea705 | |
|
565f67f805 | |
|
e48dcdf72c | |
|
e0f239fa28 | |
|
c314920a3d | |
|
8ca2b4cf49 | |
|
661ba49564 | |
|
9901d49fa5 | |
|
6622cd3277 | |
|
8e375ae340 | |
|
cdacf4f632 | |
|
10c5199d85 | |
|
014c59aa40 | |
|
572b5910bb | |
|
78b94a570c | |
|
217c4f79a0 | |
|
7c9b61b18c | |
|
e8df7c367a | |
|
f64ed60c7b | |
|
fbc2182b9f | |
|
83670fce65 | |
|
fd7e565ce3 | |
|
3432ab2ba7 | |
|
e7515529ce | |
|
ed1924800d | |
|
122d6c6988 | |
|
0d90dddfab | |
|
9de93a866f | |
|
80865351bd | |
|
c6b009ef85 | |
|
6c61de996c | |
|
0b9288ec0b | |
|
9f33d8ad39 | |
|
d87ede0d69 | |
|
3c6826df71 | |
|
84dea55b20 | |
|
004cf887b7 | |
|
1e5460d4c6 | |
|
053ebe7c27 | |
|
846e0838d6 | |
|
7f09085461 | |
|
3f87c2caea | |
|
d037c2dddc | |
|
95b657ba80 | |
|
21e8a940b8 | |
|
3c5cc02b18 | |
|
a71d565138 | |
|
b36af22c94 | |
|
b29ac8fbac | |
|
5a178c4a23 | |
|
ee3640363e | |
|
d50bbf0bbc | |
|
9f012cac2f | |
|
d5105c42e3 | |
|
1eb38e0015 | |
|
78befa59f9 | |
|
a109a80dca | |
|
9ecfa4343a | |
|
1cdef8da57 | |
|
1c10079a67 | |
|
69c71dbc8a | |
|
69e1997ebe | |
|
8ccb4bbb5f | |
|
d0bf750916 | |
|
7efc2d05b7 | |
|
5088e25eaf | |
|
55aaeb25c5 | |
|
6721444114 | |
|
e9361e0492 | |
|
6bfbf35618 | |
|
1ec97bdf4f | |
|
511e42be5e | |
|
b514d4afe2 | |
|
a9e5df44d8 | |
|
c430cd7d53 | |
|
e76583f1ad | |
|
07a5345aa5 | |
|
7709a965e5 | |
|
a2535b80ce | |
|
de5347d0f2 | |
|
96aa18ae44 | |
|
b89bcffea4 | |
|
741da702bc | |
|
80dbb9ba71 | |
|
8ecc557ab0 | |
|
e0258f4506 | |
|
7ac76aba8a | |
|
78d21fa131 | |
|
9b3f2e327f | |
|
c8b3536b01 | |
|
50d2985607 | |
|
5fd82c6f54 | |
|
d9cae04ffc | |
|
6230f7be4f | |
|
e8ad05913f | |
|
c740fccc9d | |
|
7720dde74d | |
|
00bee2a6bd | |
|
883d5b6e7c | |
|
2382684e94 | |
|
44fa2c4b49 | |
|
9601a2abf0 | |
|
52e40025c4 | |
|
10dbb00f5f | |
|
7dffe9339b | |
|
d8a422575b | |
|
46d2f80c38 | |
|
a02da8e6f6 | |
|
e6f6e1ad0a | |
|
eec2e1d3b1 | |
|
3504bb587d | |
|
73137ace84 | |
|
c5dad8b626 | |
|
fda46ce56f | |
|
70a7c7f389 | |
|
c5202b728a | |
|
675bc39658 | |
|
c2815fd44d | |
|
3695ae97b4 | |
|
4b43aebdc7 | |
|
cf5b09ede2 | |
|
27fba3df43 | |
|
b54ef3372d | |
|
00bf6674b3 | |
|
ddfee63055 | |
|
549435aee5 | |
|
fab396f149 | |
|
5d6d76c61f | |
|
bfd020047d | |
|
6ca59519c9 | |
|
f3758d1d0a | |
|
641c223d3c | |
|
2530235139 | |
|
38ec1c0e73 | |
|
ccb86448eb | |
|
78685ec6aa | |
|
27f65c2c77 | |
|
fdd9088e05 | |
|
6f873078d4 | |
|
2118a3ce47 | |
|
2827a9554c | |
|
7d52b4d0b5 | |
|
4a9e70ccde | |
|
b60c5fa450 | |
|
12cc465144 | |
|
5e19e0053a | |
|
99ef23b62c | |
|
d595a4ebe3 | |
|
91fa2ff395 | |
|
8d76d3263d | |
|
533a36f05a | |
|
b9e9e0e133 | |
|
a02ac01be3 | |
|
9396d8433a | |
|
ef94e7e847 | |
|
8ad078f46f | |
|
a290d7a78d | |
|
b6dea80907 | |
|
79be26ff1f | |
|
b86eea0897 | |
|
90cdf43b5f | |
|
3d46d3f7a1 | |
|
2fa257313a | |
|
7964a313e8 | |
|
0977633457 | |
|
8e27418dd3 | |
|
a39dc1f7a8 | |
|
45f992b27b | |
|
3d7aa73867 | |
|
7ac2ce25e3 | |
|
975d14b799 | |
|
01d21cdd9f | |
|
f1d37c54c8 | |
|
dd4c8aa45e | |
|
b3d782f818 | |
|
aa86a022fa | |
|
b6ba595862 | |
|
3417fc0cca | |
|
e0dfc14983 | |
|
c89dba9435 | |
|
7b50f5d324 | |
|
211c1e23be | |
|
703c17ae41 | |
|
23148d283f | |
|
dea94f2bad | |
|
10a2d57055 | |
|
40b2e7669a | |
|
8aa38fe73e | |
|
705b3da7cb | |
|
34e7f69d69 | |
|
50b9921642 | |
|
f8a66072e7 | |
|
a406f19479 | |
|
6becc69ec9 | |
|
5a2ef794dc | |
|
e75f483aeb | |
|
1b8330d1f8 | |
|
0db191d3bf | |
|
abb56152ff | |
|
73ffab70b0 | |
|
4af85f4c19 | |
|
44a3d6e74d | |
|
4f06ce2550 | |
|
7bc8dbb991 | |
|
922b7f415d | |
|
bb92fd4c90 | |
|
7c995b78b2 | |
|
54e5ef39c0 | |
|
306cf11d87 | |
|
ee31be167b | |
|
62f37ee319 | |
|
d6649a8a4b | |
|
f6fe439718 | |
|
702eed5cbd | |
|
966e653935 | |
|
04d89a8bc5 | |
|
f17b0f975d | |
|
cb6f584496 | |
|
6af748171a | |
|
879cadd34e | |
|
e537382991 | |
|
284233c34f | |
|
5373187186 | |
|
bf86110fc5 | |
|
b5cefada92 | |
|
c6c7fccd96 | |
|
9e98f497af | |
|
2f11914613 | |
|
32c30481d3 | |
|
b3e76d6678 | |
|
1fb9535e99 | |
|
afdf4dc890 | |
|
08a4c62aac | |
|
e128e6c08d | |
|
affc59454e | |
|
6dfc8ce00b | |
|
b7c95d483a | |
|
400f4e7f27 | |
|
ad3a455db9 | |
|
a53ab146fe | |
|
87293d1b15 | |
|
c73a8cde83 | |
|
9dd059376c | |
|
f0303978e3 | |
|
642b349e94 | |
|
cc56b4f073 | |
|
aab43b3c76 | |
|
f6f46b4ee2 | |
|
a6a0568316 | |
|
5642b880c3 | |
|
02a086599c | |
|
dc61f471da | |
|
b7dc4f2990 | |
|
5d054258af | |
|
3f7e0cf5f0 | |
|
1d461687d2 | |
|
50b120927d | |
|
76ed2255ef | |
|
07111828c5 | |
|
f574ca934c | |
|
672e8e99b7 | |
|
2585f322cb | |
|
248b8e647a | |
|
4f80fab337 | |
|
edf5082a4c | |
|
d6890cb847 | |
|
098094c5cb | |
|
8f065810f6 | |
|
7036dceb0e | |
|
c94728b53a | |
|
d9bbc416a6 | |
|
91cb0fc443 | |
|
5bd86b94f9 | |
|
cd64610c66 | |
|
5b1b43c68c | |
|
64da8f0c8d | |
|
41aa80d4a1 | |
|
34b14d2fee | |
|
dd920f602e | |
|
b482c90e1a | |
|
4ee4a36c0c | |
|
8f63557ed7 | |
|
c5c5ab9724 | |
|
129e02b57d | |
|
9192c0480a | |
|
b899a412e3 | |
|
5773794baf | |
|
1458f7d974 | |
|
826108373c | |
|
e136a4168b | |
|
87e216b740 | |
|
07d75c99db | |
|
01dcfb360e | |
|
162f160def | |
|
37602e153b | |
|
42d033e738 | |
|
bf4e2e0eac | |
|
3721dbfddb | |
|
0cba1ce747 | |
|
bf926e31a0 | |
|
d3047011d0 | |
|
83fdfa511d | |
|
5d24f6e098 | |
|
8b90d5e17f | |
|
abcab0331f | |
|
e8d56ca415 | |
|
7febdc7334 | |
|
198560fc1f | |
|
9714638f3b | |
|
adfb7cd35a | |
|
576ff57db0 | |
|
eb30cde777 | |
|
4ffd537d2d | |
|
1491ec42da | |
|
4b03bdc3ab | |
|
bec1e6b149 | |
|
92a0fc0435 | |
|
c4635c68d2 | |
|
7ea0e9f277 | |
|
caeed70f28 | |
|
23b6f3e3f5 | |
|
c012d770f7 | |
|
c5f239f411 | |
|
b9460ab724 | |
|
ae5275c09f | |
|
17dd4c9e9a | |
|
a7a230ebef | |
|
c011a0e2ed | |
|
5548406667 | |
|
71eaab9d8c | |
|
430d37846f | |
|
21ed6582ce | |
|
64a2ca4dba | |
|
54ec17ff64 | |
|
2de400a541 | |
|
85cf4b235d | |
|
f6fc4c2883 | |
|
ad4dae0844 | |
|
d3bcd63a40 | |
|
5ee8b12dc3 | |
|
d09abe86c1 | |
|
dabd2e7207 | |
|
83925f04c3 | |
|
55b02f753f | |
|
d6dbdd97e9 | |
|
defcd9b025 | |
|
de896caceb | |
|
6c08fe9796 | |
|
917ecca58e | |
|
352fdd1bb0 | |
|
248c7787c7 | |
|
019ffe8a5b | |
|
c89b131f29 | |
|
0aefa18690 | |
|
f0c1b32120 | |
|
94fda895ac | |
|
1e2c7fce86 | |
|
d37214cb16 | |
|
253f447329 | |
|
9cd3f03f65 | |
|
60001a75a2 | |
|
bdf26f87d5 | |
|
0dcdb5e7a1 | |
|
0aa2ba0c03 | |
|
da2a216934 | |
|
87bd718de5 | |
|
1ca4d6b029 | |
|
3fd8098881 | |
|
e57a52e7f7 | |
|
93cd3a79b2 | |
|
525fa6ada0 | |
|
c59aacf944 | |
|
d79a00bf02 | |
|
16a51bbab2 | |
|
858a1940b5 | |
|
441bac139f | |
|
3923ff005d | |
|
038285d496 | |
|
768131e488 | |
|
4c363a564f | |
|
12ede67c62 | |
|
e9c1f0f7d3 | |
|
f91e89fd9f | |
|
be8403e73d | |
|
6ff478632a | |
|
8a6930c138 | |
|
06ab41a160 | |
|
eb5886ddbe | |
|
b790e5ea34 | |
|
863acb26c0 | |
|
29da97c185 | |
|
e69bbfd0d6 | |
|
c9760569ae | |
|
431ec52b9c | |
|
325cba6414 | |
|
bfb59fd4d7 | |
|
037710b1d4 | |
|
44b1ff16e9 | |
|
baf2319fd3 | |
|
54b7ca56c0 | |
|
37cb3eb8dd | |
|
1ecc1b5987 | |
|
1477401acd | |
|
90c8452959 | |
|
1336ad2a23 | |
|
0e927533b0 | |
|
82443ea46b | |
|
237c2cf2fb | |
|
513eca8dab | |
|
50b5f8558e | |
|
8bc5a92a98 | |
|
de9ff46629 | |
|
2649600fa1 | |
|
d2329ac07a | |
|
83a5d03bf3 | |
|
1f15dd093d | |
|
c94ab99ae2 | |
|
49115e9d5d | |
|
c045253927 | |
|
5d008d9030 | |
|
61612ecb36 | |
|
c15ca3793e | |
|
44cea53e72 | |
|
78e9e692e8 | |
|
6485fadc16 | |
|
713c1661b7 | |
|
154fe8696f | |
|
f6c36f8881 | |
|
52805feae9 | |
|
262740bc9a | |
|
ebecc5404b | |
|
754179dacd | |
|
c491a21d25 | |
|
0e76f92de7 | |
|
cd95d70df0 | |
|
fbf11a41e1 | |
|
76bcddf071 | |
|
768fbaad54 | |
|
44a4792fd8 | |
|
63df2bcbe6 | |
|
fb3bea8014 | |
|
6284af121f | |
|
dc7c6c4860 | |
|
e18599b05e | |
|
526ae5944c | |
|
02df7b7ac8 | |
|
61f8cdfb9e | |
|
eb8360bda3 | |
|
c8d95acc37 | |
|
eef8b3dde8 | |
|
c881008e1c | |
|
8058e338ea | |
|
68a8d99055 | |
|
c11c6c4568 | |
|
ef846a8839 | |
|
1245730ea2 | |
|
6136fe87d1 | |
|
c88c54fb38 | |
|
0b40d09a21 | |
|
b0a663d39d | |
|
7c6212a0f7 | |
|
5913040110 | |
|
f47445f142 | |
|
aaa3fcf66f | |
|
2eae9ec7c8 | |
|
44531e16e0 | |
|
ce8855ca2a | |
|
07e2e0f60c | |
|
44c4773d58 | |
|
70ffda3ea3 | |
|
85b0872650 | |
|
40bfd9f8f7 | |
|
e06c9e43af | |
|
0724b3c453 | |
|
7693f61d81 | |
|
238d1c078f | |
|
9595f95452 | |
|
372a52ecc0 | |
|
8348fc3ef8 | |
|
4471a83ed0 | |
|
51fc7ddb29 | |
|
f0945e112f | |
|
8c8164c4a6 | |
|
ccd313e01a | |
|
1328477a82 | |
|
2934a72920 | |
|
c2db691cad | |
|
be1e7647c3 | |
|
1fdaaf697a | |
|
ee43ef3c9d | |
|
f0ddcd361e | |
|
0f9b2bfa64 | |
|
368d0146fb | |
|
0fdb41fe7c | |
|
7c6e06fd13 | |
|
85757665e6 | |
|
346188c015 | |
|
79c7591dc1 | |
|
b4ed8b3d74 | |
|
5217456b50 | |
|
31aa7f4c95 | |
|
009cd634a2 | |
|
41bf1c6871 | |
|
36395e5b1c | |
|
1ac5257357 | |
|
df417b7e95 | |
|
b98522b38f | |
|
e410ff8dd4 | |
|
616f06c25c | |
|
99f3c643bf | |
|
afeb941ca0 | |
|
7bb9d48dd1 | |
|
1b0e4c7e6e | |
|
feb0e1c74d | |
|
ab80ad902e | |
|
ec3f432bbb | |
|
45c1a3621c | |
|
86c0b9986b | |
|
8dec751a6d | |
|
5012121d33 | |
|
87836dcb55 | |
|
8ad2cc39eb | |
|
bae8d7593c | |
|
e4a7075a9e | |
|
3e03f786ee | |
|
e44bed0c2b | |
|
fa05d3cde6 | |
|
efe6414640 | |
|
a9cbfd950e | |
|
28cedb5623 | |
|
971de474f0 | |
|
65abd4e92a | |
|
8b744412aa | |
|
2b418b4d88 | |
|
b0144c7ded | |
|
c674241ec0 | |
|
6949d0fd38 | |
|
2072d59da5 | |
|
330c50b48d | |
|
297354f847 | |
|
bad1e9afa8 | |
|
9feeb2738d | |
|
379835c42f | |
|
801c7670b7 | |
|
c236f60bb6 | |
|
7e990a2991 | |
|
e81d2086c0 | |
|
f0d03fb892 | |
|
30226eb1fb | |
|
0032954c75 | |
|
aaf490d794 | |
|
74f7be7287 | |
|
1dbcfdaf81 | |
|
62da61716f | |
|
e8872d9ed7 | |
|
26af316b3b | |
|
c32d89ee3e | |
|
6d0ee53e1a | |
|
1ae2d976c0 | |
|
d9bb792794 | |
|
39fd2335bf | |
|
c72efcd1ce | |
|
13f35139d3 | |
|
cd4827b3b6 | |
|
9686895b4e | |
|
bd387da62d | |
|
751a21d94f | |
|
a145430afa | |
|
6ef5d18757 | |
|
842df2bd6c | |
|
595f324f8b | |
|
61e2ebac90 | |
|
b6377b59ff | |
|
cccca368c5 | |
|
92c85858a9 | |
|
b61a98c417 | |
|
b2bd536308 | |
|
58bcec9d94 | |
|
f82a27f55a | |
|
a54ed85881 | |
|
1d835f2035 | |
|
e05a85327f | |
|
b1a47245a1 | |
|
84d2f30faa | |
|
c611a8f7e7 | |
|
45c0877e34 | |
|
d2ca220fda | |
|
6c8f66ff59 | |
|
a7f48aab69 | |
|
86e20f48c6 | |
|
b937c7b05e | |
|
4a4da256dd | |
|
d5530b26d7 | |
|
c930160286 | |
|
155d57b01d | |
|
3c5dbfd97c | |
|
bf93d2e67c | |
|
58df3eda9f | |
|
d177abecae | |
|
c18c419b56 | |
|
c768309ab4 | |
|
9e68ed2159 | |
|
c618a76540 | |
|
eb22ae97b0 | |
|
315bf08733 | |
|
00ccb89288 | |
|
9814213a91 | |
|
5118189a2b | |
|
d3d1bac1c2 | |
|
cae533cad2 | |
|
a6a5a19356 | |
|
3c13527ead | |
|
4615ce9099 | |
|
7f9bbaaa17 | |
|
79e03c7d45 | |
|
a8a4a12c4b | |
|
78d6eed6b3 | |
|
e0d4f75172 | |
|
ea83957ecc | |
|
909b0213b3 | |
|
b64a8a7f98 | |
|
11b598fe33 | |
|
e77c046cf9 | |
|
9b85a8b43d | |
|
fd0a845cb4 | |
|
a3ba82885c | |
|
363bf44a35 | |
|
c9c31f803e | |
|
dc13bb827d | |
|
dcae6f1431 | |
|
8bf9f5bb8b | |
|
e8c00e918a | |
|
8f28f5b2f8 | |
|
e91417ea8d | |
|
019fe8bb7e | |
|
47a30957d1 | |
|
b03eebf7d4 | |
|
1a2e82e327 | |
|
80467f6ae8 | |
|
70f3a0bb27 | |
|
42126c2022 | |
|
034384f5aa | |
|
32148808ad | |
|
8ab4d91380 | |
|
1a23c1425f | |
|
5e0ef70cc0 | |
|
d66b9966e9 | |
|
e7a8ea84c3 | |
|
5947160630 | |
|
15d8f1806e | |
|
d6cc718472 | |
|
61095f4a12 | |
|
445750aa9a | |
|
c02e9c2bb1 | |
|
21c6cc5e4c | |
|
47e5212823 | |
|
0758a4fc9d | |
|
5c942bd597 | |
|
af2f69e6c1 | |
|
72f28ed0b3 | |
|
2176c63856 | |
|
27609ba0d9 | |
|
d28a7da95d | |
|
cfed5766b7 | |
|
1edc42157b | |
|
781ed1ff02 | |
|
666498db01 | |
|
2988ebb6f3 | |
|
347bdb6d9a | |
|
7693fdb8a7 | |
|
74c0d03f00 | |
|
42e485dcc3 | |
|
8b18d389b3 | |
|
51bbf31742 | |
|
9412d34e2d | |
|
da4df82532 | |
|
cdb6fdbc6c | |
|
c2288a7b88 | |
|
1139234117 | |
|
f72686c0b6 | |
|
2ca3bdc35e | |
|
06d5aa5780 | |
|
da63d11d34 | |
|
f8e02db4bc | |
|
c608fc89d8 | |
|
70883fd10b | |
|
69b2279092 | |
|
7a149fe5ba | |
|
c9d6b18eef | |
|
064f64dbf7 | |
|
6357e166f9 | |
|
16af1972d6 | |
|
46c83cbf3d | |
|
7720ce7827 | |
|
e3343cf7d1 | |
|
11e94c406b | |
|
61d6408fdb | |
|
90ede7f838 | |
|
904312a0b2 | |
|
044a9f28d6 | |
|
22d345d982 | |
|
d698334620 | |
|
906c0766df | |
|
98d949718c | |
|
05803511db | |
|
06f4c3945d | |
|
21397e2b4a | |
|
321537ee92 | |
|
52281cb8ba | |
|
be90062c51 | |
|
8da9d9679e | |
|
fa5d709fc3 | |
|
9d650a7c1a | |
|
ad28f57533 | |
|
e51d507799 | |
|
39af3535c3 | |
|
326c8bc818 | |
|
6129a6f93e | |
|
40513f1a0e | |
|
5dc3a9c754 | |
|
01d4506253 | |
|
455a9bd0ef | |
|
f81aa6a172 | |
|
ab4f642153 | |
|
0b882475ad | |
|
f0d818f36e | |
|
85e299e6c5 | |
|
904c37845d | |
|
ffcf25cc25 | |
|
25d0d1be3a | |
|
ab4dc1636c | |
|
f679895c77 | |
|
0281b58d2f | |
|
83c1ba7783 | |
|
50ade3671f | |
|
5f092c55d1 | |
|
d10f8a98ec | |
|
4a1015faff | |
|
9acca4fc73 | |
|
6787ff521b | |
|
507d9bc19e | |
|
d3bd5f2a7b | |
|
6977f3a843 | |
|
e041158988 | |
|
1fa9e0203b | |
|
6595db6409 | |
|
1674ca725c | |
|
7516a98167 | |
|
13db99b0f8 | |
|
30308e35fa | |
|
8707a9b7ec | |
|
c682d97841 | |
|
34303e1b47 | |
|
b614ded3fc | |
|
5ee52a3ab9 | |
|
1282c3b12f | |
|
7c309ba4d3 | |
|
3f617631cb | |
|
e0bbafc253 | |
|
07737e85cc | |
|
41f9916ae5 | |
|
a71649dde9 | |
|
e7f8cc6801 | |
|
7bce056f1d | |
|
a4c7c6db09 | |
|
4bb391c896 | |
|
175af4f74f | |
|
68a69ee079 | |
|
52c67284e2 | |
|
9be1af3afb | |
|
7017fa95b8 | |
|
8afc1ed68c | |
|
8d2e8d8a06 | |
|
a3c699eee5 | |
|
348f52b5fc | |
|
613f9c6f8d | |
|
c9859f187f | |
|
c99d156f0d | |
|
3ff6a5def3 | |
|
208e6da3c3 | |
|
471b873de3 | |
|
0df5019609 | |
|
ba14e196f6 | |
|
dfc5a40288 | |
|
37adcac5d1 | |
|
68820d6c3d | |
|
f416efa918 | |
|
7e990a6bdf | |
|
2fddec56e8 | |
|
f22a5d1704 | |
|
c5376c2d2c | |
|
e63b70263a | |
|
29726e6816 | |
|
2bad34e024 | |
|
273b280f46 | |
|
2fea2fced8 | |
|
4b051aa926 | |
|
f2943bdf61 | |
|
7f2bd0b211 | |
|
144d2041ad | |
|
f0594fb732 | |
|
1f722f5c80 | |
|
70a084c119 | |
|
57ffb35de0 | |
|
d9fdd28ac6 | |
|
b1a63bcd84 | |
|
7c05933e51 | |
|
df972677c1 | |
|
a13aeb9711 | |
|
32fc25f151 | |
|
062583ed58 | |
|
6d3f3b9300 | |
|
2b04857343 | |
|
3b35043d00 | |
|
ebdbe177d6 | |
|
802ef9da8a | |
|
e6fd880686 | |
|
b5cb6de232 | |
|
a2cbb4e417 | |
|
c067fbc010 | |
|
51f8c22f4d | |
|
5bbb44482b | |
|
1c5ca793c0 | |
|
346b43e937 | |
|
a9b1d9e838 | |
|
a7b538008b | |
|
21e1953b61 | |
|
ff29843d87 | |
|
8bb2dc68ea | |
|
7e521fed97 | |
|
31f721286a | |
|
8fc16890c7 | |
|
b5597f5b44 | |
|
f0781cd792 | |
|
5d1ba0f446 | |
|
e0e5a167ed | |
|
471f9a3f6a | |
|
1f799c1cbd | |
|
4da4a15d6b | |
|
a420d2c41e | |
|
18775fda0f | |
|
acb171804e | |
|
cff1c2f740 | |
|
fc6c0ca12e | |
|
01818ad2c8 | |
|
35bc3e662a | |
|
be4b9f7f5b | |
|
515679e4fe | |
|
774548696c | |
|
98cd11c019 | |
|
7fc58e704a | |
|
96e8e9b098 | |
|
efd294ef09 | |
|
8a5e4768e1 | |
|
6ca82087b1 | |
|
8681e4ab8a | |
|
16f22940d9 | |
|
dc6ef658b6 | |
|
ce5040a5a1 | |
|
e8db36d5d8 | |
|
fadd4706ed | |
|
5df606d8ab | |
|
5cde35923c | |
|
bcd5f7d259 | |
|
6bd7a62c09 | |
|
9cbbfa957c | |
|
e8855ee462 | |
|
e959b882d5 | |
|
2122e49bea | |
|
533ea6d7ef | |
|
3084cee7bc | |
|
2d9661f189 | |
|
d0479cc2bc | |
|
16e5e9541b | |
|
685a5a11a9 | |
|
fe72400bad | |
|
3317134adf | |
|
b58e8451b8 | |
|
6c649bab53 | |
|
cde544de81 | |
|
1e568d84df | |
|
4c9423278a | |
|
aa9ea95e1f | |
|
447835afc1 | |
|
eaa98f6aff | |
|
85a2ee6d30 | |
|
f2d3b1000f | |
|
626c98d754 | |
|
3ebf079a9a | |
|
a3c3b928a3 | |
|
b81bb2ef30 | |
|
9971db02ff | |
|
0e57effd38 | |
|
e97c2c3639 | |
|
51416738ea | |
|
fd25e2ca11 | |
|
61a6f2b928 | |
|
2a63f4fc61 | |
|
5d8bd4d343 | |
|
d113e48a2a | |
|
02d664b37f | |
|
1724261910 | |
|
6bb7639a0f | |
|
5bddb5a909 | |
|
cbb2781fed | |
|
41cbb80e25 | |
|
21b75e5d12 | |
|
480a31ea4e | |
|
2bdd1d0896 | |
|
11bf87d678 | |
|
3b4824a2fe | |
|
9796abcced |
|
@ -1,24 +1,36 @@
|
||||||
image: alpine/edge
|
image: alpine/edge
|
||||||
packages:
|
packages:
|
||||||
- eudev-dev
|
- eudev-dev
|
||||||
- freerdp-dev
|
|
||||||
- ffmpeg-dev
|
- ffmpeg-dev
|
||||||
- libcap-dev
|
- glslang
|
||||||
- libinput-dev
|
- libinput-dev
|
||||||
- libxkbcommon-dev
|
- libxkbcommon-dev
|
||||||
- mesa-dev
|
- mesa-dev
|
||||||
- meson
|
- meson
|
||||||
- pixman-dev
|
- pixman-dev
|
||||||
|
- vulkan-headers
|
||||||
|
- vulkan-loader-dev
|
||||||
- wayland-dev
|
- wayland-dev
|
||||||
- wayland-protocols
|
- wayland-protocols
|
||||||
- xcb-util-image-dev
|
- xcb-util-image-dev
|
||||||
|
- xcb-util-renderutil-dev
|
||||||
- xcb-util-wm-dev
|
- xcb-util-wm-dev
|
||||||
|
- xwayland
|
||||||
|
- libseat-dev
|
||||||
sources:
|
sources:
|
||||||
- https://github.com/swaywm/wlroots
|
- https://gitlab.freedesktop.org/wlroots/wlroots.git
|
||||||
tasks:
|
tasks:
|
||||||
- setup: |
|
- setup: |
|
||||||
cd wlroots
|
cd wlroots
|
||||||
meson build -Dauto_features=enabled -Dlogind=disabled -Dxcb-errors=disabled
|
meson build --fatal-meson-warnings --default-library=both -Dauto_features=enabled -Dxcb-errors=disabled
|
||||||
- build: |
|
- build: |
|
||||||
cd wlroots
|
cd wlroots
|
||||||
ninja -C build
|
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
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
image: archlinux
|
image: archlinux
|
||||||
packages:
|
packages:
|
||||||
- clang
|
- clang
|
||||||
- freerdp
|
|
||||||
- ffmpeg
|
- ffmpeg
|
||||||
- libcap
|
|
||||||
- libinput
|
- libinput
|
||||||
- libxkbcommon
|
- libxkbcommon
|
||||||
- mesa
|
- mesa
|
||||||
|
@ -13,17 +11,35 @@ packages:
|
||||||
- wayland-protocols
|
- wayland-protocols
|
||||||
- xcb-util-errors
|
- xcb-util-errors
|
||||||
- xcb-util-image
|
- xcb-util-image
|
||||||
|
- xcb-util-renderutil
|
||||||
- xcb-util-wm
|
- xcb-util-wm
|
||||||
|
- xorg-xwayland
|
||||||
|
- seatd
|
||||||
|
- vulkan-icd-loader
|
||||||
|
- vulkan-headers
|
||||||
|
- glslang
|
||||||
sources:
|
sources:
|
||||||
- https://github.com/swaywm/wlroots
|
- https://gitlab.freedesktop.org/wlroots/wlroots.git
|
||||||
tasks:
|
tasks:
|
||||||
- setup: |
|
- setup: |
|
||||||
cd wlroots
|
cd wlroots
|
||||||
CC=gcc meson build-gcc -Dauto_features=enabled
|
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 -Dauto_features=enabled
|
CC=clang meson build-clang --fatal-meson-warnings -Dauto_features=enabled
|
||||||
- gcc: |
|
- gcc: |
|
||||||
cd wlroots/build-gcc
|
cd wlroots/build-gcc
|
||||||
ninja
|
ninja
|
||||||
|
sudo ninja install
|
||||||
|
cd ../tinywl
|
||||||
|
CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" make
|
||||||
- clang: |
|
- clang: |
|
||||||
cd wlroots/build-clang
|
cd wlroots/build-clang
|
||||||
ninja
|
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
|
image: freebsd/latest
|
||||||
packages:
|
packages:
|
||||||
- devel/evdev-proto
|
- devel/evdev-proto
|
||||||
- devel/libepoll-shim
|
- devel/libepoll-shim
|
||||||
- devel/libudev-devd
|
- devel/libudev-devd
|
||||||
- devel/meson # implies ninja
|
- devel/meson # implies ninja
|
||||||
- devel/pkgconf
|
- devel/pkgconf
|
||||||
- graphics/libdrm
|
- graphics/glslang
|
||||||
- graphics/mesa-libs
|
- graphics/libdrm
|
||||||
- graphics/png
|
- graphics/mesa-libs
|
||||||
- graphics/wayland
|
- graphics/png
|
||||||
- graphics/wayland-protocols
|
- graphics/vulkan-headers
|
||||||
- multimedia/ffmpeg
|
- graphics/vulkan-loader
|
||||||
- net/freerdp
|
- graphics/wayland
|
||||||
- x11/libX11
|
- graphics/wayland-protocols
|
||||||
- x11/libinput
|
- multimedia/ffmpeg
|
||||||
- x11/libxcb
|
- x11/libX11
|
||||||
- x11/libxkbcommon
|
- x11/libinput
|
||||||
- x11/pixman
|
- x11/libxcb
|
||||||
- x11/xcb-util-errors
|
- x11/libxkbcommon
|
||||||
- x11/xcb-util-wm
|
- x11/pixman
|
||||||
|
- x11/xcb-util-errors
|
||||||
|
- x11/xcb-util-renderutil
|
||||||
|
- x11/xcb-util-wm
|
||||||
|
- x11-servers/xwayland
|
||||||
|
- sysutils/seatd
|
||||||
|
- gmake
|
||||||
sources:
|
sources:
|
||||||
- https://github.com/swaywm/wlroots
|
- https://gitlab.freedesktop.org/wlroots/wlroots.git
|
||||||
tasks:
|
tasks:
|
||||||
- wlroots: |
|
- wlroots: |
|
||||||
cd wlroots
|
cd wlroots
|
||||||
meson build -Dauto_features=enabled -Dlogind=disabled -Dlibcap=disabled
|
meson build --fatal-meson-warnings -Dauto_features=enabled
|
||||||
ninja -C build
|
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
|
131
CONTRIBUTING.md
131
CONTRIBUTING.md
|
@ -1,22 +1,21 @@
|
||||||
# Contributing to wlroots
|
# Contributing to wlroots
|
||||||
|
|
||||||
Contributing just involves sending a pull request. You will probably be more
|
Contributing just involves sending a merge request. You will probably be more
|
||||||
successful with your contribution if you visit
|
successful with your contribution if you visit [#sway-devel on Libera Chat]
|
||||||
[#sway-devel](https://webchat.freenode.net/?channels=sway-devel) on
|
upfront and discuss your plans.
|
||||||
irc.freenode.net upfront and discuss your plans.
|
|
||||||
|
|
||||||
Note: rules are made to be broken. Adjust or ignore any/all of these as you see
|
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.
|
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
|
don't, however, allow me to make a suggestion: feature branches pulled from
|
||||||
upstream. Try this:
|
upstream. Try this:
|
||||||
|
|
||||||
1. Fork wlroots
|
1. Fork wlroots
|
||||||
2. `git clone https://github.com/username/wlroots && cd wlroots`
|
2. `git clone git@gitlab.freedesktop.org:<username>/wlroots.git && cd wlroots`
|
||||||
3. `git remote add upstream https://github.com/swaywm/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
|
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:
|
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`
|
2. `git checkout -b add-so-and-so-feature upstream/master`
|
||||||
3. Add and commit your changes
|
3. Add and commit your changes
|
||||||
4. `git push -u origin add-so-and-so-feature`
|
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
|
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
|
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
|
(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
|
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
|
of so we can adequately test them - then verify the test plan yourself before
|
||||||
submitting.
|
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
|
## Commit Messages
|
||||||
|
|
||||||
Please strive to write good commit messages. Here's some guidelines to follow:
|
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
|
The first line should be limited to 50 characters and should be a sentence that
|
||||||
completes the thought [When applied, this commit will...] *"Implement
|
completes the thought [When applied, this commit will...] *"Implement
|
||||||
cmd_move"* or *"Fix #742"* or *"Improve performance of arrange_windows on ARM"*
|
cmd_move"* or *"Improve performance of arrange_windows on ARM"* or similar.
|
||||||
or similar.
|
|
||||||
|
|
||||||
The subsequent lines should be separated from the subject line by a single
|
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
|
blank line, and include optional details. In this you can give justification
|
||||||
for the change, [reference Github
|
for the change, [reference issues], or explain some of the subtler
|
||||||
issues](https://help.github.com/articles/closing-issues-via-commit-messages/),
|
details of your patch. This is important because when someone finds a line of
|
||||||
or explain some of the subtler details of your patch. This is important because
|
code they don't understand later, they can use the `git blame` command to find
|
||||||
when someone finds a line of code they don't understand later, they can use the
|
out what the author was thinking when they wrote it. It's also easier to review
|
||||||
`git blame` command to find out what the author was thinking when they wrote
|
your merge requests if they're separated into logical commits that have good
|
||||||
it. It's also easier to review your pull requests if they're separated into
|
commit messages and justify themselves in the extended commit description.
|
||||||
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
|
As a good rule of thumb, anything you might put into the merge request
|
||||||
description on Github is probably fair game for going into the extended commit
|
description on GitLab is probably fair game for going into the extended commit
|
||||||
message as well.
|
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
|
## 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
|
some feedback - you may be asked to make changes to your work. Our code review
|
||||||
process is:
|
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
|
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).
|
with an @mention if necessary).
|
||||||
2. **Review** the code. Look for code style violations, naming convention
|
2. **Review** the code. Look for code style violations, naming convention
|
||||||
violations, buffer overflows, memory leaks, logic errors, non-portable code
|
violations, buffer overflows, memory leaks, logic errors, non-portable code
|
||||||
(including GNU-isms), etc. For significant changes to the public API, loop in
|
(including GNU-isms), etc. For significant changes to the public API, loop in
|
||||||
a couple more people for discussion.
|
a couple more people for discussion.
|
||||||
3. **Execute** the test plan, if present.
|
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.
|
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
|
## Style Reference
|
||||||
|
|
||||||
wlroots is written in C with a style similar to the [kernel
|
wlroots is written in C with a style similar to the [kernel style], but with a
|
||||||
style](https://www.kernel.org/doc/Documentation/process/coding-style.rst), but
|
few notable differences.
|
||||||
with a few notable differences.
|
|
||||||
|
|
||||||
Try to keep your code conforming to C11 and POSIX as much as possible, and do
|
Try to keep your code conforming to C11 and POSIX as much as possible, and do
|
||||||
not use GNU extensions.
|
not use GNU extensions.
|
||||||
|
@ -163,7 +200,7 @@ zeroed value and exit cleanly; this simplifies error handling a lot.
|
||||||
### Error Codes
|
### Error Codes
|
||||||
|
|
||||||
For functions not returning a value, they should return a (stdbool.h) bool to
|
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
|
### Macros
|
||||||
|
|
||||||
|
@ -242,14 +279,12 @@ at least one struct for each interface in the protocol. For instance,
|
||||||
### Globals
|
### Globals
|
||||||
|
|
||||||
Global interfaces generally have public constructors and destructors. Their
|
Global interfaces generally have public constructors and destructors. Their
|
||||||
struct has a field holding the `wl_global` itself, a list of resources clients
|
struct has a field holding the `wl_global` itself, a destroy signal and a
|
||||||
created by binding to the global, a destroy signal and a `wl_display` destroy
|
`wl_display` destroy listener. Example:
|
||||||
listener. Example:
|
|
||||||
|
|
||||||
```c
|
```c
|
||||||
struct wlr_compositor {
|
struct wlr_compositor {
|
||||||
struct wl_global *global;
|
struct wl_global *global;
|
||||||
struct wl_list resources;
|
|
||||||
…
|
…
|
||||||
|
|
||||||
struct wl_listener display_destroy;
|
struct wl_listener display_destroy;
|
||||||
|
@ -262,8 +297,9 @@ struct wlr_compositor {
|
||||||
```
|
```
|
||||||
|
|
||||||
When the destructor is called, it should emit the destroy signal, remove the
|
When the destructor is called, it should emit the destroy signal, remove the
|
||||||
display destroy listener, destroy the `wl_global`, destroy all bound resources
|
display destroy listener, destroy the `wl_global` and then destroy the struct.
|
||||||
and then destroy the struct.
|
The destructor can assume all clients and resources have been already
|
||||||
|
destroyed.
|
||||||
|
|
||||||
### Resources
|
### Resources
|
||||||
|
|
||||||
|
@ -285,13 +321,22 @@ struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If a pointer to a `wl_resource` is stored, a resource destroy handler needs to
|
||||||
|
be registered to clean it up. libwayland will automatically destroy resources
|
||||||
|
in an arbitrary order when a client is disconnected, the compositor must handle
|
||||||
|
this correctly.
|
||||||
|
|
||||||
### Destroying resources
|
### Destroying resources
|
||||||
|
|
||||||
Object structs should only be destroyed when their resource is destroyed, ie.
|
Object structs should only be destroyed when their resource is destroyed, ie.
|
||||||
in the resource destroy handler (set with `wl_resource_set_implementation`).
|
in the resource destroy handler (set with `wl_resource_set_implementation`).
|
||||||
Destructor requests should only call `wl_resource_destroy`.
|
|
||||||
|
|
||||||
The compositor should not destroy resources on its own.
|
- If the object has a destructor request: the request handler should just call
|
||||||
|
`wl_resource_destroy` and do nothing else. The compositor must not destroy
|
||||||
|
resources on its own outside the destructor request handler.
|
||||||
|
- If the protocol specifies that an object is destroyed when an event is sent:
|
||||||
|
it's the only case where the compositor is allowed to send the event and then
|
||||||
|
call `wl_resource_destroy`. An example of this is `wl_callback`.
|
||||||
|
|
||||||
### Inert resources
|
### Inert resources
|
||||||
|
|
||||||
|
@ -354,3 +399,11 @@ static void subsurface_handle_surface_destroy(struct wl_listener *listener,
|
||||||
subsurface_destroy(subsurface);
|
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
|
||||||
|
|
86
README.md
86
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
|
# wlroots
|
||||||
|
|
||||||
Pluggable, composable, unopinionated modules for building a
|
Pluggable, composable, unopinionated modules for building a [Wayland]
|
||||||
[Wayland](http://wayland.freedesktop.org/) compositor; or about 50,000 lines of
|
compositor; or about 60,000 lines of code you were going to write anyway.
|
||||||
code you were going to write anyway.
|
|
||||||
|
|
||||||
- wlroots provides backends that abstract the underlying display and input
|
- wlroots provides backends that abstract the underlying display and input
|
||||||
hardware, including KMS/DRM, libinput, Wayland, X11, and headless backends,
|
hardware, including KMS/DRM, libinput, Wayland, X11, and headless backends,
|
||||||
plus any custom backends you choose to write, which can all be created or
|
plus any custom backends you choose to write, which can all be created or
|
||||||
destroyed at runtime and used in concert with each other.
|
destroyed at runtime and used in concert with each other.
|
||||||
- wlroots provides unopinionated, mostly standalone implementations of many
|
- wlroots provides unopinionated, mostly standalone implementations of many
|
||||||
Wayland interfaces, both from wayland.xml and various protocol extensions.
|
Wayland interfaces, both from wayland.xml and various protocol extensions.
|
||||||
We also promote the standardization of portable extensions across
|
We also promote the standardization of portable extensions across
|
||||||
many compositors.
|
many compositors.
|
||||||
- wlroots provides several powerful, standalone, and optional tools that
|
- wlroots provides several powerful, standalone, and optional tools that
|
||||||
implement components common to many compositors, such as the arrangement of
|
implement components common to many compositors, such as the arrangement of
|
||||||
outputs in physical space.
|
outputs in physical space.
|
||||||
- wlroots provides an Xwayland abstraction that allows you to have excellent
|
- wlroots provides an Xwayland abstraction that allows you to have excellent
|
||||||
Xwayland support without worrying about writing your own X11 window manager
|
Xwayland support without worrying about writing your own X11 window manager
|
||||||
on top of writing your compositor.
|
on top of writing your compositor.
|
||||||
- wlroots provides a renderer abstraction that simple compositors can use to
|
- 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
|
avoid writing GL code directly, but which steps out of the way when your
|
||||||
needs demand custom rendering code.
|
needs demand custom rendering code.
|
||||||
|
|
||||||
wlroots implements a huge variety of Wayland compositor features and implements
|
wlroots implements a huge variety of Wayland compositor features and implements
|
||||||
them *right*, so you can focus on the features that make your compositor
|
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
|
them work independently of one another and freely compose with anything you want
|
||||||
to implement yourself.
|
to implement yourself.
|
||||||
|
|
||||||
Check out our [wiki](https://github.com/swaywm/wlroots/wiki/Getting-started) to
|
Check out our [wiki] to get started with wlroots. Join our IRC channel:
|
||||||
get started with wlroots.
|
[#sway-devel on Libera Chat].
|
||||||
|
|
||||||
wlroots is developed under the direction of the
|
wlroots is developed under the direction of the [sway] project. A variety of
|
||||||
[sway](https://github.com/swaywm/sway) project. A variety of wrapper libraries
|
[wrapper libraries] are available for using it with your favorite programming
|
||||||
[are available](https://github.com/swaywm) for using it with your favorite
|
language.
|
||||||
programming language.
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
@ -45,39 +49,41 @@ Install dependencies:
|
||||||
* meson
|
* meson
|
||||||
* wayland
|
* wayland
|
||||||
* wayland-protocols
|
* wayland-protocols
|
||||||
* EGL
|
* EGL and GLESv2 (optional, for the GLES2 renderer)
|
||||||
* GLESv2
|
* Vulkan loader, headers and glslang (optional, for the Vulkan renderer)
|
||||||
* libdrm
|
* libdrm
|
||||||
* GBM
|
* GBM
|
||||||
* libinput
|
* libinput (optional, for the libinput backend)
|
||||||
* xkbcommon
|
* xkbcommon
|
||||||
* udev
|
* udev
|
||||||
* pixman
|
* pixman
|
||||||
* systemd (optional, for logind support)
|
* [libseat]
|
||||||
* elogind (optional, for logind support on systems without systemd)
|
|
||||||
* libcap (optional, for capability support)
|
|
||||||
|
|
||||||
If you choose to enable X11 support:
|
If you choose to enable X11 support:
|
||||||
|
|
||||||
* xcb
|
* xwayland (build-time only, optional at runtime)
|
||||||
* xcb-composite
|
* libxcb
|
||||||
* xcb-xfixes
|
* libxcb-render-util
|
||||||
* xcb-xinput
|
* libxcb-wm
|
||||||
* xcb-image
|
* libxcb-errors (optional, for improved error reporting)
|
||||||
* xcb-render
|
|
||||||
* x11-xcb
|
|
||||||
* xcb-errors (optional, for improved error reporting)
|
|
||||||
* x11-icccm (optional, for improved Xwayland introspection)
|
|
||||||
|
|
||||||
Run these commands:
|
Run these commands:
|
||||||
|
|
||||||
meson build
|
meson build/
|
||||||
ninja -C build
|
ninja -C build/
|
||||||
|
|
||||||
Install like so:
|
Install like so:
|
||||||
|
|
||||||
sudo ninja -C build install
|
sudo ninja -C build/ install
|
||||||
|
|
||||||
## Contributing
|
## 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,29 +1,38 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <libinput.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wlr/backend/drm.h>
|
|
||||||
#include <wlr/backend/headless.h>
|
#include <wlr/backend/headless.h>
|
||||||
#include <wlr/backend/interface.h>
|
#include <wlr/backend/interface.h>
|
||||||
#include <wlr/backend/libinput.h>
|
|
||||||
#include <wlr/backend/multi.h>
|
#include <wlr/backend/multi.h>
|
||||||
#include <wlr/backend/noop.h>
|
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/backend/wayland.h>
|
#include <wlr/backend/wayland.h>
|
||||||
#include <wlr/config.h>
|
#include <wlr/config.h>
|
||||||
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
#include "backend/backend.h"
|
||||||
#include "backend/multi.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
|
#if WLR_HAS_X11_BACKEND
|
||||||
#include <wlr/backend/x11.h>
|
#include <wlr/backend/x11.h>
|
||||||
#endif
|
#endif
|
||||||
#if WLR_HAS_RDP_BACKEND
|
|
||||||
#include <wlr/backend/rdp.h>
|
#define WAIT_SESSION_TIMEOUT 10000 // ms
|
||||||
#endif
|
|
||||||
|
|
||||||
void wlr_backend_init(struct wlr_backend *backend,
|
void wlr_backend_init(struct wlr_backend *backend,
|
||||||
const struct wlr_backend_impl *impl) {
|
const struct wlr_backend_impl *impl) {
|
||||||
|
@ -34,6 +43,10 @@ void wlr_backend_init(struct wlr_backend *backend,
|
||||||
wl_signal_init(&backend->events.new_output);
|
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) {
|
bool wlr_backend_start(struct wlr_backend *backend) {
|
||||||
if (backend->impl->start) {
|
if (backend->impl->start) {
|
||||||
return backend->impl->start(backend);
|
return backend->impl->start(backend);
|
||||||
|
@ -53,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) {
|
struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend) {
|
||||||
if (backend->impl->get_session) {
|
if (backend->impl->get_session) {
|
||||||
return backend->impl->get_session(backend);
|
return backend->impl->get_session(backend);
|
||||||
|
@ -67,6 +73,52 @@ struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend) {
|
||||||
return NULL;
|
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) {
|
clockid_t wlr_backend_get_presentation_clock(struct wlr_backend *backend) {
|
||||||
if (backend->impl->get_presentation_clock) {
|
if (backend->impl->get_presentation_clock) {
|
||||||
return backend->impl->get_presentation_clock(backend);
|
return backend->impl->get_presentation_clock(backend);
|
||||||
|
@ -74,6 +126,21 @@ clockid_t wlr_backend_get_presentation_clock(struct wlr_backend *backend) {
|
||||||
return CLOCK_MONOTONIC;
|
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) {
|
static size_t parse_outputs_env(const char *name) {
|
||||||
const char *outputs_str = getenv(name);
|
const char *outputs_str = getenv(name);
|
||||||
if (outputs_str == NULL) {
|
if (outputs_str == NULL) {
|
||||||
|
@ -90,9 +157,8 @@ static size_t parse_outputs_env(const char *name) {
|
||||||
return outputs;
|
return outputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wlr_backend *attempt_wl_backend(struct wl_display *display,
|
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);
|
||||||
struct wlr_backend *backend = wlr_wl_backend_create(display, NULL, create_renderer_func);
|
|
||||||
if (backend == NULL) {
|
if (backend == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -107,8 +173,8 @@ static struct wlr_backend *attempt_wl_backend(struct wl_display *display,
|
||||||
|
|
||||||
#if WLR_HAS_X11_BACKEND
|
#if WLR_HAS_X11_BACKEND
|
||||||
static struct wlr_backend *attempt_x11_backend(struct wl_display *display,
|
static struct wlr_backend *attempt_x11_backend(struct wl_display *display,
|
||||||
const char *x11_display, wlr_renderer_create_func_t create_renderer_func) {
|
const char *x11_display) {
|
||||||
struct wlr_backend *backend = wlr_x11_backend_create(display, x11_display, create_renderer_func);
|
struct wlr_backend *backend = wlr_x11_backend_create(display, x11_display);
|
||||||
if (backend == NULL) {
|
if (backend == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -123,8 +189,8 @@ static struct wlr_backend *attempt_x11_backend(struct wl_display *display,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static struct wlr_backend *attempt_headless_backend(
|
static struct wlr_backend *attempt_headless_backend(
|
||||||
struct wl_display *display, wlr_renderer_create_func_t create_renderer_func) {
|
struct wl_display *display) {
|
||||||
struct wlr_backend *backend = wlr_headless_backend_create(display, create_renderer_func);
|
struct wlr_backend *backend = wlr_headless_backend_create(display);
|
||||||
if (backend == NULL) {
|
if (backend == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -137,65 +203,29 @@ static struct wlr_backend *attempt_headless_backend(
|
||||||
return backend;
|
return backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WLR_HAS_RDP_BACKEND
|
#if WLR_HAS_DRM_BACKEND
|
||||||
static struct wlr_backend *attempt_rdp_backend(struct wl_display *display,
|
|
||||||
wlr_renderer_create_func_t create_renderer_func) {
|
|
||||||
const char *cert_path = getenv("WLR_RDP_TLS_CERT_PATH");
|
|
||||||
const char *key_path = getenv("WLR_RDP_TLS_KEY_PATH");
|
|
||||||
if (!cert_path || !key_path) {
|
|
||||||
wlr_log(WLR_ERROR, "The RDP backend requires WLR_RDP_TLS_CERT_PATH "
|
|
||||||
"and WLR_RDP_TLS_KEY_PATH to be set.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
struct wlr_backend *backend = wlr_rdp_backend_create(
|
|
||||||
display, create_renderer_func, cert_path, key_path);
|
|
||||||
const char *address = getenv("WLR_RDP_ADDRESS");
|
|
||||||
if (address) {
|
|
||||||
wlr_rdp_backend_set_address(backend, address);
|
|
||||||
}
|
|
||||||
const char *_port = getenv("WLR_RDP_PORT");
|
|
||||||
if (_port) {
|
|
||||||
char *endptr;
|
|
||||||
int port = strtol(_port, &endptr, 10);
|
|
||||||
if (*endptr || port <= 0 || port >= 1024) {
|
|
||||||
wlr_log(WLR_ERROR, "Expected WLR_RDP_PORT to be a "
|
|
||||||
"positive integer less than 1024");
|
|
||||||
wlr_backend_destroy(backend);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
wlr_rdp_backend_set_port(backend, port);
|
|
||||||
}
|
|
||||||
return backend;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct wlr_backend *attempt_noop_backend(struct wl_display *display) {
|
|
||||||
struct wlr_backend *backend = wlr_noop_backend_create(display);
|
|
||||||
if (backend == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t outputs = parse_outputs_env("WLR_NOOP_OUTPUTS");
|
|
||||||
for (size_t i = 0; i < outputs; ++i) {
|
|
||||||
wlr_noop_add_output(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
return backend;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
|
static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
|
||||||
struct wlr_backend *backend, struct wlr_session *session,
|
struct wlr_backend *backend, struct wlr_session *session) {
|
||||||
wlr_renderer_create_func_t create_renderer_func) {
|
struct wlr_device *gpus[8];
|
||||||
int gpus[8];
|
ssize_t num_gpus = wlr_session_find_gpus(session, 8, gpus);
|
||||||
size_t num_gpus = wlr_session_find_gpus(session, 8, gpus);
|
if (num_gpus < 0) {
|
||||||
struct wlr_backend *primary_drm = NULL;
|
wlr_log(WLR_ERROR, "Failed to find GPUs");
|
||||||
wlr_log(WLR_INFO, "Found %zu GPUs", num_gpus);
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < num_gpus; ++i) {
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_backend *primary_drm = NULL;
|
||||||
|
for (size_t i = 0; i < (size_t)num_gpus; ++i) {
|
||||||
struct wlr_backend *drm = wlr_drm_backend_create(display, session,
|
struct wlr_backend *drm = wlr_drm_backend_create(display, session,
|
||||||
gpus[i], primary_drm, create_renderer_func);
|
gpus[i], primary_drm);
|
||||||
if (!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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,50 +235,56 @@ static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
|
||||||
|
|
||||||
wlr_multi_backend_add(backend, drm);
|
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;
|
return primary_drm;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct wlr_backend *attempt_backend_by_name(struct wl_display *display,
|
static bool attempt_backend_by_name(struct wl_display *display,
|
||||||
struct wlr_backend *backend, struct wlr_session **session,
|
struct wlr_multi_backend *multi, char *name) {
|
||||||
const char *name, wlr_renderer_create_func_t create_renderer_func) {
|
struct wlr_backend *backend = NULL;
|
||||||
if (strcmp(name, "wayland") == 0) {
|
if (strcmp(name, "wayland") == 0) {
|
||||||
return attempt_wl_backend(display, create_renderer_func);
|
backend = attempt_wl_backend(display);
|
||||||
#if WLR_HAS_X11_BACKEND
|
#if WLR_HAS_X11_BACKEND
|
||||||
} else if (strcmp(name, "x11") == 0) {
|
} else if (strcmp(name, "x11") == 0) {
|
||||||
return attempt_x11_backend(display, NULL, create_renderer_func);
|
backend = attempt_x11_backend(display, NULL);
|
||||||
#endif
|
#endif
|
||||||
} else if (strcmp(name, "headless") == 0) {
|
} else if (strcmp(name, "headless") == 0) {
|
||||||
return attempt_headless_backend(display, create_renderer_func);
|
backend = attempt_headless_backend(display);
|
||||||
#if WLR_HAS_RDP_BACKEND
|
|
||||||
} else if (strcmp(name, "rdp") == 0) {
|
|
||||||
return attempt_rdp_backend(display, create_renderer_func);
|
|
||||||
#endif
|
|
||||||
} else if (strcmp(name, "noop") == 0) {
|
|
||||||
return attempt_noop_backend(display);
|
|
||||||
} else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) {
|
} else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) {
|
||||||
// DRM and libinput need a session
|
// DRM and libinput need a session
|
||||||
if (!*session) {
|
if (multi->session == NULL) {
|
||||||
*session = wlr_session_create(display);
|
multi->session = session_create_and_wait(display);
|
||||||
if (!*session) {
|
if (multi->session == NULL) {
|
||||||
wlr_log(WLR_ERROR, "failed to start a session");
|
wlr_log(WLR_ERROR, "failed to start a session");
|
||||||
return NULL;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(name, "libinput") == 0) {
|
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 {
|
} 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 wlr_multi_backend_add(&multi->backend, backend);
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
|
struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
|
||||||
wlr_renderer_create_func_t create_renderer_func) {
|
|
||||||
struct wlr_backend *backend = wlr_multi_backend_create(display);
|
struct wlr_backend *backend = wlr_multi_backend_create(display);
|
||||||
struct wlr_multi_backend *multi = (struct wlr_multi_backend *)backend;
|
struct wlr_multi_backend *multi = (struct wlr_multi_backend *)backend;
|
||||||
if (!backend) {
|
if (!backend) {
|
||||||
|
@ -258,6 +294,9 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
|
||||||
|
|
||||||
char *names = getenv("WLR_BACKENDS");
|
char *names = getenv("WLR_BACKENDS");
|
||||||
if (names) {
|
if (names) {
|
||||||
|
wlr_log(WLR_INFO, "Loading user-specified backends due to WLR_BACKENDS: %s",
|
||||||
|
names);
|
||||||
|
|
||||||
names = strdup(names);
|
names = strdup(names);
|
||||||
if (names == NULL) {
|
if (names == NULL) {
|
||||||
wlr_log(WLR_ERROR, "allocation failed");
|
wlr_log(WLR_ERROR, "allocation failed");
|
||||||
|
@ -268,17 +307,7 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
|
||||||
char *saveptr;
|
char *saveptr;
|
||||||
char *name = strtok_r(names, ",", &saveptr);
|
char *name = strtok_r(names, ",", &saveptr);
|
||||||
while (name != NULL) {
|
while (name != NULL) {
|
||||||
struct wlr_backend *subbackend = attempt_backend_by_name(display,
|
if (!attempt_backend_by_name(display, multi, name)) {
|
||||||
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)) {
|
|
||||||
wlr_log(WLR_ERROR, "failed to add backend '%s'", name);
|
wlr_log(WLR_ERROR, "failed to add backend '%s'", name);
|
||||||
wlr_session_destroy(multi->session);
|
wlr_session_destroy(multi->session);
|
||||||
wlr_backend_destroy(backend);
|
wlr_backend_destroy(backend);
|
||||||
|
@ -293,36 +322,39 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
|
||||||
return backend;
|
return backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getenv("WAYLAND_DISPLAY") || getenv("_WAYLAND_DISPLAY") ||
|
if (getenv("WAYLAND_DISPLAY") || getenv("WAYLAND_SOCKET")) {
|
||||||
getenv("WAYLAND_SOCKET")) {
|
struct wlr_backend *wl_backend = attempt_wl_backend(display);
|
||||||
struct wlr_backend *wl_backend = attempt_wl_backend(display,
|
if (!wl_backend) {
|
||||||
create_renderer_func);
|
goto error;
|
||||||
if (wl_backend) {
|
|
||||||
wlr_multi_backend_add(backend, wl_backend);
|
|
||||||
return backend;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wlr_multi_backend_add(backend, wl_backend);
|
||||||
|
return backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WLR_HAS_X11_BACKEND
|
#if WLR_HAS_X11_BACKEND
|
||||||
const char *x11_display = getenv("DISPLAY");
|
const char *x11_display = getenv("DISPLAY");
|
||||||
if (x11_display) {
|
if (x11_display) {
|
||||||
struct wlr_backend *x11_backend =
|
struct wlr_backend *x11_backend =
|
||||||
attempt_x11_backend(display, x11_display, create_renderer_func);
|
attempt_x11_backend(display, x11_display);
|
||||||
if (x11_backend) {
|
if (!x11_backend) {
|
||||||
wlr_multi_backend_add(backend, x11_backend);
|
goto error;
|
||||||
return backend;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wlr_multi_backend_add(backend, x11_backend);
|
||||||
|
return backend;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Attempt DRM+libinput
|
// Attempt DRM+libinput
|
||||||
multi->session = wlr_session_create(display);
|
multi->session = session_create_and_wait(display);
|
||||||
if (!multi->session) {
|
if (!multi->session) {
|
||||||
wlr_log(WLR_ERROR, "Failed to start a DRM session");
|
wlr_log(WLR_ERROR, "Failed to start a DRM session");
|
||||||
wlr_backend_destroy(backend);
|
wlr_backend_destroy(backend);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if WLR_HAS_LIBINPUT_BACKEND
|
||||||
struct wlr_backend *libinput = wlr_libinput_backend_create(display,
|
struct wlr_backend *libinput = wlr_libinput_backend_create(display,
|
||||||
multi->session);
|
multi->session);
|
||||||
if (!libinput) {
|
if (!libinput) {
|
||||||
|
@ -332,16 +364,37 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
wlr_multi_backend_add(backend, libinput);
|
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,
|
#if WLR_HAS_DRM_BACKEND
|
||||||
multi->session, create_renderer_func);
|
struct wlr_backend *primary_drm =
|
||||||
|
attempt_drm_backend(display, backend, multi->session);
|
||||||
if (!primary_drm) {
|
if (!primary_drm) {
|
||||||
wlr_log(WLR_ERROR, "Failed to open any DRM device");
|
wlr_log(WLR_ERROR, "Failed to open any DRM device");
|
||||||
wlr_backend_destroy(libinput);
|
|
||||||
wlr_session_destroy(multi->session);
|
wlr_session_destroy(multi->session);
|
||||||
wlr_backend_destroy(backend);
|
wlr_backend_destroy(backend);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drm_backend_monitor_create(backend, primary_drm, multi->session);
|
||||||
|
|
||||||
return backend;
|
return backend;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
error:
|
||||||
|
wlr_backend_destroy(backend);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#include <gbm.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
|
@ -9,265 +8,290 @@
|
||||||
|
|
||||||
struct atomic {
|
struct atomic {
|
||||||
drmModeAtomicReq *req;
|
drmModeAtomicReq *req;
|
||||||
int cursor;
|
|
||||||
bool failed;
|
bool failed;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void atomic_begin(struct wlr_drm_crtc *crtc, struct atomic *atom) {
|
static void atomic_begin(struct atomic *atom) {
|
||||||
if (!crtc->atomic) {
|
memset(atom, 0, sizeof(*atom));
|
||||||
crtc->atomic = drmModeAtomicAlloc();
|
|
||||||
if (!crtc->atomic) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
|
||||||
atom->failed = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
atom->req = crtc->atomic;
|
atom->req = drmModeAtomicAlloc();
|
||||||
atom->cursor = drmModeAtomicGetCursor(atom->req);
|
if (!atom->req) {
|
||||||
atom->failed = false;
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
|
atom->failed = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool atomic_end(int drm_fd, struct atomic *atom) {
|
static bool atomic_commit(struct atomic *atom,
|
||||||
|
struct wlr_drm_connector *conn, uint32_t flags) {
|
||||||
|
struct wlr_drm_backend *drm = conn->backend;
|
||||||
if (atom->failed) {
|
if (atom->failed) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t flags = DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_NONBLOCK;
|
int ret = drmModeAtomicCommit(drm->fd, atom->req, flags, drm);
|
||||||
if (drmModeAtomicCommit(drm_fd, atom->req, flags, NULL)) {
|
if (ret != 0) {
|
||||||
wlr_log_errno(WLR_ERROR, "Atomic test failed");
|
wlr_drm_conn_log_errno(conn,
|
||||||
drmModeAtomicSetCursor(atom->req, atom->cursor);
|
(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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool atomic_commit(int drm_fd, struct atomic *atom,
|
static void atomic_finish(struct atomic *atom) {
|
||||||
struct wlr_drm_connector *conn, uint32_t flags, bool modeset) {
|
drmModeAtomicFree(atom->req);
|
||||||
struct wlr_drm_backend *drm =
|
|
||||||
get_drm_backend_from_backend(conn->output.backend);
|
|
||||||
if (atom->failed) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret = drmModeAtomicCommit(drm_fd, atom->req, flags, drm);
|
|
||||||
if (ret) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "%s: Atomic commit failed (%s)",
|
|
||||||
conn->output.name, modeset ? "modeset" : "pageflip");
|
|
||||||
|
|
||||||
// Try to commit without new changes
|
|
||||||
drmModeAtomicSetCursor(atom->req, atom->cursor);
|
|
||||||
if (drmModeAtomicCommit(drm_fd, atom->req, flags, drm)) {
|
|
||||||
wlr_log_errno(WLR_ERROR,
|
|
||||||
"%s: Atomic commit without new changes failed (%s)",
|
|
||||||
conn->output.name, modeset ? "modeset" : "pageflip");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
drmModeAtomicSetCursor(atom->req, 0);
|
|
||||||
|
|
||||||
return !ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void atomic_add(struct atomic *atom, uint32_t id, uint32_t prop, uint64_t val) {
|
static void atomic_add(struct atomic *atom, uint32_t id, uint32_t prop, uint64_t val) {
|
||||||
if (!atom->failed && drmModeAtomicAddProperty(atom->req, id, prop, val) < 0) {
|
if (!atom->failed && drmModeAtomicAddProperty(atom->req, id, prop, val) < 0) {
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to add atomic DRM property");
|
wlr_log_errno(WLR_ERROR, "Failed to add atomic DRM property");
|
||||||
atom->failed = true;
|
atom->failed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_plane_props(struct atomic *atom, struct wlr_drm_plane *plane,
|
static bool create_mode_blob(struct wlr_drm_backend *drm,
|
||||||
uint32_t crtc_id, uint32_t fb_id, bool set_crtc_xy) {
|
|
||||||
uint32_t id = plane->id;
|
|
||||||
const union wlr_drm_plane_props *props = &plane->props;
|
|
||||||
|
|
||||||
// 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->crtc_id, crtc_id);
|
|
||||||
if (set_crtc_xy) {
|
|
||||||
atomic_add(atom, id, props->crtc_x, 0);
|
|
||||||
atomic_add(atom, id, props->crtc_y, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool atomic_crtc_pageflip(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_drm_connector *conn,
|
struct wlr_drm_connector *conn,
|
||||||
struct wlr_drm_crtc *crtc,
|
const struct wlr_drm_connector_state *state, uint32_t *blob_id) {
|
||||||
uint32_t fb_id, drmModeModeInfo *mode) {
|
if (!state->active) {
|
||||||
if (mode != NULL) {
|
*blob_id = 0;
|
||||||
if (crtc->mode_id != 0) {
|
|
||||||
drmModeDestroyPropertyBlob(drm->fd, crtc->mode_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (drmModeCreatePropertyBlob(drm->fd, mode, sizeof(*mode),
|
|
||||||
&crtc->mode_id)) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Unable to create property blob");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT;
|
|
||||||
if (mode != NULL) {
|
|
||||||
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
|
||||||
} else {
|
|
||||||
flags |= DRM_MODE_ATOMIC_NONBLOCK;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct atomic atom;
|
|
||||||
atomic_begin(crtc, &atom);
|
|
||||||
atomic_add(&atom, conn->id, conn->props.crtc_id, crtc->id);
|
|
||||||
if (mode != NULL && 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, crtc->mode_id);
|
|
||||||
atomic_add(&atom, crtc->id, crtc->props.active, 1);
|
|
||||||
set_plane_props(&atom, crtc->primary, crtc->id, fb_id, true);
|
|
||||||
return atomic_commit(drm->fd, &atom, conn, flags, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool atomic_conn_enable(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_drm_connector *conn, bool enable) {
|
|
||||||
struct wlr_drm_crtc *crtc = conn->crtc;
|
|
||||||
if (crtc == NULL) {
|
|
||||||
return !enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct atomic atom;
|
|
||||||
atomic_begin(crtc, &atom);
|
|
||||||
atomic_add(&atom, crtc->id, crtc->props.active, enable);
|
|
||||||
if (enable) {
|
|
||||||
atomic_add(&atom, conn->id, conn->props.crtc_id, crtc->id);
|
|
||||||
atomic_add(&atom, crtc->id, crtc->props.mode_id, crtc->mode_id);
|
|
||||||
} else {
|
|
||||||
atomic_add(&atom, conn->id, conn->props.crtc_id, 0);
|
|
||||||
atomic_add(&atom, crtc->id, crtc->props.mode_id, 0);
|
|
||||||
}
|
|
||||||
return atomic_commit(drm->fd, &atom, conn, DRM_MODE_ATOMIC_ALLOW_MODESET,
|
|
||||||
true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool legacy_crtc_set_cursor(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_drm_crtc *crtc, struct gbm_bo *bo);
|
|
||||||
|
|
||||||
static bool atomic_crtc_set_cursor(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_drm_crtc *crtc, struct gbm_bo *bo) {
|
|
||||||
if (!crtc || !crtc->cursor) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_drm_plane *plane = crtc->cursor;
|
if (drmModeCreatePropertyBlob(drm->fd, &state->mode,
|
||||||
// We can't use atomic operations on fake planes
|
sizeof(drmModeModeInfo), blob_id)) {
|
||||||
if (plane->id == 0) {
|
wlr_log_errno(WLR_ERROR, "Unable to create mode property blob");
|
||||||
return legacy_crtc_set_cursor(drm, crtc, bo);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct atomic atom;
|
return true;
|
||||||
|
|
||||||
atomic_begin(crtc, &atom);
|
|
||||||
|
|
||||||
if (bo) {
|
|
||||||
uint32_t fb_id =
|
|
||||||
get_fb_for_bo(bo, plane->drm_format, drm->addfb2_modifiers);
|
|
||||||
set_plane_props(&atom, plane, crtc->id, fb_id, false);
|
|
||||||
} else {
|
|
||||||
atomic_add(&atom, plane->id, plane->props.fb_id, 0);
|
|
||||||
atomic_add(&atom, plane->id, plane->props.crtc_id, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return atomic_end(drm->fd, &atom);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool legacy_crtc_move_cursor(struct wlr_drm_backend *drm,
|
static bool create_gamma_lut_blob(struct wlr_drm_backend *drm,
|
||||||
struct wlr_drm_crtc *crtc, int x, int y);
|
size_t size, const uint16_t *lut, uint32_t *blob_id) {
|
||||||
|
if (size == 0) {
|
||||||
static bool atomic_crtc_move_cursor(struct wlr_drm_backend *drm,
|
*blob_id = 0;
|
||||||
struct wlr_drm_crtc *crtc, int x, int y) {
|
|
||||||
if (!crtc || !crtc->cursor) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_drm_plane *plane = crtc->cursor;
|
|
||||||
// We can't use atomic operations on fake planes
|
|
||||||
if (plane->id == 0) {
|
|
||||||
return legacy_crtc_move_cursor(drm, crtc, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct atomic atom;
|
|
||||||
|
|
||||||
atomic_begin(crtc, &atom);
|
|
||||||
atomic_add(&atom, plane->id, plane->props.crtc_x, x);
|
|
||||||
atomic_add(&atom, plane->id, plane->props.crtc_y, y);
|
|
||||||
return atomic_end(drm->fd, &atom);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool atomic_crtc_set_gamma(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_drm_crtc *crtc, size_t size,
|
|
||||||
uint16_t *r, uint16_t *g, uint16_t *b) {
|
|
||||||
// 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) {
|
|
||||||
return legacy_iface.crtc_set_gamma(drm, crtc, size, r, g, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct drm_color_lut *gamma = malloc(size * sizeof(struct drm_color_lut));
|
struct drm_color_lut *gamma = malloc(size * sizeof(struct drm_color_lut));
|
||||||
if (gamma == NULL) {
|
if (gamma == NULL) {
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate gamma table");
|
wlr_log(WLR_ERROR, "Failed to allocate gamma table");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint16_t *r = lut;
|
||||||
|
const uint16_t *g = lut + size;
|
||||||
|
const uint16_t *b = lut + 2 * size;
|
||||||
for (size_t i = 0; i < size; i++) {
|
for (size_t i = 0; i < size; i++) {
|
||||||
gamma[i].red = r[i];
|
gamma[i].red = r[i];
|
||||||
gamma[i].green = g[i];
|
gamma[i].green = g[i];
|
||||||
gamma[i].blue = b[i];
|
gamma[i].blue = b[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (crtc->gamma_lut != 0) {
|
|
||||||
drmModeDestroyPropertyBlob(drm->fd, crtc->gamma_lut);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (drmModeCreatePropertyBlob(drm->fd, gamma,
|
if (drmModeCreatePropertyBlob(drm->fd, gamma,
|
||||||
size * sizeof(struct drm_color_lut), &crtc->gamma_lut)) {
|
size * sizeof(struct drm_color_lut), blob_id) != 0) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Unable to create gamma LUT property blob");
|
||||||
free(gamma);
|
free(gamma);
|
||||||
wlr_log_errno(WLR_ERROR, "Unable to create property blob");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
free(gamma);
|
free(gamma);
|
||||||
|
|
||||||
struct atomic atom;
|
return true;
|
||||||
atomic_begin(crtc, &atom);
|
|
||||||
atomic_add(&atom, crtc->id, crtc->props.gamma_lut, crtc->gamma_lut);
|
|
||||||
return atomic_end(drm->fd, &atom);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t atomic_crtc_get_gamma_size(struct wlr_drm_backend *drm,
|
static void commit_blob(struct wlr_drm_backend *drm,
|
||||||
struct wlr_drm_crtc *crtc) {
|
uint32_t *current, uint32_t next) {
|
||||||
if (crtc->props.gamma_lut_size == 0) {
|
if (*current == next) {
|
||||||
return legacy_iface.crtc_get_gamma_size(drm, crtc);
|
return;
|
||||||
|
}
|
||||||
|
if (*current != 0) {
|
||||||
|
drmModeDestroyPropertyBlob(drm->fd, *current);
|
||||||
|
}
|
||||||
|
*current = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rollback_blob(struct wlr_drm_backend *drm,
|
||||||
|
uint32_t *current, uint32_t next) {
|
||||||
|
if (*current == next) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (next != 0) {
|
||||||
|
drmModeDestroyPropertyBlob(drm->fd, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void plane_disable(struct atomic *atom, struct wlr_drm_plane *plane) {
|
||||||
|
uint32_t id = plane->id;
|
||||||
|
const union wlr_drm_plane_props *props = &plane->props;
|
||||||
|
atomic_add(atom, id, props->fb_id, 0);
|
||||||
|
atomic_add(atom, id, props->crtc_id, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_plane *plane, uint32_t crtc_id, int32_t x, int32_t y) {
|
||||||
|
uint32_t id = plane->id;
|
||||||
|
const union wlr_drm_plane_props *props = &plane->props;
|
||||||
|
struct wlr_drm_fb *fb = plane_get_next_fb(plane);
|
||||||
|
if (fb == NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to acquire FB");
|
||||||
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t gamma_lut_size;
|
uint32_t width = fb->wlr_buf->width;
|
||||||
if (!get_drm_prop(drm->fd, crtc->id, crtc->props.gamma_lut_size,
|
uint32_t height = fb->wlr_buf->height;
|
||||||
&gamma_lut_size)) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to get gamma lut size");
|
// The src_* properties are in 16.16 fixed point
|
||||||
return 0;
|
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)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);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
error:
|
||||||
|
wlr_log(WLR_ERROR, "Failed to set plane %"PRIu32" properties", plane->id);
|
||||||
|
atom->failed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (modeset) {
|
||||||
|
if (!create_mode_blob(drm, conn, state, &mode_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (size_t)gamma_lut_size;
|
uint32_t gamma_lut = crtc->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,
|
||||||
|
state->base->gamma_lut_size,
|
||||||
|
state->base->gamma_lut)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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 ((state->base->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) &&
|
||||||
|
drm_connector_supports_vrr(conn)) {
|
||||||
|
vrr_enabled = state->base->adaptive_sync_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test_only) {
|
||||||
|
flags |= DRM_MODE_ATOMIC_TEST_ONLY;
|
||||||
|
}
|
||||||
|
if (modeset) {
|
||||||
|
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
||||||
|
} else if (!test_only) {
|
||||||
|
flags |= DRM_MODE_ATOMIC_NONBLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct atomic atom;
|
||||||
|
atomic_begin(&atom);
|
||||||
|
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, active);
|
||||||
|
if (active) {
|
||||||
|
if (crtc->props.gamma_lut != 0) {
|
||||||
|
atomic_add(&atom, crtc->id, crtc->props.gamma_lut, gamma_lut);
|
||||||
|
}
|
||||||
|
if (crtc->props.vrr_enabled != 0) {
|
||||||
|
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,
|
||||||
|
conn->cursor_x, conn->cursor_y);
|
||||||
|
} else {
|
||||||
|
plane_disable(&atom, crtc->cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
plane_disable(&atom, crtc->primary);
|
||||||
|
if (crtc->cursor) {
|
||||||
|
plane_disable(&atom, crtc->cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = atomic_commit(&atom, conn, flags);
|
||||||
|
atomic_finish(&atom);
|
||||||
|
|
||||||
|
if (ok && !test_only) {
|
||||||
|
commit_blob(drm, &crtc->mode_id, mode_id);
|
||||||
|
commit_blob(drm, &crtc->gamma_lut, gamma_lut);
|
||||||
|
|
||||||
|
if (vrr_enabled != prev_vrr_enabled) {
|
||||||
|
output->adaptive_sync_status = vrr_enabled ?
|
||||||
|
WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED :
|
||||||
|
WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct wlr_drm_interface atomic_iface = {
|
const struct wlr_drm_interface atomic_iface = {
|
||||||
.conn_enable = atomic_conn_enable,
|
.crtc_commit = atomic_crtc_commit,
|
||||||
.crtc_pageflip = atomic_crtc_pageflip,
|
|
||||||
.crtc_set_cursor = atomic_crtc_set_cursor,
|
|
||||||
.crtc_move_cursor = atomic_crtc_move_cursor,
|
|
||||||
.crtc_set_gamma = atomic_crtc_set_gamma,
|
|
||||||
.crtc_get_gamma_size = atomic_crtc_get_gamma_size,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <drm_fourcc.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -8,8 +9,6 @@
|
||||||
#include <wlr/backend/interface.h>
|
#include <wlr/backend/interface.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
#include <wlr/render/egl.h>
|
|
||||||
#include <wlr/types/wlr_list.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
#include "backend/drm/drm.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) {
|
static bool backend_start(struct wlr_backend *backend) {
|
||||||
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
||||||
scan_drm_connectors(drm);
|
scan_drm_connectors(drm, NULL);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,35 +33,35 @@ static void backend_destroy(struct wlr_backend *backend) {
|
||||||
|
|
||||||
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
||||||
|
|
||||||
restore_drm_outputs(drm);
|
|
||||||
|
|
||||||
struct wlr_drm_connector *conn, *next;
|
struct wlr_drm_connector *conn, *next;
|
||||||
wl_list_for_each_safe(conn, next, &drm->outputs, link) {
|
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->display_destroy.link);
|
||||||
wl_list_remove(&drm->session_signal.link);
|
wl_list_remove(&drm->session_destroy.link);
|
||||||
wl_list_remove(&drm->drm_invalidated.link);
|
wl_list_remove(&drm->session_active.link);
|
||||||
|
wl_list_remove(&drm->parent_destroy.link);
|
||||||
finish_drm_resources(drm);
|
wl_list_remove(&drm->dev_change.link);
|
||||||
finish_drm_renderer(&drm->renderer);
|
wl_list_remove(&drm->dev_remove.link);
|
||||||
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);
|
|
||||||
|
|
||||||
if (drm->parent) {
|
if (drm->parent) {
|
||||||
return drm->parent->renderer.wlr_rend;
|
finish_drm_renderer(&drm->mgpu_renderer);
|
||||||
} else {
|
|
||||||
return drm->renderer.wlr_rend;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) {
|
||||||
|
@ -70,69 +69,95 @@ static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) {
|
||||||
return drm->clock;
|
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,
|
.start = backend_start,
|
||||||
.destroy = backend_destroy,
|
.destroy = backend_destroy,
|
||||||
.get_renderer = backend_get_renderer,
|
|
||||||
.get_presentation_clock = backend_get_presentation_clock,
|
.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) {
|
bool wlr_backend_is_drm(struct wlr_backend *b) {
|
||||||
return b->impl == &backend_impl;
|
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 =
|
struct wlr_drm_backend *drm =
|
||||||
wl_container_of(listener, drm, session_signal);
|
wl_container_of(listener, drm, session_active);
|
||||||
struct wlr_session *session = data;
|
struct wlr_session *session = drm->session;
|
||||||
|
|
||||||
if (session->active) {
|
if (session->active) {
|
||||||
wlr_log(WLR_INFO, "DRM fd resumed");
|
wlr_log(WLR_INFO, "DRM fd resumed");
|
||||||
scan_drm_connectors(drm);
|
scan_drm_connectors(drm, NULL);
|
||||||
|
|
||||||
struct wlr_drm_connector *conn;
|
struct wlr_drm_connector *conn;
|
||||||
wl_list_for_each(conn, &drm->outputs, link){
|
wl_list_for_each(conn, &drm->outputs, link) {
|
||||||
if (conn->output.enabled) {
|
struct wlr_output_mode *mode = NULL;
|
||||||
drm_connector_set_mode(&conn->output,
|
uint32_t committed = WLR_OUTPUT_STATE_ENABLED;
|
||||||
conn->output.current_mode);
|
if (conn->output.enabled && conn->output.current_mode != NULL) {
|
||||||
} else {
|
committed |= WLR_OUTPUT_STATE_MODE;
|
||||||
enable_drm_connector(&conn->output, false);
|
mode = conn->output.current_mode;
|
||||||
}
|
|
||||||
|
|
||||||
if (!conn->crtc) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_drm_plane *plane = conn->crtc->cursor;
|
|
||||||
drm->iface->crtc_set_cursor(drm, conn->crtc,
|
|
||||||
(plane && plane->cursor_enabled) ? plane->surf.back : NULL);
|
|
||||||
drm->iface->crtc_move_cursor(drm, conn->crtc, conn->cursor_x,
|
|
||||||
conn->cursor_y);
|
|
||||||
|
|
||||||
if (conn->crtc->gamma_table != NULL) {
|
|
||||||
size_t size = conn->crtc->gamma_table_size;
|
|
||||||
uint16_t *r = conn->crtc->gamma_table;
|
|
||||||
uint16_t *g = conn->crtc->gamma_table + size;
|
|
||||||
uint16_t *b = conn->crtc->gamma_table + 2 * size;
|
|
||||||
drm->iface->crtc_set_gamma(drm, conn->crtc, size, r, g, b);
|
|
||||||
} else {
|
|
||||||
set_drm_connector_gamma(&conn->output, 0, NULL, NULL, NULL);
|
|
||||||
}
|
}
|
||||||
|
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 {
|
} else {
|
||||||
wlr_log(WLR_INFO, "DRM fd paused");
|
wlr_log(WLR_INFO, "DRM fd paused");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drm_invalidated(struct wl_listener *listener, void *data) {
|
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;
|
||||||
|
|
||||||
|
if (!drm->session->active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
struct wlr_drm_backend *drm =
|
struct wlr_drm_backend *drm =
|
||||||
wl_container_of(listener, drm, drm_invalidated);
|
wl_container_of(listener, drm, session_destroy);
|
||||||
|
backend_destroy(&drm->backend);
|
||||||
char *name = drmGetDeviceNameFromFd2(drm->fd);
|
|
||||||
wlr_log(WLR_DEBUG, "%s invalidated", name);
|
|
||||||
free(name);
|
|
||||||
|
|
||||||
scan_drm_connectors(drm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
|
@ -141,16 +166,21 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
backend_destroy(&drm->backend);
|
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_backend *wlr_drm_backend_create(struct wl_display *display,
|
||||||
struct wlr_session *session, int gpu_fd, struct wlr_backend *parent,
|
struct wlr_session *session, struct wlr_device *dev,
|
||||||
wlr_renderer_create_func_t create_renderer_func) {
|
struct wlr_backend *parent) {
|
||||||
assert(display && session && gpu_fd >= 0);
|
assert(display && session && dev);
|
||||||
assert(!parent || wlr_backend_is_drm(parent));
|
assert(!parent || wlr_backend_is_drm(parent));
|
||||||
|
|
||||||
char *name = drmGetDeviceNameFromFd2(gpu_fd);
|
char *name = drmGetDeviceNameFromFd2(dev->fd);
|
||||||
drmVersion *version = drmGetVersion(gpu_fd);
|
drmVersion *version = drmGetVersion(dev->fd);
|
||||||
wlr_log(WLR_INFO, "Initializing DRM backend for %s (%s)", name, version->name);
|
wlr_log(WLR_INFO, "Initializing DRM backend for %s (%s)", name, version->name);
|
||||||
free(name);
|
|
||||||
drmFreeVersion(version);
|
drmFreeVersion(version);
|
||||||
|
|
||||||
struct wlr_drm_backend *drm = calloc(1, sizeof(struct wlr_drm_backend));
|
struct wlr_drm_backend *drm = calloc(1, sizeof(struct wlr_drm_backend));
|
||||||
|
@ -161,28 +191,40 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
|
||||||
wlr_backend_init(&drm->backend, &backend_impl);
|
wlr_backend_init(&drm->backend, &backend_impl);
|
||||||
|
|
||||||
drm->session = session;
|
drm->session = session;
|
||||||
|
wl_list_init(&drm->fbs);
|
||||||
wl_list_init(&drm->outputs);
|
wl_list_init(&drm->outputs);
|
||||||
|
|
||||||
drm->fd = gpu_fd;
|
drm->dev = dev;
|
||||||
|
drm->fd = dev->fd;
|
||||||
|
drm->name = name;
|
||||||
|
|
||||||
if (parent != NULL) {
|
if (parent != NULL) {
|
||||||
drm->parent = get_drm_backend_from_backend(parent);
|
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;
|
drm->dev_change.notify = handle_dev_change;
|
||||||
wlr_session_signal_add(session, gpu_fd, &drm->drm_invalidated);
|
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;
|
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,
|
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) {
|
if (!drm->drm_event) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create DRM event source");
|
wlr_log(WLR_ERROR, "Failed to create DRM event source");
|
||||||
goto error_fd;
|
goto error_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
drm->session_signal.notify = session_signal;
|
drm->session_active.notify = handle_session_active;
|
||||||
wl_signal_add(&session->session_signal, &drm->session_signal);
|
wl_signal_add(&session->events.active, &drm->session_active);
|
||||||
|
|
||||||
if (!check_drm_features(drm)) {
|
if (!check_drm_features(drm)) {
|
||||||
goto error_event;
|
goto error_event;
|
||||||
|
@ -192,21 +234,54 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
|
||||||
goto error_event;
|
goto error_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!init_drm_renderer(drm, &drm->renderer, create_renderer_func)) {
|
if (drm->parent) {
|
||||||
wlr_log(WLR_ERROR, "Failed to initialize renderer");
|
if (!init_drm_renderer(drm, &drm->mgpu_renderer)) {
|
||||||
goto error_event;
|
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;
|
||||||
|
wl_signal_add(&session->events.destroy, &drm->session_destroy);
|
||||||
|
|
||||||
drm->display_destroy.notify = handle_display_destroy;
|
drm->display_destroy.notify = handle_display_destroy;
|
||||||
wl_display_add_destroy_listener(display, &drm->display_destroy);
|
wl_display_add_destroy_listener(display, &drm->display_destroy);
|
||||||
|
|
||||||
return &drm->backend;
|
return &drm->backend;
|
||||||
|
|
||||||
|
error_mgpu_renderer:
|
||||||
|
finish_drm_renderer(&drm->mgpu_renderer);
|
||||||
|
error_resources:
|
||||||
|
finish_drm_resources(drm);
|
||||||
error_event:
|
error_event:
|
||||||
wl_list_remove(&drm->session_signal.link);
|
wl_list_remove(&drm->session_active.link);
|
||||||
wl_event_source_remove(drm->drm_event);
|
wl_event_source_remove(drm->drm_event);
|
||||||
error_fd:
|
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);
|
free(drm);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,269 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2005-2006 Luc Verhaegen.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "backend/drm/cvt.h"
|
||||||
|
|
||||||
|
/* top/bottom margin size (% of height) - default: 1.8 */
|
||||||
|
#define CVT_MARGIN_PERCENTAGE 1.8
|
||||||
|
|
||||||
|
/* character cell horizontal granularity (pixels) - default 8 */
|
||||||
|
#define CVT_H_GRANULARITY 8
|
||||||
|
|
||||||
|
/* Minimum vertical porch (lines) - default 3 */
|
||||||
|
#define CVT_MIN_V_PORCH 3
|
||||||
|
|
||||||
|
/* Minimum number of vertical back porch lines - default 6 */
|
||||||
|
#define CVT_MIN_V_BPORCH 6
|
||||||
|
|
||||||
|
/* Pixel clock step (kHz) */
|
||||||
|
#define CVT_CLOCK_STEP 250
|
||||||
|
|
||||||
|
/* Minimum time of vertical sync + back porch interval (µs)
|
||||||
|
* default 550.0 */
|
||||||
|
#define CVT_MIN_VSYNC_BP 550.0
|
||||||
|
|
||||||
|
/* Nominal hsync width (% of line period) - default 8 */
|
||||||
|
#define CVT_HSYNC_PERCENTAGE 8
|
||||||
|
|
||||||
|
/* Definition of Horizontal blanking time limitation */
|
||||||
|
/* Gradient (%/kHz) - default 600 */
|
||||||
|
#define CVT_M_FACTOR 600
|
||||||
|
|
||||||
|
/* Offset (%) - default 40 */
|
||||||
|
#define CVT_C_FACTOR 40
|
||||||
|
|
||||||
|
/* Blanking time scaling factor - default 128 */
|
||||||
|
#define CVT_K_FACTOR 128
|
||||||
|
|
||||||
|
/* Scaling factor weighting - default 20 */
|
||||||
|
#define CVT_J_FACTOR 20
|
||||||
|
|
||||||
|
#define CVT_M_PRIME CVT_M_FACTOR * CVT_K_FACTOR / 256
|
||||||
|
#define CVT_C_PRIME (CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \
|
||||||
|
CVT_J_FACTOR
|
||||||
|
|
||||||
|
/* Minimum vertical blanking interval time (µs) - default 460 */
|
||||||
|
#define CVT_RB_MIN_VBLANK 460.0
|
||||||
|
|
||||||
|
/* Fixed number of clocks for horizontal sync */
|
||||||
|
#define CVT_RB_H_SYNC 32.0
|
||||||
|
|
||||||
|
/* Fixed number of clocks for horizontal blanking */
|
||||||
|
#define CVT_RB_H_BLANK 160.0
|
||||||
|
|
||||||
|
/* Fixed number of lines for vertical front porch - default 3 */
|
||||||
|
#define CVT_RB_VFPORCH 3
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate a CVT standard mode from hdisplay, vdisplay and vrefresh.
|
||||||
|
*
|
||||||
|
* These calculations are stolen from the CVT calculation spreadsheet written
|
||||||
|
* by Graham Loveridge. He seems to be claiming no copyright and there seems to
|
||||||
|
* be no license attached to this. He apparently just wants to see his name
|
||||||
|
* mentioned.
|
||||||
|
*
|
||||||
|
* This file can be found at http://www.vesa.org/Public/CVT/CVTd6r1.xls
|
||||||
|
*
|
||||||
|
* Comments and structure corresponds to the comments and structure of the xls.
|
||||||
|
* This should ease importing of future changes to the standard (not very
|
||||||
|
* likely though).
|
||||||
|
*
|
||||||
|
* This function is borrowed from xorg-xserver's xf86CVTmode.
|
||||||
|
*/
|
||||||
|
void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay,
|
||||||
|
float vrefresh, bool reduced, bool interlaced) {
|
||||||
|
bool margins = false;
|
||||||
|
float vfield_rate, hperiod;
|
||||||
|
int hdisplay_rnd, hmargin;
|
||||||
|
int vdisplay_rnd, vmargin, vsync;
|
||||||
|
float interlace; /* Please rename this */
|
||||||
|
|
||||||
|
/* CVT default is 60.0Hz */
|
||||||
|
if (!vrefresh) {
|
||||||
|
vrefresh = 60.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1. Required field rate */
|
||||||
|
if (interlaced) {
|
||||||
|
vfield_rate = vrefresh * 2;
|
||||||
|
} else {
|
||||||
|
vfield_rate = vrefresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2. Horizontal pixels */
|
||||||
|
hdisplay_rnd = hdisplay - (hdisplay % CVT_H_GRANULARITY);
|
||||||
|
|
||||||
|
/* 3. Determine left and right borders */
|
||||||
|
if (margins) {
|
||||||
|
/* right margin is actually exactly the same as left */
|
||||||
|
hmargin = (((float) hdisplay_rnd) * CVT_MARGIN_PERCENTAGE / 100.0);
|
||||||
|
hmargin -= hmargin % CVT_H_GRANULARITY;
|
||||||
|
} else {
|
||||||
|
hmargin = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 4. Find total active pixels */
|
||||||
|
mode->hdisplay = hdisplay_rnd + 2 * hmargin;
|
||||||
|
|
||||||
|
/* 5. Find number of lines per field */
|
||||||
|
if (interlaced) {
|
||||||
|
vdisplay_rnd = vdisplay / 2;
|
||||||
|
} else {
|
||||||
|
vdisplay_rnd = vdisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 6. Find top and bottom margins */
|
||||||
|
/* nope. */
|
||||||
|
if (margins) {
|
||||||
|
/* top and bottom margins are equal again. */
|
||||||
|
vmargin = (((float) vdisplay_rnd) * CVT_MARGIN_PERCENTAGE / 100.0);
|
||||||
|
} else {
|
||||||
|
vmargin = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mode->vdisplay = vdisplay + 2 * vmargin;
|
||||||
|
|
||||||
|
/* 7. interlace */
|
||||||
|
if (interlaced) {
|
||||||
|
interlace = 0.5;
|
||||||
|
} else {
|
||||||
|
interlace = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine vsync Width from aspect ratio */
|
||||||
|
if (!(vdisplay % 3) && ((vdisplay * 4 / 3) == hdisplay)) {
|
||||||
|
vsync = 4;
|
||||||
|
} else if (!(vdisplay % 9) && ((vdisplay * 16 / 9) == hdisplay)) {
|
||||||
|
vsync = 5;
|
||||||
|
} else if (!(vdisplay % 10) && ((vdisplay * 16 / 10) == hdisplay)) {
|
||||||
|
vsync = 6;
|
||||||
|
} else if (!(vdisplay % 4) && ((vdisplay * 5 / 4) == hdisplay)) {
|
||||||
|
vsync = 7;
|
||||||
|
} else if (!(vdisplay % 9) && ((vdisplay * 15 / 9) == hdisplay)) {
|
||||||
|
vsync = 7;
|
||||||
|
} else { /* Custom */
|
||||||
|
vsync = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!reduced) { /* simplified GTF calculation */
|
||||||
|
float hblank_percentage;
|
||||||
|
int vsync_and_back_porch, vblank_porch;
|
||||||
|
int hblank;
|
||||||
|
|
||||||
|
/* 8. Estimated Horizontal period */
|
||||||
|
hperiod = ((float) (1000000.0 / vfield_rate - CVT_MIN_VSYNC_BP)) /
|
||||||
|
(vdisplay_rnd + 2 * vmargin + CVT_MIN_V_PORCH + interlace);
|
||||||
|
|
||||||
|
/* 9. Find number of lines in sync + backporch */
|
||||||
|
if (((int) (CVT_MIN_VSYNC_BP / hperiod) + 1) <
|
||||||
|
(vsync + CVT_MIN_V_PORCH)) {
|
||||||
|
vsync_and_back_porch = vsync + CVT_MIN_V_PORCH;
|
||||||
|
} else {
|
||||||
|
vsync_and_back_porch = (int) (CVT_MIN_VSYNC_BP / hperiod) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 10. Find number of lines in back porch */
|
||||||
|
vblank_porch = vsync_and_back_porch - vsync;
|
||||||
|
(void) vblank_porch;
|
||||||
|
|
||||||
|
/* 11. Find total number of lines in vertical field */
|
||||||
|
mode->vtotal = vdisplay_rnd + 2 * vmargin + vsync_and_back_porch + interlace
|
||||||
|
+ CVT_MIN_V_PORCH;
|
||||||
|
|
||||||
|
/* 12. Find ideal blanking duty cycle from formula */
|
||||||
|
hblank_percentage = CVT_C_PRIME - CVT_M_PRIME * hperiod / 1000.0;
|
||||||
|
|
||||||
|
/* 13. Blanking time */
|
||||||
|
if (hblank_percentage < 20) {
|
||||||
|
hblank_percentage = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
hblank = mode->hdisplay * hblank_percentage / (100.0 - hblank_percentage);
|
||||||
|
hblank -= hblank % (2 * CVT_H_GRANULARITY);
|
||||||
|
|
||||||
|
/* 14. Find total number of pixels in a line. */
|
||||||
|
mode->htotal = mode->hdisplay + hblank;
|
||||||
|
|
||||||
|
/* Fill in hsync values */
|
||||||
|
mode->hsync_end = mode->hdisplay + hblank / 2;
|
||||||
|
|
||||||
|
mode->hsync_start = mode->hsync_end -
|
||||||
|
(mode->htotal * CVT_HSYNC_PERCENTAGE) / 100;
|
||||||
|
mode->hsync_start += CVT_H_GRANULARITY -
|
||||||
|
mode->hsync_start % CVT_H_GRANULARITY;
|
||||||
|
|
||||||
|
/* Fill in vsync values */
|
||||||
|
mode->vsync_start = mode->vdisplay + CVT_MIN_V_PORCH;
|
||||||
|
mode->vsync_end = mode->vsync_start + vsync;
|
||||||
|
} else { /* reduced blanking */
|
||||||
|
int vbi_lines;
|
||||||
|
|
||||||
|
/* 8. Estimate Horizontal period. */
|
||||||
|
hperiod = ((float) (1000000.0 / vfield_rate - CVT_RB_MIN_VBLANK)) /
|
||||||
|
(vdisplay_rnd + 2 * vmargin);
|
||||||
|
|
||||||
|
/* 9. Find number of lines in vertical blanking */
|
||||||
|
vbi_lines = ((float) CVT_RB_MIN_VBLANK) / hperiod + 1;
|
||||||
|
|
||||||
|
/* 10. Check if vertical blanking is sufficient */
|
||||||
|
if (vbi_lines < (CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH)) {
|
||||||
|
vbi_lines = CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 11. Find total number of lines in vertical field */
|
||||||
|
mode->vtotal = vdisplay_rnd + 2 * vmargin + interlace + vbi_lines;
|
||||||
|
|
||||||
|
/* 12. Find total number of pixels in a line */
|
||||||
|
mode->htotal = mode->hdisplay + CVT_RB_H_BLANK;
|
||||||
|
|
||||||
|
/* Fill in hsync values */
|
||||||
|
mode->hsync_end = mode->hdisplay + CVT_RB_H_BLANK / 2;
|
||||||
|
mode->hsync_start = mode->hsync_end - CVT_RB_H_SYNC;
|
||||||
|
|
||||||
|
/* Fill in vsync values */
|
||||||
|
mode->vsync_start = mode->vdisplay + CVT_RB_VFPORCH;
|
||||||
|
mode->vsync_end = mode->vsync_start + vsync;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 15/13. Find pixel clock frequency (kHz for xf86) */
|
||||||
|
mode->clock = mode->htotal * 1000.0 / hperiod;
|
||||||
|
mode->clock -= mode->clock % CVT_CLOCK_STEP;
|
||||||
|
|
||||||
|
/* 17/15. Find actual Field rate */
|
||||||
|
mode->vrefresh = (1000.0 * ((float) mode->clock)) /
|
||||||
|
((float) (mode->htotal * mode->vtotal));
|
||||||
|
|
||||||
|
/* 18/16. Find actual vertical frame frequency */
|
||||||
|
/* ignore - just set the mode flag for interlaced */
|
||||||
|
if (interlaced) {
|
||||||
|
mode->vtotal *= 2;
|
||||||
|
mode->flags |= DRM_MODE_FLAG_INTERLACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(mode->name, sizeof(mode->name), "%dx%d", hdisplay, vdisplay);
|
||||||
|
|
||||||
|
if (reduced) {
|
||||||
|
mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC;
|
||||||
|
} else {
|
||||||
|
mode->flags |= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC;
|
||||||
|
}
|
||||||
|
}
|
1715
backend/drm/drm.c
1715
backend/drm/drm.c
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,5 @@
|
||||||
#include <gbm.h>
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
#include <xf86drmMode.h>
|
#include <xf86drmMode.h>
|
||||||
|
@ -6,84 +7,225 @@
|
||||||
#include "backend/drm/iface.h"
|
#include "backend/drm/iface.h"
|
||||||
#include "backend/drm/util.h"
|
#include "backend/drm/util.h"
|
||||||
|
|
||||||
static bool legacy_crtc_pageflip(struct wlr_drm_backend *drm,
|
static bool legacy_fb_props_match(struct wlr_drm_fb *fb1,
|
||||||
struct wlr_drm_connector *conn, struct wlr_drm_crtc *crtc,
|
struct wlr_drm_fb *fb2) {
|
||||||
uint32_t fb_id, drmModeModeInfo *mode) {
|
struct wlr_dmabuf_attributes dmabuf1 = {0}, dmabuf2 = {0};
|
||||||
if (mode) {
|
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 (state->active) {
|
||||||
|
struct wlr_drm_fb *fb = plane_get_next_fb(crtc->primary);
|
||||||
|
if (fb == NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "%s: failed to acquire primary FB",
|
||||||
|
conn->output.name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fb_id = fb->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state->modeset) {
|
||||||
|
uint32_t *conns = NULL;
|
||||||
|
size_t conns_len = 0;
|
||||||
|
drmModeModeInfo *mode = NULL;
|
||||||
|
if (state->active) {
|
||||||
|
conns = &conn->id;
|
||||||
|
conns_len = 1;
|
||||||
|
mode = (drmModeModeInfo *)&state->mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_drm_conn_log_errno(conn, WLR_ERROR,
|
||||||
|
"Failed to set DPMS property");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (drmModeSetCrtc(drm->fd, crtc->id, fb_id, 0, 0,
|
if (drmModeSetCrtc(drm->fd, crtc->id, fb_id, 0, 0,
|
||||||
&conn->id, 1, mode)) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (drmModePageFlip(drm->fd, crtc->id, fb_id, DRM_MODE_PAGE_FLIP_EVENT, drm)) {
|
if (state->base->committed & WLR_OUTPUT_STATE_GAMMA_LUT) {
|
||||||
wlr_log_errno(WLR_ERROR, "%s: Failed to page flip", conn->output.name);
|
if (!drm_legacy_crtc_set_gamma(drm, crtc,
|
||||||
return false;
|
state->base->gamma_lut_size, state->base->gamma_lut)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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,
|
||||||
static bool legacy_conn_enable(struct wlr_drm_backend *drm,
|
crtc->props.vrr_enabled,
|
||||||
struct wlr_drm_connector *conn, bool enable) {
|
state->base->adaptive_sync_enabled) != 0) {
|
||||||
int ret = drmModeConnectorSetProperty(drm->fd, conn->id, conn->props.dpms,
|
wlr_drm_conn_log_errno(conn, WLR_ERROR,
|
||||||
enable ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF);
|
"drmModeObjectSetProperty(VRR_ENABLED) failed");
|
||||||
|
return false;
|
||||||
if (!enable) {
|
}
|
||||||
drmModeSetCrtc(drm->fd, conn->crtc->id, 0, 0, 0, NULL, 0,
|
output->adaptive_sync_status = state->base->adaptive_sync_enabled ?
|
||||||
NULL);
|
WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED :
|
||||||
|
WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED;
|
||||||
|
wlr_drm_conn_log(conn, WLR_DEBUG, "VRR %s",
|
||||||
|
state->base->adaptive_sync_enabled ? "enabled" : "disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret >= 0;
|
if (cursor != NULL && drm_connector_is_cursor_visible(conn)) {
|
||||||
}
|
struct wlr_drm_fb *cursor_fb = plane_get_next_fb(cursor);
|
||||||
|
if (cursor_fb == NULL) {
|
||||||
|
wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to acquire cursor FB");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool legacy_crtc_set_cursor(struct wlr_drm_backend *drm,
|
drmModeFB *drm_fb = drmModeGetFB(drm->fd, cursor_fb->id);
|
||||||
struct wlr_drm_crtc *crtc, struct gbm_bo *bo) {
|
if (drm_fb == NULL) {
|
||||||
if (!crtc || !crtc->cursor) {
|
wlr_drm_conn_log_errno(conn, WLR_DEBUG, "Failed to get cursor "
|
||||||
return true;
|
"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);
|
||||||
|
|
||||||
if (!bo) {
|
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_drm_conn_log_errno(conn, WLR_ERROR, "drmModeMoveCursor failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (drmModeSetCursor(drm->fd, crtc->id, 0, 0, 0)) {
|
if (drmModeSetCursor(drm->fd, crtc->id, 0, 0, 0)) {
|
||||||
wlr_log_errno(WLR_DEBUG, "Failed to clear hardware cursor");
|
wlr_drm_conn_log_errno(conn, WLR_DEBUG, "drmModeSetCursor failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_drm_plane *plane = crtc->cursor;
|
if (flags & DRM_MODE_PAGE_FLIP_EVENT) {
|
||||||
|
if (drmModePageFlip(drm->fd, crtc->id, fb_id,
|
||||||
if (drmModeSetCursor(drm->fd, crtc->id, gbm_bo_get_handle(bo).u32,
|
DRM_MODE_PAGE_FLIP_EVENT, drm)) {
|
||||||
plane->surf.width, plane->surf.height)) {
|
wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModePageFlip failed");
|
||||||
wlr_log_errno(WLR_DEBUG, "Failed to set hardware cursor");
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool legacy_crtc_move_cursor(struct wlr_drm_backend *drm,
|
static void fill_empty_gamma_table(size_t size,
|
||||||
struct wlr_drm_crtc *crtc, int x, int y) {
|
|
||||||
return !drmModeMoveCursor(drm->fd, crtc->id, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool legacy_crtc_set_gamma(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_drm_crtc *crtc, size_t size,
|
|
||||||
uint16_t *r, uint16_t *g, uint16_t *b) {
|
uint16_t *r, uint16_t *g, uint16_t *b) {
|
||||||
return !drmModeCrtcSetGamma(drm->fd, crtc->id, (uint32_t)size, r, g, b);
|
assert(0xFFFF < UINT64_MAX / (size - 1));
|
||||||
|
for (uint32_t i = 0; i < size; ++i) {
|
||||||
|
uint16_t val = (uint64_t)0xFFFF * i / (size - 1);
|
||||||
|
r[i] = g[i] = b[i] = val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t legacy_crtc_get_gamma_size(struct wlr_drm_backend *drm,
|
bool drm_legacy_crtc_set_gamma(struct wlr_drm_backend *drm,
|
||||||
struct wlr_drm_crtc *crtc) {
|
struct wlr_drm_crtc *crtc, size_t size, uint16_t *lut) {
|
||||||
return (size_t)crtc->legacy_crtc->gamma_size;
|
uint16_t *linear_lut = NULL;
|
||||||
|
if (size == 0) {
|
||||||
|
// The legacy interface doesn't offer a way to reset the gamma LUT
|
||||||
|
size = drm_crtc_get_gamma_lut_size(drm, crtc);
|
||||||
|
if (size == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
linear_lut = malloc(3 * size * sizeof(uint16_t));
|
||||||
|
if (linear_lut == NULL) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fill_empty_gamma_table(size, linear_lut, linear_lut + size,
|
||||||
|
linear_lut + 2 * size);
|
||||||
|
|
||||||
|
lut = linear_lut;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t *r = lut, *g = lut + size, *b = lut + 2 * size;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(linear_lut);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct wlr_drm_interface legacy_iface = {
|
const struct wlr_drm_interface legacy_iface = {
|
||||||
.conn_enable = legacy_conn_enable,
|
.crtc_commit = legacy_crtc_commit,
|
||||||
.crtc_pageflip = legacy_crtc_pageflip,
|
|
||||||
.crtc_set_cursor = legacy_crtc_set_cursor,
|
|
||||||
.crtc_move_cursor = legacy_crtc_move_cursor,
|
|
||||||
.crtc_set_gamma = legacy_crtc_set_gamma,
|
|
||||||
.crtc_get_gamma_size = legacy_crtc_get_gamma_size,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
wlr_files += files(
|
||||||
|
'atomic.c',
|
||||||
|
'backend.c',
|
||||||
|
'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 <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
@ -24,6 +25,10 @@ static const struct prop_info connector_info[] = {
|
||||||
{ "EDID", INDEX(edid) },
|
{ "EDID", INDEX(edid) },
|
||||||
{ "PATH", INDEX(path) },
|
{ "PATH", INDEX(path) },
|
||||||
{ "link-status", INDEX(link_status) },
|
{ "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
|
#undef INDEX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -33,8 +38,7 @@ static const struct prop_info crtc_info[] = {
|
||||||
{ "GAMMA_LUT", INDEX(gamma_lut) },
|
{ "GAMMA_LUT", INDEX(gamma_lut) },
|
||||||
{ "GAMMA_LUT_SIZE", INDEX(gamma_lut_size) },
|
{ "GAMMA_LUT_SIZE", INDEX(gamma_lut_size) },
|
||||||
{ "MODE_ID", INDEX(mode_id) },
|
{ "MODE_ID", INDEX(mode_id) },
|
||||||
{ "rotation", INDEX(rotation) },
|
{ "VRR_ENABLED", INDEX(vrr_enabled) },
|
||||||
{ "scaling mode", INDEX(scaling_mode) },
|
|
||||||
#undef INDEX
|
#undef INDEX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,12 +49,14 @@ static const struct prop_info plane_info[] = {
|
||||||
{ "CRTC_W", INDEX(crtc_w) },
|
{ "CRTC_W", INDEX(crtc_w) },
|
||||||
{ "CRTC_X", INDEX(crtc_x) },
|
{ "CRTC_X", INDEX(crtc_x) },
|
||||||
{ "CRTC_Y", INDEX(crtc_y) },
|
{ "CRTC_Y", INDEX(crtc_y) },
|
||||||
|
{ "FB_DAMAGE_CLIPS", INDEX(fb_damage_clips) },
|
||||||
{ "FB_ID", INDEX(fb_id) },
|
{ "FB_ID", INDEX(fb_id) },
|
||||||
{ "IN_FORMATS", INDEX(in_formats) },
|
{ "IN_FORMATS", INDEX(in_formats) },
|
||||||
{ "SRC_H", INDEX(src_h) },
|
{ "SRC_H", INDEX(src_h) },
|
||||||
{ "SRC_W", INDEX(src_w) },
|
{ "SRC_W", INDEX(src_w) },
|
||||||
{ "SRC_X", INDEX(src_x) },
|
{ "SRC_X", INDEX(src_x) },
|
||||||
{ "SRC_Y", INDEX(src_y) },
|
{ "SRC_Y", INDEX(src_y) },
|
||||||
|
{ "rotation", INDEX(rotation) },
|
||||||
{ "type", INDEX(type) },
|
{ "type", INDEX(type) },
|
||||||
#undef INDEX
|
#undef INDEX
|
||||||
};
|
};
|
||||||
|
@ -150,3 +156,27 @@ void *get_drm_prop_blob(int fd, uint32_t obj, uint32_t prop, size_t *ret_len) {
|
||||||
drmModeFreePropertyBlob(blob);
|
drmModeFreePropertyBlob(blob);
|
||||||
return ptr;
|
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,53 +1,42 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <drm_fourcc.h>
|
#include <drm_fourcc.h>
|
||||||
#include <gbm.h>
|
#include <fcntl.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-util.h>
|
#include <wayland-util.h>
|
||||||
#include <wlr/render/egl.h>
|
|
||||||
#include <wlr/render/gles2.h>
|
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include "backend/drm/drm.h"
|
#include "backend/drm/drm.h"
|
||||||
#include "glapi.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,
|
bool init_drm_renderer(struct wlr_drm_backend *drm,
|
||||||
struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_renderer_func) {
|
struct wlr_drm_renderer *renderer) {
|
||||||
renderer->gbm = gbm_create_device(drm->fd);
|
renderer->backend = drm;
|
||||||
if (!renderer->gbm) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to create GBM device");
|
renderer->wlr_rend = renderer_autocreate_with_drm_fd(drm->fd);
|
||||||
|
if (!renderer->wlr_rend) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to create renderer");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!create_renderer_func) {
|
renderer->allocator = allocator_autocreate_with_drm_fd(&drm->backend,
|
||||||
create_renderer_func = wlr_renderer_autocreate;
|
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_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;
|
return true;
|
||||||
|
|
||||||
error_gbm:
|
|
||||||
gbm_device_destroy(renderer->gbm);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
|
void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
|
||||||
|
@ -55,14 +44,13 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wlr_allocator_destroy(renderer->allocator);
|
||||||
wlr_renderer_destroy(renderer->wlr_rend);
|
wlr_renderer_destroy(renderer->wlr_rend);
|
||||||
wlr_egl_finish(&renderer->egl);
|
|
||||||
gbm_device_destroy(renderer->gbm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height,
|
||||||
uint32_t format, uint32_t flags) {
|
const struct wlr_drm_format *drm_format) {
|
||||||
if (surf->width == width && surf->height == height) {
|
if (surf->width == width && surf->height == height) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -71,227 +59,357 @@ bool init_drm_surface(struct wlr_drm_surface *surf,
|
||||||
surf->width = width;
|
surf->width = width;
|
||||||
surf->height = height;
|
surf->height = height;
|
||||||
|
|
||||||
if (surf->gbm) {
|
wlr_swapchain_destroy(surf->swapchain);
|
||||||
if (surf->front) {
|
surf->swapchain = NULL;
|
||||||
gbm_surface_release_buffer(surf->gbm, surf->front);
|
|
||||||
surf->front = NULL;
|
|
||||||
}
|
|
||||||
if (surf->back) {
|
|
||||||
gbm_surface_release_buffer(surf->gbm, surf->back);
|
|
||||||
surf->back = NULL;
|
|
||||||
}
|
|
||||||
gbm_surface_destroy(surf->gbm);
|
|
||||||
}
|
|
||||||
wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl);
|
|
||||||
|
|
||||||
surf->gbm = gbm_surface_create(renderer->gbm, width, height,
|
surf->swapchain = wlr_swapchain_create(renderer->allocator, width, height,
|
||||||
format, GBM_BO_USE_RENDERING | flags);
|
drm_format);
|
||||||
if (!surf->gbm) {
|
if (surf->swapchain == NULL) {
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to create GBM surface");
|
wlr_log(WLR_ERROR, "Failed to create swapchain");
|
||||||
goto error_zero;
|
memset(surf, 0, sizeof(*surf));
|
||||||
}
|
return false;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
error_gbm:
|
|
||||||
gbm_surface_destroy(surf->gbm);
|
|
||||||
error_zero:
|
|
||||||
memset(surf, 0, sizeof(*surf));
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void finish_drm_surface(struct wlr_drm_surface *surf) {
|
static void finish_drm_surface(struct wlr_drm_surface *surf) {
|
||||||
if (!surf || !surf->renderer) {
|
if (!surf || !surf->renderer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (surf->front) {
|
wlr_swapchain_destroy(surf->swapchain);
|
||||||
gbm_surface_release_buffer(surf->gbm, surf->front);
|
|
||||||
}
|
|
||||||
if (surf->back) {
|
|
||||||
gbm_surface_release_buffer(surf->gbm, surf->back);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl);
|
|
||||||
if (surf->gbm) {
|
|
||||||
gbm_surface_destroy(surf->gbm);
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(surf, 0, sizeof(*surf));
|
memset(surf, 0, sizeof(*surf));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool make_drm_surface_current(struct wlr_drm_surface *surf,
|
struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf,
|
||||||
int *buffer_damage) {
|
struct wlr_buffer *buffer) {
|
||||||
return wlr_egl_make_current(&surf->renderer->egl, surf->egl, buffer_damage);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct gbm_bo *swap_drm_surface_buffers(struct wlr_drm_surface *surf,
|
|
||||||
pixman_region32_t *damage) {
|
|
||||||
if (surf->front) {
|
|
||||||
gbm_surface_release_buffer(surf->gbm, surf->front);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_egl_swap_buffers(&surf->renderer->egl, surf->egl, damage);
|
|
||||||
|
|
||||||
surf->front = surf->back;
|
|
||||||
surf->back = gbm_surface_lock_front_buffer(surf->gbm);
|
|
||||||
return surf->back;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct gbm_bo *get_drm_surface_front(struct wlr_drm_surface *surf) {
|
|
||||||
if (surf->front) {
|
|
||||||
return surf->front;
|
|
||||||
}
|
|
||||||
|
|
||||||
make_drm_surface_current(surf, NULL);
|
|
||||||
struct wlr_renderer *renderer = surf->renderer->wlr_rend;
|
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 });
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_texture *tex = wlr_texture_from_buffer(renderer, buffer);
|
||||||
|
if (tex == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_buffer *dst = wlr_swapchain_acquire(surf->swapchain, NULL);
|
||||||
|
if (!dst) {
|
||||||
|
wlr_texture_destroy(tex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_renderer_end(renderer);
|
||||||
return swap_drm_surface_buffers(surf, NULL);
|
|
||||||
|
wlr_texture_destroy(tex);
|
||||||
|
|
||||||
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
void post_drm_surface(struct wlr_drm_surface *surf) {
|
|
||||||
if (surf->front) {
|
void drm_plane_finish_surface(struct wlr_drm_plane *plane) {
|
||||||
gbm_surface_release_buffer(surf->gbm, surf->front);
|
if (!plane) {
|
||||||
surf->front = NULL;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drm_fb_clear(&plane->pending_fb);
|
||||||
|
drm_fb_clear(&plane->queued_fb);
|
||||||
|
drm_fb_clear(&plane->current_fb);
|
||||||
|
|
||||||
|
finish_drm_surface(&plane->mgpu_surf);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct gbm_bo *import_gbm_bo(struct wlr_drm_renderer *renderer,
|
struct wlr_drm_format *drm_plane_pick_render_format(
|
||||||
struct wlr_dmabuf_attributes *attribs) {
|
struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer) {
|
||||||
if (attribs->modifier == DRM_FORMAT_MOD_INVALID && attribs->n_planes == 1
|
const struct wlr_drm_format_set *render_formats =
|
||||||
&& attribs->offset[0] == 0) {
|
wlr_renderer_get_render_formats(renderer->wlr_rend);
|
||||||
struct gbm_import_fd_data data = {
|
if (render_formats == NULL) {
|
||||||
.fd = attribs->fd[0],
|
wlr_log(WLR_ERROR, "Failed to get render formats");
|
||||||
.width = attribs->width,
|
return NULL;
|
||||||
.height = attribs->height,
|
}
|
||||||
.stride = attribs->stride[0],
|
|
||||||
.format = attribs->format,
|
|
||||||
};
|
|
||||||
|
|
||||||
return gbm_bo_import(renderer->gbm, GBM_BO_IMPORT_FD,
|
const struct wlr_drm_format_set *plane_formats = &plane->formats;
|
||||||
&data, GBM_BO_USE_SCANOUT);
|
|
||||||
|
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 {
|
} else {
|
||||||
struct gbm_import_fd_modifier_data data = {
|
if (dmabuf->modifier != DRM_FORMAT_MOD_INVALID &&
|
||||||
.width = attribs->width,
|
dmabuf->modifier != DRM_FORMAT_MOD_LINEAR) {
|
||||||
.height = attribs->height,
|
wlr_log(WLR_ERROR, "Cannot import DRM framebuffer with explicit "
|
||||||
.format = attribs->format,
|
"modifier 0x%"PRIX64, dmabuf->modifier);
|
||||||
.num_fds = attribs->n_planes,
|
return 0;
|
||||||
.modifier = attribs->modifier,
|
|
||||||
};
|
|
||||||
|
|
||||||
if ((size_t)attribs->n_planes > sizeof(data.fds) / sizeof(data.fds[0])) {
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < (size_t)attribs->n_planes; ++i) {
|
int ret = drmModeAddFB2(drm->fd, dmabuf->width, dmabuf->height,
|
||||||
data.fds[i] = attribs->fd[i];
|
dmabuf->format, handles, dmabuf->stride, dmabuf->offset, &id, 0);
|
||||||
data.strides[i] = attribs->stride[i];
|
if (ret != 0 && dmabuf->format == DRM_FORMAT_ARGB8888 &&
|
||||||
data.offsets[i] = attribs->offset[i];
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return gbm_bo_import(renderer->gbm, GBM_BO_IMPORT_FD_MODIFIER,
|
// If multiple planes share the same BO handle, avoid double-closing it
|
||||||
&data, GBM_BO_USE_SCANOUT);
|
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 export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs) {
|
static void drm_poisoned_fb_handle_destroy(struct wlr_addon *addon) {
|
||||||
memset(attribs, 0, sizeof(struct wlr_dmabuf_attributes));
|
wlr_addon_finish(addon);
|
||||||
|
free(addon);
|
||||||
|
}
|
||||||
|
|
||||||
attribs->n_planes = gbm_bo_get_plane_count(bo);
|
static const struct wlr_addon_interface poisoned_fb_addon_impl = {
|
||||||
if (attribs->n_planes > WLR_DMABUF_MAX_PLANES) {
|
.name = "wlr_drm_poisoned_fb",
|
||||||
return false;
|
.destroy = drm_poisoned_fb_handle_destroy,
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
wlr_addon_init(addon, &buf->addons, drm, &poisoned_fb_addon_impl);
|
||||||
|
wlr_log(WLR_DEBUG, "Poisoning buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
wlr_log(WLR_DEBUG, "Failed to get DMA-BUF from buffer");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
attribs->width = gbm_bo_get_width(bo);
|
if (is_buffer_poisoned(drm, buf)) {
|
||||||
attribs->height = gbm_bo_get_height(bo);
|
wlr_log(WLR_DEBUG, "Buffer is poisoned");
|
||||||
attribs->format = gbm_bo_get_format(bo);
|
return NULL;
|
||||||
attribs->modifier = gbm_bo_get_modifier(bo);
|
}
|
||||||
|
|
||||||
for (int i = 0; i < attribs->n_planes; ++i) {
|
struct wlr_drm_fb *fb = calloc(1, sizeof(*fb));
|
||||||
attribs->offset[i] = gbm_bo_get_offset(bo, i);
|
if (!fb) {
|
||||||
attribs->stride[i] = gbm_bo_get_stride_for_plane(bo, i);
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
attribs->fd[i] = gbm_bo_get_fd(bo);
|
return NULL;
|
||||||
if (attribs->fd[i] < 0) {
|
}
|
||||||
for (int j = 0; j < i; ++j) {
|
|
||||||
close(attribs->fd[j]);
|
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.
|
||||||
|
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 {
|
||||||
|
wlr_log(WLR_DEBUG, "Buffer format 0x%"PRIX32" with modifier "
|
||||||
|
"0x%"PRIX64" cannot be scanned out",
|
||||||
|
attribs.format, attribs.modifier);
|
||||||
|
goto error_fb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
fb = drm_fb_create(drm, buf, formats);
|
||||||
|
if (!fb) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wlr_buffer_lock(buf);
|
||||||
|
drm_fb_move(fb_ptr, &fb);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_tex(struct gbm_bo *bo, void *data) {
|
void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old) {
|
||||||
struct wlr_texture *tex = data;
|
drm_fb_clear(new);
|
||||||
wlr_texture_destroy(tex);
|
*new = *old;
|
||||||
}
|
*old = NULL;
|
||||||
|
|
||||||
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)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
tex = wlr_texture_from_dmabuf(renderer->wlr_rend, &attribs);
|
|
||||||
if (tex) {
|
|
||||||
gbm_bo_set_user_data(bo, tex, free_tex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tex;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest,
|
|
||||||
struct gbm_bo *src) {
|
|
||||||
make_drm_surface_current(dest, NULL);
|
|
||||||
|
|
||||||
struct wlr_texture *tex = get_tex_for_bo(dest->renderer, src);
|
|
||||||
assert(tex);
|
|
||||||
|
|
||||||
float mat[9];
|
|
||||||
wlr_matrix_projection(mat, 1, 1, WL_OUTPUT_TRANSFORM_NORMAL);
|
|
||||||
|
|
||||||
struct wlr_renderer *renderer = dest->renderer->wlr_rend;
|
|
||||||
wlr_renderer_begin(renderer, dest->width, dest->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);
|
|
||||||
|
|
||||||
return swap_drm_surface_buffers(dest, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool init_drm_plane_surfaces(struct wlr_drm_plane *plane,
|
|
||||||
struct wlr_drm_backend *drm, int32_t width, uint32_t height,
|
|
||||||
uint32_t format) {
|
|
||||||
if (!drm->parent) {
|
|
||||||
return init_drm_surface(&plane->surf, &drm->renderer, width, height,
|
|
||||||
format, GBM_BO_USE_SCANOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!init_drm_surface(&plane->surf, &drm->parent->renderer,
|
|
||||||
width, height, format, GBM_BO_USE_LINEAR)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!init_drm_surface(&plane->mgpu_surf, &drm->renderer,
|
|
||||||
width, height, format, GBM_BO_USE_SCANOUT)) {
|
|
||||||
finish_drm_surface(&plane->surf);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#include <drm_fourcc.h>
|
#include <drm_fourcc.h>
|
||||||
#include <drm_mode.h>
|
#include <drm_mode.h>
|
||||||
#include <drm.h>
|
#include <drm.h>
|
||||||
#include <gbm.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
@ -39,6 +38,7 @@ static const char *get_manufacturer(uint16_t id) {
|
||||||
case ID('A', 'S', 'K'): return "Ask A/S";
|
case ID('A', 'S', 'K'): return "Ask A/S";
|
||||||
case ID('A', 'V', 'T'): return "Avtek (Electronics) Pty Ltd";
|
case ID('A', 'V', 'T'): return "Avtek (Electronics) Pty Ltd";
|
||||||
case ID('B', 'N', 'O'): return "Bang & Olufsen";
|
case ID('B', 'N', 'O'): return "Bang & Olufsen";
|
||||||
|
case ID('B', 'N', 'Q'): return "BenQ Corporation";
|
||||||
case ID('C', 'M', 'N'): return "Chimei Innolux Corporation";
|
case ID('C', 'M', 'N'): return "Chimei Innolux Corporation";
|
||||||
case ID('C', 'M', 'O'): return "Chi Mei Optoelectronics corp.";
|
case ID('C', 'M', 'O'): return "Chi Mei Optoelectronics corp.";
|
||||||
case ID('C', 'R', 'O'): return "Extraordinary Technologies PTY Limited";
|
case ID('C', 'R', 'O'): return "Extraordinary Technologies PTY Limited";
|
||||||
|
@ -94,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', 'E', 'S'): return "Vestel Elektronik Sanayi ve Ticaret A. S.";
|
||||||
case ID('V', 'I', 'T'): return "Visitech AS";
|
case ID('V', 'I', 'T'): return "Visitech AS";
|
||||||
case ID('V', 'I', 'Z'): return "VIZIO, Inc";
|
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('V', 'S', 'C'): return "ViewSonic Corporation";
|
||||||
case ID('Y', 'M', 'H'): return "Yamaha Corporation";
|
case ID('Y', 'M', 'H'): return "Yamaha Corporation";
|
||||||
default: return "Unknown";
|
default: return "Unknown";
|
||||||
|
@ -162,65 +163,19 @@ const char *conn_get_name(uint32_t type_id) {
|
||||||
case DRM_MODE_CONNECTOR_eDP: return "eDP";
|
case DRM_MODE_CONNECTOR_eDP: return "eDP";
|
||||||
case DRM_MODE_CONNECTOR_VIRTUAL: return "Virtual";
|
case DRM_MODE_CONNECTOR_VIRTUAL: return "Virtual";
|
||||||
case DRM_MODE_CONNECTOR_DSI: return "DSI";
|
case DRM_MODE_CONNECTOR_DSI: return "DSI";
|
||||||
#ifdef DRM_MODE_CONNECTOR_DPI
|
|
||||||
case DRM_MODE_CONNECTOR_DPI: return "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
|
#endif
|
||||||
default: return "Unknown";
|
default: return "Unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_fb(struct gbm_bo *bo, void *data) {
|
static bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key) {
|
||||||
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, uint32_t drm_format,
|
|
||||||
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 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, drm_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, drm_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) {
|
|
||||||
for (size_t i = 0; i < n; ++i) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
if (arr[i] == key) {
|
if (arr[i] == key) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -2,11 +2,8 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
#include <wlr/interfaces/wlr_input_device.h>
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
#include <wlr/render/egl.h>
|
|
||||||
#include <wlr/render/gles2.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include "backend/headless.h"
|
#include "backend/headless.h"
|
||||||
#include "glapi.h"
|
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
struct wlr_headless_backend *headless_backend_from_backend(
|
struct wlr_headless_backend *headless_backend_from_backend(
|
||||||
|
@ -29,8 +26,7 @@ static bool backend_start(struct wlr_backend *wlr_backend) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_headless_input_device *input_device;
|
struct wlr_headless_input_device *input_device;
|
||||||
wl_list_for_each(input_device, &backend->input_devices,
|
wl_list_for_each(input_device, &backend->input_devices, link) {
|
||||||
wlr_input_device.link) {
|
|
||||||
wlr_signal_emit_safe(&backend->backend.events.new_input,
|
wlr_signal_emit_safe(&backend->backend.events.new_input,
|
||||||
&input_device->wlr_input_device);
|
&input_device->wlr_input_device);
|
||||||
}
|
}
|
||||||
|
@ -55,28 +51,25 @@ static void backend_destroy(struct wlr_backend *wlr_backend) {
|
||||||
|
|
||||||
struct wlr_headless_input_device *input_device, *input_device_tmp;
|
struct wlr_headless_input_device *input_device, *input_device_tmp;
|
||||||
wl_list_for_each_safe(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_input_device_destroy(&input_device->wlr_input_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_signal_emit_safe(&wlr_backend->events.destroy, backend);
|
wlr_backend_finish(wlr_backend);
|
||||||
|
|
||||||
wlr_renderer_destroy(backend->renderer);
|
|
||||||
wlr_egl_finish(&backend->egl);
|
|
||||||
free(backend);
|
free(backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wlr_renderer *backend_get_renderer(
|
static uint32_t get_buffer_caps(struct wlr_backend *wlr_backend) {
|
||||||
struct wlr_backend *wlr_backend) {
|
return WLR_BUFFER_CAP_DATA_PTR
|
||||||
struct wlr_headless_backend *backend =
|
| WLR_BUFFER_CAP_DMABUF
|
||||||
headless_backend_from_backend(wlr_backend);
|
| WLR_BUFFER_CAP_SHM;
|
||||||
return backend->renderer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wlr_backend_impl backend_impl = {
|
static const struct wlr_backend_impl backend_impl = {
|
||||||
.start = backend_start,
|
.start = backend_start,
|
||||||
.destroy = backend_destroy,
|
.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) {
|
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
|
@ -85,8 +78,7 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
backend_destroy(&backend->backend);
|
backend_destroy(&backend->backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_backend *wlr_headless_backend_create(struct wl_display *display,
|
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");
|
wlr_log(WLR_INFO, "Creating headless backend");
|
||||||
|
|
||||||
struct wlr_headless_backend *backend =
|
struct wlr_headless_backend *backend =
|
||||||
|
@ -95,32 +87,13 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_display *display,
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_backend");
|
wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_backend");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_backend_init(&backend->backend, &backend_impl);
|
wlr_backend_init(&backend->backend, &backend_impl);
|
||||||
|
|
||||||
backend->display = display;
|
backend->display = display;
|
||||||
wl_list_init(&backend->outputs);
|
wl_list_init(&backend->outputs);
|
||||||
wl_list_init(&backend->input_devices);
|
wl_list_init(&backend->input_devices);
|
||||||
|
|
||||||
static const EGLint config_attribs[] = {
|
|
||||||
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
|
|
||||||
EGL_ALPHA_SIZE, 0,
|
|
||||||
EGL_BLUE_SIZE, 1,
|
|
||||||
EGL_GREEN_SIZE, 1,
|
|
||||||
EGL_RED_SIZE, 1,
|
|
||||||
EGL_NONE,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!create_renderer_func) {
|
|
||||||
create_renderer_func = wlr_renderer_autocreate;
|
|
||||||
}
|
|
||||||
|
|
||||||
backend->renderer = create_renderer_func(&backend->egl,
|
|
||||||
EGL_PLATFORM_SURFACELESS_MESA, NULL, (EGLint*)config_attribs, 0);
|
|
||||||
if (!backend->renderer) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to create renderer");
|
|
||||||
free(backend);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
backend->display_destroy.notify = handle_display_destroy;
|
backend->display_destroy.notify = handle_display_destroy;
|
||||||
wl_display_add_destroy_listener(display, &backend->display_destroy);
|
wl_display_add_destroy_listener(display, &backend->display_destroy);
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,16 @@
|
||||||
#include "backend/headless.h"
|
#include "backend/headless.h"
|
||||||
#include "util/signal.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) {
|
bool wlr_input_device_is_headless(struct wlr_input_device *wlr_dev) {
|
||||||
return wlr_dev->impl == &input_device_impl;
|
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);
|
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) {
|
if (backend->started) {
|
||||||
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_device);
|
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_device);
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
wlr_files += files(
|
||||||
|
'backend.c',
|
||||||
|
'input_device.c',
|
||||||
|
'output.c',
|
||||||
|
)
|
|
@ -1,84 +1,88 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include "backend/headless.h"
|
#include "backend/headless.h"
|
||||||
#include "util/signal.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(
|
static struct wlr_headless_output *headless_output_from_output(
|
||||||
struct wlr_output *wlr_output) {
|
struct wlr_output *wlr_output) {
|
||||||
assert(wlr_output_is_headless(wlr_output));
|
assert(wlr_output_is_headless(wlr_output));
|
||||||
return (struct wlr_headless_output *)wlr_output;
|
return (struct wlr_headless_output *)wlr_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
static EGLSurface egl_create_surface(struct wlr_egl *egl, unsigned int width,
|
static bool output_set_custom_mode(struct wlr_headless_output *output,
|
||||||
unsigned int height) {
|
int32_t width, int32_t height, int32_t refresh) {
|
||||||
EGLint attribs[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE};
|
|
||||||
|
|
||||||
EGLSurface surf = eglCreatePbufferSurface(egl->display, egl->config, attribs);
|
|
||||||
if (surf == EGL_NO_SURFACE) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to create EGL surface");
|
|
||||||
return EGL_NO_SURFACE;
|
|
||||||
}
|
|
||||||
return surf;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
struct wlr_headless_backend *backend = output->backend;
|
|
||||||
|
|
||||||
if (refresh <= 0) {
|
if (refresh <= 0) {
|
||||||
refresh = HEADLESS_DEFAULT_REFRESH;
|
refresh = HEADLESS_DEFAULT_REFRESH;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_egl_destroy_surface(&backend->egl, output->egl_surface);
|
|
||||||
|
|
||||||
output->egl_surface = egl_create_surface(&backend->egl, width, height);
|
|
||||||
if (output->egl_surface == EGL_NO_SURFACE) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to recreate EGL surface");
|
|
||||||
wlr_output_destroy(wlr_output);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
output->frame_delay = 1000000 / refresh;
|
output->frame_delay = 1000000 / refresh;
|
||||||
|
|
||||||
wlr_output_update_custom_mode(&output->wlr_output, width, height, refresh);
|
wlr_output_update_custom_mode(&output->wlr_output, width, height, refresh);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool output_attach_render(struct wlr_output *wlr_output,
|
static bool output_test(struct wlr_output *wlr_output) {
|
||||||
int *buffer_age) {
|
uint32_t unsupported =
|
||||||
struct wlr_headless_output *output =
|
wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE;
|
||||||
headless_output_from_output(wlr_output);
|
if (unsupported != 0) {
|
||||||
return wlr_egl_make_current(&output->backend->egl, output->egl_surface,
|
wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32,
|
||||||
buffer_age);
|
unsupported);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
|
||||||
|
assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool output_commit(struct wlr_output *wlr_output) {
|
static bool output_commit(struct wlr_output *wlr_output) {
|
||||||
// Nothing needs to be done for pbuffers
|
struct wlr_headless_output *output =
|
||||||
wlr_output_send_present(wlr_output, NULL);
|
headless_output_from_output(wlr_output);
|
||||||
|
|
||||||
|
if (!output_test(wlr_output)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
|
||||||
|
if (!output_set_custom_mode(output,
|
||||||
|
wlr_output->pending.custom_mode.width,
|
||||||
|
wlr_output->pending.custom_mode.height,
|
||||||
|
wlr_output->pending.custom_mode.refresh)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
|
||||||
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_destroy(struct wlr_output *wlr_output) {
|
static void output_destroy(struct wlr_output *wlr_output) {
|
||||||
struct wlr_headless_output *output =
|
struct wlr_headless_output *output =
|
||||||
headless_output_from_output(wlr_output);
|
headless_output_from_output(wlr_output);
|
||||||
|
|
||||||
wl_list_remove(&output->link);
|
wl_list_remove(&output->link);
|
||||||
|
|
||||||
wl_event_source_remove(output->frame_timer);
|
wl_event_source_remove(output->frame_timer);
|
||||||
|
|
||||||
wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface);
|
|
||||||
free(output);
|
free(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wlr_output_impl output_impl = {
|
static const struct wlr_output_impl output_impl = {
|
||||||
.set_custom_mode = output_set_custom_mode,
|
|
||||||
.destroy = output_destroy,
|
.destroy = output_destroy,
|
||||||
.attach_render = output_attach_render,
|
|
||||||
.commit = output_commit,
|
.commit = output_commit,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -109,26 +113,18 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
|
||||||
backend->display);
|
backend->display);
|
||||||
struct wlr_output *wlr_output = &output->wlr_output;
|
struct wlr_output *wlr_output = &output->wlr_output;
|
||||||
|
|
||||||
output->egl_surface = egl_create_surface(&backend->egl, width, height);
|
output_set_custom_mode(output, width, height, 0);
|
||||||
if (output->egl_surface == EGL_NO_SURFACE) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to create EGL surface");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
output_set_custom_mode(wlr_output, width, height, 0);
|
|
||||||
strncpy(wlr_output->make, "headless", sizeof(wlr_output->make));
|
strncpy(wlr_output->make, "headless", sizeof(wlr_output->make));
|
||||||
strncpy(wlr_output->model, "headless", sizeof(wlr_output->model));
|
strncpy(wlr_output->model, "headless", sizeof(wlr_output->model));
|
||||||
snprintf(wlr_output->name, sizeof(wlr_output->name), "HEADLESS-%zd",
|
|
||||||
++backend->last_output_num);
|
|
||||||
|
|
||||||
if (!wlr_egl_make_current(&output->backend->egl, output->egl_surface,
|
char name[64];
|
||||||
NULL)) {
|
snprintf(name, sizeof(name), "HEADLESS-%zu", ++backend->last_output_num);
|
||||||
goto error;
|
wlr_output_set_name(wlr_output, name);
|
||||||
}
|
|
||||||
|
|
||||||
wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height);
|
char description[128];
|
||||||
wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 });
|
snprintf(description, sizeof(description),
|
||||||
wlr_renderer_end(backend->renderer);
|
"Headless output %zu", backend->last_output_num);
|
||||||
|
wlr_output_set_description(wlr_output, description);
|
||||||
|
|
||||||
struct wl_event_loop *ev = wl_display_get_event_loop(backend->display);
|
struct wl_event_loop *ev = wl_display_get_event_loop(backend->display);
|
||||||
output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output);
|
output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output);
|
||||||
|
@ -142,8 +138,4 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
|
||||||
}
|
}
|
||||||
|
|
||||||
return wlr_output;
|
return wlr_output;
|
||||||
|
|
||||||
error:
|
|
||||||
wlr_output_destroy(&output->wlr_output);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <libinput.h>
|
#include <libinput.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <wlr/backend/interface.h>
|
#include <wlr/backend/interface.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/util/log.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,
|
static int libinput_open_restricted(const char *path,
|
||||||
int flags, void *_backend) {
|
int flags, void *_backend) {
|
||||||
struct wlr_libinput_backend *backend = _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) {
|
static void libinput_close_restricted(int fd, void *_backend) {
|
||||||
struct wlr_libinput_backend *backend = _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 = {
|
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) {
|
static int handle_libinput_readable(int fd, uint32_t mask, void *_backend) {
|
||||||
struct wlr_libinput_backend *backend = _backend;
|
struct wlr_libinput_backend *backend = _backend;
|
||||||
if (libinput_dispatch(backend->libinput_context) != 0) {
|
int ret = libinput_dispatch(backend->libinput_context);
|
||||||
wlr_log(WLR_ERROR, "Failed to dispatch libinput");
|
if (ret != 0) {
|
||||||
// TODO: some kind of abort?
|
wlr_log(WLR_ERROR, "Failed to dispatch libinput: %s", strerror(-ret));
|
||||||
|
wl_display_terminate(backend->display);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
struct libinput_event *event;
|
struct libinput_event *event;
|
||||||
|
@ -44,15 +61,30 @@ static int handle_libinput_readable(int fd, uint32_t mask, void *_backend) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static enum wlr_log_importance libinput_log_priority_to_wlr(
|
||||||
|
enum libinput_log_priority priority) {
|
||||||
|
switch (priority) {
|
||||||
|
case LIBINPUT_LOG_PRIORITY_ERROR:
|
||||||
|
return WLR_ERROR;
|
||||||
|
case LIBINPUT_LOG_PRIORITY_INFO:
|
||||||
|
return WLR_INFO;
|
||||||
|
default:
|
||||||
|
return WLR_DEBUG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void log_libinput(struct libinput *libinput_context,
|
static void log_libinput(struct libinput *libinput_context,
|
||||||
enum libinput_log_priority priority, const char *fmt, va_list args) {
|
enum libinput_log_priority priority, const char *fmt, va_list args) {
|
||||||
_wlr_vlog(WLR_ERROR, fmt, args);
|
enum wlr_log_importance importance = libinput_log_priority_to_wlr(priority);
|
||||||
|
static char wlr_fmt[1024];
|
||||||
|
snprintf(wlr_fmt, sizeof(wlr_fmt), "[libinput] %s", fmt);
|
||||||
|
_wlr_vlog(importance, wlr_fmt, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool backend_start(struct wlr_backend *wlr_backend) {
|
static bool backend_start(struct wlr_backend *wlr_backend) {
|
||||||
struct wlr_libinput_backend *backend =
|
struct wlr_libinput_backend *backend =
|
||||||
get_libinput_backend_from_backend(wlr_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->libinput_context = libinput_udev_create_context(&libinput_impl,
|
||||||
backend, backend->session->udev);
|
backend, backend->session->udev);
|
||||||
|
@ -78,9 +110,9 @@ static bool backend_start(struct wlr_backend *wlr_backend) {
|
||||||
no_devs = NULL;
|
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);
|
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, "libinput initialization failed, no input devices");
|
||||||
wlr_log(WLR_ERROR, "Set WLR_LIBINPUT_NO_DEVICES=1 to suppress this check");
|
wlr_log(WLR_ERROR, "Set WLR_LIBINPUT_NO_DEVICES=1 to suppress this check");
|
||||||
return false;
|
return false;
|
||||||
|
@ -109,21 +141,22 @@ static void backend_destroy(struct wlr_backend *wlr_backend) {
|
||||||
struct wlr_libinput_backend *backend =
|
struct wlr_libinput_backend *backend =
|
||||||
get_libinput_backend_from_backend(wlr_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_ptr;
|
||||||
struct wl_list *wlr_devices = backend->wlr_device_lists.items[i];
|
wl_array_for_each(wlr_devices_ptr, &backend->wlr_device_lists) {
|
||||||
struct wlr_input_device *wlr_dev, *next;
|
struct wlr_libinput_input_device *dev, *tmp;
|
||||||
wl_list_for_each_safe(wlr_dev, next, wlr_devices, link) {
|
wl_list_for_each_safe(dev, tmp, *wlr_devices_ptr, link) {
|
||||||
wlr_input_device_destroy(wlr_dev);
|
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->display_destroy.link);
|
||||||
|
wl_list_remove(&backend->session_destroy.link);
|
||||||
wl_list_remove(&backend->session_signal.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) {
|
if (backend->input_event) {
|
||||||
wl_event_source_remove(backend->input_event);
|
wl_event_source_remove(backend->input_event);
|
||||||
}
|
}
|
||||||
|
@ -143,7 +176,7 @@ bool wlr_backend_is_libinput(struct wlr_backend *b) {
|
||||||
static void session_signal(struct wl_listener *listener, void *data) {
|
static void session_signal(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_libinput_backend *backend =
|
struct wlr_libinput_backend *backend =
|
||||||
wl_container_of(listener, backend, session_signal);
|
wl_container_of(listener, backend, session_signal);
|
||||||
struct wlr_session *session = data;
|
struct wlr_session *session = backend->session;
|
||||||
|
|
||||||
if (!backend->libinput_context) {
|
if (!backend->libinput_context) {
|
||||||
return;
|
return;
|
||||||
|
@ -156,6 +189,12 @@ static void session_signal(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handle_session_destroy(struct wl_listener *listener, void *data) {
|
||||||
|
struct wlr_libinput_backend *backend =
|
||||||
|
wl_container_of(listener, backend, session_destroy);
|
||||||
|
backend_destroy(&backend->backend);
|
||||||
|
}
|
||||||
|
|
||||||
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_libinput_backend *backend =
|
struct wlr_libinput_backend *backend =
|
||||||
wl_container_of(listener, backend, display_destroy);
|
wl_container_of(listener, backend, display_destroy);
|
||||||
|
@ -172,24 +211,21 @@ struct wlr_backend *wlr_libinput_backend_create(struct wl_display *display,
|
||||||
}
|
}
|
||||||
wlr_backend_init(&backend->backend, &backend_impl);
|
wlr_backend_init(&backend->backend, &backend_impl);
|
||||||
|
|
||||||
if (!wlr_list_init(&backend->wlr_device_lists)) {
|
wl_array_init(&backend->wlr_device_lists);
|
||||||
wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno));
|
|
||||||
goto error_backend;
|
|
||||||
}
|
|
||||||
|
|
||||||
backend->session = session;
|
backend->session = session;
|
||||||
backend->display = display;
|
backend->display = display;
|
||||||
|
|
||||||
backend->session_signal.notify = session_signal;
|
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);
|
||||||
|
|
||||||
backend->display_destroy.notify = handle_display_destroy;
|
backend->display_destroy.notify = handle_display_destroy;
|
||||||
wl_display_add_destroy_listener(display, &backend->display_destroy);
|
wl_display_add_destroy_listener(display, &backend->display_destroy);
|
||||||
|
|
||||||
return &backend->backend;
|
return &backend->backend;
|
||||||
error_backend:
|
|
||||||
free(backend);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct libinput_device *wlr_libinput_get_device_handle(
|
struct libinput_device *wlr_libinput_get_device_handle(
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
#include <wlr/interfaces/wlr_input_device.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include "backend/libinput.h"
|
#include "backend/libinput.h"
|
||||||
|
#include "util/array.h"
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
struct wlr_libinput_input_device *get_libinput_device_from_device(
|
static struct wlr_libinput_input_device *get_libinput_device_from_device(
|
||||||
struct wlr_input_device *wlr_dev) {
|
struct wlr_input_device *wlr_dev) {
|
||||||
assert(wlr_input_device_is_libinput(wlr_dev));
|
assert(wlr_input_device_is_libinput(wlr_dev));
|
||||||
return (struct wlr_libinput_input_device *)wlr_dev;
|
return (struct wlr_libinput_input_device *)wlr_dev;
|
||||||
|
@ -22,10 +23,10 @@ struct wlr_input_device *get_appropriate_device(
|
||||||
if (!wlr_devices) {
|
if (!wlr_devices) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
struct wlr_input_device *dev;
|
struct wlr_libinput_input_device *dev;
|
||||||
wl_list_for_each(dev, wlr_devices, link) {
|
wl_list_for_each(dev, wlr_devices, link) {
|
||||||
if (dev->type == desired_type) {
|
if (dev->wlr_input_device.type == desired_type) {
|
||||||
return dev;
|
return &dev->wlr_input_device;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -35,7 +36,7 @@ static void input_device_destroy(struct wlr_input_device *wlr_dev) {
|
||||||
struct wlr_libinput_input_device *dev =
|
struct wlr_libinput_input_device *dev =
|
||||||
get_libinput_device_from_device(wlr_dev);
|
get_libinput_device_from_device(wlr_dev);
|
||||||
libinput_device_unref(dev->handle);
|
libinput_device_unref(dev->handle);
|
||||||
wl_list_remove(&dev->wlr_input_device.link);
|
wl_list_remove(&dev->link);
|
||||||
free(dev);
|
free(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +63,7 @@ static struct wlr_input_device *allocate_device(
|
||||||
if (output_name != NULL) {
|
if (output_name != NULL) {
|
||||||
wlr_dev->output_name = strdup(output_name);
|
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;
|
dev->handle = libinput_dev;
|
||||||
libinput_device_ref(libinput_dev);
|
libinput_device_ref(libinput_dev);
|
||||||
wlr_input_device_init(wlr_dev, type, &input_device_impl,
|
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)) {
|
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);
|
libinput_device_set_user_data(libinput_dev, wlr_devices);
|
||||||
wlr_list_push(&backend->wlr_device_lists, wlr_devices);
|
|
||||||
} else {
|
} else {
|
||||||
free(wlr_devices);
|
free(wlr_devices);
|
||||||
}
|
}
|
||||||
|
@ -192,7 +198,7 @@ static void handle_device_added(struct wlr_libinput_backend *backend,
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
wlr_log(WLR_ERROR, "Could not allocate new device");
|
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) {
|
wl_list_for_each_safe(dev, tmp_dev, wlr_devices, link) {
|
||||||
free(dev);
|
free(dev);
|
||||||
}
|
}
|
||||||
|
@ -209,15 +215,20 @@ static void handle_device_removed(struct wlr_libinput_backend *backend,
|
||||||
if (!wlr_devices) {
|
if (!wlr_devices) {
|
||||||
return;
|
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) {
|
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++) {
|
size_t i = 0;
|
||||||
if (backend->wlr_device_lists.items[i] == wlr_devices) {
|
struct wl_list **ptr;
|
||||||
wlr_list_del(&backend->wlr_device_lists, i);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
free(wlr_devices);
|
free(wlr_devices);
|
||||||
}
|
}
|
||||||
|
@ -261,7 +272,7 @@ void handle_libinput_event(struct wlr_libinput_backend *backend,
|
||||||
handle_touch_cancel(event, libinput_dev);
|
handle_touch_cancel(event, libinput_dev);
|
||||||
break;
|
break;
|
||||||
case LIBINPUT_EVENT_TOUCH_FRAME:
|
case LIBINPUT_EVENT_TOUCH_FRAME:
|
||||||
// no-op (at least for now)
|
handle_touch_frame(event, libinput_dev);
|
||||||
break;
|
break;
|
||||||
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
|
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
|
||||||
handle_tablet_tool_axis(event, libinput_dev);
|
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:
|
case LIBINPUT_EVENT_GESTURE_PINCH_END:
|
||||||
handle_pointer_pinch_end(event, libinput_dev);
|
handle_pointer_pinch_end(event, libinput_dev);
|
||||||
break;
|
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:
|
default:
|
||||||
wlr_log(WLR_DEBUG, "Unknown libinput event %d", event_type);
|
wlr_log(WLR_DEBUG, "Unknown libinput event %d", event_type);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -72,10 +72,10 @@ void handle_keyboard_key(struct libinput_event *event,
|
||||||
libinput_event_keyboard_get_key_state(kbevent);
|
libinput_event_keyboard_get_key_state(kbevent);
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case LIBINPUT_KEY_STATE_RELEASED:
|
case LIBINPUT_KEY_STATE_RELEASED:
|
||||||
wlr_event.state = WLR_KEY_RELEASED;
|
wlr_event.state = WL_KEYBOARD_KEY_STATE_RELEASED;
|
||||||
break;
|
break;
|
||||||
case LIBINPUT_KEY_STATE_PRESSED:
|
case LIBINPUT_KEY_STATE_PRESSED:
|
||||||
wlr_event.state = WLR_KEY_PRESSED;
|
wlr_event.state = WL_KEYBOARD_KEY_STATE_PRESSED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
wlr_event.update_state = true;
|
wlr_event.update_state = true;
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
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',
|
||||||
|
'keyboard.c',
|
||||||
|
'pointer.c',
|
||||||
|
'switch.c',
|
||||||
|
'tablet_pad.c',
|
||||||
|
'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);
|
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);
|
libinput_device_tablet_pad_get_num_strips(libinput_dev);
|
||||||
|
|
||||||
struct udev_device *udev = libinput_device_get_udev_device(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);
|
int groups = libinput_device_tablet_pad_get_num_mode_groups(libinput_dev);
|
||||||
for (int i = 0; i < groups; ++i) {
|
for (int i = 0; i < groups; ++i) {
|
||||||
|
|
|
@ -11,9 +11,10 @@
|
||||||
#include <wlr/types/wlr_input_device.h>
|
#include <wlr/types/wlr_input_device.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include "backend/libinput.h"
|
#include "backend/libinput.h"
|
||||||
|
#include "util/array.h"
|
||||||
#include "util/signal.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) {
|
static bool tablet_is_libinput(struct wlr_tablet *tablet) {
|
||||||
return tablet->impl == &tablet_impl;
|
return tablet->impl == &tablet_impl;
|
||||||
|
@ -29,18 +30,9 @@ struct wlr_libinput_tablet_tool {
|
||||||
size_t pad_refs;
|
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_libinput_tablet {
|
||||||
struct wlr_tablet wlr_tablet;
|
struct wlr_tablet wlr_tablet;
|
||||||
|
struct wl_array tools; // struct wlr_libinput_tablet_tool *
|
||||||
struct wl_list tools; // tablet_tool_list_elem::link
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void destroy_tool(struct wlr_libinput_tablet_tool *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 =
|
struct wlr_libinput_tablet *tablet =
|
||||||
wl_container_of(wlr_tablet, tablet, wlr_tablet);
|
wl_container_of(wlr_tablet, tablet, wlr_tablet);
|
||||||
|
|
||||||
struct tablet_tool_list_elem *pos;
|
struct wlr_libinput_tablet_tool **tool_ptr;
|
||||||
struct tablet_tool_list_elem *tmp;
|
wl_array_for_each(tool_ptr, &tablet->tools) {
|
||||||
wl_list_for_each_safe(pos, tmp, &tablet->tools, link) {
|
struct wlr_libinput_tablet_tool *tool = *tool_ptr;
|
||||||
struct wlr_libinput_tablet_tool *tool = pos->tool;
|
|
||||||
wl_list_remove(&pos->link);
|
|
||||||
free(pos);
|
|
||||||
|
|
||||||
if (--tool->pad_refs == 0) {
|
if (--tool->pad_refs == 0) {
|
||||||
destroy_tool(tool);
|
destroy_tool(tool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
wl_array_release(&tablet->tools);
|
||||||
|
|
||||||
free(tablet);
|
free(tablet);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wlr_tablet_impl tablet_impl = {
|
static const struct wlr_tablet_impl tablet_impl = {
|
||||||
.destroy = destroy_tablet,
|
.destroy = destroy_tablet,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -84,15 +73,18 @@ struct wlr_tablet *create_libinput_tablet(
|
||||||
wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_tool");
|
wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_tool");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_tablet *wlr_tablet = &libinput_tablet->wlr_tablet;
|
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);
|
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;
|
return wlr_tablet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,13 +105,10 @@ static enum wlr_tablet_tool_type wlr_type_from_libinput_type(
|
||||||
return WLR_TABLET_TOOL_TYPE_MOUSE;
|
return WLR_TABLET_TOOL_TYPE_MOUSE;
|
||||||
case LIBINPUT_TABLET_TOOL_TYPE_LENS:
|
case LIBINPUT_TABLET_TOOL_TYPE_LENS:
|
||||||
return WLR_TABLET_TOOL_TYPE_LENS;
|
return WLR_TABLET_TOOL_TYPE_LENS;
|
||||||
#if LIBINPUT_MINOR >= 14
|
|
||||||
case LIBINPUT_TABLET_TOOL_TYPE_TOTEM:
|
case LIBINPUT_TABLET_TOOL_TYPE_TOTEM:
|
||||||
return WLR_TABLET_TOOL_TYPE_TOTEM;
|
return WLR_TABLET_TOOL_TYPE_TOTEM;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
abort(); // unreachable
|
||||||
assert(false && "UNREACHABLE");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wlr_libinput_tablet_tool *get_wlr_tablet_tool(
|
static struct wlr_libinput_tablet_tool *get_wlr_tablet_tool(
|
||||||
|
@ -163,9 +152,9 @@ static void ensure_tool_reference(struct wlr_libinput_tablet_tool *tool,
|
||||||
struct wlr_libinput_tablet *tablet =
|
struct wlr_libinput_tablet *tablet =
|
||||||
wl_container_of(wlr_dev, tablet, wlr_tablet);
|
wl_container_of(wlr_dev, tablet, wlr_tablet);
|
||||||
|
|
||||||
struct tablet_tool_list_elem *pos;
|
struct wlr_libinput_tablet_tool **tool_ptr;
|
||||||
wl_list_for_each(pos, &tablet->tools, link) {
|
wl_array_for_each(tool_ptr, &tablet->tools) {
|
||||||
if (pos->tool == tool) { // We already have a ref
|
if (*tool_ptr == tool) { // We already have a ref
|
||||||
// XXX: We *could* optimize the tool to the front of
|
// XXX: We *could* optimize the tool to the front of
|
||||||
// the list here, since we will probably get the next
|
// the list here, since we will probably get the next
|
||||||
// couple of events from the same tool.
|
// couple of events from the same tool.
|
||||||
|
@ -176,15 +165,13 @@ static void ensure_tool_reference(struct wlr_libinput_tablet_tool *tool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct tablet_tool_list_elem *new =
|
struct wlr_libinput_tablet_tool **dst =
|
||||||
calloc(1, sizeof(struct tablet_tool_list_elem));
|
wl_array_add(&tablet->tools, sizeof(tool));
|
||||||
if (!new) {
|
if (!dst) {
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate memory for tracking tablet tool");
|
wlr_log(WLR_ERROR, "Failed to allocate memory for tracking tablet tool");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
*dst = tool;
|
||||||
new->tool = tool;
|
|
||||||
wl_list_insert(&tablet->tools, &new->link);
|
|
||||||
++tool->pad_refs;
|
++tool->pad_refs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,6 +256,9 @@ void handle_tablet_tool_proximity(struct libinput_event *event,
|
||||||
wlr_event.device = wlr_dev;
|
wlr_event.device = wlr_dev;
|
||||||
wlr_event.time_msec =
|
wlr_event.time_msec =
|
||||||
usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent));
|
usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent));
|
||||||
|
wlr_event.x = libinput_event_tablet_tool_get_x_transformed(tevent, 1);
|
||||||
|
wlr_event.y = libinput_event_tablet_tool_get_y_transformed(tevent, 1);
|
||||||
|
|
||||||
switch (libinput_event_tablet_tool_get_proximity_state(tevent)) {
|
switch (libinput_event_tablet_tool_get_proximity_state(tevent)) {
|
||||||
case LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT:
|
case LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT:
|
||||||
wlr_event.state = WLR_TABLET_TOOL_PROXIMITY_OUT;
|
wlr_event.state = WLR_TABLET_TOOL_PROXIMITY_OUT;
|
||||||
|
@ -294,15 +284,15 @@ void handle_tablet_tool_proximity(struct libinput_event *event,
|
||||||
assert(tablet_is_libinput(wlr_dev->tablet));
|
assert(tablet_is_libinput(wlr_dev->tablet));
|
||||||
struct wlr_libinput_tablet *tablet =
|
struct wlr_libinput_tablet *tablet =
|
||||||
wl_container_of(wlr_dev->tablet, tablet, wlr_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) {
|
size_t i = 0;
|
||||||
if (pos->tool == tool) {
|
struct wlr_libinput_tablet_tool **tool_ptr;
|
||||||
wl_list_remove(&pos->link);
|
wl_array_for_each(tool_ptr, &tablet->tools) {
|
||||||
free(pos);
|
if (*tool_ptr == tool) {
|
||||||
|
array_remove_at(&tablet->tools, i * sizeof(tool), sizeof(tool));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy_tool(tool);
|
destroy_tool(tool);
|
||||||
|
@ -330,6 +320,9 @@ void handle_tablet_tool_tip(struct libinput_event *event,
|
||||||
wlr_event.tool = &tool->wlr_tool;
|
wlr_event.tool = &tool->wlr_tool;
|
||||||
wlr_event.time_msec =
|
wlr_event.time_msec =
|
||||||
usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent));
|
usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent));
|
||||||
|
wlr_event.x = libinput_event_tablet_tool_get_x_transformed(tevent, 1);
|
||||||
|
wlr_event.y = libinput_event_tablet_tool_get_y_transformed(tevent, 1);
|
||||||
|
|
||||||
switch (libinput_event_tablet_tool_get_tip_state(tevent)) {
|
switch (libinput_event_tablet_tool_get_tip_state(tevent)) {
|
||||||
case LIBINPUT_TABLET_TOOL_TIP_UP:
|
case LIBINPUT_TABLET_TOOL_TIP_UP:
|
||||||
wlr_event.state = WLR_TABLET_TOOL_TIP_UP;
|
wlr_event.state = WLR_TABLET_TOOL_TIP_UP;
|
||||||
|
|
|
@ -95,3 +95,14 @@ void handle_touch_cancel(struct libinput_event *event,
|
||||||
wlr_event.touch_id = libinput_event_touch_get_seat_slot(tevent);
|
wlr_event.touch_id = libinput_event_touch_get_seat_slot(tevent);
|
||||||
wlr_signal_emit_safe(&wlr_dev->touch->events.cancel, &wlr_event);
|
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,81 +1,21 @@
|
||||||
backend_parts = []
|
wlr_files += files('backend.c')
|
||||||
backend_files = files(
|
|
||||||
'backend.c',
|
|
||||||
'drm/atomic.c',
|
|
||||||
'drm/backend.c',
|
|
||||||
'drm/drm.c',
|
|
||||||
'drm/legacy.c',
|
|
||||||
'drm/properties.c',
|
|
||||||
'drm/renderer.c',
|
|
||||||
'drm/util.c',
|
|
||||||
'headless/backend.c',
|
|
||||||
'headless/input_device.c',
|
|
||||||
'headless/output.c',
|
|
||||||
'libinput/backend.c',
|
|
||||||
'libinput/events.c',
|
|
||||||
'libinput/keyboard.c',
|
|
||||||
'libinput/pointer.c',
|
|
||||||
'libinput/switch.c',
|
|
||||||
'libinput/tablet_pad.c',
|
|
||||||
'libinput/tablet_tool.c',
|
|
||||||
'libinput/touch.c',
|
|
||||||
'multi/backend.c',
|
|
||||||
'noop/backend.c',
|
|
||||||
'noop/output.c',
|
|
||||||
'session/direct-ipc.c',
|
|
||||||
'session/noop.c',
|
|
||||||
'session/session.c',
|
|
||||||
'wayland/backend.c',
|
|
||||||
'wayland/output.c',
|
|
||||||
'wayland/wl_seat.c',
|
|
||||||
'wayland/tablet_v2.c',
|
|
||||||
)
|
|
||||||
|
|
||||||
backend_deps = [
|
all_backends = ['drm', 'libinput', 'x11']
|
||||||
drm,
|
backends = get_option('backends')
|
||||||
egl,
|
if 'auto' in backends and get_option('auto_features').enabled()
|
||||||
gbm,
|
backends = all_backends
|
||||||
libinput,
|
elif 'auto' in backends and get_option('auto_features').disabled()
|
||||||
pixman,
|
backends = []
|
||||||
xkbcommon,
|
|
||||||
wayland_server,
|
|
||||||
wlr_protos,
|
|
||||||
wlr_render,
|
|
||||||
]
|
|
||||||
|
|
||||||
if host_machine.system().startswith('freebsd')
|
|
||||||
backend_files += files('session/direct-freebsd.c')
|
|
||||||
else
|
|
||||||
backend_files += files('session/direct.c')
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if logind.found()
|
foreach backend : all_backends
|
||||||
backend_files += files('session/logind.c')
|
if backend in backends or 'auto' in backends
|
||||||
backend_deps += logind
|
subdir(backend)
|
||||||
endif
|
endif
|
||||||
|
endforeach
|
||||||
|
|
||||||
if freerdp.found() and winpr2.found()
|
subdir('multi')
|
||||||
backend_files += files(
|
subdir('wayland')
|
||||||
'rdp/backend.c',
|
subdir('headless')
|
||||||
'rdp/keyboard.c',
|
|
||||||
'rdp/listener.c',
|
|
||||||
'rdp/output.c',
|
|
||||||
'rdp/peer.c',
|
|
||||||
'rdp/pointer.c',
|
|
||||||
)
|
|
||||||
backend_deps += [
|
|
||||||
freerdp,
|
|
||||||
winpr2
|
|
||||||
]
|
|
||||||
conf_data.set10('WLR_HAS_RDP_BACKEND', true)
|
|
||||||
endif
|
|
||||||
|
|
||||||
subdir('x11')
|
subdir('session')
|
||||||
|
|
||||||
lib_wlr_backend = static_library(
|
|
||||||
'wlr_backend',
|
|
||||||
backend_files,
|
|
||||||
include_directories: wlr_inc,
|
|
||||||
link_whole: backend_parts,
|
|
||||||
dependencies: backend_deps,
|
|
||||||
)
|
|
||||||
|
|
|
@ -5,7 +5,9 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <wlr/backend/interface.h>
|
#include <wlr/backend/interface.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
|
#include <wlr/types/wlr_buffer.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
#include "backend/backend.h"
|
||||||
#include "backend/multi.h"
|
#include "backend/multi.h"
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
|
@ -49,30 +51,19 @@ static void multi_backend_destroy(struct wlr_backend *wlr_backend) {
|
||||||
|
|
||||||
wl_list_remove(&backend->display_destroy.link);
|
wl_list_remove(&backend->display_destroy.link);
|
||||||
|
|
||||||
struct subbackend_state *sub, *next;
|
// Some backends may depend on other backends, ie. destroying a backend may
|
||||||
wl_list_for_each_safe(sub, next, &backend->backends, link) {
|
// also destroy other backends
|
||||||
|
while (!wl_list_empty(&backend->backends)) {
|
||||||
|
struct subbackend_state *sub =
|
||||||
|
wl_container_of(backend->backends.next, sub, link);
|
||||||
wlr_backend_destroy(sub->backend);
|
wlr_backend_destroy(sub->backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy this backend only after removing all sub-backends
|
// 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);
|
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(
|
static struct wlr_session *multi_backend_get_session(
|
||||||
struct wlr_backend *_backend) {
|
struct wlr_backend *_backend) {
|
||||||
struct wlr_multi_backend *backend = multi_backend_from_backend(_backend);
|
struct wlr_multi_backend *backend = multi_backend_from_backend(_backend);
|
||||||
|
@ -93,12 +84,48 @@ static clockid_t multi_backend_get_presentation_clock(
|
||||||
return CLOCK_MONOTONIC;
|
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,
|
.start = multi_backend_start,
|
||||||
.destroy = multi_backend_destroy,
|
.destroy = multi_backend_destroy,
|
||||||
.get_renderer = multi_backend_get_renderer,
|
|
||||||
.get_session = multi_backend_get_session,
|
.get_session = multi_backend_get_session,
|
||||||
.get_presentation_clock = multi_backend_get_presentation_clock,
|
.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) {
|
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
|
@ -162,6 +189,9 @@ static struct subbackend_state *multi_backend_get_subbackend(struct wlr_multi_ba
|
||||||
|
|
||||||
bool wlr_multi_backend_add(struct wlr_backend *_multi,
|
bool wlr_multi_backend_add(struct wlr_backend *_multi,
|
||||||
struct wlr_backend *backend) {
|
struct wlr_backend *backend) {
|
||||||
|
assert(_multi && backend);
|
||||||
|
assert(_multi != backend);
|
||||||
|
|
||||||
struct wlr_multi_backend *multi = multi_backend_from_backend(_multi);
|
struct wlr_multi_backend *multi = multi_backend_from_backend(_multi);
|
||||||
|
|
||||||
if (multi_backend_get_subbackend(multi, backend)) {
|
if (multi_backend_get_subbackend(multi, backend)) {
|
||||||
|
@ -169,21 +199,12 @@ bool wlr_multi_backend_add(struct wlr_backend *_multi,
|
||||||
return true;
|
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));
|
struct subbackend_state *sub = calloc(1, sizeof(struct subbackend_state));
|
||||||
if (sub == NULL) {
|
if (sub == NULL) {
|
||||||
wlr_log(WLR_ERROR, "Could not add backend: allocation failed");
|
wlr_log(WLR_ERROR, "Could not add backend: allocation failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
wl_list_insert(&multi->backends, &sub->link);
|
wl_list_insert(multi->backends.prev, &sub->link);
|
||||||
|
|
||||||
sub->backend = backend;
|
sub->backend = backend;
|
||||||
sub->container = &multi->backend;
|
sub->container = &multi->backend;
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
wlr_files += files('backend.c')
|
|
@ -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,76 +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_set_custom_mode(struct wlr_output *wlr_output,
|
|
||||||
int32_t width, int32_t height, int32_t refresh) {
|
|
||||||
wlr_output_update_custom_mode(wlr_output, width, height, refresh);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_attach_render(struct wlr_output *wlr_output,
|
|
||||||
int *buffer_age) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_commit(struct wlr_output *wlr_output) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = {
|
|
||||||
.set_custom_mode = output_set_custom_mode,
|
|
||||||
.destroy = output_destroy,
|
|
||||||
.attach_render = output_attach_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,134 +0,0 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <wlr/render/egl.h>
|
|
||||||
#include <wlr/render/gles2.h>
|
|
||||||
#include <wlr/util/log.h>
|
|
||||||
#include "backend/rdp.h"
|
|
||||||
#include "glapi.h"
|
|
||||||
#include "util/signal.h"
|
|
||||||
|
|
||||||
struct wlr_rdp_backend *rdp_backend_from_backend(
|
|
||||||
struct wlr_backend *wlr_backend) {
|
|
||||||
assert(wlr_backend_is_rdp(wlr_backend));
|
|
||||||
return (struct wlr_rdp_backend *)wlr_backend;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool backend_start(struct wlr_backend *wlr_backend) {
|
|
||||||
struct wlr_rdp_backend *backend =
|
|
||||||
rdp_backend_from_backend(wlr_backend);
|
|
||||||
assert(backend->listener == NULL);
|
|
||||||
wlr_log(WLR_INFO, "Starting RDP backend");
|
|
||||||
if (!rdp_configure_listener(backend)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void wlr_rdp_backend_set_address(struct wlr_backend *wlr_backend,
|
|
||||||
const char *address) {
|
|
||||||
struct wlr_rdp_backend *backend =
|
|
||||||
rdp_backend_from_backend(wlr_backend);
|
|
||||||
assert(backend->listener == NULL);
|
|
||||||
backend->address = strdup(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wlr_rdp_backend_set_port(struct wlr_backend *wlr_backend, int port) {
|
|
||||||
struct wlr_rdp_backend *backend =
|
|
||||||
rdp_backend_from_backend(wlr_backend);
|
|
||||||
assert(backend->listener == NULL);
|
|
||||||
backend->port = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void backend_destroy(struct wlr_backend *wlr_backend) {
|
|
||||||
struct wlr_rdp_backend *backend =
|
|
||||||
rdp_backend_from_backend(wlr_backend);
|
|
||||||
if (!wlr_backend) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_list_remove(&backend->display_destroy.link);
|
|
||||||
|
|
||||||
struct wlr_rdp_peer_context *client;
|
|
||||||
wl_list_for_each(client, &backend->clients, link) {
|
|
||||||
freerdp_peer_context_free(client->peer);
|
|
||||||
freerdp_peer_free(client->peer);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&wlr_backend->events.destroy, backend);
|
|
||||||
|
|
||||||
wlr_renderer_destroy(backend->renderer);
|
|
||||||
wlr_egl_finish(&backend->egl);
|
|
||||||
free(backend->address);
|
|
||||||
free(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_renderer *backend_get_renderer(
|
|
||||||
struct wlr_backend *wlr_backend) {
|
|
||||||
struct wlr_rdp_backend *backend =
|
|
||||||
rdp_backend_from_backend(wlr_backend);
|
|
||||||
return backend->renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wlr_backend_impl backend_impl = {
|
|
||||||
.start = backend_start,
|
|
||||||
.destroy = backend_destroy,
|
|
||||||
.get_renderer = backend_get_renderer,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
|
||||||
struct wlr_rdp_backend *backend =
|
|
||||||
wl_container_of(listener, backend, display_destroy);
|
|
||||||
backend_destroy(&backend->backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_backend *wlr_rdp_backend_create(struct wl_display *display,
|
|
||||||
wlr_renderer_create_func_t create_renderer_func,
|
|
||||||
const char *tls_cert_path, const char *tls_key_path) {
|
|
||||||
wlr_log(WLR_INFO, "Creating RDP backend");
|
|
||||||
|
|
||||||
struct wlr_rdp_backend *backend =
|
|
||||||
calloc(1, sizeof(struct wlr_rdp_backend));
|
|
||||||
if (!backend) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate wlr_rdp_backend");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
wlr_backend_init(&backend->backend, &backend_impl);
|
|
||||||
backend->display = display;
|
|
||||||
backend->tls_cert_path = tls_cert_path;
|
|
||||||
backend->tls_key_path = tls_key_path;
|
|
||||||
backend->address = strdup("127.0.0.1");
|
|
||||||
backend->port = 3389;
|
|
||||||
wl_list_init(&backend->clients);
|
|
||||||
|
|
||||||
static const EGLint config_attribs[] = {
|
|
||||||
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
|
|
||||||
EGL_ALPHA_SIZE, 0,
|
|
||||||
EGL_BLUE_SIZE, 1,
|
|
||||||
EGL_GREEN_SIZE, 1,
|
|
||||||
EGL_RED_SIZE, 1,
|
|
||||||
EGL_NONE,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!create_renderer_func) {
|
|
||||||
create_renderer_func = wlr_renderer_autocreate;
|
|
||||||
}
|
|
||||||
|
|
||||||
backend->renderer = create_renderer_func(&backend->egl,
|
|
||||||
EGL_PLATFORM_SURFACELESS_MESA, NULL, (EGLint*)config_attribs, 0);
|
|
||||||
if (!backend->renderer) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to create renderer");
|
|
||||||
free(backend);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
backend->display_destroy.notify = handle_display_destroy;
|
|
||||||
wl_display_add_destroy_listener(display, &backend->display_destroy);
|
|
||||||
|
|
||||||
return &backend->backend;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool wlr_backend_is_rdp(struct wlr_backend *backend) {
|
|
||||||
return backend->impl == &backend_impl;
|
|
||||||
}
|
|
|
@ -1,219 +0,0 @@
|
||||||
#include <stdlib.h>
|
|
||||||
#include <wayland-server-core.h>
|
|
||||||
#include <wlr/types/wlr_input_device.h>
|
|
||||||
#include <wlr/types/wlr_keyboard.h>
|
|
||||||
#include <wlr/interfaces/wlr_keyboard.h>
|
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
|
||||||
#include <wlr/util/log.h>
|
|
||||||
#include <xkbcommon/xkbcommon.h>
|
|
||||||
#include "backend/rdp.h"
|
|
||||||
#include "util/signal.h"
|
|
||||||
|
|
||||||
struct rdp_to_xkb_keyboard_layout {
|
|
||||||
UINT32 rdp_layout_code;
|
|
||||||
const char *xkb_layout;
|
|
||||||
const char *xkb_variant;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* table reversed from
|
|
||||||
https://github.com/awakecoding/FreeRDP/blob/master/libfreerdp/locale/xkb_layout_ids.c#L811 */
|
|
||||||
static struct rdp_to_xkb_keyboard_layout rdp_keyboards[] = {
|
|
||||||
{KBD_ARABIC_101, "ara", 0},
|
|
||||||
{KBD_BULGARIAN, 0, 0},
|
|
||||||
{KBD_CHINESE_TRADITIONAL_US, 0, 0},
|
|
||||||
{KBD_CZECH, "cz", 0},
|
|
||||||
{KBD_CZECH_PROGRAMMERS, "cz", "bksl"},
|
|
||||||
{KBD_CZECH_QWERTY, "cz", "qwerty"},
|
|
||||||
{KBD_DANISH, "dk", 0},
|
|
||||||
{KBD_GERMAN, "de", 0},
|
|
||||||
{KBD_GERMAN_NEO, "de", "neo"},
|
|
||||||
{KBD_GERMAN_IBM, "de", "qwerty"},
|
|
||||||
{KBD_GREEK, "gr", 0},
|
|
||||||
{KBD_GREEK_220, "gr", "simple"},
|
|
||||||
{KBD_GREEK_319, "gr", "extended"},
|
|
||||||
{KBD_GREEK_POLYTONIC, "gr", "polytonic"},
|
|
||||||
{KBD_US, "us", 0},
|
|
||||||
{KBD_US_ENGLISH_TABLE_FOR_IBM_ARABIC_238_L, "ara", "buckwalter"},
|
|
||||||
{KBD_SPANISH, "es", 0},
|
|
||||||
{KBD_SPANISH_VARIATION, "es", "nodeadkeys"},
|
|
||||||
{KBD_FINNISH, "fi", 0},
|
|
||||||
{KBD_FRENCH, "fr", 0},
|
|
||||||
{KBD_HEBREW, "il", 0},
|
|
||||||
{KBD_HUNGARIAN, "hu", 0},
|
|
||||||
{KBD_HUNGARIAN_101_KEY, "hu", "standard"},
|
|
||||||
{KBD_ICELANDIC, "is", 0},
|
|
||||||
{KBD_ITALIAN, "it", 0},
|
|
||||||
{KBD_ITALIAN_142, "it", "nodeadkeys"},
|
|
||||||
{KBD_JAPANESE, "jp", 0},
|
|
||||||
{KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002, "jp", "kana"},
|
|
||||||
{KBD_KOREAN, "kr", 0},
|
|
||||||
{KBD_KOREAN_INPUT_SYSTEM_IME_2000, "kr", "kr104"},
|
|
||||||
{KBD_DUTCH, "nl", 0},
|
|
||||||
{KBD_NORWEGIAN, "no", 0},
|
|
||||||
{KBD_POLISH_PROGRAMMERS, "pl", 0},
|
|
||||||
{KBD_POLISH_214, "pl", "qwertz"},
|
|
||||||
{KBD_ROMANIAN, "ro", 0},
|
|
||||||
{KBD_RUSSIAN, "ru", 0},
|
|
||||||
{KBD_RUSSIAN_TYPEWRITER, "ru", "typewriter"},
|
|
||||||
{KBD_CROATIAN, "hr", 0},
|
|
||||||
{KBD_SLOVAK, "sk", 0},
|
|
||||||
{KBD_SLOVAK_QWERTY, "sk", "qwerty"},
|
|
||||||
{KBD_ALBANIAN, 0, 0},
|
|
||||||
{KBD_SWEDISH, "se", 0},
|
|
||||||
{KBD_THAI_KEDMANEE, "th", 0},
|
|
||||||
{KBD_THAI_KEDMANEE_NON_SHIFTLOCK, "th", "tis"},
|
|
||||||
{KBD_TURKISH_Q, "tr", 0},
|
|
||||||
{KBD_TURKISH_F, "tr", "f"},
|
|
||||||
{KBD_URDU, "in", "urd-phonetic3"},
|
|
||||||
{KBD_UKRAINIAN, "ua", 0},
|
|
||||||
{KBD_BELARUSIAN, "by", 0},
|
|
||||||
{KBD_SLOVENIAN, "si", 0},
|
|
||||||
{KBD_ESTONIAN, "ee", 0},
|
|
||||||
{KBD_LATVIAN, "lv", 0},
|
|
||||||
{KBD_LITHUANIAN_IBM, "lt", "ibm"},
|
|
||||||
{KBD_FARSI, "af", 0},
|
|
||||||
{KBD_VIETNAMESE, "vn", 0},
|
|
||||||
{KBD_ARMENIAN_EASTERN, "am", 0},
|
|
||||||
{KBD_AZERI_LATIN, 0, 0},
|
|
||||||
{KBD_FYRO_MACEDONIAN, "mk", 0},
|
|
||||||
{KBD_GEORGIAN, "ge", 0},
|
|
||||||
{KBD_FAEROESE, 0, 0},
|
|
||||||
{KBD_DEVANAGARI_INSCRIPT, 0, 0},
|
|
||||||
{KBD_MALTESE_47_KEY, 0, 0},
|
|
||||||
{KBD_NORWEGIAN_WITH_SAMI, "no", "smi"},
|
|
||||||
{KBD_KAZAKH, "kz", 0},
|
|
||||||
{KBD_KYRGYZ_CYRILLIC, "kg", "phonetic"},
|
|
||||||
{KBD_TATAR, "ru", "tt"},
|
|
||||||
{KBD_BENGALI, "bd", 0},
|
|
||||||
{KBD_BENGALI_INSCRIPT, "bd", "probhat"},
|
|
||||||
{KBD_PUNJABI, 0, 0},
|
|
||||||
{KBD_GUJARATI, "in", "guj"},
|
|
||||||
{KBD_TAMIL, "in", "tam"},
|
|
||||||
{KBD_TELUGU, "in", "tel"},
|
|
||||||
{KBD_KANNADA, "in", "kan"},
|
|
||||||
{KBD_MALAYALAM, "in", "mal"},
|
|
||||||
{KBD_HINDI_TRADITIONAL, "in", 0},
|
|
||||||
{KBD_MARATHI, 0, 0},
|
|
||||||
{KBD_MONGOLIAN_CYRILLIC, "mn", 0},
|
|
||||||
{KBD_UNITED_KINGDOM_EXTENDED, "gb", "intl"},
|
|
||||||
{KBD_SYRIAC, "syc", 0},
|
|
||||||
{KBD_SYRIAC_PHONETIC, "syc", "syc_phonetic"},
|
|
||||||
{KBD_NEPALI, "np", 0},
|
|
||||||
{KBD_PASHTO, "af", "ps"},
|
|
||||||
{KBD_DIVEHI_PHONETIC, 0, 0},
|
|
||||||
{KBD_LUXEMBOURGISH, 0, 0},
|
|
||||||
{KBD_MAORI, "mao", 0},
|
|
||||||
{KBD_CHINESE_SIMPLIFIED_US, 0, 0},
|
|
||||||
{KBD_SWISS_GERMAN, "ch", "de_nodeadkeys"},
|
|
||||||
{KBD_UNITED_KINGDOM, "gb", 0},
|
|
||||||
{KBD_LATIN_AMERICAN, "latam", 0},
|
|
||||||
{KBD_BELGIAN_FRENCH, "be", 0},
|
|
||||||
{KBD_BELGIAN_PERIOD, "be", "oss_sundeadkeys"},
|
|
||||||
{KBD_PORTUGUESE, "pt", 0},
|
|
||||||
{KBD_SERBIAN_LATIN, "rs", 0},
|
|
||||||
{KBD_AZERI_CYRILLIC, "az", "cyrillic"},
|
|
||||||
{KBD_SWEDISH_WITH_SAMI, "se", "smi"},
|
|
||||||
{KBD_UZBEK_CYRILLIC, "af", "uz"},
|
|
||||||
{KBD_INUKTITUT_LATIN, "ca", "ike"},
|
|
||||||
{KBD_CANADIAN_FRENCH_LEGACY, "ca", "fr-legacy"},
|
|
||||||
{KBD_SERBIAN_CYRILLIC, "rs", 0},
|
|
||||||
{KBD_CANADIAN_FRENCH, "ca", "fr-legacy"},
|
|
||||||
{KBD_SWISS_FRENCH, "ch", "fr"},
|
|
||||||
{KBD_BOSNIAN, "ba", 0},
|
|
||||||
{KBD_IRISH, 0, 0},
|
|
||||||
{KBD_BOSNIAN_CYRILLIC, "ba", "us"},
|
|
||||||
{KBD_UNITED_STATES_DVORAK, "us", "dvorak"},
|
|
||||||
{KBD_PORTUGUESE_BRAZILIAN_ABNT2, "br", "nativo"},
|
|
||||||
{KBD_CANADIAN_MULTILINGUAL_STANDARD, "ca", "multix"},
|
|
||||||
{KBD_GAELIC, "ie", "CloGaelach"},
|
|
||||||
|
|
||||||
{0x00000000, 0, 0},
|
|
||||||
};
|
|
||||||
|
|
||||||
/* taken from 2.2.7.1.6 Input Capability Set (TS_INPUT_CAPABILITYSET) */
|
|
||||||
static char *rdp_keyboard_types[] = {
|
|
||||||
"", /* 0: unused */
|
|
||||||
"", /* 1: IBM PC/XT or compatible (83-key) keyboard */
|
|
||||||
"", /* 2: Olivetti "ICO" (102-key) keyboard */
|
|
||||||
"", /* 3: IBM PC/AT (84-key) or similar keyboard */
|
|
||||||
"pc102",/* 4: IBM enhanced (101- or 102-key) keyboard */
|
|
||||||
"", /* 5: Nokia 1050 and similar keyboards */
|
|
||||||
"", /* 6: Nokia 9140 and similar keyboards */
|
|
||||||
"" /* 7: Japanese keyboard */
|
|
||||||
};
|
|
||||||
|
|
||||||
static void keyboard_destroy(struct wlr_input_device *wlr_device) {
|
|
||||||
struct wlr_rdp_keyboard *keyboard =
|
|
||||||
(struct wlr_rdp_keyboard *)wlr_device->keyboard;
|
|
||||||
xkb_keymap_unref(keyboard->keymap);
|
|
||||||
free(keyboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_input_device_impl input_device_impl = {
|
|
||||||
.destroy = keyboard_destroy,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_rdp_input_device *wlr_rdp_keyboard_create(
|
|
||||||
struct wlr_rdp_backend *backend, rdpSettings *settings) {
|
|
||||||
struct wlr_rdp_input_device *device =
|
|
||||||
calloc(1, sizeof(struct wlr_rdp_input_device));
|
|
||||||
if (!device) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allcoate RDP input device");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int vendor = 0;
|
|
||||||
int product = 0;
|
|
||||||
const char *name = "rdp";
|
|
||||||
struct wlr_input_device *wlr_device = &device->wlr_input_device;
|
|
||||||
wlr_input_device_init(wlr_device, WLR_INPUT_DEVICE_KEYBOARD,
|
|
||||||
&input_device_impl, name, vendor, product);
|
|
||||||
|
|
||||||
struct wlr_rdp_keyboard *keyboard =
|
|
||||||
calloc(1, sizeof(struct wlr_rdp_keyboard));
|
|
||||||
if (!keyboard) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate RDP pointer device");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
wlr_device->keyboard = (struct wlr_keyboard *)keyboard;
|
|
||||||
wlr_keyboard_init(wlr_device->keyboard, NULL);
|
|
||||||
|
|
||||||
wlr_log(WLR_DEBUG, "RDP keyboard layout: 0x%x type: 0x%x subtype: 0x%x "
|
|
||||||
"function_keys 0x%x", settings->KeyboardLayout,
|
|
||||||
settings->KeyboardType, settings->KeyboardSubType,
|
|
||||||
settings->KeyboardFunctionKey);
|
|
||||||
|
|
||||||
// We need to set up an XKB context and jump through some hoops to convert
|
|
||||||
// RDP input events into scancodes
|
|
||||||
struct xkb_rule_names xkb_rule_names = { 0 };
|
|
||||||
if (settings->KeyboardType <= 7) {
|
|
||||||
xkb_rule_names.model = rdp_keyboard_types[settings->KeyboardType];
|
|
||||||
}
|
|
||||||
for (int i = 0; rdp_keyboards[i].rdp_layout_code; ++i) {
|
|
||||||
if (rdp_keyboards[i].rdp_layout_code == settings->KeyboardLayout) {
|
|
||||||
xkb_rule_names.layout = rdp_keyboards[i].xkb_layout;
|
|
||||||
xkb_rule_names.variant = rdp_keyboards[i].xkb_variant;
|
|
||||||
wlr_log(WLR_DEBUG, "Mapped RDP keyboard to xkb layout %s variant "
|
|
||||||
"%s", xkb_rule_names.layout, xkb_rule_names.variant);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (xkb_rule_names.layout) {
|
|
||||||
struct xkb_context *xkb_context = xkb_context_new(0);
|
|
||||||
if (!xkb_context) {
|
|
||||||
wlr_log(WLR_DEBUG, "Failed to allocate xkb context");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
keyboard->keymap =
|
|
||||||
xkb_keymap_new_from_names(xkb_context, &xkb_rule_names, 0);
|
|
||||||
xkb_context_unref(xkb_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap);
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_device);
|
|
||||||
return device;
|
|
||||||
error:
|
|
||||||
wlr_input_device_destroy(wlr_device);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
#include <stdlib.h>
|
|
||||||
#include <wlr/util/log.h>
|
|
||||||
#include "backend/rdp.h"
|
|
||||||
|
|
||||||
static int rdp_incoming_peer(
|
|
||||||
freerdp_listener *listener, freerdp_peer *client) {
|
|
||||||
struct wlr_rdp_backend *backend =
|
|
||||||
(struct wlr_rdp_backend *)listener->param4;
|
|
||||||
if (rdp_peer_init(client, backend) < 0) {
|
|
||||||
wlr_log(WLR_ERROR, "Error initializing incoming peer");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int rdp_listener_activity(int fd, uint32_t mask, void *data) {
|
|
||||||
freerdp_listener *listener = data;
|
|
||||||
if (!(mask & WL_EVENT_READABLE)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!listener->CheckFileDescriptor(listener)) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to check FreeRDP file descriptor");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool rdp_configure_listener(struct wlr_rdp_backend *backend) {
|
|
||||||
backend->listener = freerdp_listener_new();
|
|
||||||
if (!backend->listener) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate FreeRDP listener");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
backend->listener->PeerAccepted = rdp_incoming_peer;
|
|
||||||
backend->listener->param4 = backend;
|
|
||||||
if (!backend->listener->Open(backend->listener,
|
|
||||||
backend->address, backend->port)) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to bind to RDP socket");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int rcount = 0;
|
|
||||||
void *rfds[MAX_FREERDP_FDS];
|
|
||||||
if (!backend->listener->GetFileDescriptor(
|
|
||||||
backend->listener, rfds, &rcount)) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to get FreeRDP file descriptors");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
struct wl_event_loop *event_loop =
|
|
||||||
wl_display_get_event_loop(backend->display);
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < rcount; ++i) {
|
|
||||||
int fd = (int)(long)(rfds[i]);
|
|
||||||
backend->listener_events[i] = wl_event_loop_add_fd(
|
|
||||||
event_loop, fd, WL_EVENT_READABLE, rdp_listener_activity,
|
|
||||||
backend->listener);
|
|
||||||
}
|
|
||||||
for (; i < MAX_FREERDP_FDS; ++i) {
|
|
||||||
backend->listener_events[i] = NULL;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,300 +0,0 @@
|
||||||
#include <assert.h>
|
|
||||||
#include <EGL/egl.h>
|
|
||||||
#include <EGL/eglext.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <wayland-server-core.h>
|
|
||||||
#include <wlr/types/wlr_output.h>
|
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
|
||||||
#include <wlr/util/log.h>
|
|
||||||
#include <wlr/render/wlr_renderer.h>
|
|
||||||
#include "backend/rdp.h"
|
|
||||||
#include "util/signal.h"
|
|
||||||
|
|
||||||
static struct wlr_rdp_output *rdp_output_from_output(
|
|
||||||
struct wlr_output *wlr_output) {
|
|
||||||
assert(wlr_output_is_rdp(wlr_output));
|
|
||||||
return (struct wlr_rdp_output *)wlr_output;
|
|
||||||
}
|
|
||||||
|
|
||||||
static EGLSurface egl_create_surface(struct wlr_egl *egl, unsigned int width,
|
|
||||||
unsigned int height) {
|
|
||||||
EGLint attribs[] = {
|
|
||||||
EGL_WIDTH, width,
|
|
||||||
EGL_HEIGHT, height,
|
|
||||||
EGL_NONE,
|
|
||||||
};
|
|
||||||
EGLSurface surf = eglCreatePbufferSurface(egl->display, egl->config, attribs);
|
|
||||||
if (surf == EGL_NO_SURFACE) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to create EGL surface");
|
|
||||||
return EGL_NO_SURFACE;
|
|
||||||
}
|
|
||||||
return surf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width,
|
|
||||||
int32_t height, int32_t refresh) {
|
|
||||||
struct wlr_rdp_output *output =
|
|
||||||
rdp_output_from_output(wlr_output);
|
|
||||||
struct wlr_rdp_backend *backend = output->backend;
|
|
||||||
|
|
||||||
if (refresh <= 0) {
|
|
||||||
refresh = 60 * 1000; // 60 Hz
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_egl_destroy_surface(&backend->egl, output->egl_surface);
|
|
||||||
|
|
||||||
output->egl_surface = egl_create_surface(&backend->egl, width, height);
|
|
||||||
if (output->egl_surface == EGL_NO_SURFACE) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to recreate EGL surface");
|
|
||||||
wlr_output_destroy(wlr_output);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
output->frame_delay = 1000000 / refresh;
|
|
||||||
|
|
||||||
if (output->shadow_surface) {
|
|
||||||
pixman_image_unref(output->shadow_surface);
|
|
||||||
}
|
|
||||||
output->shadow_surface = pixman_image_create_bits(PIXMAN_x8r8g8b8,
|
|
||||||
width, height, NULL, width * 4);
|
|
||||||
|
|
||||||
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_rdp_output *output =
|
|
||||||
rdp_output_from_output(wlr_output);
|
|
||||||
return wlr_egl_make_current(&output->backend->egl, output->egl_surface,
|
|
||||||
buffer_age);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool rfx_swap_buffers(
|
|
||||||
struct wlr_rdp_output *output, pixman_region32_t *damage) {
|
|
||||||
if (!pixman_region32_not_empty(damage)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
struct wlr_rdp_peer_context *context = output->context;
|
|
||||||
freerdp_peer *peer = context->peer;
|
|
||||||
rdpUpdate *update = peer->update;
|
|
||||||
|
|
||||||
Stream_Clear(context->encode_stream);
|
|
||||||
Stream_SetPosition(context->encode_stream, 0);
|
|
||||||
int width = damage->extents.x2 - damage->extents.x1;
|
|
||||||
int height = damage->extents.y2 - damage->extents.y1;
|
|
||||||
|
|
||||||
SURFACE_BITS_COMMAND cmd;
|
|
||||||
cmd.skipCompression = TRUE;
|
|
||||||
cmd.destLeft = damage->extents.x1;
|
|
||||||
cmd.destTop = damage->extents.y1;
|
|
||||||
cmd.destRight = damage->extents.x2;
|
|
||||||
cmd.destBottom = damage->extents.y2;
|
|
||||||
cmd.bmp.bpp = pixman_image_get_depth(output->shadow_surface);
|
|
||||||
cmd.bmp.codecID = peer->settings->RemoteFxCodecId;
|
|
||||||
cmd.bmp.width = width;
|
|
||||||
cmd.bmp.height = height;
|
|
||||||
|
|
||||||
uint32_t *ptr = pixman_image_get_data(output->shadow_surface) +
|
|
||||||
damage->extents.x1 + damage->extents.y1 *
|
|
||||||
(pixman_image_get_stride(output->shadow_surface) / sizeof(uint32_t));
|
|
||||||
|
|
||||||
RFX_RECT *rfx_rect;
|
|
||||||
int nrects;
|
|
||||||
pixman_box32_t *rects =
|
|
||||||
pixman_region32_rectangles(damage, &nrects);
|
|
||||||
rfx_rect = realloc(context->rfx_rects, nrects * sizeof(*rfx_rect));
|
|
||||||
if (rfx_rect == NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "RDP swap buffers failed: could not realloc rects");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
context->rfx_rects = rfx_rect;
|
|
||||||
|
|
||||||
for (int i = 0; i < nrects; ++i) {
|
|
||||||
pixman_box32_t *region = &rects[i];
|
|
||||||
rfx_rect = &context->rfx_rects[i];
|
|
||||||
rfx_rect->x = region->x1 - damage->extents.x1;
|
|
||||||
rfx_rect->y = region->y1 - damage->extents.y1;
|
|
||||||
rfx_rect->width = region->x2 - region->x1;
|
|
||||||
rfx_rect->height = region->y2 - region->y1;
|
|
||||||
}
|
|
||||||
|
|
||||||
rfx_compose_message(context->rfx_context, context->encode_stream,
|
|
||||||
context->rfx_rects, nrects, (BYTE *)ptr, width, height,
|
|
||||||
pixman_image_get_stride(output->shadow_surface));
|
|
||||||
cmd.bmp.bitmapDataLength = Stream_GetPosition(context->encode_stream);
|
|
||||||
cmd.bmp.bitmapData = Stream_Buffer(context->encode_stream);
|
|
||||||
|
|
||||||
update->SurfaceBits(update->context, &cmd);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool nsc_swap_buffers(
|
|
||||||
struct wlr_rdp_output *output, pixman_region32_t *damage) {
|
|
||||||
struct wlr_rdp_peer_context *context = output->context;
|
|
||||||
freerdp_peer *peer = context->peer;
|
|
||||||
rdpUpdate *update = peer->update;
|
|
||||||
|
|
||||||
Stream_Clear(context->encode_stream);
|
|
||||||
Stream_SetPosition(context->encode_stream, 0);
|
|
||||||
int width = damage->extents.x2 - damage->extents.x1;
|
|
||||||
int height = damage->extents.y2 - damage->extents.y1;
|
|
||||||
|
|
||||||
SURFACE_BITS_COMMAND cmd;
|
|
||||||
cmd.skipCompression = TRUE;
|
|
||||||
cmd.destLeft = damage->extents.x1;
|
|
||||||
cmd.destTop = damage->extents.y1;
|
|
||||||
cmd.destRight = damage->extents.x2;
|
|
||||||
cmd.destBottom = damage->extents.y2;
|
|
||||||
cmd.bmp.bpp = pixman_image_get_depth(output->shadow_surface);
|
|
||||||
cmd.bmp.codecID = peer->settings->NSCodecId;
|
|
||||||
cmd.bmp.width = width;
|
|
||||||
cmd.bmp.height = height;
|
|
||||||
|
|
||||||
uint32_t *ptr = pixman_image_get_data(output->shadow_surface) +
|
|
||||||
damage->extents.x1 + damage->extents.y1 *
|
|
||||||
(pixman_image_get_stride(output->shadow_surface) / sizeof(uint32_t));
|
|
||||||
|
|
||||||
nsc_compose_message(context->nsc_context, context->encode_stream,
|
|
||||||
(BYTE *)ptr, width, height,
|
|
||||||
pixman_image_get_stride(output->shadow_surface));
|
|
||||||
|
|
||||||
cmd.bmp.bitmapDataLength = Stream_GetPosition(context->encode_stream);
|
|
||||||
cmd.bmp.bitmapData = Stream_Buffer(context->encode_stream);
|
|
||||||
|
|
||||||
update->SurfaceBits(update->context, &cmd);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_commit(struct wlr_output *wlr_output) {
|
|
||||||
struct wlr_rdp_output *output =
|
|
||||||
rdp_output_from_output(wlr_output);
|
|
||||||
bool ret = false;
|
|
||||||
|
|
||||||
pixman_region32_t output_region;
|
|
||||||
pixman_region32_init(&output_region);
|
|
||||||
pixman_region32_union_rect(&output_region, &output_region,
|
|
||||||
0, 0, wlr_output->width, wlr_output->height);
|
|
||||||
|
|
||||||
pixman_region32_t *damage = &output_region;
|
|
||||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) {
|
|
||||||
damage = &wlr_output->pending.damage;
|
|
||||||
}
|
|
||||||
|
|
||||||
int x = damage->extents.x1;
|
|
||||||
int y = damage->extents.y1;
|
|
||||||
int width = damage->extents.x2 - damage->extents.x1;
|
|
||||||
int height = damage->extents.y2 - damage->extents.y1;
|
|
||||||
|
|
||||||
// Update shadow buffer
|
|
||||||
struct wlr_renderer *renderer =
|
|
||||||
wlr_backend_get_renderer(&output->backend->backend);
|
|
||||||
// TODO performance: add support for flags
|
|
||||||
ret = wlr_renderer_read_pixels(renderer, WL_SHM_FORMAT_XRGB8888,
|
|
||||||
NULL, pixman_image_get_stride(output->shadow_surface),
|
|
||||||
width, height, x, y, x, y,
|
|
||||||
pixman_image_get_data(output->shadow_surface));
|
|
||||||
if (!ret) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send along to clients
|
|
||||||
rdpSettings *settings = output->context->peer->settings;
|
|
||||||
if (settings->RemoteFxCodec) {
|
|
||||||
ret = rfx_swap_buffers(output, damage);
|
|
||||||
} else if (settings->NSCodec) {
|
|
||||||
ret = nsc_swap_buffers(output, damage);
|
|
||||||
} else {
|
|
||||||
// This would perform like ass so why bother
|
|
||||||
wlr_log(WLR_ERROR, "Raw updates are not supported; use rfx or nsc");
|
|
||||||
ret = false;
|
|
||||||
}
|
|
||||||
if (!ret) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_output_send_present(wlr_output, NULL);
|
|
||||||
|
|
||||||
out:
|
|
||||||
pixman_region32_fini(&output_region);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void output_destroy(struct wlr_output *wlr_output) {
|
|
||||||
struct wlr_rdp_output *output =
|
|
||||||
rdp_output_from_output(wlr_output);
|
|
||||||
if (output->frame_timer) {
|
|
||||||
wl_event_source_remove(output->frame_timer);
|
|
||||||
}
|
|
||||||
wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface);
|
|
||||||
if (output->shadow_surface) {
|
|
||||||
pixman_image_unref(output->shadow_surface);
|
|
||||||
}
|
|
||||||
free(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wlr_output_impl output_impl = {
|
|
||||||
.set_custom_mode = output_set_custom_mode,
|
|
||||||
.destroy = output_destroy,
|
|
||||||
.attach_render = output_attach_render,
|
|
||||||
.commit = output_commit,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool wlr_output_is_rdp(struct wlr_output *wlr_output) {
|
|
||||||
return wlr_output->impl == &output_impl;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int signal_frame(void *data) {
|
|
||||||
struct wlr_rdp_output *output = data;
|
|
||||||
wlr_output_send_frame(&output->wlr_output);
|
|
||||||
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_rdp_output *wlr_rdp_output_create(struct wlr_rdp_backend *backend,
|
|
||||||
struct wlr_rdp_peer_context *context, unsigned int width,
|
|
||||||
unsigned int height) {
|
|
||||||
struct wlr_rdp_output *output =
|
|
||||||
calloc(1, sizeof(struct wlr_rdp_output));
|
|
||||||
if (output == NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate wlr_rdp_output");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
output->backend = backend;
|
|
||||||
output->context = context;
|
|
||||||
wlr_output_init(&output->wlr_output, &backend->backend, &output_impl,
|
|
||||||
backend->display);
|
|
||||||
struct wlr_output *wlr_output = &output->wlr_output;
|
|
||||||
|
|
||||||
output->egl_surface = egl_create_surface(&backend->egl, width, height);
|
|
||||||
if (output->egl_surface == EGL_NO_SURFACE) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to create EGL surface");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
output_set_custom_mode(wlr_output, width, height, 0);
|
|
||||||
strncpy(wlr_output->make, "RDP", sizeof(wlr_output->make));
|
|
||||||
strncpy(wlr_output->model, "RDP", sizeof(wlr_output->model));
|
|
||||||
snprintf(wlr_output->name, sizeof(wlr_output->name), "RDP-%d",
|
|
||||||
wl_list_length(&backend->clients));
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
struct wl_event_loop *ev = wl_display_get_event_loop(backend->display);
|
|
||||||
output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output);
|
|
||||||
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
|
|
||||||
wlr_output_update_enabled(wlr_output, true);
|
|
||||||
wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output);
|
|
||||||
return output;
|
|
||||||
|
|
||||||
error:
|
|
||||||
wlr_output_destroy(&output->wlr_output);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
|
@ -1,360 +0,0 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
#include <linux/input.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <wlr/types/wlr_output.h>
|
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
|
||||||
#include <wlr/interfaces/wlr_keyboard.h>
|
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
|
||||||
#include <wlr/util/log.h>
|
|
||||||
#include "backend/rdp.h"
|
|
||||||
#include "util/signal.h"
|
|
||||||
|
|
||||||
static BOOL xf_peer_capabilities(freerdp_peer *client) {
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL xf_peer_post_connect(freerdp_peer *client) {
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL xf_peer_activate(freerdp_peer *client) {
|
|
||||||
struct wlr_rdp_peer_context *context =
|
|
||||||
(struct wlr_rdp_peer_context *)client->context;
|
|
||||||
struct wlr_rdp_backend *backend = context->backend;
|
|
||||||
rdpSettings *settings = client->settings;
|
|
||||||
|
|
||||||
if (!settings->SurfaceCommandsEnabled) {
|
|
||||||
wlr_log(WLR_ERROR, "RDP peer does not support SurfaceCommands");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
context->output = wlr_rdp_output_create(backend, context,
|
|
||||||
(int)settings->DesktopWidth, (int)settings->DesktopHeight);
|
|
||||||
if (!context->output) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allcoate output for RDP peer");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
rfx_context_reset(context->rfx_context,
|
|
||||||
context->output->wlr_output.width,
|
|
||||||
context->output->wlr_output.height);
|
|
||||||
nsc_context_reset(context->nsc_context,
|
|
||||||
context->output->wlr_output.width,
|
|
||||||
context->output->wlr_output.height);
|
|
||||||
|
|
||||||
if (context->flags & RDP_PEER_ACTIVATED) {
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
context->pointer = wlr_rdp_pointer_create(backend, context);
|
|
||||||
if (!context->pointer) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate pointer for RDP peer");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use wlroots' software cursors instead of remote cursors
|
|
||||||
POINTER_SYSTEM_UPDATE pointer_system;
|
|
||||||
rdpPointerUpdate *pointer = client->update->pointer;
|
|
||||||
pointer_system.type = SYSPTR_NULL;
|
|
||||||
pointer->PointerSystem(client->context, &pointer_system);
|
|
||||||
|
|
||||||
context->keyboard = wlr_rdp_keyboard_create(backend, settings);
|
|
||||||
if (!context->keyboard) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate keyboard for RDP peer");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
context->flags |= RDP_PEER_ACTIVATED;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xf_suppress_output(rdpContext *context,
|
|
||||||
BYTE allow, const RECTANGLE_16 *area) {
|
|
||||||
struct wlr_rdp_peer_context *peer_context =
|
|
||||||
(struct wlr_rdp_peer_context *)context;
|
|
||||||
if (allow) {
|
|
||||||
peer_context->flags |= RDP_PEER_OUTPUT_ENABLED;
|
|
||||||
} else {
|
|
||||||
peer_context->flags &= ~RDP_PEER_OUTPUT_ENABLED;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xf_input_synchronize_event(rdpInput *input, UINT32 flags) {
|
|
||||||
struct wlr_rdp_peer_context *context =
|
|
||||||
(struct wlr_rdp_peer_context *)input->context;
|
|
||||||
wlr_output_damage_whole(&context->output->wlr_output);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int64_t timespec_to_msec(const struct timespec *a) {
|
|
||||||
return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xf_input_mouse_event(rdpInput *input,
|
|
||||||
UINT16 flags, UINT16 x, UINT16 y) {
|
|
||||||
struct wlr_rdp_peer_context *context =
|
|
||||||
(struct wlr_rdp_peer_context *)input->context;
|
|
||||||
struct wlr_input_device *wlr_device = &context->pointer->wlr_input_device;
|
|
||||||
struct wlr_pointer *pointer = wlr_device->pointer;
|
|
||||||
bool frame = false;
|
|
||||||
struct timespec now;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
||||||
|
|
||||||
if (flags & PTR_FLAGS_MOVE) {
|
|
||||||
struct wlr_event_pointer_motion_absolute event = { 0 };
|
|
||||||
event.device = wlr_device;
|
|
||||||
event.time_msec = timespec_to_msec(&now);
|
|
||||||
event.x = x / (double)context->output->wlr_output.width;
|
|
||||||
event.y = y / (double)context->output->wlr_output.height;
|
|
||||||
wlr_signal_emit_safe(&pointer->events.motion_absolute, &event);
|
|
||||||
frame = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t button = 0;
|
|
||||||
if (flags & PTR_FLAGS_BUTTON1) {
|
|
||||||
button = BTN_LEFT;
|
|
||||||
} else if (flags & PTR_FLAGS_BUTTON2) {
|
|
||||||
button = BTN_RIGHT;
|
|
||||||
} else if (flags & PTR_FLAGS_BUTTON3) {
|
|
||||||
button = BTN_MIDDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (button) {
|
|
||||||
struct wlr_event_pointer_button event = { 0 };
|
|
||||||
event.device = wlr_device;
|
|
||||||
event.time_msec = timespec_to_msec(&now);
|
|
||||||
event.button = button;
|
|
||||||
event.state = (flags & PTR_FLAGS_DOWN) ?
|
|
||||||
WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED;
|
|
||||||
wlr_signal_emit_safe(&pointer->events.button, &event);
|
|
||||||
frame = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & PTR_FLAGS_WHEEL) {
|
|
||||||
double value = -(flags & 0xFF) / 120.0;
|
|
||||||
if (flags & PTR_FLAGS_WHEEL_NEGATIVE) {
|
|
||||||
value = -value;
|
|
||||||
}
|
|
||||||
struct wlr_event_pointer_axis event = { 0 };
|
|
||||||
event.device = &context->pointer->wlr_input_device;
|
|
||||||
event.time_msec = timespec_to_msec(&now);
|
|
||||||
event.source = WLR_AXIS_SOURCE_WHEEL;
|
|
||||||
event.orientation = WLR_AXIS_ORIENTATION_VERTICAL;
|
|
||||||
event.delta = value;
|
|
||||||
event.delta_discrete = (int32_t)value;
|
|
||||||
wlr_signal_emit_safe(&pointer->events.axis, &event);
|
|
||||||
frame = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frame) {
|
|
||||||
wlr_signal_emit_safe(&pointer->events.frame, pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xf_input_extended_mouse_event(
|
|
||||||
rdpInput *input, UINT16 flags, UINT16 x, UINT16 y) {
|
|
||||||
struct wlr_rdp_peer_context *context =
|
|
||||||
(struct wlr_rdp_peer_context *)input->context;
|
|
||||||
struct wlr_input_device *wlr_device = &context->pointer->wlr_input_device;
|
|
||||||
struct wlr_pointer *pointer = wlr_device->pointer;
|
|
||||||
struct timespec now;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
||||||
struct wlr_event_pointer_motion_absolute event = { 0 };
|
|
||||||
event.device = wlr_device;
|
|
||||||
event.time_msec = timespec_to_msec(&now);
|
|
||||||
event.x = x / (double)context->output->wlr_output.width;
|
|
||||||
event.y = y / (double)context->output->wlr_output.height;
|
|
||||||
wlr_signal_emit_safe(&pointer->events.motion_absolute, &event);
|
|
||||||
wlr_signal_emit_safe(&pointer->events.frame, pointer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) {
|
|
||||||
struct wlr_rdp_peer_context *context =
|
|
||||||
(struct wlr_rdp_peer_context *)input->context;
|
|
||||||
struct wlr_input_device *wlr_device = &context->keyboard->wlr_input_device;
|
|
||||||
struct wlr_keyboard *keyboard = wlr_device->keyboard;
|
|
||||||
struct timespec now;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
||||||
|
|
||||||
if (!(context->flags & RDP_PEER_ACTIVATED)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool notify = false;
|
|
||||||
enum wlr_key_state state;
|
|
||||||
if ((flags & KBD_FLAGS_DOWN)) {
|
|
||||||
state = WLR_KEY_PRESSED;
|
|
||||||
notify = true;
|
|
||||||
} else if ((flags & KBD_FLAGS_RELEASE)) {
|
|
||||||
state = WLR_KEY_RELEASED;
|
|
||||||
notify = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notify) {
|
|
||||||
uint32_t full_code = code;
|
|
||||||
if (flags & KBD_FLAGS_EXTENDED) {
|
|
||||||
full_code |= KBD_FLAGS_EXTENDED;
|
|
||||||
}
|
|
||||||
uint32_t vk_code = GetVirtualKeyCodeFromVirtualScanCode(full_code, 4);
|
|
||||||
if (flags & KBD_FLAGS_EXTENDED) {
|
|
||||||
vk_code |= KBDEXT;
|
|
||||||
}
|
|
||||||
uint32_t scan_code = GetKeycodeFromVirtualKeyCode(
|
|
||||||
vk_code, KEYCODE_TYPE_EVDEV);
|
|
||||||
struct wlr_event_keyboard_key event = { 0 };
|
|
||||||
event.time_msec = timespec_to_msec(&now);
|
|
||||||
event.keycode = scan_code - 8;
|
|
||||||
event.state = state;
|
|
||||||
event.update_state = true;
|
|
||||||
wlr_keyboard_notify_key(keyboard, &event);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int xf_input_unicode_keyboard_event(rdpInput *input,
|
|
||||||
UINT16 flags, UINT16 code) {
|
|
||||||
wlr_log(WLR_DEBUG, "Unhandled RDP unicode keyboard event "
|
|
||||||
"(flags:0x%X code:0x%X)\n", flags, code);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int rdp_client_activity(int fd, uint32_t mask, void *data) {
|
|
||||||
freerdp_peer *client = (freerdp_peer *)data;
|
|
||||||
if (!client->CheckFileDescriptor(client)) {
|
|
||||||
wlr_log(WLR_ERROR,
|
|
||||||
"Unable to check client file descriptor for %p", client);
|
|
||||||
freerdp_peer_context_free(client);
|
|
||||||
freerdp_peer_free(client);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int rdp_peer_context_new(
|
|
||||||
freerdp_peer *client, struct wlr_rdp_peer_context *context) {
|
|
||||||
context->peer = client;
|
|
||||||
context->flags = RDP_PEER_OUTPUT_ENABLED;
|
|
||||||
context->rfx_context = rfx_context_new(TRUE);
|
|
||||||
if (!context->rfx_context) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
context->rfx_context->mode = RLGR3;
|
|
||||||
context->rfx_context->width = client->settings->DesktopWidth;
|
|
||||||
context->rfx_context->height = client->settings->DesktopHeight;
|
|
||||||
rfx_context_set_pixel_format(context->rfx_context, PIXEL_FORMAT_BGRA32);
|
|
||||||
|
|
||||||
context->nsc_context = nsc_context_new();
|
|
||||||
if (!context->nsc_context) {
|
|
||||||
rfx_context_free(context->rfx_context);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsc_context_set_pixel_format(context->nsc_context, PIXEL_FORMAT_BGRA32);
|
|
||||||
|
|
||||||
context->encode_stream = Stream_New(NULL, 65536);
|
|
||||||
if (!context->encode_stream) {
|
|
||||||
nsc_context_free(context->nsc_context);
|
|
||||||
rfx_context_free(context->rfx_context);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rdp_peer_context_free(
|
|
||||||
freerdp_peer *client, struct wlr_rdp_peer_context *context) {
|
|
||||||
if (!context) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_FREERDP_FDS; ++i) {
|
|
||||||
if (context->events[i]) {
|
|
||||||
wl_event_source_remove(context->events[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context->flags & RDP_PEER_ACTIVATED) {
|
|
||||||
wlr_output_destroy(&context->output->wlr_output);
|
|
||||||
wlr_input_device_destroy(&context->pointer->wlr_input_device);
|
|
||||||
wlr_input_device_destroy(&context->keyboard->wlr_input_device);
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_list_remove(&context->link);
|
|
||||||
wlr_output_destroy(&context->output->wlr_output);
|
|
||||||
|
|
||||||
Stream_Free(context->encode_stream, TRUE);
|
|
||||||
nsc_context_free(context->nsc_context);
|
|
||||||
rfx_context_free(context->rfx_context);
|
|
||||||
free(context->rfx_rects);
|
|
||||||
}
|
|
||||||
|
|
||||||
int rdp_peer_init(freerdp_peer *client,
|
|
||||||
struct wlr_rdp_backend *backend) {
|
|
||||||
client->ContextSize = sizeof(struct wlr_rdp_peer_context);
|
|
||||||
client->ContextNew = (psPeerContextNew)rdp_peer_context_new;
|
|
||||||
client->ContextFree = (psPeerContextFree)rdp_peer_context_free;
|
|
||||||
freerdp_peer_context_new(client);
|
|
||||||
|
|
||||||
struct wlr_rdp_peer_context *peer_context =
|
|
||||||
(struct wlr_rdp_peer_context *)client->context;
|
|
||||||
peer_context->backend = backend;
|
|
||||||
|
|
||||||
client->settings->CertificateFile = strdup(backend->tls_cert_path);
|
|
||||||
client->settings->PrivateKeyFile = strdup(backend->tls_key_path);
|
|
||||||
client->settings->NlaSecurity = FALSE;
|
|
||||||
|
|
||||||
if (!client->Initialize(client)) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to initialize FreeRDP peer");
|
|
||||||
goto err_init;
|
|
||||||
}
|
|
||||||
|
|
||||||
client->settings->OsMajorType = OSMAJORTYPE_UNIX;
|
|
||||||
client->settings->OsMinorType = OSMINORTYPE_PSEUDO_XSERVER;
|
|
||||||
client->settings->ColorDepth = 32;
|
|
||||||
client->settings->RefreshRect = TRUE;
|
|
||||||
client->settings->RemoteFxCodec = TRUE;
|
|
||||||
client->settings->NSCodec = TRUE;
|
|
||||||
client->settings->FrameMarkerCommandEnabled = TRUE;
|
|
||||||
client->settings->SurfaceFrameMarkerEnabled = TRUE;
|
|
||||||
|
|
||||||
client->Capabilities = xf_peer_capabilities;
|
|
||||||
client->PostConnect = xf_peer_post_connect;
|
|
||||||
client->Activate = xf_peer_activate;
|
|
||||||
|
|
||||||
client->update->SuppressOutput = (pSuppressOutput)xf_suppress_output;
|
|
||||||
|
|
||||||
client->input->SynchronizeEvent = xf_input_synchronize_event;
|
|
||||||
client->input->MouseEvent = xf_input_mouse_event;
|
|
||||||
client->input->ExtendedMouseEvent = xf_input_extended_mouse_event;
|
|
||||||
client->input->KeyboardEvent = xf_input_keyboard_event;
|
|
||||||
client->input->UnicodeKeyboardEvent = xf_input_unicode_keyboard_event;
|
|
||||||
|
|
||||||
int rcount = 0;
|
|
||||||
void *rfds[MAX_FREERDP_FDS];
|
|
||||||
if (!client->GetFileDescriptor(client, rfds, &rcount)) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to retrieve client file descriptors");
|
|
||||||
goto err_init;
|
|
||||||
}
|
|
||||||
struct wl_event_loop *event_loop =
|
|
||||||
wl_display_get_event_loop(backend->display);
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < rcount; ++i) {
|
|
||||||
int fd = (int)(long)(rfds[i]);
|
|
||||||
peer_context->events[i] = wl_event_loop_add_fd(
|
|
||||||
event_loop, fd, WL_EVENT_READABLE, rdp_client_activity,
|
|
||||||
client);
|
|
||||||
}
|
|
||||||
for (; i < MAX_FREERDP_FDS; ++i) {
|
|
||||||
peer_context->events[i] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_list_insert(&backend->clients, &peer_context->link);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_init:
|
|
||||||
client->Close(client);
|
|
||||||
return -1;
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <wayland-server-core.h>
|
|
||||||
#include <wlr/types/wlr_input_device.h>
|
|
||||||
#include <wlr/types/wlr_pointer.h>
|
|
||||||
#include <wlr/interfaces/wlr_pointer.h>
|
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
|
||||||
#include <wlr/util/log.h>
|
|
||||||
#include "backend/rdp.h"
|
|
||||||
#include "util/signal.h"
|
|
||||||
|
|
||||||
static struct wlr_input_device_impl input_device_impl = { 0 };
|
|
||||||
|
|
||||||
struct wlr_rdp_input_device *wlr_rdp_pointer_create(
|
|
||||||
struct wlr_rdp_backend *backend, struct wlr_rdp_peer_context *context) {
|
|
||||||
struct wlr_rdp_input_device *device =
|
|
||||||
calloc(1, sizeof(struct wlr_rdp_input_device));
|
|
||||||
if (!device) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate RDP input device");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int vendor = 0;
|
|
||||||
int product = 0;
|
|
||||||
const char *name = "rdp";
|
|
||||||
struct wlr_input_device *wlr_device = &device->wlr_input_device;
|
|
||||||
wlr_input_device_init(wlr_device, WLR_INPUT_DEVICE_POINTER,
|
|
||||||
&input_device_impl, name, vendor, product);
|
|
||||||
|
|
||||||
if (!(wlr_device->pointer = calloc(1, sizeof(struct wlr_pointer)))) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate RDP pointer device");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
wlr_device->output_name = strdup(context->output->wlr_output.name);
|
|
||||||
wlr_pointer_init(wlr_device->pointer, NULL);
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_device);
|
|
||||||
return device;
|
|
||||||
}
|
|
|
@ -1,268 +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 "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 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];
|
|
||||||
snprintf(tty_path, sizeof(tty_path), "/dev/ttyv%d", tty - 1);
|
|
||||||
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->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;
|
|
||||||
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,269 +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 };
|
|
||||||
|
|
||||||
#if WLR_HAS_LIBCAP
|
|
||||||
#include <sys/capability.h>
|
|
||||||
|
|
||||||
static bool have_permissions(void) {
|
|
||||||
cap_t cap = cap_get_proc();
|
|
||||||
cap_flag_value_t val;
|
|
||||||
|
|
||||||
if (!cap || cap_get_flag(cap, CAP_SYS_ADMIN, CAP_PERMITTED, &val) || val != CAP_SET) {
|
|
||||||
wlr_log(WLR_ERROR, "Do not have CAP_SYS_ADMIN; cannot become DRM master");
|
|
||||||
cap_free(cap);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
cap_free(cap);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static bool have_permissions(void) {
|
|
||||||
#ifdef __linux__
|
|
||||||
if (geteuid() != 0) {
|
|
||||||
wlr_log(WLR_ERROR, "Do not have root privileges; cannot become DRM master");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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) {
|
|
||||||
if (!have_permissions()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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,288 +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 "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->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;
|
|
||||||
|
|
||||||
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,695 +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 "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;
|
|
||||||
|
|
||||||
// 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));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool logind_change_vt(struct wlr_session *base, unsigned vt) {
|
|
||||||
struct logind_session *session = logind_session_from_session(base);
|
|
||||||
|
|
||||||
// Only seat0 has VTs associated with it
|
|
||||||
if (strcmp(session->base.seat, "seat0") != 0) {
|
|
||||||
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 '%d'", 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 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 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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 bool add_signal_matches(struct logind_session *session) {
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
char str[256];
|
|
||||||
const char fmt[] = "type='signal',"
|
|
||||||
"sender='org.freedesktop.login1',"
|
|
||||||
"interface='org.freedesktop.login1.%s',"
|
|
||||||
"member='%s',"
|
|
||||||
"path='%s'";
|
|
||||||
|
|
||||||
snprintf(str, sizeof(str), fmt, "Manager", "SessionRemoved",
|
|
||||||
"/org/freedesktop/login1");
|
|
||||||
ret = sd_bus_add_match(session->bus, NULL, str, session_removed, session);
|
|
||||||
if (ret < 0) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(str, sizeof(str), fmt, "Session", "PauseDevice", session->path);
|
|
||||||
ret = sd_bus_add_match(session->bus, NULL, str, pause_device, session);
|
|
||||||
if (ret < 0) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(str, sizeof(str), fmt, "Session", "ResumeDevice", session->path);
|
|
||||||
ret = sd_bus_add_match(session->bus, NULL, str, 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, "org.freedesktop.login1",
|
|
||||||
session->path, "org.freedesktop.DBus.Properties", "PropertiesChanged",
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (strcmp(seat, "seat0") == 0) {
|
|
||||||
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 (!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;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_log(WLR_INFO, "Successfully loaded logind session");
|
|
||||||
|
|
||||||
session->base.impl = &session_logind;
|
|
||||||
return &session->base;
|
|
||||||
|
|
||||||
error_bus:
|
|
||||||
sd_bus_unref(session->bus);
|
|
||||||
free(session->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,
|
|
||||||
};
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
libseat = dependency('libseat',
|
||||||
|
version: '>=0.2.0',
|
||||||
|
fallback: ['seatd', 'libseat'],
|
||||||
|
default_options: ['server=disabled', 'man-pages=disabled'],
|
||||||
|
)
|
||||||
|
wlr_files += files('session.c')
|
||||||
|
wlr_deps += libseat
|
|
@ -1,48 +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 "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->impl = &session_noop;
|
|
||||||
|
|
||||||
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,33 +1,174 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <libudev.h>
|
#include <libudev.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/backend/session/interface.h>
|
|
||||||
#include <wlr/config.h>
|
#include <wlr/config.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
#include <xf86drmMode.h>
|
#include <xf86drmMode.h>
|
||||||
|
#include "backend/session/session.h"
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
extern const struct session_impl session_logind;
|
#include <libseat.h>
|
||||||
extern const struct session_impl session_direct;
|
|
||||||
extern const struct session_impl session_noop;
|
|
||||||
|
|
||||||
static const struct session_impl *impls[] = {
|
#define WAIT_GPU_TIMEOUT 10000 // ms
|
||||||
#if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND
|
|
||||||
&session_logind,
|
static void handle_enable_seat(struct libseat *seat, void *data) {
|
||||||
#endif
|
struct wlr_session *session = data;
|
||||||
&session_direct,
|
session->active = true;
|
||||||
NULL,
|
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 wlr_session *session = data;
|
||||||
|
|
||||||
struct udev_device *udev_dev = udev_monitor_receive_device(session->mon);
|
struct udev_device *udev_dev = udev_monitor_receive_device(session->mon);
|
||||||
|
@ -35,21 +176,48 @@ static int udev_event(int fd, uint32_t mask, void *data) {
|
||||||
return 1;
|
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);
|
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)",
|
if (!is_drm_card(sysname) || !action || !devnode) {
|
||||||
udev_device_get_sysname(udev_dev), action);
|
|
||||||
|
|
||||||
if (!action || strcmp(action, "change") != 0) {
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_t devnum = udev_device_get_devnum(udev_dev);
|
const char *seat = udev_device_get_property_value(udev_dev, "ID_SEAT");
|
||||||
struct wlr_device *dev;
|
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 (strcmp(action, "add") == 0) {
|
||||||
if (dev->dev == devnum) {
|
wlr_log(WLR_DEBUG, "DRM device %s added", sysname);
|
||||||
wlr_signal_emit_safe(&dev->signal, session);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,42 +234,22 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_session *wlr_session_create(struct wl_display *disp) {
|
struct wlr_session *wlr_session_create(struct wl_display *disp) {
|
||||||
struct wlr_session *session = NULL;
|
struct wlr_session *session = calloc(1, sizeof(*session));
|
||||||
|
|
||||||
const char *env_wlr_session = getenv("WLR_SESSION");
|
|
||||||
if (env_wlr_session) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
wlr_log(WLR_ERROR, "Failed to load session backend");
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
session->active = true;
|
wl_signal_init(&session->events.active);
|
||||||
wl_signal_init(&session->session_signal);
|
wl_signal_init(&session->events.add_drm_card);
|
||||||
wl_signal_init(&session->events.destroy);
|
wl_signal_init(&session->events.destroy);
|
||||||
wl_list_init(&session->devices);
|
wl_list_init(&session->devices);
|
||||||
|
|
||||||
|
if (libseat_session_init(session, disp) == -1) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to load session backend");
|
||||||
|
goto error_open;
|
||||||
|
}
|
||||||
|
|
||||||
session->udev = udev_new();
|
session->udev = udev_new();
|
||||||
if (!session->udev) {
|
if (!session->udev) {
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to create udev context");
|
wlr_log_errno(WLR_ERROR, "Failed to create udev context");
|
||||||
|
@ -121,12 +269,14 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) {
|
||||||
int fd = udev_monitor_get_fd(session->mon);
|
int fd = udev_monitor_get_fd(session->mon);
|
||||||
|
|
||||||
session->udev_event = wl_event_loop_add_fd(event_loop, fd,
|
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) {
|
if (!session->udev_event) {
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to create udev event source");
|
wlr_log_errno(WLR_ERROR, "Failed to create udev event source");
|
||||||
goto error_mon;
|
goto error_mon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
session->display = disp;
|
||||||
|
|
||||||
session->display_destroy.notify = handle_display_destroy;
|
session->display_destroy.notify = handle_display_destroy;
|
||||||
wl_display_add_destroy_listener(disp, &session->display_destroy);
|
wl_display_add_destroy_listener(disp, &session->display_destroy);
|
||||||
|
|
||||||
|
@ -137,7 +287,9 @@ error_mon:
|
||||||
error_udev:
|
error_udev:
|
||||||
udev_unref(session->udev);
|
udev_unref(session->udev);
|
||||||
error_session:
|
error_session:
|
||||||
session->impl->destroy(session);
|
libseat_session_finish(session);
|
||||||
|
error_open:
|
||||||
|
free(session);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,13 +305,22 @@ void wlr_session_destroy(struct wlr_session *session) {
|
||||||
udev_monitor_unref(session->mon);
|
udev_monitor_unref(session->mon);
|
||||||
udev_unref(session->udev);
|
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) {
|
struct wlr_device *wlr_session_open_file(struct wlr_session *session,
|
||||||
int fd = session->impl->open(session, path);
|
const char *path) {
|
||||||
if (fd < 0) {
|
int fd;
|
||||||
return 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));
|
struct wlr_device *dev = malloc(sizeof(*dev));
|
||||||
|
@ -176,86 +337,65 @@ int wlr_session_open_file(struct wlr_session *session, const char *path) {
|
||||||
|
|
||||||
dev->fd = fd;
|
dev->fd = fd;
|
||||||
dev->dev = st.st_rdev;
|
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);
|
wl_list_insert(&session->devices, &dev->link);
|
||||||
|
|
||||||
return fd;
|
return dev;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
libseat_close_device(session->seat_handle, device_id);
|
||||||
free(dev);
|
free(dev);
|
||||||
return fd;
|
close(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);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wlr_session_close_file(struct wlr_session *session, int fd) {
|
void wlr_session_close_file(struct wlr_session *session,
|
||||||
struct wlr_device *dev = find_device(session, fd);
|
struct wlr_device *dev) {
|
||||||
|
if (libseat_close_device(session->seat_handle, dev->device_id) == -1) {
|
||||||
session->impl->close(session, fd);
|
wlr_log_errno(WLR_ERROR, "Failed to close device %d", dev->device_id);
|
||||||
|
}
|
||||||
|
close(dev->fd);
|
||||||
wl_list_remove(&dev->link);
|
wl_list_remove(&dev->link);
|
||||||
free(dev);
|
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) {
|
bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) {
|
||||||
if (!session) {
|
if (!session) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return libseat_switch_session(session->seat_handle, vt) == 0;
|
||||||
return session->impl->change_vt(session, vt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tests if 'path' is KMS compatible by trying to open it.
|
/* Tests if 'path' is KMS compatible by trying to open it. Returns the opened
|
||||||
* It leaves the open device in *fd_out it it succeeds.
|
* device on success. */
|
||||||
*/
|
struct wlr_device *session_open_if_kms(struct wlr_session *restrict session,
|
||||||
static int open_if_kms(struct wlr_session *restrict session,
|
|
||||||
const char *restrict path) {
|
const char *restrict path) {
|
||||||
if (!path) {
|
if (!path) {
|
||||||
return -1;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int fd = wlr_session_open_file(session, path);
|
struct wlr_device *dev = wlr_session_open_file(session, path);
|
||||||
if (fd < 0) {
|
if (!dev) {
|
||||||
return -1;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
drmVersion *ver = drmGetVersion(fd);
|
if (!drmIsKMS(dev->fd)) {
|
||||||
if (!ver) {
|
wlr_log(WLR_DEBUG, "Ignoring '%s': not a KMS device", path);
|
||||||
goto out_fd;
|
wlr_session_close_file(session, dev);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
drmFreeVersion(ver);
|
return dev;
|
||||||
return fd;
|
|
||||||
|
|
||||||
out_fd:
|
|
||||||
wlr_session_close_file(session, fd);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t explicit_find_gpus(struct wlr_session *session,
|
static ssize_t explicit_find_gpus(struct wlr_session *session,
|
||||||
size_t ret_len, int ret[static ret_len], const char *str) {
|
size_t ret_len, struct wlr_device *ret[static ret_len], const char *str) {
|
||||||
char *gpus = strdup(str);
|
char *gpus = strdup(str);
|
||||||
if (!gpus) {
|
if (!gpus) {
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
return 0;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
@ -266,8 +406,8 @@ static size_t explicit_find_gpus(struct wlr_session *session,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret[i] = open_if_kms(session, ptr);
|
ret[i] = session_open_if_kms(session, ptr);
|
||||||
if (ret[i] < 0) {
|
if (!ret[i]) {
|
||||||
wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr);
|
wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr);
|
||||||
} else {
|
} else {
|
||||||
++i;
|
++i;
|
||||||
|
@ -278,30 +418,92 @@ static size_t explicit_find_gpus(struct wlr_session *session,
|
||||||
return i;
|
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.
|
/* 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.
|
* If it's not found, it returns the first valid GPU it finds.
|
||||||
*/
|
*/
|
||||||
size_t wlr_session_find_gpus(struct wlr_session *session,
|
ssize_t wlr_session_find_gpus(struct wlr_session *session,
|
||||||
size_t ret_len, int *ret) {
|
size_t ret_len, struct wlr_device **ret) {
|
||||||
const char *explicit = getenv("WLR_DRM_DEVICES");
|
const char *explicit = getenv("WLR_DRM_DEVICES");
|
||||||
if (explicit) {
|
if (explicit) {
|
||||||
return explicit_find_gpus(session, ret_len, ret, explicit);
|
return explicit_find_gpus(session, ret_len, ret, explicit);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __FreeBSD__
|
struct udev_enumerate *en = enumerate_drm_cards(session->udev);
|
||||||
// XXX: libudev-devd does not return any GPUs (yet?)
|
|
||||||
return explicit_find_gpus(session, ret_len, ret, "/dev/drm/0");
|
|
||||||
#else
|
|
||||||
|
|
||||||
struct udev_enumerate *en = udev_enumerate_new(session->udev);
|
|
||||||
if (!en) {
|
if (!en) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create udev enumeration");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
udev_enumerate_add_match_subsystem(en, "drm");
|
if (udev_enumerate_get_list_entry(en) == NULL) {
|
||||||
udev_enumerate_add_match_sysname(en, "card[0-9]*");
|
udev_enumerate_unref(en);
|
||||||
udev_enumerate_scan_devices(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;
|
struct udev_list_entry *entry;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
@ -339,17 +541,18 @@ size_t wlr_session_find_gpus(struct wlr_session *session,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int fd = open_if_kms(session, udev_device_get_devnode(dev));
|
struct wlr_device *wlr_dev =
|
||||||
if (fd < 0) {
|
session_open_if_kms(session, udev_device_get_devnode(dev));
|
||||||
|
if (!wlr_dev) {
|
||||||
udev_device_unref(dev);
|
udev_device_unref(dev);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
udev_device_unref(dev);
|
udev_device_unref(dev);
|
||||||
|
|
||||||
ret[i] = fd;
|
ret[i] = wlr_dev;
|
||||||
if (is_boot_vga) {
|
if (is_boot_vga) {
|
||||||
int tmp = ret[0];
|
struct wlr_device *tmp = ret[0];
|
||||||
ret[0] = ret[i];
|
ret[0] = ret[i];
|
||||||
ret[i] = tmp;
|
ret[i] = tmp;
|
||||||
}
|
}
|
||||||
|
@ -360,5 +563,4 @@ size_t wlr_session_find_gpus(struct wlr_session *session,
|
||||||
udev_enumerate_unref(en);
|
udev_enumerate_unref(en);
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,51 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <wlr/config.h>
|
#include <drm_fourcc.h>
|
||||||
|
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
|
#include <xf86drm.h>
|
||||||
|
|
||||||
#include <wlr/backend/interface.h>
|
#include <wlr/backend/interface.h>
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
#include <wlr/interfaces/wlr_input_device.h>
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
#include <wlr/render/egl.h>
|
|
||||||
#include <wlr/render/gles2.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
#include "backend/wayland.h"
|
#include "backend/wayland.h"
|
||||||
|
#include "render/drm_format_set.h"
|
||||||
|
#include "render/pixel_format.h"
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
#include "xdg-decoration-unstable-v1-client-protocol.h"
|
|
||||||
|
#include "drm-client-protocol.h"
|
||||||
|
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
||||||
#include "pointer-gestures-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 "xdg-shell-client-protocol.h"
|
||||||
#include "tablet-unstable-v2-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) {
|
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend) {
|
||||||
assert(wlr_backend_is_wl(backend));
|
assert(wlr_backend_is_wl(backend));
|
||||||
|
@ -30,24 +56,31 @@ static int dispatch_events(int fd, uint32_t mask, void *data) {
|
||||||
struct wlr_wl_backend *wl = data;
|
struct wlr_wl_backend *wl = data;
|
||||||
|
|
||||||
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
|
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
|
||||||
|
if (mask & WL_EVENT_ERROR) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to read from remote Wayland display");
|
||||||
|
}
|
||||||
wl_display_terminate(wl->local_display);
|
wl_display_terminate(wl->local_display);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
if (mask & WL_EVENT_READABLE) {
|
if (mask & WL_EVENT_READABLE) {
|
||||||
return wl_display_dispatch(wl->remote_display);
|
count = wl_display_dispatch(wl->remote_display);
|
||||||
}
|
}
|
||||||
if (mask & WL_EVENT_WRITABLE) {
|
if (mask & WL_EVENT_WRITABLE) {
|
||||||
wl_display_flush(wl->remote_display);
|
wl_display_flush(wl->remote_display);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
if (mask == 0) {
|
if (mask == 0) {
|
||||||
int count = wl_display_dispatch_pending(wl->remote_display);
|
count = wl_display_dispatch_pending(wl->remote_display);
|
||||||
wl_display_flush(wl->remote_display);
|
wl_display_flush(wl->remote_display);
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
if (count < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to dispatch remote Wayland display");
|
||||||
|
wl_display_terminate(wl->local_display);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xdg_wm_base_handle_ping(void *data,
|
static void xdg_wm_base_handle_ping(void *data,
|
||||||
|
@ -59,19 +92,266 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = {
|
||||||
xdg_wm_base_handle_ping,
|
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
|
||||||
|
struct wlr_wl_backend *wl = data;
|
||||||
|
|
||||||
|
wlr_drm_format_set_add(&wl->linux_dmabuf_v1_formats, format,
|
||||||
|
DRM_FORMAT_MOD_INVALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void linux_dmabuf_v1_handle_modifier(void *data,
|
||||||
|
struct zwp_linux_dmabuf_v1 *linux_dmabuf_v1, uint32_t format,
|
||||||
|
uint32_t modifier_hi, uint32_t modifier_lo) {
|
||||||
|
struct wlr_wl_backend *wl = data;
|
||||||
|
|
||||||
|
uint64_t modifier = ((uint64_t)modifier_hi << 32) | modifier_lo;
|
||||||
|
wlr_drm_format_set_add(&wl->linux_dmabuf_v1_formats, format, modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_v1_listener = {
|
||||||
|
.format = linux_dmabuf_v1_handle_format,
|
||||||
|
.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,
|
static void registry_global(void *data, struct wl_registry *registry,
|
||||||
uint32_t name, const char *iface, uint32_t version) {
|
uint32_t name, const char *iface, uint32_t version) {
|
||||||
struct wlr_wl_backend *wl = data;
|
struct wlr_wl_backend *wl = data;
|
||||||
|
|
||||||
wlr_log(WLR_DEBUG, "Remote wayland global: %s v%d", iface, version);
|
wlr_log(WLR_DEBUG, "Remote wayland global: %s v%" PRIu32, iface, version);
|
||||||
|
|
||||||
if (strcmp(iface, wl_compositor_interface.name) == 0) {
|
if (strcmp(iface, wl_compositor_interface.name) == 0) {
|
||||||
wl->compositor = wl_registry_bind(registry, name,
|
wl->compositor = wl_registry_bind(registry, name,
|
||||||
&wl_compositor_interface, 4);
|
&wl_compositor_interface, 4);
|
||||||
} else if (strcmp(iface, wl_seat_interface.name) == 0) {
|
} else if (strcmp(iface, wl_seat_interface.name) == 0) {
|
||||||
wl->seat = wl_registry_bind(registry, name,
|
struct wl_seat *wl_seat = wl_registry_bind(registry, name,
|
||||||
&wl_seat_interface, 5);
|
&wl_seat_interface, 5);
|
||||||
wl_seat_add_listener(wl->seat, &seat_listener, wl);
|
if (!create_wl_seat(wl_seat, wl)) {
|
||||||
|
wl_seat_destroy(wl_seat);
|
||||||
|
}
|
||||||
} else if (strcmp(iface, xdg_wm_base_interface.name) == 0) {
|
} else if (strcmp(iface, xdg_wm_base_interface.name) == 0) {
|
||||||
wl->xdg_wm_base = wl_registry_bind(registry, name,
|
wl->xdg_wm_base = wl_registry_bind(registry, name,
|
||||||
&xdg_wm_base_interface, 1);
|
&xdg_wm_base_interface, 1);
|
||||||
|
@ -81,10 +361,33 @@ static void registry_global(void *data, struct wl_registry *registry,
|
||||||
&zxdg_decoration_manager_v1_interface, 1);
|
&zxdg_decoration_manager_v1_interface, 1);
|
||||||
} else if (strcmp(iface, zwp_pointer_gestures_v1_interface.name) == 0) {
|
} else if (strcmp(iface, zwp_pointer_gestures_v1_interface.name) == 0) {
|
||||||
wl->zwp_pointer_gestures_v1 = wl_registry_bind(registry, name,
|
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) {
|
} else if (strcmp(iface, zwp_tablet_manager_v2_interface.name) == 0) {
|
||||||
wl->tablet_manager = wl_registry_bind(registry, name,
|
wl->tablet_manager = wl_registry_bind(registry, name,
|
||||||
&zwp_tablet_manager_v2_interface, 1);
|
&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, 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,17 +408,19 @@ static const struct wl_registry_listener registry_listener = {
|
||||||
*/
|
*/
|
||||||
static bool backend_start(struct wlr_backend *backend) {
|
static bool backend_start(struct wlr_backend *backend) {
|
||||||
struct wlr_wl_backend *wl = get_wl_backend_from_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;
|
wl->started = true;
|
||||||
|
|
||||||
if (wl->keyboard) {
|
struct wlr_wl_seat *seat;
|
||||||
create_wl_keyboard(wl->keyboard, wl);
|
wl_list_for_each(seat, &wl->seats, link) {
|
||||||
}
|
if (seat->keyboard) {
|
||||||
|
create_wl_keyboard(seat);
|
||||||
|
}
|
||||||
|
|
||||||
if (wl->tablet_manager && wl->seat) {
|
if (wl->tablet_manager) {
|
||||||
wl_add_tablet_seat(wl->tablet_manager,
|
wl_add_tablet_seat(wl->tablet_manager, seat);
|
||||||
wl->seat, wl);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < wl->requested_outputs; ++i) {
|
for (size_t i = 0; i < wl->requested_outputs; ++i) {
|
||||||
|
@ -137,50 +442,78 @@ static void backend_destroy(struct wlr_backend *backend) {
|
||||||
wlr_output_destroy(&output->wlr_output);
|
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) {
|
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_list_remove(&wl->local_display_destroy.link);
|
||||||
|
|
||||||
free(wl->seat_name);
|
|
||||||
|
|
||||||
wl_event_source_remove(wl->remote_display_src);
|
wl_event_source_remove(wl->remote_display_src);
|
||||||
|
|
||||||
wlr_renderer_destroy(wl->renderer);
|
close(wl->drm_fd);
|
||||||
wlr_egl_finish(&wl->egl);
|
|
||||||
|
|
||||||
if (wl->pointer) {
|
wlr_drm_format_set_finish(&wl->shm_formats);
|
||||||
wl_pointer_destroy(wl->pointer);
|
wlr_drm_format_set_finish(&wl->linux_dmabuf_v1_formats);
|
||||||
}
|
|
||||||
if (wl->seat) {
|
destroy_wl_seats(wl);
|
||||||
wl_seat_destroy(wl->seat);
|
|
||||||
}
|
|
||||||
if (wl->zxdg_decoration_manager_v1) {
|
if (wl->zxdg_decoration_manager_v1) {
|
||||||
zxdg_decoration_manager_v1_destroy(wl->zxdg_decoration_manager_v1);
|
zxdg_decoration_manager_v1_destroy(wl->zxdg_decoration_manager_v1);
|
||||||
}
|
}
|
||||||
if (wl->zwp_pointer_gestures_v1) {
|
if (wl->zwp_pointer_gestures_v1) {
|
||||||
zwp_pointer_gestures_v1_destroy(wl->zwp_pointer_gestures_v1);
|
zwp_pointer_gestures_v1_destroy(wl->zwp_pointer_gestures_v1);
|
||||||
}
|
}
|
||||||
|
if (wl->presentation) {
|
||||||
|
wp_presentation_destroy(wl->presentation);
|
||||||
|
}
|
||||||
|
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);
|
xdg_wm_base_destroy(wl->xdg_wm_base);
|
||||||
wl_compositor_destroy(wl->compositor);
|
wl_compositor_destroy(wl->compositor);
|
||||||
wl_registry_destroy(wl->registry);
|
wl_registry_destroy(wl->registry);
|
||||||
|
wl_display_flush(wl->remote_display);
|
||||||
wl_display_disconnect(wl->remote_display);
|
wl_display_disconnect(wl->remote_display);
|
||||||
free(wl);
|
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);
|
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,
|
.start = backend_start,
|
||||||
.destroy = backend_destroy,
|
.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) {
|
bool wlr_backend_is_wl(struct wlr_backend *b) {
|
||||||
|
@ -194,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,
|
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");
|
wlr_log(WLR_INFO, "Creating wayland backend");
|
||||||
|
|
||||||
struct wlr_wl_backend *wl = calloc(1, sizeof(*wl));
|
struct wlr_wl_backend *wl = calloc(1, sizeof(*wl));
|
||||||
|
@ -208,6 +541,9 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
||||||
wl->local_display = display;
|
wl->local_display = display;
|
||||||
wl_list_init(&wl->devices);
|
wl_list_init(&wl->devices);
|
||||||
wl_list_init(&wl->outputs);
|
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);
|
wl->remote_display = wl_display_connect(remote);
|
||||||
if (!wl->remote_display) {
|
if (!wl->remote_display) {
|
||||||
|
@ -220,10 +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");
|
wlr_log_errno(WLR_ERROR, "Could not obtain reference to remote registry");
|
||||||
goto error_display;
|
goto error_display;
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_registry_add_listener(wl->registry, ®istry_listener, wl);
|
wl_registry_add_listener(wl->registry, ®istry_listener, wl);
|
||||||
wl_display_dispatch(wl->remote_display);
|
|
||||||
wl_display_roundtrip(wl->remote_display);
|
wl_display_roundtrip(wl->remote_display); // get globals
|
||||||
|
|
||||||
if (!wl->compositor) {
|
if (!wl->compositor) {
|
||||||
wlr_log(WLR_ERROR,
|
wlr_log(WLR_ERROR,
|
||||||
|
@ -236,10 +571,38 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
||||||
goto error_registry;
|
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);
|
struct wl_event_loop *loop = wl_display_get_event_loop(wl->local_display);
|
||||||
int fd = wl_display_get_fd(wl->remote_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, WL_EVENT_READABLE,
|
||||||
wl->remote_display_src = wl_event_loop_add_fd(loop, fd, events,
|
|
||||||
dispatch_events, wl);
|
dispatch_events, wl);
|
||||||
if (!wl->remote_display_src) {
|
if (!wl->remote_display_src) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create event source");
|
wlr_log(WLR_ERROR, "Failed to create event source");
|
||||||
|
@ -247,35 +610,33 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
||||||
}
|
}
|
||||||
wl_event_source_check(wl->remote_display_src);
|
wl_event_source_check(wl->remote_display_src);
|
||||||
|
|
||||||
static EGLint config_attribs[] = {
|
if (wl->drm_render_name != NULL) {
|
||||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
wlr_log(WLR_DEBUG, "Opening DRM render node %s", wl->drm_render_name);
|
||||||
EGL_RED_SIZE, 1,
|
wl->drm_fd = open(wl->drm_render_name, O_RDWR | O_NONBLOCK | O_CLOEXEC);
|
||||||
EGL_GREEN_SIZE, 1,
|
if (wl->drm_fd < 0) {
|
||||||
EGL_BLUE_SIZE, 1,
|
wlr_log_errno(WLR_ERROR, "Failed to open DRM render node %s",
|
||||||
EGL_ALPHA_SIZE, 1,
|
wl->drm_render_name);
|
||||||
EGL_NONE,
|
goto error_remote_display_src;
|
||||||
};
|
}
|
||||||
|
} else {
|
||||||
if (!create_renderer_func) {
|
wl->drm_fd = -1;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wl->local_display_destroy.notify = handle_display_destroy;
|
wl->local_display_destroy.notify = handle_display_destroy;
|
||||||
wl_display_add_destroy_listener(display, &wl->local_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;
|
return &wl->backend;
|
||||||
|
|
||||||
error_event:
|
error_remote_display_src:
|
||||||
wl_event_source_remove(wl->remote_display_src);
|
wl_event_source_remove(wl->remote_display_src);
|
||||||
error_registry:
|
error_registry:
|
||||||
|
free(wl->drm_render_name);
|
||||||
if (wl->compositor) {
|
if (wl->compositor) {
|
||||||
wl_compositor_destroy(wl->compositor);
|
wl_compositor_destroy(wl->compositor);
|
||||||
}
|
}
|
||||||
|
@ -286,6 +647,12 @@ error_registry:
|
||||||
error_display:
|
error_display:
|
||||||
wl_display_disconnect(wl->remote_display);
|
wl_display_disconnect(wl->remote_display);
|
||||||
error_wl:
|
error_wl:
|
||||||
|
wlr_backend_finish(&wl->backend);
|
||||||
free(wl);
|
free(wl);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct wl_display *wlr_wl_backend_get_remote_display(struct wlr_backend *backend) {
|
||||||
|
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
||||||
|
return wl->remote_display;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
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',
|
||||||
|
'seat.c',
|
||||||
|
'tablet_v2.c',
|
||||||
|
)
|
||||||
|
|
||||||
|
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',
|
||||||
|
]
|
||||||
|
|
||||||
|
foreach proto : client_protos
|
||||||
|
wlr_files += protocols_client_header[proto]
|
||||||
|
endforeach
|
|
@ -7,6 +7,7 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <drm_fourcc.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
|
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
|
@ -15,18 +16,28 @@
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
#include "backend/wayland.h"
|
#include "backend/wayland.h"
|
||||||
|
#include "render/pixel_format.h"
|
||||||
|
#include "render/swapchain.h"
|
||||||
|
#include "render/wlr_renderer.h"
|
||||||
#include "util/signal.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-decoration-unstable-v1-client-protocol.h"
|
||||||
#include "xdg-shell-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(
|
static struct wlr_wl_output *get_wl_output_from_output(
|
||||||
struct wlr_output *wlr_output) {
|
struct wlr_output *wlr_output) {
|
||||||
assert(wlr_output_is_wl(wlr_output));
|
assert(wlr_output_is_wl(wlr_output));
|
||||||
return (struct wlr_wl_output *)wlr_output;
|
return (struct wlr_wl_output *)wlr_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wl_callback_listener frame_listener;
|
|
||||||
|
|
||||||
static void surface_frame_callback(void *data, struct wl_callback *cb,
|
static void surface_frame_callback(void *data, struct wl_callback *cb,
|
||||||
uint32_t time) {
|
uint32_t time) {
|
||||||
struct wlr_wl_output *output = data;
|
struct wlr_wl_output *output = data;
|
||||||
|
@ -37,76 +48,319 @@ static void surface_frame_callback(void *data, struct wl_callback *cb,
|
||||||
wlr_output_send_frame(&output->wlr_output);
|
wlr_output_send_frame(&output->wlr_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wl_callback_listener frame_listener = {
|
static const struct wl_callback_listener frame_listener = {
|
||||||
.done = surface_frame_callback
|
.done = surface_frame_callback
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void presentation_feedback_destroy(
|
||||||
|
struct wlr_wl_presentation_feedback *feedback) {
|
||||||
|
wl_list_remove(&feedback->link);
|
||||||
|
wp_presentation_feedback_destroy(feedback->feedback);
|
||||||
|
free(feedback);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void presentation_feedback_handle_sync_output(void *data,
|
||||||
|
struct wp_presentation_feedback *feedback, struct wl_output *output) {
|
||||||
|
// This space is intentionally left blank
|
||||||
|
}
|
||||||
|
|
||||||
|
static void presentation_feedback_handle_presented(void *data,
|
||||||
|
struct wp_presentation_feedback *wp_feedback, uint32_t tv_sec_hi,
|
||||||
|
uint32_t tv_sec_lo, uint32_t tv_nsec, uint32_t refresh_ns,
|
||||||
|
uint32_t seq_hi, uint32_t seq_lo, uint32_t flags) {
|
||||||
|
struct wlr_wl_presentation_feedback *feedback = data;
|
||||||
|
|
||||||
|
struct timespec t = {
|
||||||
|
.tv_sec = ((uint64_t)tv_sec_hi << 32) | tv_sec_lo,
|
||||||
|
.tv_nsec = tv_nsec,
|
||||||
|
};
|
||||||
|
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,
|
||||||
|
.flags = flags,
|
||||||
|
};
|
||||||
|
wlr_output_send_present(&feedback->output->wlr_output, &event);
|
||||||
|
|
||||||
|
presentation_feedback_destroy(feedback);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void presentation_feedback_handle_discarded(void *data,
|
||||||
|
struct wp_presentation_feedback *wp_feedback) {
|
||||||
|
struct wlr_wl_presentation_feedback *feedback = data;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wp_presentation_feedback_listener
|
||||||
|
presentation_feedback_listener = {
|
||||||
|
.sync_output = presentation_feedback_handle_sync_output,
|
||||||
|
.presented = presentation_feedback_handle_presented,
|
||||||
|
.discarded = presentation_feedback_handle_discarded,
|
||||||
|
};
|
||||||
|
|
||||||
static bool output_set_custom_mode(struct wlr_output *wlr_output,
|
static bool output_set_custom_mode(struct wlr_output *wlr_output,
|
||||||
int32_t width, int32_t height, int32_t refresh) {
|
int32_t width, int32_t height, int32_t refresh) {
|
||||||
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
wlr_output_update_custom_mode(wlr_output, width, height, 0);
|
||||||
wlr_egl_swap_buffers(&output->backend->egl, output->egl_surface, NULL);
|
|
||||||
wl_egl_window_resize(output->egl_window, width, height, 0, 0);
|
|
||||||
wlr_output_update_custom_mode(&output->wlr_output, width, height, 0);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool output_attach_render(struct wlr_output *wlr_output,
|
void destroy_wl_buffer(struct wlr_wl_buffer *buffer) {
|
||||||
int *buffer_age) {
|
if (buffer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_list_remove(&buffer->buffer_destroy.link);
|
||||||
|
wl_list_remove(&buffer->link);
|
||||||
|
wl_buffer_destroy(buffer->wl_buffer);
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) {
|
||||||
|
struct wlr_wl_buffer *buffer = data;
|
||||||
|
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 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
struct wlr_buffer *wlr_buffer) {
|
||||||
|
if (!test_buffer(wl, wlr_buffer)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if (wl_buffer == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_wl_buffer *buffer = calloc(1, sizeof(struct wlr_wl_buffer));
|
||||||
|
if (buffer == NULL) {
|
||||||
|
wl_buffer_destroy(wl_buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
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 =
|
struct wlr_wl_output *output =
|
||||||
get_wl_output_from_output(wlr_output);
|
get_wl_output_from_output(wlr_output);
|
||||||
return wlr_egl_make_current(&output->backend->egl, output->egl_surface,
|
|
||||||
buffer_age);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
|
||||||
|
assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) &&
|
||||||
|
!test_buffer(output->backend, wlr_output->pending.buffer)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool output_commit(struct wlr_output *wlr_output) {
|
static bool output_commit(struct wlr_output *wlr_output) {
|
||||||
struct wlr_wl_output *output =
|
struct wlr_wl_output *output =
|
||||||
get_wl_output_from_output(wlr_output);
|
get_wl_output_from_output(wlr_output);
|
||||||
|
|
||||||
if (output->frame_callback != NULL) {
|
if (!output_test(wlr_output)) {
|
||||||
wlr_log(WLR_ERROR, "Skipping buffer swap");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
output->frame_callback = wl_surface_frame(output->surface);
|
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
|
||||||
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
|
if (!output_set_custom_mode(wlr_output,
|
||||||
|
wlr_output->pending.custom_mode.width,
|
||||||
pixman_region32_t *damage = NULL;
|
wlr_output->pending.custom_mode.height,
|
||||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) {
|
wlr_output->pending.custom_mode.refresh)) {
|
||||||
damage = &wlr_output->pending.damage;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wlr_egl_swap_buffers(&output->backend->egl,
|
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
|
||||||
output->egl_surface, damage)) {
|
struct wp_presentation_feedback *wp_feedback = NULL;
|
||||||
return false;
|
if (output->backend->presentation != NULL) {
|
||||||
|
wp_feedback = wp_presentation_feedback(output->backend->presentation,
|
||||||
|
output->surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
pixman_region32_t *damage = NULL;
|
||||||
|
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) {
|
||||||
|
damage = &wlr_output->pending.damage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output->frame_callback != NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "Skipping buffer swap");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
output->frame_callback = wl_surface_frame(output->surface);
|
||||||
|
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
|
||||||
|
|
||||||
|
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));
|
||||||
|
if (feedback == NULL) {
|
||||||
|
wp_presentation_feedback_destroy(wp_feedback);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
feedback->output = output;
|
||||||
|
feedback->feedback = wp_feedback;
|
||||||
|
feedback->commit_seq = output->wlr_output.commit_seq + 1;
|
||||||
|
wl_list_insert(&output->presentation_feedbacks, &feedback->link);
|
||||||
|
|
||||||
|
wp_presentation_feedback_add_listener(wp_feedback,
|
||||||
|
&presentation_feedback_listener, feedback);
|
||||||
|
} else {
|
||||||
|
struct wlr_output_event_present present_event = {
|
||||||
|
.commit_seq = wlr_output->commit_seq + 1,
|
||||||
|
.presented = true,
|
||||||
|
};
|
||||||
|
wlr_output_send_present(wlr_output, &present_event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: if available, use the presentation-time protocol
|
wl_display_flush(output->backend->remote_display);
|
||||||
wlr_output_send_present(wlr_output, NULL);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool output_set_cursor(struct wlr_output *wlr_output,
|
static bool output_set_cursor(struct wlr_output *wlr_output,
|
||||||
struct wlr_texture *texture, int32_t scale,
|
struct wlr_buffer *wlr_buffer, int hotspot_x, int hotspot_y) {
|
||||||
enum wl_output_transform transform,
|
|
||||||
int32_t hotspot_x, int32_t hotspot_y, bool update_texture) {
|
|
||||||
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
||||||
struct wlr_wl_backend *backend = output->backend;
|
struct wlr_wl_backend *backend = output->backend;
|
||||||
|
|
||||||
struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y };
|
output->cursor.hotspot_x = hotspot_x;
|
||||||
wlr_box_transform(&hotspot, &hotspot,
|
output->cursor.hotspot_y = hotspot_y;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (output->cursor.surface == NULL) {
|
if (output->cursor.surface == NULL) {
|
||||||
output->cursor.surface =
|
output->cursor.surface =
|
||||||
|
@ -114,53 +368,37 @@ static bool output_set_cursor(struct wlr_output *wlr_output,
|
||||||
}
|
}
|
||||||
struct wl_surface *surface = output->cursor.surface;
|
struct wl_surface *surface = output->cursor.surface;
|
||||||
|
|
||||||
if (texture != NULL) {
|
if (wlr_buffer != NULL) {
|
||||||
int width, height;
|
struct wlr_wl_buffer *buffer =
|
||||||
wlr_texture_get_size(texture, &width, &height);
|
get_or_create_wl_buffer(output->backend, wlr_buffer);
|
||||||
width = width * wlr_output->scale / scale;
|
if (buffer == NULL) {
|
||||||
height = height * wlr_output->scale / scale;
|
return false;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
wl_egl_window_resize(output->cursor.egl_window, width, height, 0, 0);
|
|
||||||
|
|
||||||
EGLSurface egl_surface =
|
wl_surface_attach(surface, buffer->wl_buffer, 0, 0);
|
||||||
wlr_egl_create_surface(&backend->egl, output->cursor.egl_window);
|
wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX);
|
||||||
|
wl_surface_commit(surface);
|
||||||
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);
|
|
||||||
} else {
|
} else {
|
||||||
wl_surface_attach(surface, NULL, 0, 0);
|
wl_surface_attach(surface, NULL, 0, 0);
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
update_wl_output_cursor(output);
|
update_wl_output_cursor(output);
|
||||||
|
wl_display_flush(backend->remote_display);
|
||||||
return true;
|
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) {
|
static void output_destroy(struct wlr_output *wlr_output) {
|
||||||
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
||||||
if (output == NULL) {
|
if (output == NULL) {
|
||||||
|
@ -169,9 +407,6 @@ static void output_destroy(struct wlr_output *wlr_output) {
|
||||||
|
|
||||||
wl_list_remove(&output->link);
|
wl_list_remove(&output->link);
|
||||||
|
|
||||||
if (output->cursor.egl_window != NULL) {
|
|
||||||
wl_egl_window_destroy(output->cursor.egl_window);
|
|
||||||
}
|
|
||||||
if (output->cursor.surface) {
|
if (output->cursor.surface) {
|
||||||
wl_surface_destroy(output->cursor.surface);
|
wl_surface_destroy(output->cursor.surface);
|
||||||
}
|
}
|
||||||
|
@ -180,20 +415,28 @@ static void output_destroy(struct wlr_output *wlr_output) {
|
||||||
wl_callback_destroy(output->frame_callback);
|
wl_callback_destroy(output->frame_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface);
|
struct wlr_wl_presentation_feedback *feedback, *feedback_tmp;
|
||||||
wl_egl_window_destroy(output->egl_window);
|
wl_list_for_each_safe(feedback, feedback_tmp,
|
||||||
|
&output->presentation_feedbacks, link) {
|
||||||
|
presentation_feedback_destroy(feedback);
|
||||||
|
}
|
||||||
|
|
||||||
if (output->zxdg_toplevel_decoration_v1) {
|
if (output->zxdg_toplevel_decoration_v1) {
|
||||||
zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1);
|
zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1);
|
||||||
}
|
}
|
||||||
xdg_toplevel_destroy(output->xdg_toplevel);
|
xdg_toplevel_destroy(output->xdg_toplevel);
|
||||||
xdg_surface_destroy(output->xdg_surface);
|
xdg_surface_destroy(output->xdg_surface);
|
||||||
wl_surface_destroy(output->surface);
|
wl_surface_destroy(output->surface);
|
||||||
|
wl_display_flush(output->backend->remote_display);
|
||||||
free(output);
|
free(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_wl_output_cursor(struct wlr_wl_output *output) {
|
void update_wl_output_cursor(struct wlr_wl_output *output) {
|
||||||
if (output->backend->pointer && output->enter_serial) {
|
struct wlr_wl_pointer *pointer = output->cursor.pointer;
|
||||||
wl_pointer_set_cursor(output->backend->pointer, output->enter_serial,
|
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.surface, output->cursor.hotspot_x,
|
||||||
output->cursor.hotspot_y);
|
output->cursor.hotspot_y);
|
||||||
}
|
}
|
||||||
|
@ -204,28 +447,14 @@ static bool output_move_cursor(struct wlr_output *_output, int x, int y) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool output_schedule_frame(struct wlr_output *wlr_output) {
|
|
||||||
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
|
||||||
|
|
||||||
if (output->frame_callback != NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "Skipping frame scheduling");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
output->frame_callback = wl_surface_frame(output->surface);
|
|
||||||
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
|
|
||||||
wl_surface_commit(output->surface);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wlr_output_impl output_impl = {
|
static const struct wlr_output_impl output_impl = {
|
||||||
.set_custom_mode = output_set_custom_mode,
|
|
||||||
.destroy = output_destroy,
|
.destroy = output_destroy,
|
||||||
.attach_render = output_attach_render,
|
.test = output_test,
|
||||||
.commit = output_commit,
|
.commit = output_commit,
|
||||||
.set_cursor = output_set_cursor,
|
.set_cursor = output_set_cursor,
|
||||||
.move_cursor = output_move_cursor,
|
.move_cursor = output_move_cursor,
|
||||||
.schedule_frame = output_schedule_frame,
|
.get_cursor_formats = output_get_formats,
|
||||||
|
.get_primary_formats = output_get_formats,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool wlr_output_is_wl(struct wlr_output *wlr_output) {
|
bool wlr_output_is_wl(struct wlr_output *wlr_output) {
|
||||||
|
@ -242,7 +471,7 @@ static void xdg_surface_handle_configure(void *data,
|
||||||
// nothing else?
|
// nothing else?
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct xdg_surface_listener xdg_surface_listener = {
|
static const struct xdg_surface_listener xdg_surface_listener = {
|
||||||
.configure = xdg_surface_handle_configure,
|
.configure = xdg_surface_handle_configure,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -264,10 +493,10 @@ static void xdg_toplevel_handle_close(void *data,
|
||||||
struct wlr_wl_output *output = data;
|
struct wlr_wl_output *output = data;
|
||||||
assert(output && output->xdg_toplevel == xdg_toplevel);
|
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,
|
.configure = xdg_toplevel_handle_configure,
|
||||||
.close = xdg_toplevel_handle_close,
|
.close = xdg_toplevel_handle_close,
|
||||||
};
|
};
|
||||||
|
@ -291,10 +520,18 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) {
|
||||||
wlr_output_update_custom_mode(wlr_output, 1280, 720, 0);
|
wlr_output_update_custom_mode(wlr_output, 1280, 720, 0);
|
||||||
strncpy(wlr_output->make, "wayland", sizeof(wlr_output->make));
|
strncpy(wlr_output->make, "wayland", sizeof(wlr_output->make));
|
||||||
strncpy(wlr_output->model, "wayland", sizeof(wlr_output->model));
|
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 %zu", backend->last_output_num);
|
||||||
|
wlr_output_set_description(wlr_output, description);
|
||||||
|
|
||||||
output->backend = backend;
|
output->backend = backend;
|
||||||
|
wl_list_init(&output->presentation_feedbacks);
|
||||||
|
|
||||||
output->surface = wl_compositor_create_surface(backend->compositor);
|
output->surface = wl_compositor_create_surface(backend->compositor);
|
||||||
if (!output->surface) {
|
if (!output->surface) {
|
||||||
|
@ -336,40 +573,29 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) {
|
||||||
&xdg_toplevel_listener, output);
|
&xdg_toplevel_listener, output);
|
||||||
wl_surface_commit(output->surface);
|
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);
|
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);
|
wl_list_insert(&backend->outputs, &output->link);
|
||||||
wlr_output_update_enabled(wlr_output, true);
|
wlr_output_update_enabled(wlr_output, true);
|
||||||
|
|
||||||
wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output);
|
wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output);
|
||||||
|
|
||||||
if (backend->pointer != NULL) {
|
struct wlr_wl_seat *seat;
|
||||||
create_wl_pointer(backend->pointer, output);
|
wl_list_for_each(seat, &backend->seats, link) {
|
||||||
|
if (seat->pointer) {
|
||||||
|
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;
|
return wlr_output;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
@ -389,4 +615,10 @@ void wlr_wl_output_set_title(struct wlr_output *output, const char *title) {
|
||||||
}
|
}
|
||||||
|
|
||||||
xdg_toplevel_set_title(wl_output->xdg_toplevel, 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) {
|
||||||
|
struct wlr_wl_output *wl_output = get_wl_output_from_output(output);
|
||||||
|
return wl_output->surface;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,935 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include <wlr/interfaces/wlr_input_device.h>
|
||||||
|
#include <wlr/interfaces/wlr_keyboard.h>
|
||||||
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
|
#include <wlr/interfaces/wlr_pointer.h>
|
||||||
|
#include <wlr/interfaces/wlr_touch.h>
|
||||||
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
|
#include "pointer-gestures-unstable-v1-client-protocol.h"
|
||||||
|
#include "relative-pointer-unstable-v1-client-protocol.h"
|
||||||
|
#include "backend/wayland.h"
|
||||||
|
#include "util/signal.h"
|
||||||
|
#include "util/time.h"
|
||||||
|
|
||||||
|
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(dev->wlr_input_device.pointer);
|
||||||
|
if (pointer->output == output && pointer->wl_pointer == wl_pointer) {
|
||||||
|
return pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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, 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;
|
||||||
|
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_seat *seat = data;
|
||||||
|
if (surface == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_wl_output *output = wl_surface_get_user_data(surface);
|
||||||
|
assert(output);
|
||||||
|
|
||||||
|
if (seat->active_pointer != NULL &&
|
||||||
|
seat->active_pointer->output == output) {
|
||||||
|
seat->active_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_seat *seat = data;
|
||||||
|
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
||||||
|
if (pointer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_output *wlr_output = &pointer->output->wlr_output;
|
||||||
|
struct wlr_event_pointer_motion_absolute event = {
|
||||||
|
.device = &pointer->input_device->wlr_input_device,
|
||||||
|
.time_msec = time,
|
||||||
|
.x = wl_fixed_to_double(sx) / wlr_output->width,
|
||||||
|
.y = wl_fixed_to_double(sy) / wlr_output->height,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&pointer->wlr_pointer.events.motion_absolute, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_seat *seat = data;
|
||||||
|
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
||||||
|
if (pointer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_event_pointer_button event = {
|
||||||
|
.device = &pointer->input_device->wlr_input_device,
|
||||||
|
.button = button,
|
||||||
|
.state = state,
|
||||||
|
.time_msec = time,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&pointer->wlr_pointer.events.button, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_seat *seat = data;
|
||||||
|
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
||||||
|
if (pointer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_event_pointer_axis event = {
|
||||||
|
.device = &pointer->input_device->wlr_input_device,
|
||||||
|
.delta = wl_fixed_to_double(value),
|
||||||
|
.delta_discrete = pointer->axis_discrete,
|
||||||
|
.orientation = axis,
|
||||||
|
.time_msec = time,
|
||||||
|
.source = pointer->axis_source,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&pointer->wlr_pointer.events.axis, &event);
|
||||||
|
|
||||||
|
pointer->axis_discrete = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
|
||||||
|
struct wlr_wl_seat *seat = data;
|
||||||
|
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
||||||
|
if (pointer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_signal_emit_safe(&pointer->wlr_pointer.events.frame,
|
||||||
|
&pointer->wlr_pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_axis_source(void *data,
|
||||||
|
struct wl_pointer *wl_pointer, uint32_t axis_source) {
|
||||||
|
struct wlr_wl_seat *seat = data;
|
||||||
|
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
||||||
|
if (pointer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer->axis_source = axis_source;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer,
|
||||||
|
uint32_t time, uint32_t axis) {
|
||||||
|
struct wlr_wl_seat *seat = data;
|
||||||
|
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
||||||
|
if (pointer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_event_pointer_axis event = {
|
||||||
|
.device = &pointer->input_device->wlr_input_device,
|
||||||
|
.delta = 0,
|
||||||
|
.delta_discrete = 0,
|
||||||
|
.orientation = axis,
|
||||||
|
.time_msec = time,
|
||||||
|
.source = pointer->axis_source,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&pointer->wlr_pointer.events.axis, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_axis_discrete(void *data,
|
||||||
|
struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
|
||||||
|
struct wlr_wl_seat *seat = data;
|
||||||
|
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
||||||
|
if (pointer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer->axis_discrete = discrete;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_pointer_listener pointer_listener = {
|
||||||
|
.enter = pointer_handle_enter,
|
||||||
|
.leave = pointer_handle_leave,
|
||||||
|
.motion = pointer_handle_motion,
|
||||||
|
.button = pointer_handle_button,
|
||||||
|
.axis = pointer_handle_axis,
|
||||||
|
.frame = pointer_handle_frame,
|
||||||
|
.axis_source = pointer_handle_axis_source,
|
||||||
|
.axis_stop = pointer_handle_axis_stop,
|
||||||
|
.axis_discrete = pointer_handle_axis_discrete,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
uint32_t format, int32_t fd, uint32_t size) {
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
|
||||||
|
struct wlr_input_device *dev = data;
|
||||||
|
|
||||||
|
uint32_t time = get_current_time_msec();
|
||||||
|
|
||||||
|
uint32_t *keycode_ptr;
|
||||||
|
wl_array_for_each(keycode_ptr, keys) {
|
||||||
|
struct wlr_event_keyboard_key event = {
|
||||||
|
.keycode = *keycode_ptr,
|
||||||
|
.state = WL_KEYBOARD_KEY_STATE_PRESSED,
|
||||||
|
.time_msec = time,
|
||||||
|
.update_state = false,
|
||||||
|
};
|
||||||
|
wlr_keyboard_notify_key(dev->keyboard, &event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
uint32_t serial, struct wl_surface *surface) {
|
||||||
|
struct wlr_input_device *dev = data;
|
||||||
|
|
||||||
|
uint32_t time = get_current_time_msec();
|
||||||
|
|
||||||
|
size_t num_keycodes = dev->keyboard->num_keycodes;
|
||||||
|
uint32_t pressed[num_keycodes + 1];
|
||||||
|
memcpy(pressed, dev->keyboard->keycodes,
|
||||||
|
num_keycodes * sizeof(uint32_t));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_keycodes; ++i) {
|
||||||
|
uint32_t keycode = pressed[i];
|
||||||
|
|
||||||
|
struct wlr_event_keyboard_key event = {
|
||||||
|
.keycode = keycode,
|
||||||
|
.state = WL_KEYBOARD_KEY_STATE_RELEASED,
|
||||||
|
.time_msec = time,
|
||||||
|
.update_state = false,
|
||||||
|
};
|
||||||
|
wlr_keyboard_notify_key(dev->keyboard, &event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
|
||||||
|
struct wlr_input_device *dev = data;
|
||||||
|
assert(dev && dev->keyboard);
|
||||||
|
|
||||||
|
struct wlr_event_keyboard_key wlr_event = {
|
||||||
|
.keycode = key,
|
||||||
|
.state = state,
|
||||||
|
.time_msec = time,
|
||||||
|
.update_state = false,
|
||||||
|
};
|
||||||
|
wlr_keyboard_notify_key(dev->keyboard, &wlr_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
|
||||||
|
uint32_t mods_locked, uint32_t group) {
|
||||||
|
struct wlr_input_device *dev = data;
|
||||||
|
assert(dev && dev->keyboard);
|
||||||
|
wlr_keyboard_notify_modifiers(dev->keyboard, mods_depressed, mods_latched,
|
||||||
|
mods_locked, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_handle_repeat_info(void *data,
|
||||||
|
struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) {
|
||||||
|
// This space is intentionally left blank
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_keyboard_listener keyboard_listener = {
|
||||||
|
.keymap = keyboard_handle_keymap,
|
||||||
|
.enter = keyboard_handle_enter,
|
||||||
|
.leave = keyboard_handle_leave,
|
||||||
|
.key = keyboard_handle_key,
|
||||||
|
.modifiers = keyboard_handle_modifiers,
|
||||||
|
.repeat_info = keyboard_handle_repeat_info
|
||||||
|
};
|
||||||
|
|
||||||
|
static void touch_coordinates_to_absolute(struct wlr_wl_input_device *device,
|
||||||
|
wl_fixed_t x, wl_fixed_t y, double *sx, double *sy) {
|
||||||
|
// TODO: each output needs its own touch
|
||||||
|
struct wlr_wl_output *output, *tmp;
|
||||||
|
wl_list_for_each_safe(output, tmp, &device->backend->outputs, link) {
|
||||||
|
*sx = wl_fixed_to_double(x) / output->wlr_output.width;
|
||||||
|
*sy = wl_fixed_to_double(y) / output->wlr_output.height;
|
||||||
|
return; // Choose the first output in the list
|
||||||
|
}
|
||||||
|
|
||||||
|
*sx = *sy = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void touch_handle_down(void *data, struct wl_touch *wl_touch,
|
||||||
|
uint32_t serial, uint32_t time, struct wl_surface *surface,
|
||||||
|
int32_t id, wl_fixed_t x, wl_fixed_t y) {
|
||||||
|
struct wlr_wl_input_device *device = data;
|
||||||
|
assert(device && device->wlr_input_device.touch);
|
||||||
|
|
||||||
|
double sx, sy;
|
||||||
|
touch_coordinates_to_absolute(device, x, y, &sx, &sy);
|
||||||
|
struct wlr_event_touch_down event = {
|
||||||
|
.device = &device->wlr_input_device,
|
||||||
|
.time_msec = time,
|
||||||
|
.touch_id = id,
|
||||||
|
.x = sx,
|
||||||
|
.y = sy
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&device->wlr_input_device.touch->events.down, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void touch_handle_up(void *data, struct wl_touch *wl_touch,
|
||||||
|
uint32_t serial, uint32_t time, int32_t id) {
|
||||||
|
struct wlr_wl_input_device *device = data;
|
||||||
|
assert(device && device->wlr_input_device.touch);
|
||||||
|
|
||||||
|
struct wlr_event_touch_up event = {
|
||||||
|
.device = &device->wlr_input_device,
|
||||||
|
.time_msec = time,
|
||||||
|
.touch_id = id,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&device->wlr_input_device.touch->events.up, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void touch_handle_motion(void *data, struct wl_touch *wl_touch,
|
||||||
|
uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) {
|
||||||
|
struct wlr_wl_input_device *device = data;
|
||||||
|
assert(device && device->wlr_input_device.touch);
|
||||||
|
|
||||||
|
double sx, sy;
|
||||||
|
touch_coordinates_to_absolute(device, x, y, &sx, &sy);
|
||||||
|
struct wlr_event_touch_motion event = {
|
||||||
|
.device = &device->wlr_input_device,
|
||||||
|
.time_msec = time,
|
||||||
|
.touch_id = id,
|
||||||
|
.x = sx,
|
||||||
|
.y = sy
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&device->wlr_input_device.touch->events.motion, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void touch_handle_frame(void *data, struct wl_touch *wl_touch) {
|
||||||
|
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) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
static void touch_handle_shape(void *data, struct wl_touch *wl_touch,
|
||||||
|
int32_t id, wl_fixed_t major, wl_fixed_t minor) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
static void touch_handle_orientation(void *data, struct wl_touch *wl_touch,
|
||||||
|
int32_t id, wl_fixed_t orientation) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_touch_listener touch_listener = {
|
||||||
|
.down = touch_handle_down,
|
||||||
|
.up = touch_handle_up,
|
||||||
|
.motion = touch_handle_motion,
|
||||||
|
.frame = touch_handle_frame,
|
||||||
|
.cancel = touch_handle_cancel,
|
||||||
|
.shape = touch_handle_shape,
|
||||||
|
.orientation = touch_handle_orientation,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct wlr_wl_input_device *get_wl_input_device_from_input_device(
|
||||||
|
struct wlr_input_device *wlr_dev) {
|
||||||
|
assert(wlr_input_device_is_wl(wlr_dev));
|
||||||
|
return (struct wlr_wl_input_device *)wlr_dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl) {
|
||||||
|
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;
|
||||||
|
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, *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);
|
||||||
|
|
||||||
|
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->seat);
|
||||||
|
return dev->seat;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void input_device_destroy(struct wlr_input_device *wlr_dev) {
|
||||||
|
struct wlr_wl_input_device *dev =
|
||||||
|
get_wl_input_device_from_input_device(wlr_dev);
|
||||||
|
if (dev->wlr_input_device.type == WLR_INPUT_DEVICE_KEYBOARD) {
|
||||||
|
struct wlr_wl_seat *seat = input_device_get_seat(wlr_dev);
|
||||||
|
wl_keyboard_release(seat->keyboard);
|
||||||
|
seat->keyboard = NULL;
|
||||||
|
}
|
||||||
|
// We can't destroy pointer here because we might have multiple devices
|
||||||
|
// exposing it to compositor.
|
||||||
|
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_wl(struct wlr_input_device *dev) {
|
||||||
|
return dev->impl == &input_device_impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_wl_input_device *create_wl_input_device(
|
||||||
|
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 = seat->backend;
|
||||||
|
dev->seat = seat;
|
||||||
|
|
||||||
|
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
||||||
|
|
||||||
|
unsigned int vendor = 0, product = 0;
|
||||||
|
|
||||||
|
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(&seat->backend->devices, &dev->link);
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
return (struct wlr_wl_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->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 const struct wlr_pointer_impl pointer_impl = {
|
||||||
|
.destroy = pointer_destroy,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void gesture_swipe_begin(void *data,
|
||||||
|
struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_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_swipe_begin wlr_event = {
|
||||||
|
.device = wlr_dev,
|
||||||
|
.time_msec = time,
|
||||||
|
.fingers = fingers,
|
||||||
|
};
|
||||||
|
input_device->fingers = fingers;
|
||||||
|
wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_begin, &wlr_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gesture_swipe_update(void *data,
|
||||||
|
struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1,
|
||||||
|
uint32_t time, wl_fixed_t dx, wl_fixed_t dy) {
|
||||||
|
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_swipe_update wlr_event = {
|
||||||
|
.device = wlr_dev,
|
||||||
|
.time_msec = time,
|
||||||
|
.fingers = input_device->fingers,
|
||||||
|
.dx = wl_fixed_to_double(dx),
|
||||||
|
.dy = wl_fixed_to_double(dy),
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_update, &wlr_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gesture_swipe_end(void *data,
|
||||||
|
struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_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_swipe_end wlr_event = {
|
||||||
|
.device = wlr_dev,
|
||||||
|
.time_msec = time,
|
||||||
|
.cancelled = cancelled,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_end, &wlr_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwp_pointer_gesture_swipe_v1_listener gesture_swipe_impl = {
|
||||||
|
.begin = gesture_swipe_begin,
|
||||||
|
.update = gesture_swipe_update,
|
||||||
|
.end = gesture_swipe_end,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void gesture_pinch_begin(void *data,
|
||||||
|
struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_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_pinch_begin wlr_event = {
|
||||||
|
.device = wlr_dev,
|
||||||
|
.time_msec = time,
|
||||||
|
.fingers = fingers,
|
||||||
|
};
|
||||||
|
input_device->fingers = fingers;
|
||||||
|
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_begin, &wlr_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gesture_pinch_update(void *data,
|
||||||
|
struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1,
|
||||||
|
uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) {
|
||||||
|
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_pinch_update wlr_event = {
|
||||||
|
.device = wlr_dev,
|
||||||
|
.time_msec = time,
|
||||||
|
.fingers = input_device->fingers,
|
||||||
|
.dx = wl_fixed_to_double(dx),
|
||||||
|
.dy = wl_fixed_to_double(dy),
|
||||||
|
.scale = wl_fixed_to_double(scale),
|
||||||
|
.rotation = wl_fixed_to_double(rotation),
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_update, &wlr_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gesture_pinch_end(void *data,
|
||||||
|
struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_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_pinch_end wlr_event = {
|
||||||
|
.device = wlr_dev,
|
||||||
|
.time_msec = time,
|
||||||
|
.cancelled = cancelled,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_end, &wlr_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
uint32_t utime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel,
|
||||||
|
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->seat->active_pointer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t time_usec = (uint64_t)utime_hi << 32 | utime_lo;
|
||||||
|
|
||||||
|
struct wlr_event_pointer_motion wlr_event = {
|
||||||
|
.device = wlr_dev,
|
||||||
|
.time_msec = (uint32_t)(time_usec / 1000),
|
||||||
|
.delta_x = wl_fixed_to_double(dx),
|
||||||
|
.delta_y = wl_fixed_to_double(dy),
|
||||||
|
.unaccel_dx = wl_fixed_to_double(dx_unaccel),
|
||||||
|
.unaccel_dy = wl_fixed_to_double(dy_unaccel),
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&wlr_dev->pointer->events.motion, &wlr_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
|
||||||
|
.relative_motion = relative_pointer_handle_relative_motion,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void pointer_handle_output_destroy(struct wl_listener *listener,
|
||||||
|
void *data) {
|
||||||
|
struct wlr_wl_pointer *pointer =
|
||||||
|
wl_container_of(listener, pointer, output_destroy);
|
||||||
|
wlr_input_device_destroy(&pointer->input_device->wlr_input_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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));
|
||||||
|
if (pointer == NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "Allocation failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pointer->wl_pointer = wl_pointer;
|
||||||
|
pointer->output = output; // we need output to map absolute coordinates onto
|
||||||
|
|
||||||
|
struct wlr_wl_input_device *dev =
|
||||||
|
create_wl_input_device(seat, WLR_INPUT_DEVICE_POINTER);
|
||||||
|
if (dev == NULL) {
|
||||||
|
free(pointer);
|
||||||
|
wlr_log(WLR_ERROR, "Allocation failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pointer->input_device = dev;
|
||||||
|
|
||||||
|
wl_signal_add(&output->wlr_output.events.destroy, &pointer->output_destroy);
|
||||||
|
pointer->output_destroy.notify = pointer_handle_output_destroy;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
pointer->relative_pointer =
|
||||||
|
zwp_relative_pointer_manager_v1_get_relative_pointer(
|
||||||
|
backend->zwp_relative_pointer_manager_v1, wl_pointer);
|
||||||
|
zwp_relative_pointer_v1_add_listener(pointer->relative_pointer,
|
||||||
|
&relative_pointer_listener, dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 wlr_wl_seat *seat) {
|
||||||
|
assert(seat->keyboard);
|
||||||
|
struct wl_keyboard *wl_keyboard = seat->keyboard;
|
||||||
|
struct wlr_wl_input_device *dev =
|
||||||
|
create_wl_input_device(seat, WLR_INPUT_DEVICE_KEYBOARD);
|
||||||
|
if (!dev) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
||||||
|
|
||||||
|
wlr_dev->keyboard = calloc(1, sizeof(*wlr_dev->keyboard));
|
||||||
|
if (!wlr_dev->keyboard) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
|
wlr_input_device_destroy(wlr_dev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wlr_keyboard_init(wlr_dev->keyboard, NULL);
|
||||||
|
|
||||||
|
wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, wlr_dev);
|
||||||
|
wlr_signal_emit_safe(&seat->backend->backend.events.new_input, wlr_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(seat, WLR_INPUT_DEVICE_TOUCH);
|
||||||
|
if (!dev) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
||||||
|
|
||||||
|
wlr_dev->touch = calloc(1, sizeof(*wlr_dev->touch));
|
||||||
|
if (!wlr_dev->touch) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
|
wlr_input_device_destroy(wlr_dev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wlr_touch_init(wlr_dev->touch, NULL);
|
||||||
|
|
||||||
|
wl_touch_add_listener(wl_touch, &touch_listener, 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_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);
|
||||||
|
|
||||||
|
struct wl_pointer *wl_pointer = wl_seat_get_pointer(wl_seat);
|
||||||
|
seat->pointer = wl_pointer;
|
||||||
|
|
||||||
|
struct wlr_wl_output *output;
|
||||||
|
wl_list_for_each(output, &backend->outputs, link) {
|
||||||
|
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 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->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);
|
||||||
|
seat->pointer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->keyboard == NULL) {
|
||||||
|
wlr_log(WLR_DEBUG, "seat %p offered keyboard", (void *)wl_seat);
|
||||||
|
|
||||||
|
struct wl_keyboard *wl_keyboard = wl_seat_get_keyboard(wl_seat);
|
||||||
|
seat->keyboard = wl_keyboard;
|
||||||
|
|
||||||
|
if (backend->started) {
|
||||||
|
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_wl_input_device *device, *tmp;
|
||||||
|
wl_list_for_each_safe(device, tmp, &backend->devices, link) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((caps & WL_SEAT_CAPABILITY_TOUCH) && seat->touch == NULL) {
|
||||||
|
wlr_log(WLR_DEBUG, "seat %p offered touch", (void *)wl_seat);
|
||||||
|
|
||||||
|
seat->touch = wl_seat_get_touch(wl_seat);
|
||||||
|
if (backend->started) {
|
||||||
|
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_wl_input_device *device, *tmp;
|
||||||
|
wl_list_for_each_safe(device, tmp, &backend->devices, link) {
|
||||||
|
if (device->wlr_input_device.type == WLR_INPUT_DEVICE_TOUCH) {
|
||||||
|
wlr_input_device_destroy(&device->wlr_input_device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_touch_release(seat->touch);
|
||||||
|
seat->touch = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void seat_handle_name(void *data, struct wl_seat *wl_seat,
|
||||||
|
const char *name) {
|
||||||
|
struct wlr_wl_seat *seat = data;
|
||||||
|
free(seat->name);
|
||||||
|
seat->name = strdup(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct wl_seat_listener seat_listener = {
|
||||||
|
.capabilities = seat_handle_capabilities,
|
||||||
|
.name = seat_handle_name,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wl_seat *wlr_wl_input_device_get_seat(struct wlr_input_device *wlr_dev) {
|
||||||
|
return input_device_get_seat(wlr_dev)->wl_seat;
|
||||||
|
}
|
|
@ -13,6 +13,7 @@
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
#include <wlr/interfaces/wlr_input_device.h>
|
||||||
|
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
#include "util/time.h"
|
||||||
#include "wlr/util/log.h"
|
#include "wlr/util/log.h"
|
||||||
#include "tablet-unstable-v2-client-protocol.h"
|
#include "tablet-unstable-v2-client-protocol.h"
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@ struct wlr_wl_tablet_tool {
|
||||||
struct wlr_tablet_tool wlr_tool;
|
struct wlr_tablet_tool wlr_tool;
|
||||||
|
|
||||||
/* semi-static */
|
/* semi-static */
|
||||||
|
struct wlr_wl_output *output;
|
||||||
struct wlr_wl_input_device *tablet;
|
struct wlr_wl_input_device *tablet;
|
||||||
double pre_x, pre_y;
|
double pre_x, pre_y;
|
||||||
|
|
||||||
|
@ -83,12 +85,6 @@ struct wlr_wl_tablet_pad_group {
|
||||||
struct wl_list strips; // wlr_wl_tablet_pad_strips::link
|
struct wl_list strips; // wlr_wl_tablet_pad_strips::link
|
||||||
};
|
};
|
||||||
|
|
||||||
static uint32_t get_current_time_msec(void) {
|
|
||||||
struct timespec now;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
||||||
return now.tv_nsec / (1000 * 1000) + now.tv_sec * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_ring_source(void *data,
|
static void handle_tablet_pad_ring_source(void *data,
|
||||||
struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2,
|
struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2,
|
||||||
uint32_t source) {
|
uint32_t source) {
|
||||||
|
@ -230,7 +226,7 @@ static void handle_tablet_pad_group_ring(void *data,
|
||||||
}
|
}
|
||||||
tablet_ring->index = group->pad->ring_count++;
|
tablet_ring->index = group->pad->ring_count++;
|
||||||
tablet_ring->group = group;
|
tablet_ring->group = group;
|
||||||
zwp_tablet_pad_ring_v2_add_listener(ring, &tablet_pad_ring_listener,
|
zwp_tablet_pad_ring_v2_add_listener(ring, &tablet_pad_ring_listener,
|
||||||
tablet_ring);
|
tablet_ring);
|
||||||
|
|
||||||
group->group.rings = realloc(group->group.rings,
|
group->group.rings = realloc(group->group.rings,
|
||||||
|
@ -251,7 +247,7 @@ static void handle_tablet_pad_group_strip(void *data,
|
||||||
}
|
}
|
||||||
tablet_strip->index = group->pad->strip_count++;
|
tablet_strip->index = group->pad->strip_count++;
|
||||||
tablet_strip->group = group;
|
tablet_strip->group = group;
|
||||||
zwp_tablet_pad_strip_v2_add_listener(strip, &tablet_pad_strip_listener,
|
zwp_tablet_pad_strip_v2_add_listener(strip, &tablet_pad_strip_listener,
|
||||||
tablet_strip);
|
tablet_strip);
|
||||||
|
|
||||||
group->group.strips = realloc(group->group.strips,
|
group->group.strips = realloc(group->group.strips,
|
||||||
|
@ -340,7 +336,8 @@ static void handle_tablet_pad_path(void *data,
|
||||||
struct wlr_wl_input_device *dev = data;
|
struct wlr_wl_input_device *dev = data;
|
||||||
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
|
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,
|
static void handle_tablet_pad_buttons(void *data,
|
||||||
|
@ -405,7 +402,7 @@ static void handle_tablet_pad_removed(void *data,
|
||||||
/* This doesn't free anything, but emits the destroy signal */
|
/* This doesn't free anything, but emits the destroy signal */
|
||||||
wlr_input_device_destroy(&dev->wlr_input_device);
|
wlr_input_device_destroy(&dev->wlr_input_device);
|
||||||
/* This is a bit ugly, but we need to remove it from our list */
|
/* 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;
|
struct wlr_wl_tablet_pad_group *group, *it;
|
||||||
wl_list_for_each_safe(group, it, &tablet_pad->groups, group.link) {
|
wl_list_for_each_safe(group, it, &tablet_pad->groups, group.link) {
|
||||||
|
@ -433,9 +430,9 @@ static void handle_pad_added(void *data,
|
||||||
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
|
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
|
||||||
struct zwp_tablet_pad_v2 *id) {
|
struct zwp_tablet_pad_v2 *id) {
|
||||||
wlr_log(WLR_DEBUG, "New tablet pad");
|
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(
|
struct wlr_wl_input_device *dev = create_wl_input_device(
|
||||||
backend, WLR_INPUT_DEVICE_TABLET_PAD);
|
seat, WLR_INPUT_DEVICE_TABLET_PAD);
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
/* This leaks a couple of server-sent resource ids. iirc this
|
/* This leaks a couple of server-sent resource ids. iirc this
|
||||||
* shouldn't ever be a problem, but it isn't exactly nice
|
* shouldn't ever be a problem, but it isn't exactly nice
|
||||||
|
@ -466,7 +463,6 @@ static void handle_tablet_tool_done(void *data,
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum wlr_tablet_tool_type tablet_type_to_wlr_type(enum zwp_tablet_tool_v2_type type) {
|
static enum wlr_tablet_tool_type tablet_type_to_wlr_type(enum zwp_tablet_tool_v2_type type) {
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ZWP_TABLET_TOOL_V2_TYPE_PEN:
|
case ZWP_TABLET_TOOL_V2_TYPE_PEN:
|
||||||
return WLR_TABLET_TOOL_TYPE_PEN;
|
return WLR_TABLET_TOOL_TYPE_PEN;
|
||||||
|
@ -482,11 +478,12 @@ static enum wlr_tablet_tool_type tablet_type_to_wlr_type(enum zwp_tablet_tool_v2
|
||||||
return WLR_TABLET_TOOL_TYPE_MOUSE;
|
return WLR_TABLET_TOOL_TYPE_MOUSE;
|
||||||
case ZWP_TABLET_TOOL_V2_TYPE_LENS:
|
case ZWP_TABLET_TOOL_V2_TYPE_LENS:
|
||||||
return WLR_TABLET_TOOL_TYPE_LENS;
|
return WLR_TABLET_TOOL_TYPE_LENS;
|
||||||
default:
|
case ZWP_TABLET_TOOL_V2_TYPE_FINGER:
|
||||||
break;
|
// unused, see:
|
||||||
|
// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/18
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
|
abort(); // unreachable
|
||||||
assert(false && "Unreachable");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_tablet_tool_type(void *data,
|
static void handle_tablet_tool_type(void *data,
|
||||||
|
@ -521,7 +518,7 @@ static void handle_tablet_tool_capability(void *data,
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
|
||||||
enum zwp_tablet_tool_v2_capability cap = capability;
|
enum zwp_tablet_tool_v2_capability cap = capability;
|
||||||
|
|
||||||
switch (cap) {
|
switch (cap) {
|
||||||
case ZWP_TABLET_TOOL_V2_CAPABILITY_TILT:
|
case ZWP_TABLET_TOOL_V2_CAPABILITY_TILT:
|
||||||
tool->wlr_tool.tilt = true;
|
tool->wlr_tool.tilt = true;
|
||||||
|
@ -551,12 +548,14 @@ static void handle_tablet_tool_proximity_in(void *data,
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
tool->is_in = true;
|
tool->is_in = true;
|
||||||
tool->tablet = zwp_tablet_v2_get_user_data(tablet_id);
|
tool->tablet = zwp_tablet_v2_get_user_data(tablet_id);
|
||||||
|
tool->output = wl_surface_get_user_data(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_tablet_tool_proximity_out(void *data,
|
static void handle_tablet_tool_proximity_out(void *data,
|
||||||
struct zwp_tablet_tool_v2 *id) {
|
struct zwp_tablet_tool_v2 *id) {
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
tool->is_out = true;
|
tool->is_out = true;
|
||||||
|
tool->output = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_tablet_tool_down(void *data,
|
static void handle_tablet_tool_down(void *data,
|
||||||
|
@ -576,8 +575,11 @@ static void handle_tablet_tool_motion(void *data,
|
||||||
struct zwp_tablet_tool_v2 *id,
|
struct zwp_tablet_tool_v2 *id,
|
||||||
wl_fixed_t x, wl_fixed_t y) {
|
wl_fixed_t x, wl_fixed_t y) {
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
tool->x = wl_fixed_to_double(x);
|
struct wlr_wl_output *output = tool->output;
|
||||||
tool->y = wl_fixed_to_double(y);
|
assert(output);
|
||||||
|
|
||||||
|
tool->x = wl_fixed_to_double(x) / output->wlr_output.width;
|
||||||
|
tool->y = wl_fixed_to_double(y) / output->wlr_output.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_tablet_tool_pressure(void *data,
|
static void handle_tablet_tool_pressure(void *data,
|
||||||
|
@ -674,7 +676,7 @@ static void handle_tablet_tool_frame(void *data,
|
||||||
.y = tool->y,
|
.y = tool->y,
|
||||||
.state = WLR_TABLET_TOOL_PROXIMITY_IN,
|
.state = WLR_TABLET_TOOL_PROXIMITY_IN,
|
||||||
};
|
};
|
||||||
|
|
||||||
wlr_signal_emit_safe(&tablet->events.proximity, &evt);
|
wlr_signal_emit_safe(&tablet->events.proximity, &evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -732,7 +734,7 @@ static void handle_tablet_tool_frame(void *data,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evt.updated_axes) {
|
if (evt.updated_axes) {
|
||||||
wlr_signal_emit_safe(&tablet->events.proximity, &evt);
|
wlr_signal_emit_safe(&tablet->events.axis, &evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -750,7 +752,7 @@ static void handle_tablet_tool_frame(void *data,
|
||||||
.y = tool->y,
|
.y = tool->y,
|
||||||
.state = WLR_TABLET_TOOL_TIP_DOWN,
|
.state = WLR_TABLET_TOOL_TIP_DOWN,
|
||||||
};
|
};
|
||||||
|
|
||||||
wlr_signal_emit_safe(&tablet->events.tip, &evt);
|
wlr_signal_emit_safe(&tablet->events.tip, &evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -763,7 +765,7 @@ static void handle_tablet_tool_frame(void *data,
|
||||||
.y = tool->y,
|
.y = tool->y,
|
||||||
.state = WLR_TABLET_TOOL_TIP_UP,
|
.state = WLR_TABLET_TOOL_TIP_UP,
|
||||||
};
|
};
|
||||||
|
|
||||||
wlr_signal_emit_safe(&tablet->events.tip, &evt);
|
wlr_signal_emit_safe(&tablet->events.tip, &evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,7 +778,7 @@ static void handle_tablet_tool_frame(void *data,
|
||||||
.y = tool->y,
|
.y = tool->y,
|
||||||
.state = WLR_TABLET_TOOL_PROXIMITY_OUT,
|
.state = WLR_TABLET_TOOL_PROXIMITY_OUT,
|
||||||
};
|
};
|
||||||
|
|
||||||
wlr_signal_emit_safe(&tablet->events.proximity, &evt);
|
wlr_signal_emit_safe(&tablet->events.proximity, &evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -853,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_wl_input_device *dev = data;
|
||||||
struct wlr_tablet *tablet = dev->wlr_input_device.tablet;
|
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) {
|
static void handle_tablet_done(void *data, struct zwp_tablet_v2 *zwp_tablet_v2) {
|
||||||
|
@ -870,7 +873,7 @@ static void handle_tablet_removed(void *data,
|
||||||
/* This doesn't free anything, but emits the destroy signal */
|
/* This doesn't free anything, but emits the destroy signal */
|
||||||
wlr_input_device_destroy(&dev->wlr_input_device);
|
wlr_input_device_destroy(&dev->wlr_input_device);
|
||||||
/* This is a bit ugly, but we need to remove it from our list */
|
/* 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);
|
zwp_tablet_v2_destroy(dev->resource);
|
||||||
free(dev);
|
free(dev);
|
||||||
|
@ -888,9 +891,9 @@ static void handle_tab_added(void *data,
|
||||||
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
|
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
|
||||||
struct zwp_tablet_v2 *id) {
|
struct zwp_tablet_v2 *id) {
|
||||||
wlr_log(WLR_DEBUG, "New tablet");
|
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(
|
struct wlr_wl_input_device *dev = create_wl_input_device(
|
||||||
backend, WLR_INPUT_DEVICE_TABLET_TOOL);
|
seat, WLR_INPUT_DEVICE_TABLET_TOOL);
|
||||||
|
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
zwp_tablet_v2_destroy(id);
|
zwp_tablet_v2_destroy(id);
|
||||||
|
@ -918,18 +921,18 @@ static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
|
||||||
|
|
||||||
struct wlr_wl_tablet_seat *wl_add_tablet_seat(
|
struct wlr_wl_tablet_seat *wl_add_tablet_seat(
|
||||||
struct zwp_tablet_manager_v2 *manager,
|
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 =
|
struct wlr_wl_tablet_seat *ret =
|
||||||
calloc(1, sizeof(struct wlr_wl_tablet_seat));
|
calloc(1, sizeof(struct wlr_wl_tablet_seat));
|
||||||
|
|
||||||
if (!(ret->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);
|
free(ret);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
zwp_tablet_seat_v2_add_listener(ret->tablet_seat,
|
zwp_tablet_seat_v2_add_listener(ret->tablet_seat,
|
||||||
&tablet_seat_listener, backend);
|
&tablet_seat_listener, seat);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,571 +0,0 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include <wayland-client.h>
|
|
||||||
|
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
|
||||||
#include <wlr/interfaces/wlr_keyboard.h>
|
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
|
||||||
#include <wlr/interfaces/wlr_pointer.h>
|
|
||||||
#include <wlr/interfaces/wlr_touch.h>
|
|
||||||
#include <wlr/util/log.h>
|
|
||||||
|
|
||||||
#include "pointer-gestures-unstable-v1-client-protocol.h"
|
|
||||||
#include "backend/wayland.h"
|
|
||||||
#include "util/signal.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) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer);
|
|
||||||
if (pointer->output == output) {
|
|
||||||
return pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
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);
|
|
||||||
|
|
||||||
output->enter_serial = serial;
|
|
||||||
backend->current_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;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
backend->current_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;
|
|
||||||
if (pointer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_output *wlr_output = &pointer->output->wlr_output;
|
|
||||||
struct wlr_event_pointer_motion_absolute event = {
|
|
||||||
.device = &pointer->input_device->wlr_input_device,
|
|
||||||
.time_msec = time,
|
|
||||||
.x = wl_fixed_to_double(sx) / wlr_output->width,
|
|
||||||
.y = wl_fixed_to_double(sy) / wlr_output->height,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&pointer->wlr_pointer.events.motion_absolute, &event);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
if (pointer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_event_pointer_button event = {
|
|
||||||
.device = &pointer->input_device->wlr_input_device,
|
|
||||||
.button = button,
|
|
||||||
.state = state,
|
|
||||||
.time_msec = time,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&pointer->wlr_pointer.events.button, &event);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
if (pointer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_event_pointer_axis event = {
|
|
||||||
.device = &pointer->input_device->wlr_input_device,
|
|
||||||
.delta = wl_fixed_to_double(value),
|
|
||||||
.delta_discrete = pointer->axis_discrete,
|
|
||||||
.orientation = axis,
|
|
||||||
.time_msec = time,
|
|
||||||
.source = pointer->axis_source,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&pointer->wlr_pointer.events.axis, &event);
|
|
||||||
|
|
||||||
pointer->axis_discrete = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
if (pointer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&pointer->wlr_pointer.events.frame,
|
|
||||||
&pointer->wlr_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;
|
|
||||||
if (pointer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pointer->axis_source = axis_source;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
if (pointer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_event_pointer_axis event = {
|
|
||||||
.device = &pointer->input_device->wlr_input_device,
|
|
||||||
.delta = 0,
|
|
||||||
.delta_discrete = 0,
|
|
||||||
.orientation = axis,
|
|
||||||
.time_msec = time,
|
|
||||||
.source = pointer->axis_source,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&pointer->wlr_pointer.events.axis, &event);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
if (pointer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pointer->axis_discrete = discrete;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_pointer_listener pointer_listener = {
|
|
||||||
.enter = pointer_handle_enter,
|
|
||||||
.leave = pointer_handle_leave,
|
|
||||||
.motion = pointer_handle_motion,
|
|
||||||
.button = pointer_handle_button,
|
|
||||||
.axis = pointer_handle_axis,
|
|
||||||
.frame = pointer_handle_frame,
|
|
||||||
.axis_source = pointer_handle_axis_source,
|
|
||||||
.axis_stop = pointer_handle_axis_stop,
|
|
||||||
.axis_discrete = pointer_handle_axis_discrete,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t format, int32_t fd, uint32_t size) {
|
|
||||||
// TODO: set keymap
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t get_current_time_msec(void) {
|
|
||||||
struct timespec now;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
||||||
return now.tv_nsec / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
|
|
||||||
struct wlr_input_device *dev = data;
|
|
||||||
|
|
||||||
uint32_t time = get_current_time_msec();
|
|
||||||
|
|
||||||
uint32_t *keycode_ptr;
|
|
||||||
wl_array_for_each(keycode_ptr, keys) {
|
|
||||||
struct wlr_event_keyboard_key event = {
|
|
||||||
.keycode = *keycode_ptr,
|
|
||||||
.state = WLR_KEY_PRESSED,
|
|
||||||
.time_msec = time,
|
|
||||||
.update_state = false,
|
|
||||||
};
|
|
||||||
wlr_keyboard_notify_key(dev->keyboard, &event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, struct wl_surface *surface) {
|
|
||||||
struct wlr_input_device *dev = data;
|
|
||||||
|
|
||||||
uint32_t time = get_current_time_msec();
|
|
||||||
|
|
||||||
uint32_t pressed[dev->keyboard->num_keycodes + 1];
|
|
||||||
memcpy(pressed, dev->keyboard->keycodes,
|
|
||||||
dev->keyboard->num_keycodes * sizeof(uint32_t));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < sizeof(pressed)/sizeof(pressed[0]); ++i) {
|
|
||||||
uint32_t keycode = pressed[i];
|
|
||||||
|
|
||||||
struct wlr_event_keyboard_key event = {
|
|
||||||
.keycode = keycode,
|
|
||||||
.state = WLR_KEY_RELEASED,
|
|
||||||
.time_msec = time,
|
|
||||||
.update_state = false,
|
|
||||||
};
|
|
||||||
wlr_keyboard_notify_key(dev->keyboard, &event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
|
|
||||||
struct wlr_input_device *dev = data;
|
|
||||||
assert(dev && dev->keyboard);
|
|
||||||
|
|
||||||
struct wlr_event_keyboard_key wlr_event = {
|
|
||||||
.keycode = key,
|
|
||||||
.state = state,
|
|
||||||
.time_msec = time,
|
|
||||||
.update_state = false,
|
|
||||||
};
|
|
||||||
wlr_keyboard_notify_key(dev->keyboard, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
|
|
||||||
uint32_t mods_locked, uint32_t group) {
|
|
||||||
struct wlr_input_device *dev = data;
|
|
||||||
assert(dev && dev->keyboard);
|
|
||||||
wlr_keyboard_notify_modifiers(dev->keyboard, mods_depressed, mods_latched,
|
|
||||||
mods_locked, group);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_handle_repeat_info(void *data,
|
|
||||||
struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) {
|
|
||||||
// This space is intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wl_keyboard_listener keyboard_listener = {
|
|
||||||
.keymap = keyboard_handle_keymap,
|
|
||||||
.enter = keyboard_handle_enter,
|
|
||||||
.leave = keyboard_handle_leave,
|
|
||||||
.key = keyboard_handle_key,
|
|
||||||
.modifiers = keyboard_handle_modifiers,
|
|
||||||
.repeat_info = keyboard_handle_repeat_info
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct wlr_wl_input_device *get_wl_input_device_from_input_device(
|
|
||||||
struct wlr_input_device *wlr_dev) {
|
|
||||||
assert(wlr_input_device_is_wl(wlr_dev));
|
|
||||||
return (struct wlr_wl_input_device *)wlr_dev;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void input_device_destroy(struct wlr_input_device *wlr_dev) {
|
|
||||||
struct wlr_wl_input_device *dev =
|
|
||||||
get_wl_input_device_from_input_device(wlr_dev);
|
|
||||||
if (dev->resource) {
|
|
||||||
wl_proxy_destroy(dev->resource);
|
|
||||||
}
|
|
||||||
wl_list_remove(&dev->wlr_input_device.link);
|
|
||||||
free(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_input_device_impl input_device_impl = {
|
|
||||||
.destroy = input_device_destroy,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool wlr_input_device_is_wl(struct wlr_input_device *dev) {
|
|
||||||
return dev->impl == &input_device_impl;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_wl_input_device *create_wl_input_device(
|
|
||||||
struct wlr_wl_backend *backend, 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;
|
|
||||||
|
|
||||||
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
|
||||||
|
|
||||||
unsigned int vendor = 0, product = 0;
|
|
||||||
const char *name = "wayland";
|
|
||||||
wlr_input_device_init(wlr_dev, type, &input_device_impl, name, vendor,
|
|
||||||
product);
|
|
||||||
wl_list_insert(&backend->devices, &wlr_dev->link);
|
|
||||||
return dev;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_pointer_impl pointer_impl;
|
|
||||||
|
|
||||||
struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer) {
|
|
||||||
assert(wlr_pointer->impl == &pointer_impl);
|
|
||||||
return (struct wlr_wl_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_list_remove(&pointer->output_destroy.link);
|
|
||||||
free(pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_pointer_impl pointer_impl = {
|
|
||||||
.destroy = pointer_destroy,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void gesture_swipe_begin(void *data,
|
|
||||||
struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_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_swipe_begin wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.fingers = fingers,
|
|
||||||
};
|
|
||||||
input_device->fingers = fingers;
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_begin, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gesture_swipe_update(void *data,
|
|
||||||
struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1,
|
|
||||||
uint32_t time, wl_fixed_t dx, wl_fixed_t dy) {
|
|
||||||
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_swipe_update wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.fingers = input_device->fingers,
|
|
||||||
.dx = wl_fixed_to_double(dx),
|
|
||||||
.dy = wl_fixed_to_double(dy),
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_update, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gesture_swipe_end(void *data,
|
|
||||||
struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_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_swipe_end wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.cancelled = cancelled,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_end, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct zwp_pointer_gesture_swipe_v1_listener gesture_swipe_impl = {
|
|
||||||
.begin = gesture_swipe_begin,
|
|
||||||
.update = gesture_swipe_update,
|
|
||||||
.end = gesture_swipe_end,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void gesture_pinch_begin(void *data,
|
|
||||||
struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_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_pinch_begin wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.fingers = fingers,
|
|
||||||
};
|
|
||||||
input_device->fingers = fingers;
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_begin, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gesture_pinch_update(void *data,
|
|
||||||
struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1,
|
|
||||||
uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) {
|
|
||||||
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_pinch_update wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.fingers = input_device->fingers,
|
|
||||||
.dx = wl_fixed_to_double(dx),
|
|
||||||
.dy = wl_fixed_to_double(dy),
|
|
||||||
.scale = wl_fixed_to_double(scale),
|
|
||||||
.rotation = wl_fixed_to_double(rotation),
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_update, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gesture_pinch_end(void *data,
|
|
||||||
struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_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_pinch_end wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.cancelled = cancelled,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_end, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct zwp_pointer_gesture_pinch_v1_listener gesture_pinch_impl = {
|
|
||||||
.begin = gesture_pinch_begin,
|
|
||||||
.update = gesture_pinch_update,
|
|
||||||
.end = gesture_pinch_end,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static void pointer_handle_output_destroy(struct wl_listener *listener,
|
|
||||||
void *data) {
|
|
||||||
struct wlr_wl_pointer *pointer =
|
|
||||||
wl_container_of(listener, pointer, output_destroy);
|
|
||||||
wlr_input_device_destroy(&pointer->input_device->wlr_input_device);
|
|
||||||
}
|
|
||||||
|
|
||||||
void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *output) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_wl_pointer *pointer = calloc(1, sizeof(struct wlr_wl_pointer));
|
|
||||||
if (pointer == NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "Allocation failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pointer->wl_pointer = wl_pointer;
|
|
||||||
pointer->output = output;
|
|
||||||
|
|
||||||
wl_signal_add(&output->wlr_output.events.destroy, &pointer->output_destroy);
|
|
||||||
pointer->output_destroy.notify = pointer_handle_output_destroy;
|
|
||||||
|
|
||||||
struct wlr_wl_input_device *dev =
|
|
||||||
create_wl_input_device(backend, WLR_INPUT_DEVICE_POINTER);
|
|
||||||
if (dev == NULL) {
|
|
||||||
free(pointer);
|
|
||||||
wlr_log(WLR_ERROR, "Allocation failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pointer->input_device = dev;
|
|
||||||
|
|
||||||
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) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
struct wlr_wl_input_device *dev =
|
|
||||||
create_wl_input_device(wl, WLR_INPUT_DEVICE_KEYBOARD);
|
|
||||||
if (!dev) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
|
||||||
|
|
||||||
wlr_dev->keyboard = calloc(1, sizeof(*wlr_dev->keyboard));
|
|
||||||
if (!wlr_dev->keyboard) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
|
||||||
free(dev);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wlr_keyboard_init(wlr_dev->keyboard, NULL);
|
|
||||||
|
|
||||||
wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, wlr_dev);
|
|
||||||
dev->resource = wl_keyboard;
|
|
||||||
wlr_signal_emit_safe(&wl->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;
|
|
||||||
assert(backend->seat == wl_seat);
|
|
||||||
|
|
||||||
if ((caps & WL_SEAT_CAPABILITY_POINTER)) {
|
|
||||||
wlr_log(WLR_DEBUG, "seat %p offered pointer", (void*) wl_seat);
|
|
||||||
|
|
||||||
struct wl_pointer *wl_pointer = wl_seat_get_pointer(wl_seat);
|
|
||||||
backend->pointer = wl_pointer;
|
|
||||||
|
|
||||||
struct wlr_wl_output *output;
|
|
||||||
wl_list_for_each(output, &backend->outputs, link) {
|
|
||||||
create_wl_pointer(wl_pointer, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_pointer_add_listener(wl_pointer, &pointer_listener, backend);
|
|
||||||
}
|
|
||||||
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
|
|
||||||
wlr_log(WLR_DEBUG, "seat %p offered keyboard", (void*) wl_seat);
|
|
||||||
|
|
||||||
struct wl_keyboard *wl_keyboard = wl_seat_get_keyboard(wl_seat);
|
|
||||||
backend->keyboard = wl_keyboard;
|
|
||||||
|
|
||||||
if (backend->started) {
|
|
||||||
create_wl_keyboard(wl_keyboard, backend);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void seat_handle_name(void *data, struct wl_seat *wl_seat,
|
|
||||||
const char *name) {
|
|
||||||
struct wlr_wl_backend *backend = data;
|
|
||||||
assert(backend->seat == wl_seat);
|
|
||||||
// Do we need to check if seatName was previously set for name change?
|
|
||||||
free(backend->seat_name);
|
|
||||||
backend->seat_name = strdup(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct wl_seat_listener seat_listener = {
|
|
||||||
.capabilities = seat_handle_capabilities,
|
|
||||||
.name = seat_handle_name,
|
|
||||||
};
|
|
|
@ -1,17 +1,24 @@
|
||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <xf86drm.h>
|
||||||
|
|
||||||
#include <wlr/config.h>
|
#include <wlr/config.h>
|
||||||
|
|
||||||
#include <X11/Xlib-xcb.h>
|
#include <drm_fourcc.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <xcb/xcb.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/xfixes.h>
|
||||||
#include <xcb/xinput.h>
|
#include <xcb/xinput.h>
|
||||||
|
|
||||||
|
@ -20,13 +27,27 @@
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
#include <wlr/interfaces/wlr_input_device.h>
|
||||||
#include <wlr/interfaces/wlr_keyboard.h>
|
#include <wlr/interfaces/wlr_keyboard.h>
|
||||||
#include <wlr/interfaces/wlr_pointer.h>
|
#include <wlr/interfaces/wlr_pointer.h>
|
||||||
#include <wlr/render/egl.h>
|
|
||||||
#include <wlr/render/wlr_renderer.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
#include "backend/x11.h"
|
#include "backend/x11.h"
|
||||||
|
#include "render/drm_format_set.h"
|
||||||
#include "util/signal.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_output *get_x11_output_from_window_id(
|
||||||
struct wlr_x11_backend *x11, xcb_window_t window) {
|
struct wlr_x11_backend *x11, xcb_window_t window) {
|
||||||
struct wlr_x11_output *output;
|
struct wlr_x11_output *output;
|
||||||
|
@ -38,6 +59,10 @@ struct wlr_x11_output *get_x11_output_from_window_id(
|
||||||
return NULL;
|
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,
|
static void handle_x11_event(struct wlr_x11_backend *x11,
|
||||||
xcb_generic_event_t *event) {
|
xcb_generic_event_t *event) {
|
||||||
switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) {
|
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 =
|
struct wlr_x11_output *output =
|
||||||
get_x11_output_from_window_id(x11, ev->window);
|
get_x11_output_from_window_id(x11, ev->window);
|
||||||
if (output != NULL) {
|
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);
|
wlr_output_update_needs_frame(&output->wlr_output);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -68,6 +96,9 @@ static void handle_x11_event(struct wlr_x11_backend *x11,
|
||||||
if (output != NULL) {
|
if (output != NULL) {
|
||||||
wlr_output_destroy(&output->wlr_output);
|
wlr_output_destroy(&output->wlr_output);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
wlr_log(WLR_DEBUG, "Unhandled client message %"PRIu32,
|
||||||
|
ev->data.data32[0]);
|
||||||
}
|
}
|
||||||
break;
|
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;
|
xcb_ge_generic_event_t *ev = (xcb_ge_generic_event_t *)event;
|
||||||
if (ev->extension == x11->xinput_opcode) {
|
if (ev->extension == x11->xinput_opcode) {
|
||||||
handle_x11_xinput_event(x11, ev);
|
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;
|
struct wlr_x11_backend *x11 = data;
|
||||||
|
|
||||||
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
|
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);
|
wl_display_terminate(x11->wl_display);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -94,6 +144,12 @@ static int x11_event(int fd, uint32_t mask, void *data) {
|
||||||
free(e);
|
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;
|
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);
|
struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend);
|
||||||
x11->started = true;
|
x11->started = true;
|
||||||
|
|
||||||
|
wlr_log(WLR_INFO, "Starting X11 backend");
|
||||||
|
|
||||||
wlr_signal_emit_safe(&x11->backend.events.new_input, &x11->keyboard_dev);
|
wlr_signal_emit_safe(&x11->backend.events.new_input, &x11->keyboard_dev);
|
||||||
|
|
||||||
for (size_t i = 0; i < x11->requested_outputs; ++i) {
|
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_input_device_destroy(&x11->keyboard_dev);
|
||||||
|
|
||||||
wlr_signal_emit_safe(&backend->events.destroy, backend);
|
wlr_backend_finish(backend);
|
||||||
|
|
||||||
if (x11->event_source) {
|
if (x11->event_source) {
|
||||||
wl_event_source_remove(x11->event_source);
|
wl_event_source_remove(x11->event_source);
|
||||||
}
|
}
|
||||||
wl_list_remove(&x11->display_destroy.link);
|
wl_list_remove(&x11->display_destroy.link);
|
||||||
|
|
||||||
wlr_renderer_destroy(x11->renderer);
|
wlr_drm_format_set_finish(&x11->primary_dri3_formats);
|
||||||
wlr_egl_finish(&x11->egl);
|
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) {
|
#if HAS_XCB_ERRORS
|
||||||
XCloseDisplay(x11->xlib_conn);
|
xcb_errors_context_free(x11->errors_context);
|
||||||
}
|
#endif
|
||||||
|
|
||||||
|
close(x11->drm_fd);
|
||||||
|
xcb_disconnect(x11->xcb);
|
||||||
free(x11);
|
free(x11);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wlr_renderer *backend_get_renderer(
|
static int backend_get_drm_fd(struct wlr_backend *backend) {
|
||||||
struct wlr_backend *backend) {
|
|
||||||
struct wlr_x11_backend *x11 = get_x11_backend_from_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 = {
|
static const struct wlr_backend_impl backend_impl = {
|
||||||
.start = backend_start,
|
.start = backend_start,
|
||||||
.destroy = backend_destroy,
|
.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) {
|
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);
|
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,
|
struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
||||||
const char *x11_display,
|
const char *x11_display) {
|
||||||
wlr_renderer_create_func_t create_renderer_func) {
|
wlr_log(WLR_INFO, "Creating X11 backend");
|
||||||
|
|
||||||
struct wlr_x11_backend *x11 = calloc(1, sizeof(*x11));
|
struct wlr_x11_backend *x11 = calloc(1, sizeof(*x11));
|
||||||
if (!x11) {
|
if (!x11) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -180,20 +405,12 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
||||||
x11->wl_display = display;
|
x11->wl_display = display;
|
||||||
wl_list_init(&x11->outputs);
|
wl_list_init(&x11->outputs);
|
||||||
|
|
||||||
x11->xlib_conn = XOpenDisplay(x11_display);
|
x11->xcb = xcb_connect(x11_display, NULL);
|
||||||
if (!x11->xlib_conn) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to open X connection");
|
|
||||||
goto error_x11;
|
|
||||||
}
|
|
||||||
|
|
||||||
x11->xcb = XGetXCBConnection(x11->xlib_conn);
|
|
||||||
if (!x11->xcb || xcb_connection_has_error(x11->xcb)) {
|
if (!x11->xcb || xcb_connection_has_error(x11->xcb)) {
|
||||||
wlr_log(WLR_ERROR, "Failed to open xcb connection");
|
wlr_log(WLR_ERROR, "Failed to open xcb connection");
|
||||||
goto error_display;
|
goto error_x11;
|
||||||
}
|
}
|
||||||
|
|
||||||
XSetEventQueueOwner(x11->xlib_conn, XCBOwnsEventQueue);
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
xcb_intern_atom_cookie_t cookie;
|
xcb_intern_atom_cookie_t cookie;
|
||||||
|
@ -203,6 +420,7 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
||||||
{ .name = "WM_DELETE_WINDOW", .atom = &x11->atoms.wm_delete_window },
|
{ .name = "WM_DELETE_WINDOW", .atom = &x11->atoms.wm_delete_window },
|
||||||
{ .name = "_NET_WM_NAME", .atom = &x11->atoms.net_wm_name },
|
{ .name = "_NET_WM_NAME", .atom = &x11->atoms.net_wm_name },
|
||||||
{ .name = "UTF8_STRING", .atom = &x11->atoms.utf8_string },
|
{ .name = "UTF8_STRING", .atom = &x11->atoms.utf8_string },
|
||||||
|
{ .name = "_VARIABLE_REFRESH", .atom = &x11->atoms.variable_refresh },
|
||||||
};
|
};
|
||||||
|
|
||||||
for (size_t i = 0; i < sizeof(atom) / sizeof(atom[0]); ++i) {
|
for (size_t i = 0; i < sizeof(atom) / sizeof(atom[0]); ++i) {
|
||||||
|
@ -224,6 +442,80 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
||||||
|
|
||||||
const xcb_query_extension_reply_t *ext;
|
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);
|
ext = xcb_get_extension_data(x11->xcb, &xcb_xfixes_id);
|
||||||
if (!ext || !ext->present) {
|
if (!ext || !ext->present) {
|
||||||
wlr_log(WLR_ERROR, "X11 does not support Xfixes extension");
|
wlr_log(WLR_ERROR, "X11 does not support Xfixes extension");
|
||||||
|
@ -234,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(x11->xcb, 4, 0);
|
||||||
xcb_xfixes_query_version_reply_t *fixes_reply =
|
xcb_xfixes_query_version_reply_t *fixes_reply =
|
||||||
xcb_xfixes_query_version_reply(x11->xcb, fixes_cookie, NULL);
|
xcb_xfixes_query_version_reply(x11->xcb, fixes_cookie, NULL);
|
||||||
|
|
||||||
if (!fixes_reply || fixes_reply->major_version < 4) {
|
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);
|
free(fixes_reply);
|
||||||
goto error_display;
|
goto error_display;
|
||||||
}
|
}
|
||||||
free(fixes_reply);
|
free(fixes_reply);
|
||||||
|
|
||||||
|
// Xinput extension
|
||||||
|
|
||||||
ext = xcb_get_extension_data(x11->xcb, &xcb_input_id);
|
ext = xcb_get_extension_data(x11->xcb, &xcb_input_id);
|
||||||
if (!ext || !ext->present) {
|
if (!ext || !ext->present) {
|
||||||
wlr_log(WLR_ERROR, "X11 does not support Xinput extension");
|
wlr_log(WLR_ERROR, "X11 does not support Xinput extension");
|
||||||
|
@ -253,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(x11->xcb, 2, 0);
|
||||||
xcb_input_xi_query_version_reply_t *xi_reply =
|
xcb_input_xi_query_version_reply_t *xi_reply =
|
||||||
xcb_input_xi_query_version_reply(x11->xcb, xi_cookie, NULL);
|
xcb_input_xi_query_version_reply(x11->xcb, xi_cookie, NULL);
|
||||||
|
|
||||||
if (!xi_reply || xi_reply->major_version < 2) {
|
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);
|
free(xi_reply);
|
||||||
goto error_display;
|
goto error_display;
|
||||||
}
|
}
|
||||||
|
@ -272,28 +568,76 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
||||||
wl_event_source_check(x11->event_source);
|
wl_event_source_check(x11->event_source);
|
||||||
|
|
||||||
x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb)).data;
|
x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb)).data;
|
||||||
|
if (!x11->screen) {
|
||||||
if (!create_renderer_func) {
|
wlr_log(WLR_ERROR, "Failed to get X11 screen");
|
||||||
create_renderer_func = wlr_renderer_autocreate;
|
|
||||||
}
|
|
||||||
|
|
||||||
static EGLint config_attribs[] = {
|
|
||||||
EGL_SURFACE_TYPE, EGL_WINDOW_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");
|
|
||||||
goto error_event;
|
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,
|
wlr_input_device_init(&x11->keyboard_dev, WLR_INPUT_DEVICE_KEYBOARD,
|
||||||
&input_device_impl, "X11 keyboard", 0, 0);
|
&input_device_impl, "X11 keyboard", 0, 0);
|
||||||
wlr_keyboard_init(&x11->keyboard, &keyboard_impl);
|
wlr_keyboard_init(&x11->keyboard, &keyboard_impl);
|
||||||
|
@ -302,13 +646,87 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
||||||
x11->display_destroy.notify = handle_display_destroy;
|
x11->display_destroy.notify = handle_display_destroy;
|
||||||
wl_display_add_destroy_listener(display, &x11->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;
|
return &x11->backend;
|
||||||
|
|
||||||
error_event:
|
error_event:
|
||||||
wl_event_source_remove(x11->event_source);
|
wl_event_source_remove(x11->event_source);
|
||||||
error_display:
|
error_display:
|
||||||
XCloseDisplay(x11->xlib_conn);
|
xcb_disconnect(x11->xcb);
|
||||||
error_x11:
|
error_x11:
|
||||||
free(x11);
|
free(x11);
|
||||||
return NULL;
|
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 <linux/input-event-codes.h>
|
||||||
|
|
||||||
|
#include <wayland-server-protocol.h>
|
||||||
|
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
#include <xcb/xfixes.h>
|
#include <xcb/xfixes.h>
|
||||||
#include <xcb/xinput.h>
|
#include <xcb/xinput.h>
|
||||||
|
@ -18,7 +20,7 @@
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
static void send_key_event(struct wlr_x11_backend *x11, uint32_t key,
|
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 = {
|
struct wlr_event_keyboard_key ev = {
|
||||||
.time_msec = time,
|
.time_msec = time,
|
||||||
.keycode = key,
|
.keycode = key,
|
||||||
|
@ -77,6 +79,7 @@ static void send_touch_down_event(struct wlr_x11_output *output,
|
||||||
.touch_id = touch_id,
|
.touch_id = touch_id,
|
||||||
};
|
};
|
||||||
wlr_signal_emit_safe(&output->touch.events.down, &ev);
|
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,
|
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,
|
.touch_id = touch_id,
|
||||||
};
|
};
|
||||||
wlr_signal_emit_safe(&output->touch.events.motion, &ev);
|
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,
|
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,
|
.touch_id = touch_id,
|
||||||
};
|
};
|
||||||
wlr_signal_emit_safe(&output->touch.events.up, &ev);
|
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,
|
static struct wlr_x11_touchpoint *get_touchpoint_from_x11_touch_id(
|
||||||
uint32_t id) {
|
struct wlr_x11_output *output, uint32_t id) {
|
||||||
struct wlr_x11_touchpoint *touchpoint;
|
struct wlr_x11_touchpoint *touchpoint;
|
||||||
wl_list_for_each(touchpoint, &output->touchpoints, link) {
|
wl_list_for_each(touchpoint, &output->touchpoints, link) {
|
||||||
if (touchpoint->x11_id == id) {
|
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 *ev =
|
||||||
(xcb_input_key_press_event_t *)event;
|
(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,
|
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
|
||||||
ev->mods.latched, ev->mods.locked, ev->mods.effective);
|
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;
|
x11->time = ev->time;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -133,7 +142,7 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
|
||||||
|
|
||||||
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
|
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
|
||||||
ev->mods.latched, ev->mods.locked, ev->mods.effective);
|
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;
|
x11->time = ev->time;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -210,36 +219,6 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
|
||||||
x11->time = ev->time;
|
x11->time = ev->time;
|
||||||
break;
|
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: {
|
case XCB_INPUT_TOUCH_BEGIN: {
|
||||||
xcb_input_touch_begin_event_t *ev = (xcb_input_touch_begin_event_t *)event;
|
xcb_input_touch_begin_event_t *ev = (xcb_input_touch_begin_event_t *)event;
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,25 @@
|
||||||
x11_libs = []
|
x11_libs = []
|
||||||
x11_required = [
|
x11_required = [
|
||||||
'x11-xcb',
|
|
||||||
'xcb',
|
'xcb',
|
||||||
'xcb-xinput',
|
'xcb-dri3',
|
||||||
|
'xcb-present',
|
||||||
|
'xcb-render',
|
||||||
|
'xcb-renderutil',
|
||||||
|
'xcb-shm',
|
||||||
'xcb-xfixes',
|
'xcb-xfixes',
|
||||||
|
'xcb-xinput',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
msg = ['Required for X11 backend support.']
|
||||||
|
if 'x11' in backends
|
||||||
|
msg += 'Install "@0@" or disable the X11 backend.'
|
||||||
|
endif
|
||||||
|
|
||||||
foreach lib : x11_required
|
foreach lib : x11_required
|
||||||
dep = dependency(lib, required: get_option('x11-backend'))
|
dep = dependency(lib,
|
||||||
|
required: 'x11' in backends,
|
||||||
|
not_found_message: '\n'.join(msg).format(lib),
|
||||||
|
)
|
||||||
if not dep.found()
|
if not dep.found()
|
||||||
subdir_done()
|
subdir_done()
|
||||||
endif
|
endif
|
||||||
|
@ -15,21 +27,10 @@ foreach lib : x11_required
|
||||||
x11_libs += dep
|
x11_libs += dep
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
lib_wlr_backend_x11 = static_library(
|
wlr_files += files(
|
||||||
'wlr_backend_x11',
|
'backend.c',
|
||||||
files(
|
'input_device.c',
|
||||||
'backend.c',
|
'output.c',
|
||||||
'input_device.c',
|
|
||||||
'output.c',
|
|
||||||
),
|
|
||||||
include_directories: wlr_inc,
|
|
||||||
dependencies: [
|
|
||||||
wayland_server,
|
|
||||||
pixman,
|
|
||||||
xkbcommon,
|
|
||||||
x11_libs,
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
wlr_deps += x11_libs
|
||||||
backend_parts += lib_wlr_backend_x11
|
features += { 'x11-backend': true }
|
||||||
conf_data.set10('WLR_HAS_X11_BACKEND', true)
|
|
||||||
|
|
|
@ -3,24 +3,31 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.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/xcb.h>
|
||||||
#include <xcb/xinput.h>
|
#include <xcb/xinput.h>
|
||||||
|
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
#include <wlr/interfaces/wlr_pointer.h>
|
#include <wlr/interfaces/wlr_pointer.h>
|
||||||
#include <wlr/interfaces/wlr_touch.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 <wlr/util/log.h>
|
||||||
|
|
||||||
#include "backend/x11.h"
|
#include "backend/x11.h"
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
#include "util/time.h"
|
||||||
|
|
||||||
static int signal_frame(void *data) {
|
static const uint32_t SUPPORTED_OUTPUT_STATE =
|
||||||
struct wlr_x11_output *output = data;
|
WLR_OUTPUT_STATE_BACKEND_OPTIONAL |
|
||||||
wlr_output_send_frame(&output->wlr_output);
|
WLR_OUTPUT_STATE_BUFFER |
|
||||||
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
|
WLR_OUTPUT_STATE_MODE;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void parse_xcb_setup(struct wlr_output *output,
|
static void parse_xcb_setup(struct wlr_output *output,
|
||||||
xcb_connection_t *xcb) {
|
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;
|
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,
|
static bool output_set_custom_mode(struct wlr_output *wlr_output,
|
||||||
int32_t width, int32_t height, int32_t refresh) {
|
int32_t width, int32_t height, int32_t refresh) {
|
||||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
||||||
output_set_refresh(&output->wlr_output, refresh);
|
|
||||||
|
|
||||||
const uint32_t values[] = { width, height };
|
const uint32_t values[] = { width, height };
|
||||||
xcb_void_cookie_t cookie = xcb_configure_window_checked(
|
xcb_void_cookie_t cookie = xcb_configure_window_checked(
|
||||||
x11->xcb, output->win,
|
x11->xcb, output->win,
|
||||||
|
@ -76,51 +68,427 @@ static bool output_set_custom_mode(struct wlr_output *wlr_output,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void destroy_x11_buffer(struct wlr_x11_buffer *buffer);
|
||||||
|
|
||||||
static void output_destroy(struct wlr_output *wlr_output) {
|
static void output_destroy(struct wlr_output *wlr_output) {
|
||||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
||||||
|
pixman_region32_fini(&output->exposed);
|
||||||
|
|
||||||
wlr_input_device_destroy(&output->pointer_dev);
|
wlr_input_device_destroy(&output->pointer_dev);
|
||||||
wlr_input_device_destroy(&output->touch_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_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_destroy_window(x11->xcb, output->win);
|
||||||
xcb_flush(x11->xcb);
|
xcb_flush(x11->xcb);
|
||||||
free(output);
|
free(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool output_attach_render(struct wlr_output *wlr_output,
|
static bool output_test(struct wlr_output *wlr_output) {
|
||||||
int *buffer_age) {
|
uint32_t unsupported =
|
||||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE;
|
||||||
|
if (unsupported != 0) {
|
||||||
|
wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32,
|
||||||
|
unsupported);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
|
||||||
|
assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
||||||
return wlr_egl_make_current(&x11->egl, output->surf, buffer_age);
|
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) {
|
static bool output_commit(struct wlr_output *wlr_output) {
|
||||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
||||||
pixman_region32_t *damage = NULL;
|
if (!output_test(wlr_output)) {
|
||||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) {
|
|
||||||
damage = &wlr_output->pending.damage;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wlr_egl_swap_buffers(&x11->egl, output->surf, damage)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_output_send_present(wlr_output, NULL);
|
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
|
||||||
|
if (!output_set_custom_mode(wlr_output,
|
||||||
|
wlr_output->pending.custom_mode.width,
|
||||||
|
wlr_output->pending.custom_mode.height,
|
||||||
|
wlr_output->pending.custom_mode.refresh)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED &&
|
||||||
|
x11->atoms.variable_refresh != XCB_ATOM_NONE) {
|
||||||
|
if (wlr_output->pending.adaptive_sync_enabled) {
|
||||||
|
uint32_t enabled = 1;
|
||||||
|
xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win,
|
||||||
|
x11->atoms.variable_refresh, XCB_ATOM_CARDINAL, 32, 1,
|
||||||
|
&enabled);
|
||||||
|
wlr_output->adaptive_sync_status = WLR_OUTPUT_ADAPTIVE_SYNC_UNKNOWN;
|
||||||
|
} else {
|
||||||
|
xcb_delete_property(x11->xcb, output->win,
|
||||||
|
x11->atoms.variable_refresh);
|
||||||
|
wlr_output->adaptive_sync_status = WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
|
||||||
|
if (!output_commit_buffer(output)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_flush(x11->xcb);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
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 = {
|
static const struct wlr_output_impl output_impl = {
|
||||||
.set_custom_mode = output_set_custom_mode,
|
|
||||||
.destroy = output_destroy,
|
.destroy = output_destroy,
|
||||||
.attach_render = output_attach_render,
|
.test = output_test,
|
||||||
.commit = output_commit,
|
.commit = output_commit,
|
||||||
|
.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) {
|
struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
||||||
|
@ -136,27 +504,39 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
output->x11 = x11;
|
output->x11 = x11;
|
||||||
|
wl_list_init(&output->buffers);
|
||||||
|
pixman_region32_init(&output->exposed);
|
||||||
|
|
||||||
struct wlr_output *wlr_output = &output->wlr_output;
|
struct wlr_output *wlr_output = &output->wlr_output;
|
||||||
wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display);
|
wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display);
|
||||||
|
|
||||||
wlr_output->width = 1024;
|
wlr_output_update_custom_mode(wlr_output, 1024, 768, 0);
|
||||||
wlr_output->height = 768;
|
|
||||||
|
|
||||||
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);
|
parse_xcb_setup(wlr_output, x11->xcb);
|
||||||
|
|
||||||
uint32_t mask = XCB_CW_EVENT_MASK;
|
char description[128];
|
||||||
|
snprintf(description, sizeof(description),
|
||||||
|
"X11 output %zu", x11->last_output_num);
|
||||||
|
wlr_output_set_description(wlr_output, description);
|
||||||
|
|
||||||
|
// 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[] = {
|
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);
|
output->win = xcb_generate_id(x11->xcb);
|
||||||
xcb_create_window(x11->xcb, XCB_COPY_FROM_PARENT, output->win,
|
xcb_create_window(x11->xcb, x11->depth->depth, output->win,
|
||||||
x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 1,
|
x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 0,
|
||||||
XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->screen->root_visual, mask, values);
|
XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->visualid, mask, values);
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
xcb_input_event_mask_t head;
|
xcb_input_event_mask_t head;
|
||||||
|
@ -168,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_PRESS |
|
||||||
XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE |
|
XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE |
|
||||||
XCB_INPUT_XI_EVENT_MASK_MOTION |
|
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_BEGIN |
|
||||||
XCB_INPUT_XI_EVENT_MASK_TOUCH_END |
|
XCB_INPUT_XI_EVENT_MASK_TOUCH_END |
|
||||||
XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE,
|
XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE,
|
||||||
};
|
};
|
||||||
xcb_input_xi_select_events(x11->xcb, output->win, 1, &xinput_mask.head);
|
xcb_input_xi_select_events(x11->xcb, output->win, 1, &xinput_mask.head);
|
||||||
|
|
||||||
output->surf = wlr_egl_create_surface(&x11->egl, &output->win);
|
uint32_t present_mask = XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY |
|
||||||
if (!output->surf) {
|
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY;
|
||||||
wlr_log(WLR_ERROR, "Failed to create EGL surface");
|
output->present_event_id = xcb_generate_id(x11->xcb);
|
||||||
free(output);
|
xcb_present_select_input(x11->xcb, output->present_event_id, output->win,
|
||||||
return NULL;
|
present_mask);
|
||||||
}
|
|
||||||
|
|
||||||
xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win,
|
xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win,
|
||||||
x11->atoms.wm_protocols, XCB_ATOM_ATOM, 32, 1,
|
x11->atoms.wm_protocols, XCB_ATOM_ATOM, 32, 1,
|
||||||
|
@ -192,12 +569,8 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
||||||
xcb_map_window(x11->xcb, output->win);
|
xcb_map_window(x11->xcb, output->win);
|
||||||
xcb_flush(x11->xcb);
|
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_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_output_update_enabled(wlr_output, true);
|
||||||
|
|
||||||
wlr_input_device_init(&output->pointer_dev, WLR_INPUT_DEVICE_POINTER,
|
wlr_input_device_init(&output->pointer_dev, WLR_INPUT_DEVICE_POINTER,
|
||||||
|
@ -217,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->pointer_dev);
|
||||||
wlr_signal_emit_safe(&x11->backend.events.new_input, &output->touch_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;
|
return wlr_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_x11_configure_notify(struct wlr_x11_output *output,
|
void handle_x11_configure_notify(struct wlr_x11_output *output,
|
||||||
xcb_configure_notify_event_t *ev) {
|
xcb_configure_notify_event_t *ev) {
|
||||||
// ignore events that set an invalid size:
|
// ignore events that set an invalid size:
|
||||||
if (ev->width > 0 && ev->height > 0) {
|
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 {
|
|
||||||
wlr_log(WLR_DEBUG,
|
wlr_log(WLR_DEBUG,
|
||||||
"Ignoring X11 configure event for height=%d, width=%d",
|
"Ignoring X11 configure event for height=%d, width=%d",
|
||||||
ev->width, ev->height);
|
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) {
|
bool wlr_output_is_x11(struct wlr_output *wlr_output) {
|
||||||
|
@ -255,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,
|
x11_output->x11->atoms.net_wm_name, x11_output->x11->atoms.utf8_string, 8,
|
||||||
strlen(title), title);
|
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,33 +0,0 @@
|
||||||
#!/bin/sh -eu
|
|
||||||
old_version="$1"
|
|
||||||
new_version="$2"
|
|
||||||
sed -i meson.build -e "s/version: '$old_version'/version: '$new_version'/g"
|
|
||||||
|
|
||||||
printf "Backwards-incompatible ABI changes? (y/n) "
|
|
||||||
read inc_age
|
|
||||||
if [ "$inc_age" = 'n' ]
|
|
||||||
then
|
|
||||||
printf "Interface(s) added, removed, or changed? (y/n) "
|
|
||||||
read inc_current
|
|
||||||
fi
|
|
||||||
|
|
||||||
so_version=$(egrep '^so_version =' meson.build | cut -d'[' -f2- | cut -d']' -f1)
|
|
||||||
current=$(echo "$so_version" | cut -d',' -f1 | sed -e "s/'//g" -e "s/ //g")
|
|
||||||
revision=$(echo "$so_version" | cut -d',' -f2 | sed -e "s/'//g" -e "s/ //g")
|
|
||||||
age=$(echo "$so_version" | cut -d',' -f3 | sed -e "s/'//g" -e "s/ //g")
|
|
||||||
|
|
||||||
if [ "$inc_age" = 'y' ]
|
|
||||||
then
|
|
||||||
age=$((age+1))
|
|
||||||
current=$((current+1))
|
|
||||||
elif [ "$inc_current" = 'y' ]
|
|
||||||
then
|
|
||||||
current=$((current+1))
|
|
||||||
fi
|
|
||||||
revision=$((revision+1))
|
|
||||||
|
|
||||||
sed -i meson.build \
|
|
||||||
-e "s/so_version = .*/so_version = ['$current', '$revision', '$age']/g"
|
|
||||||
|
|
||||||
git add meson.build
|
|
||||||
git commit -m "Update version to $new_version"
|
|
|
@ -2,63 +2,53 @@ wlroots reads these environment variables
|
||||||
|
|
||||||
# wlroots specific
|
# wlroots specific
|
||||||
|
|
||||||
|
* *WLR_BACKENDS*: comma-separated list of backends to use (available backends:
|
||||||
|
libinput, drm, wayland, x11, headless)
|
||||||
|
* *WLR_NO_HARDWARE_CURSORS*: set to 1 to use software cursors instead of
|
||||||
|
hardware cursors
|
||||||
|
* *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
|
||||||
|
|
||||||
* *WLR_DRM_DEVICES*: specifies the DRM devices (as a colon separated list)
|
* *WLR_DRM_DEVICES*: specifies the DRM devices (as a colon separated list)
|
||||||
instead of auto probing them. The first existing device in this list is
|
instead of auto probing them. The first existing device in this list is
|
||||||
considered the primary DRM device.
|
considered the primary DRM device.
|
||||||
* *WLR_DRM_NO_ATOMIC*: set to 1 to use legacy DRM interface instead of atomic
|
* *WLR_DRM_NO_ATOMIC*: set to 1 to use legacy DRM interface instead of atomic
|
||||||
mode setting
|
mode setting
|
||||||
* *WLR_LIBINPUT_NO_DEVICES*: set to 1 to not fail without any input devices
|
* *WLR_DRM_NO_MODIFIERS*: set to 1 to always allocate planes without modifiers,
|
||||||
* *WLR_BACKENDS*: comma-separated list of backends to use (available backends:
|
this can fix certain modeset failures because of bandwidth restrictions.
|
||||||
wayland, x11, headless, noop)
|
|
||||||
* *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)
|
|
||||||
|
|
||||||
# Headless backend
|
## Headless backend
|
||||||
|
|
||||||
* *WLR_HEADLESS_OUTPUTS*: when using the headless backend specifies the number
|
* *WLR_HEADLESS_OUTPUTS*: when using the headless backend specifies the number
|
||||||
of outputs
|
of outputs
|
||||||
|
|
||||||
# RDP backend
|
## libinput backend
|
||||||
|
|
||||||
* *WLR_RDP_TLS_CERT_PATH*: required when using `wlr_backend_autocreate`,
|
* *WLR_LIBINPUT_NO_DEVICES*: set to 1 to not fail without any input devices
|
||||||
specifies the path to the TLS certificate to use for encrypting connections
|
|
||||||
* *WLR_RDP_TLS_KEY_PATH*: required when using `wlr_backend_autocreate`,
|
|
||||||
specifies the path to the TLS private key to use for encrypting connections
|
|
||||||
* *WLR_RDP_ADDRESS*: the IP address to bind to, defaults to `127.0.0.1`
|
|
||||||
* *WLR_RDP_PORT*: the port to bind to, defaults to 3389.
|
|
||||||
|
|
||||||
Note: a TLS certificate and key can be generated like so:
|
## Wayland backend
|
||||||
|
|
||||||
```
|
|
||||||
$ openssl genrsa -out tls.key 2048
|
|
||||||
$ openssl req -new -key tls.key -out tls.csr
|
|
||||||
$ openssl x509 -req -days 365 -signkey tls.key -in tls.csr -out tls.crt
|
|
||||||
```
|
|
||||||
|
|
||||||
`tls.csr` can be discarded. Connecting to the RDP backend with xfreedrp can be
|
|
||||||
done like so:
|
|
||||||
|
|
||||||
xfreerdp -v localhost --bpp 32 --size 1920x1080 --rfx
|
|
||||||
|
|
||||||
# Wayland backend
|
|
||||||
|
|
||||||
* *WLR_WL_OUTPUTS*: when using the wayland backend specifies the number of outputs
|
* *WLR_WL_OUTPUTS*: when using the wayland backend specifies the number of outputs
|
||||||
|
|
||||||
# X11 backend
|
## X11 backend
|
||||||
|
|
||||||
* *WLR_X11_OUTPUTS*: when using the X11 backend specifies the number of outputs
|
* *WLR_X11_OUTPUTS*: when using the X11 backend specifies the number of outputs
|
||||||
|
|
||||||
# Rootston specific
|
## gles2 renderer
|
||||||
|
|
||||||
* *XKB_DEFAULT_RULES*, *XKB_DEFAULT_MODEL*, *XKB_DEFAULT_LAYOUT*,
|
* *WLR_RENDERER_ALLOW_SOFTWARE*: allows the gles2 renderer to use software
|
||||||
*XKB_DEFAULT_VARIANT*, *XKB_DEFAULT_OPTIONS*: xkb setup
|
rendering
|
||||||
|
|
||||||
# Generic
|
# Generic
|
||||||
|
|
||||||
* *DISPLAY*: if set probe X11 backend in *wlr_backend_autocreate*
|
* *DISPLAY*: if set probe X11 backend in `wlr_backend_autocreate`
|
||||||
* *WAYLAND_DISPLAY*, *_WAYLAND_DISPLAY*, *WAYLAND_SOCKET*: if set probe Wayland
|
* *WAYLAND_DISPLAY*, *WAYLAND_SOCKET*: if set probe Wayland backend in
|
||||||
backend in *wlr_backend_autocreate*
|
`wlr_backend_autocreate`
|
||||||
* *XCURSOR_PATH*: directory where xcursors are located
|
* *XCURSOR_PATH*: directory where xcursors are located
|
||||||
|
* *XDG_SESSION_ID*: if set, session ID used by the logind session
|
||||||
|
|
|
@ -205,10 +205,11 @@ static void remove_output(struct wayland_output *out) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wayland_output *find_output(struct capture_context *ctx,
|
static struct wayland_output *find_output(struct capture_context *ctx,
|
||||||
struct wl_output *out, uint32_t id) {
|
struct wl_output *out, int id) {
|
||||||
struct wayland_output *output, *tmp;
|
struct wayland_output *output, *tmp;
|
||||||
wl_list_for_each_safe(output, tmp, &ctx->output_list, link) {
|
wl_list_for_each_safe(output, tmp, &ctx->output_list, link) {
|
||||||
if ((output->output == out) || (output->id == id)) {
|
if (output->output == out || (id >= 0 && output->id == (uint32_t)id)
|
||||||
|
|| id == -1) {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -461,7 +462,7 @@ static void register_cb(struct capture_context *ctx) {
|
||||||
&frame_listener, ctx);
|
&frame_listener, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *vid_encode_thread(void *arg) {
|
static void *vid_encode_thread(void *arg) {
|
||||||
int err = 0;
|
int err = 0;
|
||||||
struct capture_context *ctx = arg;
|
struct capture_context *ctx = arg;
|
||||||
|
|
||||||
|
@ -489,26 +490,27 @@ void *vid_encode_thread(void *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
AVPacket pkt;
|
AVPacket *pkt = av_packet_alloc();
|
||||||
av_init_packet(&pkt);
|
int ret = avcodec_receive_packet(ctx->avctx, pkt);
|
||||||
|
|
||||||
int ret = avcodec_receive_packet(ctx->avctx, &pkt);
|
|
||||||
if (ret == AVERROR(EAGAIN)) {
|
if (ret == AVERROR(EAGAIN)) {
|
||||||
|
av_packet_free(&pkt);
|
||||||
break;
|
break;
|
||||||
} else if (ret == AVERROR_EOF) {
|
} else if (ret == AVERROR_EOF) {
|
||||||
av_log(ctx, AV_LOG_INFO, "Encoder flushed!\n");
|
av_log(ctx, AV_LOG_INFO, "Encoder flushed!\n");
|
||||||
|
av_packet_free(&pkt);
|
||||||
goto end;
|
goto end;
|
||||||
} else if (ret) {
|
} else if (ret) {
|
||||||
av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n",
|
av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n",
|
||||||
av_err2str(ret));
|
av_err2str(ret));
|
||||||
|
av_packet_free(&pkt);
|
||||||
err = ret;
|
err = ret;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
pkt.stream_index = 0;
|
pkt->stream_index = 0;
|
||||||
err = av_interleaved_write_frame(ctx->avf, &pkt);
|
err = av_interleaved_write_frame(ctx->avf, pkt);
|
||||||
|
|
||||||
av_packet_unref(&pkt);
|
av_packet_free(&pkt);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
av_log(ctx, AV_LOG_ERROR, "Writing packet fail: %s!\n",
|
av_log(ctx, AV_LOG_ERROR, "Writing packet fail: %s!\n",
|
||||||
|
@ -686,7 +688,7 @@ static int init_encoding(struct capture_context *ctx) {
|
||||||
|
|
||||||
struct capture_context *q_ctx = NULL;
|
struct capture_context *q_ctx = NULL;
|
||||||
|
|
||||||
void on_quit_signal(int signo) {
|
static void on_quit_signal(int signo) {
|
||||||
printf("\r");
|
printf("\r");
|
||||||
av_log(q_ctx, AV_LOG_WARNING, "Quitting!\n");
|
av_log(q_ctx, AV_LOG_WARNING, "Quitting!\n");
|
||||||
q_ctx->quit = true;
|
q_ctx->quit = true;
|
||||||
|
@ -752,8 +754,11 @@ static int init(struct capture_context *ctx) {
|
||||||
ctx->registry = wl_display_get_registry(ctx->display);
|
ctx->registry = wl_display_get_registry(ctx->display);
|
||||||
wl_registry_add_listener(ctx->registry, ®istry_listener, ctx);
|
wl_registry_add_listener(ctx->registry, ®istry_listener, ctx);
|
||||||
|
|
||||||
|
// First roundtrip to fetch globals
|
||||||
|
wl_display_roundtrip(ctx->display);
|
||||||
|
|
||||||
|
// Second roundtrip to fetch wl_output information
|
||||||
wl_display_roundtrip(ctx->display);
|
wl_display_roundtrip(ctx->display);
|
||||||
wl_display_dispatch(ctx->display);
|
|
||||||
|
|
||||||
if (!ctx->export_manager) {
|
if (!ctx->export_manager) {
|
||||||
av_log(ctx, AV_LOG_ERROR, "Compositor doesn't support %s!\n",
|
av_log(ctx, AV_LOG_ERROR, "Compositor doesn't support %s!\n",
|
||||||
|
@ -766,16 +771,77 @@ static int init(struct capture_context *ctx) {
|
||||||
|
|
||||||
static void uninit(struct capture_context *ctx);
|
static void uninit(struct capture_context *ctx);
|
||||||
|
|
||||||
|
static const char usage[] = "usage: dmabuf-capture [options...] <destination file path>\n"
|
||||||
|
" -o <output ID>\n"
|
||||||
|
" -t <hardware device type>\n"
|
||||||
|
" -d <device path>\n"
|
||||||
|
" -e <encoder>\n"
|
||||||
|
" -f <pixel format>\n"
|
||||||
|
" -r <bitrate in Mbps>\n"
|
||||||
|
"\n"
|
||||||
|
"Example:\n"
|
||||||
|
" dmabuf-capture -o 32 -t vaapi -d /dev/dri/renderD129 \\\n"
|
||||||
|
" -e libx264 -f nv12 -r 12 recording.mkv\n";
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
int err;
|
struct capture_context ctx = {
|
||||||
struct capture_context ctx = { 0 };
|
.hardware_device = "/dev/dri/renderD128",
|
||||||
ctx.class = &((AVClass) {
|
.encoder_name = "libx264",
|
||||||
|
.out_bitrate = 12,
|
||||||
|
};
|
||||||
|
int output_id = -1;
|
||||||
|
const char *hw_device_type = "vaapi";
|
||||||
|
const char *software_format = "nv12";
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt(argc, argv, "ho:t:d:e:f:r:")) != -1) {
|
||||||
|
char *end;
|
||||||
|
switch (opt) {
|
||||||
|
case 'o':
|
||||||
|
output_id = strtol(optarg, &end, 10);
|
||||||
|
if (optarg[0] == '\0' || end[0] != '\0') {
|
||||||
|
fprintf(stderr, "Output ID is not an integer\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
hw_device_type = optarg;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
ctx.hardware_device = optarg;
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
ctx.encoder_name = optarg;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
software_format = optarg;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
ctx.out_bitrate = strtof(optarg, &end);
|
||||||
|
if (optarg[0] == '\0' || end[0] != '\0') {
|
||||||
|
fprintf(stderr, "Bitrate is not a floating-pointer number\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "%s", usage);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind >= argc) {
|
||||||
|
fprintf(stderr, "Missing destination file argument\n");
|
||||||
|
fprintf(stderr, "%s", usage);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ctx.out_filename = argv[optind];
|
||||||
|
|
||||||
|
ctx.class = &((AVClass){
|
||||||
.class_name = "dmabuf-capture",
|
.class_name = "dmabuf-capture",
|
||||||
.item_name = av_default_item_name,
|
.item_name = av_default_item_name,
|
||||||
.version = LIBAVUTIL_VERSION_INT,
|
.version = LIBAVUTIL_VERSION_INT,
|
||||||
});
|
});
|
||||||
|
|
||||||
err = init(&ctx);
|
int err = init(&ctx);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
@ -786,31 +852,16 @@ int main(int argc, char *argv[]) {
|
||||||
o->make, o->model, o->id);
|
o->make, o->model, o->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc != 8) {
|
o = find_output(&ctx, NULL, output_id);
|
||||||
printf("Invalid number of arguments! Usage and example:\n"
|
|
||||||
"./dmabuf-capture <source id> <hardware device type> <device> "
|
|
||||||
"<encoder name> <pixel format> <bitrate in Mbps> <file path>\n"
|
|
||||||
"./dmabuf-capture 0 vaapi /dev/dri/renderD129 libx264 nv12 12 "
|
|
||||||
"dmabuf_recording_01.mkv\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int o_id = strtol(argv[1], NULL, 10);
|
|
||||||
o = find_output(&ctx, NULL, o_id);
|
|
||||||
if (!o) {
|
if (!o) {
|
||||||
printf("Unable to find output with ID %i!\n", o_id);
|
printf("Unable to find output with ID %d\n", output_id);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.target_output = o->output;
|
ctx.target_output = o->output;
|
||||||
ctx.with_cursor = true;
|
ctx.with_cursor = true;
|
||||||
ctx.hw_device_type = av_hwdevice_find_type_by_name(argv[2]);
|
ctx.hw_device_type = av_hwdevice_find_type_by_name(hw_device_type);
|
||||||
ctx.hardware_device = argv[3];
|
ctx.software_format = av_get_pix_fmt(software_format);
|
||||||
|
|
||||||
ctx.encoder_name = argv[4];
|
|
||||||
ctx.software_format = av_get_pix_fmt(argv[5]);
|
|
||||||
ctx.out_bitrate = strtof(argv[6], NULL);
|
|
||||||
ctx.out_filename = argv[7];
|
|
||||||
|
|
||||||
av_dict_set(&ctx.encoder_opts, "preset", "veryfast", 0);
|
av_dict_set(&ctx.encoder_opts, "preset", "veryfast", 0);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
@ -7,28 +7,29 @@
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
|
#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION 2
|
#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION 3
|
||||||
|
|
||||||
/**
|
|
||||||
* Usage:
|
static void print_help(void) {
|
||||||
* 1. foreign-toplevel
|
static const char usage[] =
|
||||||
* Prints a list of opened toplevels
|
"Usage: foreign-toplevel [OPTIONS] ...\n"
|
||||||
* 2. foreign-toplevel -f <id>
|
"Manage and view information about toplevel windows.\n"
|
||||||
* Focus the toplevel with the given id
|
"\n"
|
||||||
* 3. foreign-toplevel -a <id>
|
" -f <id> focus\n"
|
||||||
* Maximize the toplevel with the given id
|
" -s <id> fullscreen\n"
|
||||||
* 4. foreign-toplevel -u <id>
|
" -o <output_id> select output for fullscreen toplevel to appear on. Use this\n"
|
||||||
* Unmaximize the toplevel with the given id
|
" option with -s. View available outputs with wayland-info.\n"
|
||||||
* 5. foreign-toplevel -i <id>
|
" -S <id> unfullscreen\n"
|
||||||
* Minimize the toplevel with the given id
|
" -a <id> maximize\n"
|
||||||
* 6. foreign-toplevel -r <id>
|
" -u <id> unmaximize\n"
|
||||||
* Restore(unminimize) the toplevel with the given id
|
" -i <id> minimize\n"
|
||||||
* 7. foreign-toplevel -c <id>
|
" -r <id> restore(unminimize)\n"
|
||||||
* Close the toplevel with the given id
|
" -c <id> close\n"
|
||||||
* 8. foreign-toplevel -m
|
" -m continuously print changes to the list of opened toplevels\n"
|
||||||
* Continuously print changes to the list of opened toplevels.
|
" Can be used together with some of the previous options.\n"
|
||||||
* Can be used together with some of the previous options.
|
" -h print help message and quit\n";
|
||||||
*/
|
fprintf(stderr, "%s", usage);
|
||||||
|
}
|
||||||
|
|
||||||
enum toplevel_state_field {
|
enum toplevel_state_field {
|
||||||
TOPLEVEL_STATE_MAXIMIZED = (1 << 0),
|
TOPLEVEL_STATE_MAXIMIZED = (1 << 0),
|
||||||
|
@ -38,11 +39,16 @@ enum toplevel_state_field {
|
||||||
TOPLEVEL_STATE_INVALID = (1 << 4),
|
TOPLEVEL_STATE_INVALID = (1 << 4),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 {
|
struct toplevel_state {
|
||||||
char *title;
|
char *title;
|
||||||
char *app_id;
|
char *app_id;
|
||||||
|
|
||||||
uint32_t state;
|
uint32_t state;
|
||||||
|
uint32_t parent_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void copy_state(struct toplevel_state *current,
|
static void copy_state(struct toplevel_state *current,
|
||||||
|
@ -67,6 +73,8 @@ static void copy_state(struct toplevel_state *current,
|
||||||
current->state = pending->state;
|
current->state = pending->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
current->parent_id = pending->parent_id;
|
||||||
|
|
||||||
pending->state = TOPLEVEL_STATE_INVALID;
|
pending->state = TOPLEVEL_STATE_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +92,12 @@ static void print_toplevel(struct toplevel_v1 *toplevel, bool print_endl) {
|
||||||
toplevel->current.title ?: "(nil)",
|
toplevel->current.title ?: "(nil)",
|
||||||
toplevel->current.app_id ?: "(nil)");
|
toplevel->current.app_id ?: "(nil)");
|
||||||
|
|
||||||
|
if (toplevel->current.parent_id != no_parent) {
|
||||||
|
printf(" parent=%u", toplevel->current.parent_id);
|
||||||
|
} else {
|
||||||
|
printf(" no parent");
|
||||||
|
}
|
||||||
|
|
||||||
if (print_endl) {
|
if (print_endl) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
@ -114,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,
|
static void toplevel_handle_title(void *data,
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
|
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
|
||||||
const char *title) {
|
const char *title) {
|
||||||
|
@ -172,6 +191,28 @@ static void toplevel_handle_state(void *data,
|
||||||
toplevel->pending.state = array_to_state(state);
|
toplevel->pending.state = array_to_state(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager = NULL;
|
||||||
|
static struct wl_list toplevel_list;
|
||||||
|
|
||||||
|
static void toplevel_handle_parent(void *data,
|
||||||
|
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
|
||||||
|
struct zwlr_foreign_toplevel_handle_v1 *zwlr_parent) {
|
||||||
|
struct toplevel_v1 *toplevel = data;
|
||||||
|
toplevel->pending.parent_id = no_parent;
|
||||||
|
if (zwlr_parent) {
|
||||||
|
struct toplevel_v1 *toplevel_tmp;
|
||||||
|
wl_list_for_each(toplevel_tmp, &toplevel_list, link) {
|
||||||
|
if (toplevel_tmp->zwlr_toplevel == zwlr_parent) {
|
||||||
|
toplevel->pending.parent_id = toplevel_tmp->id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (toplevel->pending.parent_id == no_parent) {
|
||||||
|
fprintf(stderr, "Cannot find parent toplevel!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void toplevel_handle_done(void *data,
|
static void toplevel_handle_done(void *data,
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
|
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
|
||||||
struct toplevel_v1 *toplevel = data;
|
struct toplevel_v1 *toplevel = data;
|
||||||
|
@ -192,6 +233,9 @@ static void toplevel_handle_closed(void *data,
|
||||||
printf(" closed\n");
|
printf(" closed\n");
|
||||||
|
|
||||||
zwlr_foreign_toplevel_handle_v1_destroy(zwlr_toplevel);
|
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 = {
|
static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = {
|
||||||
|
@ -202,11 +246,9 @@ static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = {
|
||||||
.state = toplevel_handle_state,
|
.state = toplevel_handle_state,
|
||||||
.done = toplevel_handle_done,
|
.done = toplevel_handle_done,
|
||||||
.closed = toplevel_handle_closed,
|
.closed = toplevel_handle_closed,
|
||||||
|
.parent = toplevel_handle_parent
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager = NULL;
|
|
||||||
static struct wl_list toplevel_list;
|
|
||||||
|
|
||||||
static void toplevel_manager_handle_toplevel(void *data,
|
static void toplevel_manager_handle_toplevel(void *data,
|
||||||
struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager,
|
struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager,
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
|
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
|
||||||
|
@ -218,6 +260,8 @@ static void toplevel_manager_handle_toplevel(void *data,
|
||||||
|
|
||||||
toplevel->id = global_id++;
|
toplevel->id = global_id++;
|
||||||
toplevel->zwlr_toplevel = zwlr_toplevel;
|
toplevel->zwlr_toplevel = zwlr_toplevel;
|
||||||
|
toplevel->current.parent_id = no_parent;
|
||||||
|
toplevel->pending.parent_id = no_parent;
|
||||||
wl_list_insert(&toplevel_list, &toplevel->link);
|
wl_list_insert(&toplevel_list, &toplevel->link);
|
||||||
|
|
||||||
zwlr_foreign_toplevel_handle_v1_add_listener(zwlr_toplevel, &toplevel_impl,
|
zwlr_foreign_toplevel_handle_v1_add_listener(zwlr_toplevel, &toplevel_impl,
|
||||||
|
@ -238,9 +282,11 @@ struct wl_seat *seat = NULL;
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
static void handle_global(void *data, struct wl_registry *registry,
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
uint32_t name, const char *interface, uint32_t version) {
|
||||||
if (strcmp(interface, wl_output_interface.name) == 0) {
|
if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||||
struct wl_output *output = wl_registry_bind(registry, name,
|
if (name == pref_output_id) {
|
||||||
&wl_output_interface, version);
|
pref_output = wl_registry_bind(registry, name,
|
||||||
wl_output_set_user_data(output, (void*)(size_t)name); // assign some ID to the output
|
&wl_output_interface, version);
|
||||||
|
|
||||||
|
}
|
||||||
} else if (strcmp(interface,
|
} else if (strcmp(interface,
|
||||||
zwlr_foreign_toplevel_manager_v1_interface.name) == 0) {
|
zwlr_foreign_toplevel_manager_v1_interface.name) == 0) {
|
||||||
toplevel_manager = wl_registry_bind(registry, name,
|
toplevel_manager = wl_registry_bind(registry, name,
|
||||||
|
@ -291,8 +337,7 @@ int main(int argc, char **argv) {
|
||||||
int one_shot = 1;
|
int one_shot = 1;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
// TODO maybe print usage with -h?
|
while ((c = getopt(argc, argv, "f:a:u:i:r:c:s:S:mo:h")) != -1) {
|
||||||
while ((c = getopt(argc, argv, "f:a:u:i:r:c:s:S:m")) != -1) {
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'f':
|
case 'f':
|
||||||
focus_id = atoi(optarg);
|
focus_id = atoi(optarg);
|
||||||
|
@ -321,6 +366,17 @@ int main(int argc, char **argv) {
|
||||||
case 'm':
|
case 'm':
|
||||||
one_shot = 0;
|
one_shot = 0;
|
||||||
break;
|
break;
|
||||||
|
case 'o':
|
||||||
|
pref_output_id = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
print_help();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
print_help();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,7 +388,6 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
wl_display_dispatch(display);
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
if (toplevel_manager == NULL) {
|
if (toplevel_manager == NULL) {
|
||||||
|
@ -359,7 +414,11 @@ int main(int argc, char **argv) {
|
||||||
zwlr_foreign_toplevel_handle_v1_unset_minimized(toplevel->zwlr_toplevel);
|
zwlr_foreign_toplevel_handle_v1_unset_minimized(toplevel->zwlr_toplevel);
|
||||||
}
|
}
|
||||||
if ((toplevel = toplevel_by_id_or_bail(fullscreen_id))) {
|
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))) {
|
if ((toplevel = toplevel_by_id_or_bail(unfullscreen_id))) {
|
||||||
zwlr_foreign_toplevel_handle_v1_unset_fullscreen(toplevel->zwlr_toplevel);
|
zwlr_foreign_toplevel_handle_v1_unset_fullscreen(toplevel->zwlr_toplevel);
|
||||||
|
|
|
@ -7,14 +7,15 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
|
#include <wlr/render/allocator.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_compositor.h>
|
#include <wlr/types/wlr_compositor.h>
|
||||||
#include <wlr/types/wlr_fullscreen_shell_v1.h>
|
#include <wlr/types/wlr_fullscreen_shell_v1.h>
|
||||||
#include <wlr/types/wlr_linux_dmabuf_v1.h>
|
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
#include <wlr/types/wlr_output.h>
|
#include <wlr/types/wlr_output.h>
|
||||||
#include <wlr/types/wlr_surface.h>
|
#include <wlr/types/wlr_surface.h>
|
||||||
|
#include <wlr/util/box.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,6 +26,7 @@ struct fullscreen_server {
|
||||||
struct wl_display *wl_display;
|
struct wl_display *wl_display;
|
||||||
struct wlr_backend *backend;
|
struct wlr_backend *backend;
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
|
struct wlr_allocator *allocator;
|
||||||
|
|
||||||
struct wlr_fullscreen_shell_v1 *fullscreen_shell;
|
struct wlr_fullscreen_shell_v1 *fullscreen_shell;
|
||||||
struct wl_listener present_surface;
|
struct wl_listener present_surface;
|
||||||
|
@ -47,7 +49,6 @@ struct fullscreen_output {
|
||||||
struct render_data {
|
struct render_data {
|
||||||
struct wlr_output *output;
|
struct wlr_output *output;
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
struct tinywl_view *view;
|
|
||||||
struct timespec *when;
|
struct timespec *when;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -147,11 +148,7 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) {
|
||||||
wl_container_of(listener, server, new_output);
|
wl_container_of(listener, server, new_output);
|
||||||
struct wlr_output *wlr_output = data;
|
struct wlr_output *wlr_output = data;
|
||||||
|
|
||||||
if (!wl_list_empty(&wlr_output->modes)) {
|
wlr_output_init_render(wlr_output, server->allocator, server->renderer);
|
||||||
struct wlr_output_mode *mode =
|
|
||||||
wl_container_of(wlr_output->modes.prev, mode, link);
|
|
||||||
wlr_output_set_mode(wlr_output, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct fullscreen_output *output =
|
struct fullscreen_output *output =
|
||||||
calloc(1, sizeof(struct fullscreen_output));
|
calloc(1, sizeof(struct fullscreen_output));
|
||||||
|
@ -163,6 +160,13 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) {
|
||||||
|
|
||||||
wlr_output_layout_add_auto(server->output_layout, wlr_output);
|
wlr_output_layout_add_auto(server->output_layout, wlr_output);
|
||||||
wlr_output_create_global(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void server_handle_present_surface(struct wl_listener *listener,
|
static void server_handle_present_surface(struct wl_listener *listener,
|
||||||
|
@ -202,12 +206,13 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
struct fullscreen_server server = {0};
|
struct fullscreen_server server = {0};
|
||||||
server.wl_display = wl_display_create();
|
server.wl_display = wl_display_create();
|
||||||
server.backend = wlr_backend_autocreate(server.wl_display, NULL);
|
server.backend = wlr_backend_autocreate(server.wl_display);
|
||||||
server.renderer = wlr_backend_get_renderer(server.backend);
|
server.renderer = wlr_renderer_autocreate(server.backend);
|
||||||
wlr_renderer_init_wl_display(server.renderer, server.wl_display);
|
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);
|
wlr_compositor_create(server.wl_display, server.renderer);
|
||||||
wlr_linux_dmabuf_v1_create(server.wl_display, server.renderer);
|
|
||||||
|
|
||||||
server.output_layout = wlr_output_layout_create();
|
server.output_layout = wlr_output_layout_create();
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,6 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
wl_display_dispatch(display);
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
if (gamma_control_manager == NULL) {
|
if (gamma_control_manager == NULL) {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
#include <wlr/render/egl.h>
|
#include "egl_common.h"
|
||||||
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
||||||
#include "xdg-shell-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_inhibit_manager_v1 *idle_inhibit_manager = NULL;
|
||||||
static struct zwp_idle_inhibitor_v1 *idle_inhibitor = NULL;
|
static struct zwp_idle_inhibitor_v1 *idle_inhibitor = NULL;
|
||||||
|
|
||||||
struct wlr_egl egl;
|
|
||||||
struct wl_egl_window *egl_window;
|
struct wl_egl_window *egl_window;
|
||||||
struct wlr_egl_surface *egl_surface;
|
struct wlr_egl_surface *egl_surface;
|
||||||
|
|
||||||
static void draw(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};
|
float color[] = {1.0, 1.0, 0.0, 1.0};
|
||||||
if (idle_inhibitor) {
|
if (idle_inhibitor) {
|
||||||
|
@ -43,7 +42,7 @@ static void draw(void) {
|
||||||
glClearColor(color[0], color[1], color[2], 1.0);
|
glClearColor(color[0], color[1], color[2], 1.0);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
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,
|
static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
|
||||||
|
@ -177,7 +176,6 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
wl_display_dispatch(display);
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
if (compositor == NULL) {
|
if (compositor == NULL) {
|
||||||
|
@ -193,8 +191,7 @@ int main(int argc, char **argv) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
egl_init(display);
|
||||||
WL_SHM_FORMAT_ARGB8888);
|
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||||
struct xdg_surface *xdg_surface =
|
struct xdg_surface *xdg_surface =
|
||||||
|
@ -215,7 +212,8 @@ int main(int argc, char **argv) {
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
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);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = {
|
||||||
.resumed = handle_resume,
|
.resumed = handle_resume,
|
||||||
};
|
};
|
||||||
|
|
||||||
int parse_args(int argc, char *argv[]) {
|
static int parse_args(int argc, char *argv[]) {
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt(argc, argv, "c:hs:t:")) != -1) {
|
while ((c = getopt(argc, argv, "c:hs:t:")) != -1) {
|
||||||
switch(c)
|
switch(c)
|
||||||
|
@ -81,7 +81,7 @@ int parse_args(int argc, char *argv[]) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *simulate_activity(void *data) {
|
static void *simulate_activity(void *data) {
|
||||||
sleep(simulate_activity_timeout);
|
sleep(simulate_activity_timeout);
|
||||||
fprintf(stdout, "simulate user activity\n");
|
fprintf(stdout, "simulate user activity\n");
|
||||||
struct thread_args *arg = data;
|
struct thread_args *arg = data;
|
||||||
|
@ -90,7 +90,7 @@ void *simulate_activity(void *data) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *close_program(void *data) {
|
static void *close_program(void *data) {
|
||||||
sleep(close_timeout);
|
sleep(close_timeout);
|
||||||
struct thread_args *arg = data;
|
struct thread_args *arg = data;
|
||||||
org_kde_kwin_idle_timeout_release(arg->timer);
|
org_kde_kwin_idle_timeout_release(arg->timer);
|
||||||
|
@ -100,7 +100,7 @@ void *close_program(void *data) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *main_loop(void *data) {
|
static void *main_loop(void *data) {
|
||||||
struct wl_display *display = data;
|
struct wl_display *display = data;
|
||||||
while (wl_display_dispatch(display) != -1) {
|
while (wl_display_dispatch(display) != -1) {
|
||||||
;
|
;
|
||||||
|
@ -125,9 +125,8 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
wl_display_dispatch(display);
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
free(registry);
|
wl_registry_destroy(registry);
|
||||||
|
|
||||||
if (idle_manager == NULL) {
|
if (idle_manager == NULL) {
|
||||||
fprintf(stderr, "display doesn't support idle protocol\n");
|
fprintf(stderr, "display doesn't support idle protocol\n");
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
#include <wlr/render/egl.h>
|
#include "egl_common.h"
|
||||||
#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
|
#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
|
||||||
#include "xdg-shell-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_inhibit_manager_v1 *input_inhibit_manager = NULL;
|
||||||
static struct zwlr_input_inhibitor_v1 *input_inhibitor = NULL;
|
static struct zwlr_input_inhibitor_v1 *input_inhibitor = NULL;
|
||||||
|
|
||||||
struct wlr_egl egl;
|
|
||||||
struct wl_egl_window *egl_window;
|
struct wl_egl_window *egl_window;
|
||||||
struct wlr_egl_surface *egl_surface;
|
struct wlr_egl_surface *egl_surface;
|
||||||
|
|
||||||
static void render_frame(void) {
|
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);
|
glViewport(0, 0, width, height);
|
||||||
if (keys) {
|
if (keys) {
|
||||||
|
@ -33,7 +32,7 @@ static void render_frame(void) {
|
||||||
}
|
}
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
eglSwapBuffers(egl.display, egl_surface);
|
eglSwapBuffers(egl_display, egl_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xdg_surface_handle_configure(void *data,
|
static void xdg_surface_handle_configure(void *data,
|
||||||
|
@ -150,7 +149,6 @@ int main(int argc, char **argv) {
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
assert(registry);
|
assert(registry);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
wl_display_dispatch(display);
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
assert(compositor && seat && wm_base && input_inhibit_manager);
|
assert(compositor && seat && wm_base && input_inhibit_manager);
|
||||||
|
|
||||||
|
@ -158,8 +156,7 @@ int main(int argc, char **argv) {
|
||||||
input_inhibit_manager);
|
input_inhibit_manager);
|
||||||
assert(input_inhibitor);
|
assert(input_inhibitor);
|
||||||
|
|
||||||
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
egl_init(display);
|
||||||
WL_SHM_FORMAT_ARGB8888);
|
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||||
assert(surface);
|
assert(surface);
|
||||||
|
@ -175,7 +172,8 @@ int main(int argc, char **argv) {
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
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);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,216 @@
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
#include "input-method-unstable-v2-client-protocol.h"
|
||||||
|
|
||||||
|
static struct wl_display *display = NULL;
|
||||||
|
static struct wl_seat *seat = NULL;
|
||||||
|
static struct zwp_input_method_manager_v2 *input_method_manager = NULL;
|
||||||
|
static struct zwp_input_method_v2 *input_method = NULL;
|
||||||
|
static struct zwp_input_method_keyboard_grab_v2 *kb_grab = NULL;
|
||||||
|
|
||||||
|
static bool active = false;
|
||||||
|
static bool pending_active = false;
|
||||||
|
|
||||||
|
static struct xkb_context *xkb_context = NULL;
|
||||||
|
static struct xkb_keymap *keymap = NULL;
|
||||||
|
static struct xkb_state *xkb_state = NULL;
|
||||||
|
|
||||||
|
static void handle_key(void *data,
|
||||||
|
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
|
||||||
|
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
|
||||||
|
printf("handle_key %u %u %u %u\n", serial, time, key, state);
|
||||||
|
xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, key + 8);
|
||||||
|
char keysym_name[64];
|
||||||
|
xkb_keysym_get_name(keysym, keysym_name, sizeof(keysym_name));
|
||||||
|
printf("xkb translated to %s\n", keysym_name);
|
||||||
|
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||||
|
if (keysym == XKB_KEY_KP_Enter || keysym == XKB_KEY_Return) {
|
||||||
|
printf("Stopping grab\n");
|
||||||
|
zwp_input_method_keyboard_grab_v2_release(kb_grab);
|
||||||
|
kb_grab = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_modifiers(void *data,
|
||||||
|
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
|
||||||
|
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
|
||||||
|
uint32_t mods_locked, uint32_t group) {
|
||||||
|
printf("handle_modifiers %u %u %u %u %u\n", serial, mods_depressed,
|
||||||
|
mods_latched, mods_locked, group);
|
||||||
|
xkb_state_update_mask(xkb_state, mods_depressed, mods_latched,
|
||||||
|
mods_locked, 0, 0, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_keymap(void *data,
|
||||||
|
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
|
||||||
|
uint32_t format, int32_t fd, uint32_t size) {
|
||||||
|
printf("handle_keymap\n");
|
||||||
|
char *keymap_string = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||||
|
xkb_keymap_unref(keymap);
|
||||||
|
keymap = xkb_keymap_new_from_string(xkb_context, keymap_string,
|
||||||
|
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
|
munmap(keymap_string, size);
|
||||||
|
close(fd);
|
||||||
|
xkb_state_unref(xkb_state);
|
||||||
|
xkb_state = xkb_state_new(keymap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_repeat_info(void *data,
|
||||||
|
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
|
||||||
|
int32_t rate, int32_t delay) {
|
||||||
|
printf("handle_repeat_info %d %d", rate, delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const struct zwp_input_method_keyboard_grab_v2_listener grab_listener = {
|
||||||
|
.key = handle_key,
|
||||||
|
.modifiers = handle_modifiers,
|
||||||
|
.keymap = handle_keymap,
|
||||||
|
.repeat_info = handle_repeat_info,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void handle_activate(void *data,
|
||||||
|
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
||||||
|
pending_active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_deactivate(void *data,
|
||||||
|
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
||||||
|
pending_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_unavailable(void *data,
|
||||||
|
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
||||||
|
printf("IM unavailable\n");
|
||||||
|
zwp_input_method_v2_destroy(zwp_input_method_v2);
|
||||||
|
input_method = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void im_activate(void *data,
|
||||||
|
struct zwp_input_method_v2 *id) {
|
||||||
|
kb_grab = zwp_input_method_v2_grab_keyboard(input_method);
|
||||||
|
if (kb_grab == NULL) {
|
||||||
|
fprintf(stderr, "Failed to grab\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
zwp_input_method_keyboard_grab_v2_add_listener(kb_grab, &grab_listener,
|
||||||
|
NULL);
|
||||||
|
printf("Started grab, press enter to stop grab\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void im_deactivate(void *data,
|
||||||
|
struct zwp_input_method_v2 *context) {
|
||||||
|
if (kb_grab != NULL) {
|
||||||
|
zwp_input_method_keyboard_grab_v2_release(kb_grab);
|
||||||
|
kb_grab = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_done(void *data,
|
||||||
|
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
||||||
|
bool prev_active = active;
|
||||||
|
if (active != pending_active) {
|
||||||
|
printf("Now %s\n", pending_active ? "active" : "inactive");
|
||||||
|
}
|
||||||
|
active = pending_active;
|
||||||
|
if (active && !prev_active) {
|
||||||
|
im_activate(data, zwp_input_method_v2);
|
||||||
|
} else if (!active && prev_active) {
|
||||||
|
im_deactivate(data, zwp_input_method_v2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_surrounding_text(void *data,
|
||||||
|
struct zwp_input_method_v2 *zwp_input_method_v2,
|
||||||
|
const char *text, uint32_t cursor, uint32_t anchor) {
|
||||||
|
// not for this test
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_text_change_cause(void *data,
|
||||||
|
struct zwp_input_method_v2 *zwp_input_method_v2,
|
||||||
|
uint32_t cause) {
|
||||||
|
// not for this test
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_content_type(void *data,
|
||||||
|
struct zwp_input_method_v2 *zwp_input_method_v2,
|
||||||
|
uint32_t hint, uint32_t purpose) {
|
||||||
|
// not for this test
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwp_input_method_v2_listener im_listener = {
|
||||||
|
.activate = handle_activate,
|
||||||
|
.deactivate = handle_deactivate,
|
||||||
|
.surrounding_text = handle_surrounding_text,
|
||||||
|
.text_change_cause = handle_text_change_cause,
|
||||||
|
.content_type = handle_content_type,
|
||||||
|
.done = handle_done,
|
||||||
|
.unavailable = handle_unavailable,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void handle_global(void *data, struct wl_registry *registry,
|
||||||
|
uint32_t name, const char *interface, uint32_t version) {
|
||||||
|
if (strcmp(interface, zwp_input_method_manager_v2_interface.name) == 0) {
|
||||||
|
input_method_manager = wl_registry_bind(registry, name,
|
||||||
|
&zwp_input_method_manager_v2_interface, 1);
|
||||||
|
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
||||||
|
seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_global_remove(void *data, struct wl_registry *registry,
|
||||||
|
uint32_t name) {
|
||||||
|
// who cares
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_registry_listener registry_listener = {
|
||||||
|
.global = handle_global,
|
||||||
|
.global_remove = handle_global_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
|
if (xkb_context == NULL) {
|
||||||
|
fprintf(stderr, "Failed to create xkb context\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
display = wl_display_connect(NULL);
|
||||||
|
if (display == NULL) {
|
||||||
|
fprintf(stderr, "Failed to create display\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
if (input_method_manager == NULL) {
|
||||||
|
fprintf(stderr, "input-method not available\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (seat == NULL) {
|
||||||
|
fprintf(stderr, "seat not available\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_method = zwp_input_method_manager_v2_get_input_method(
|
||||||
|
input_method_manager, seat);
|
||||||
|
zwp_input_method_v2_add_listener(input_method, &im_listener, NULL);
|
||||||
|
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
while (wl_display_dispatch(display) != -1) {
|
||||||
|
// This space is intentionally left blank
|
||||||
|
};
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
|
@ -324,7 +324,6 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
wl_display_dispatch(display);
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
if (compositor == NULL) {
|
if (compositor == NULL) {
|
||||||
|
|
|
@ -0,0 +1,261 @@
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <wayland-egl.h>
|
||||||
|
#include "egl_common.h"
|
||||||
|
#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
|
||||||
|
#include "xdg-shell-client-protocol.h"
|
||||||
|
|
||||||
|
#include <linux/input-event-codes.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Usage: keyboard-shortcuts-inhibit
|
||||||
|
* Creates a xdg-toplevel using the keyboard-shortcuts-inhibit protocol.
|
||||||
|
* It will be solid green, when it has an keyboard shortcuts inhibitor, and
|
||||||
|
* solid yellow if it does not.
|
||||||
|
* Left click with a pointer will toggle this state. (Touch is not supported
|
||||||
|
* for now).
|
||||||
|
* The compositor (de-)activating the inhibitor will also toggle state.
|
||||||
|
* With a compositor supporting the protocol, compositor shortcuts will be
|
||||||
|
* suspended while the inhibitor is active and the window has focus.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int width = 500, height = 300;
|
||||||
|
|
||||||
|
static struct wl_compositor *compositor = NULL;
|
||||||
|
static struct wl_seat *seat = NULL;
|
||||||
|
static struct xdg_wm_base *wm_base = NULL;
|
||||||
|
static struct zwp_keyboard_shortcuts_inhibit_manager_v1 *
|
||||||
|
keyboard_shortcuts_inhibit_manager = NULL;
|
||||||
|
static struct zwp_keyboard_shortcuts_inhibitor_v1 *
|
||||||
|
keyboard_shortcuts_inhibitor = NULL;
|
||||||
|
static bool active = false;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
float color[] = {1.0, 1.0, 0.0, 1.0};
|
||||||
|
if (keyboard_shortcuts_inhibitor) {
|
||||||
|
color[0] = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
glClearColor(color[0], color[1], color[2], 1.0);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
eglSwapBuffers(egl_display, egl_surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_shortcuts_inhibit_handle_active(void *data,
|
||||||
|
struct zwp_keyboard_shortcuts_inhibitor_v1 *
|
||||||
|
zwp_keyboard_shortcuts_inhibitor_v1) {
|
||||||
|
active = 1;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_shortcuts_inhibit_handle_inactive(void *data,
|
||||||
|
struct zwp_keyboard_shortcuts_inhibitor_v1 *
|
||||||
|
zwp_keyboard_shortcuts_inhibitor_v1) {
|
||||||
|
active = 0;
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwp_keyboard_shortcuts_inhibitor_v1_listener
|
||||||
|
keyboard_shortcuts_inhibitor_listener = {
|
||||||
|
.active = keyboard_shortcuts_inhibit_handle_active,
|
||||||
|
.inactive = keyboard_shortcuts_inhibit_handle_inactive,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
|
||||||
|
uint32_t time, uint32_t button, uint32_t state_w) {
|
||||||
|
struct wl_surface *surface = data;
|
||||||
|
|
||||||
|
if (button == BTN_LEFT && state_w == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||||
|
if (keyboard_shortcuts_inhibitor) {
|
||||||
|
zwp_keyboard_shortcuts_inhibitor_v1_destroy(
|
||||||
|
keyboard_shortcuts_inhibitor);
|
||||||
|
keyboard_shortcuts_inhibitor = NULL;
|
||||||
|
active = false;
|
||||||
|
} else {
|
||||||
|
keyboard_shortcuts_inhibitor =
|
||||||
|
zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
|
||||||
|
keyboard_shortcuts_inhibit_manager, surface, seat);
|
||||||
|
zwp_keyboard_shortcuts_inhibitor_v1_add_listener(
|
||||||
|
keyboard_shortcuts_inhibitor,
|
||||||
|
&keyboard_shortcuts_inhibitor_listener, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
|
||||||
|
uint32_t serial, struct wl_surface *surface,
|
||||||
|
wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
||||||
|
// This space intentionally left blank
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
|
||||||
|
uint32_t serial, struct wl_surface *surface) {
|
||||||
|
// This space intentionally left blank
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
|
||||||
|
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
||||||
|
// This space intentionally left blank
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
|
||||||
|
uint32_t time, uint32_t axis, wl_fixed_t value) {
|
||||||
|
// This space intentionally left blank
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
|
||||||
|
// This space intentionally left blank
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_axis_source(void *data,
|
||||||
|
struct wl_pointer *wl_pointer, uint32_t axis_source) {
|
||||||
|
// This space intentionally left blank
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_axis_stop(void *data,
|
||||||
|
struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) {
|
||||||
|
// This space intentionally left blank
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_axis_discrete(void *data,
|
||||||
|
struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
|
||||||
|
// This space intentionally left blank
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_pointer_listener pointer_listener = {
|
||||||
|
.enter = pointer_handle_enter,
|
||||||
|
.leave = pointer_handle_leave,
|
||||||
|
.motion = pointer_handle_motion,
|
||||||
|
.button = pointer_handle_button,
|
||||||
|
.axis = pointer_handle_axis,
|
||||||
|
.frame = pointer_handle_frame,
|
||||||
|
.axis_source = pointer_handle_axis_source,
|
||||||
|
.axis_stop = pointer_handle_axis_stop,
|
||||||
|
.axis_discrete = pointer_handle_axis_discrete,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void xdg_surface_handle_configure(void *data,
|
||||||
|
struct xdg_surface *xdg_surface, uint32_t serial) {
|
||||||
|
xdg_surface_ack_configure(xdg_surface, serial);
|
||||||
|
wl_egl_window_resize(egl_window, width, height, 0, 0);
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct xdg_surface_listener xdg_surface_listener = {
|
||||||
|
.configure = xdg_surface_handle_configure,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void xdg_toplevel_handle_configure(void *data,
|
||||||
|
struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h,
|
||||||
|
struct wl_array *states) {
|
||||||
|
width = w;
|
||||||
|
height = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_toplevel_handle_close(void *data,
|
||||||
|
struct xdg_toplevel *xdg_toplevel) {
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
||||||
|
.configure = xdg_toplevel_handle_configure,
|
||||||
|
.close = xdg_toplevel_handle_close,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void handle_global(void *data, struct wl_registry *registry,
|
||||||
|
uint32_t name, const char *interface, uint32_t version) {
|
||||||
|
if (strcmp(interface, "wl_compositor") == 0) {
|
||||||
|
compositor = wl_registry_bind(registry, name,
|
||||||
|
&wl_compositor_interface, 1);
|
||||||
|
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
||||||
|
wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
|
||||||
|
} else if (strcmp(interface, zwp_keyboard_shortcuts_inhibit_manager_v1_interface.name) == 0) {
|
||||||
|
keyboard_shortcuts_inhibit_manager = wl_registry_bind(registry, name,
|
||||||
|
&zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
|
||||||
|
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
||||||
|
seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_global_remove(void *data, struct wl_registry *registry,
|
||||||
|
uint32_t name) {
|
||||||
|
// who cares
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_registry_listener registry_listener = {
|
||||||
|
.global = handle_global,
|
||||||
|
.global_remove = handle_global_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
struct wl_display *display = wl_display_connect(NULL);
|
||||||
|
if (display == NULL) {
|
||||||
|
fprintf(stderr, "Failed to create display\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
if (compositor == NULL) {
|
||||||
|
fprintf(stderr, "wl-compositor not available\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (wm_base == NULL) {
|
||||||
|
fprintf(stderr, "xdg-shell not available\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (keyboard_shortcuts_inhibit_manager == NULL) {
|
||||||
|
fprintf(stderr, "keyboard-shortcuts-inhibit not available\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
egl_init(display);
|
||||||
|
|
||||||
|
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||||
|
struct xdg_surface *xdg_surface =
|
||||||
|
xdg_wm_base_get_xdg_surface(wm_base, surface);
|
||||||
|
struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
|
||||||
|
|
||||||
|
struct wl_pointer *pointer = wl_seat_get_pointer(seat);
|
||||||
|
wl_pointer_add_listener(pointer, &pointer_listener, surface);
|
||||||
|
|
||||||
|
xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL);
|
||||||
|
xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
|
||||||
|
|
||||||
|
wl_surface_commit(surface);
|
||||||
|
|
||||||
|
egl_window = wl_egl_window_create(surface, width, height);
|
||||||
|
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
||||||
|
egl_display, egl_config, egl_window, NULL);
|
||||||
|
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
keyboard_shortcuts_inhibitor =
|
||||||
|
zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
|
||||||
|
keyboard_shortcuts_inhibit_manager, surface, seat);
|
||||||
|
zwp_keyboard_shortcuts_inhibitor_v1_add_listener(
|
||||||
|
keyboard_shortcuts_inhibitor,
|
||||||
|
&keyboard_shortcuts_inhibitor_listener, NULL);
|
||||||
|
|
||||||
|
draw();
|
||||||
|
|
||||||
|
while (wl_display_dispatch(display) != -1) {
|
||||||
|
// This space intentionally left blank
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
|
@ -11,8 +11,7 @@
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-cursor.h>
|
#include <wayland-cursor.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
#include <wlr/render/egl.h>
|
#include "egl_common.h"
|
||||||
#include <wlr/util/log.h>
|
|
||||||
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
#include "xdg-shell-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;
|
static struct wl_output *wl_output;
|
||||||
|
|
||||||
struct wl_surface *wl_surface;
|
struct wl_surface *wl_surface;
|
||||||
struct wlr_egl egl;
|
|
||||||
struct wl_egl_window *egl_window;
|
struct wl_egl_window *egl_window;
|
||||||
struct wlr_egl_surface *egl_surface;
|
struct wlr_egl_surface *egl_surface;
|
||||||
struct wl_callback *frame_callback;
|
struct wl_callback *frame_callback;
|
||||||
|
@ -50,7 +48,8 @@ static int32_t margin_top = 0;
|
||||||
static double alpha = 1.0;
|
static double alpha = 1.0;
|
||||||
static bool run_display = true;
|
static bool run_display = true;
|
||||||
static bool animate = false;
|
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 double frame = 0;
|
||||||
static int cur_x = -1, cur_y = -1;
|
static int cur_x = -1, cur_y = -1;
|
||||||
static int buttons = 0;
|
static int buttons = 0;
|
||||||
|
@ -93,7 +92,7 @@ static struct wl_callback_listener popup_frame_listener = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static void draw(void) {
|
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;
|
struct timespec ts;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
|
||||||
|
@ -142,7 +141,7 @@ static void draw(void) {
|
||||||
frame_callback = wl_surface_frame(wl_surface);
|
frame_callback = wl_surface_frame(wl_surface);
|
||||||
wl_callback_add_listener(frame_callback, &frame_listener, NULL);
|
wl_callback_add_listener(frame_callback, &frame_listener, NULL);
|
||||||
|
|
||||||
eglSwapBuffers(egl.display, egl_surface);
|
eglSwapBuffers(egl_display, egl_surface);
|
||||||
|
|
||||||
demo.last_frame = ts;
|
demo.last_frame = ts;
|
||||||
}
|
}
|
||||||
|
@ -150,7 +149,7 @@ static void draw(void) {
|
||||||
static void draw_popup(void) {
|
static void draw_popup(void) {
|
||||||
static float alpha_mod = -0.01;
|
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);
|
glViewport(0, 0, popup_width, popup_height);
|
||||||
glClearColor(popup_red, 0.5f, 0.5f, popup_alpha);
|
glClearColor(popup_red, 0.5f, 0.5f, popup_alpha);
|
||||||
popup_alpha += alpha_mod;
|
popup_alpha += alpha_mod;
|
||||||
|
@ -162,7 +161,7 @@ static void draw_popup(void) {
|
||||||
popup_frame_callback = wl_surface_frame(popup_wl_surface);
|
popup_frame_callback = wl_surface_frame(popup_wl_surface);
|
||||||
assert(popup_frame_callback);
|
assert(popup_frame_callback);
|
||||||
wl_callback_add_listener(popup_frame_callback, &popup_frame_listener, NULL);
|
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);
|
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,
|
static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup,
|
||||||
int32_t x, int32_t y, int32_t width, int32_t height) {
|
int32_t x, int32_t y, int32_t width, int32_t height) {
|
||||||
wlr_log(WLR_DEBUG, "Popup configured %dx%d@%d,%d",
|
fprintf(stderr, "Popup configured %dx%d@%d,%d\n", width, height, x, y);
|
||||||
width, height, x, y);
|
|
||||||
popup_width = width;
|
popup_width = width;
|
||||||
popup_height = height;
|
popup_height = height;
|
||||||
if (popup_egl_window) {
|
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) {
|
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);
|
wl_egl_window_destroy(popup_egl_window);
|
||||||
xdg_popup_destroy(popup);
|
xdg_popup_destroy(popup);
|
||||||
wl_surface_destroy(popup_wl_surface);
|
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) {
|
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();
|
popup_destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,8 +239,9 @@ static void create_popup(uint32_t serial) {
|
||||||
popup_wl_surface = surface;
|
popup_wl_surface = surface;
|
||||||
popup_egl_window = wl_egl_window_create(surface, popup_width, popup_height);
|
popup_egl_window = wl_egl_window_create(surface, popup_width, popup_height);
|
||||||
assert(popup_egl_window);
|
assert(popup_egl_window);
|
||||||
popup_egl_surface = wlr_egl_create_surface(&egl, popup_egl_window);
|
popup_egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
||||||
assert(popup_egl_surface);
|
egl_display, egl_config, popup_egl_window, NULL);
|
||||||
|
assert(popup_egl_surface != EGL_NO_SURFACE);
|
||||||
draw_popup();
|
draw_popup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,7 +258,7 @@ static void layer_surface_configure(void *data,
|
||||||
|
|
||||||
static void layer_surface_closed(void *data,
|
static void layer_surface_closed(void *data,
|
||||||
struct zwlr_layer_surface_v1 *surface) {
|
struct zwlr_layer_surface_v1 *surface) {
|
||||||
wlr_egl_destroy_surface(&egl, egl_surface);
|
eglDestroySurface(egl_display, egl_surface);
|
||||||
wl_egl_window_destroy(egl_window);
|
wl_egl_window_destroy(egl_window);
|
||||||
zwlr_layer_surface_v1_destroy(surface);
|
zwlr_layer_surface_v1_destroy(surface);
|
||||||
wl_surface_destroy(wl_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,
|
static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
|
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,
|
static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
uint32_t serial, struct wl_surface *surface) {
|
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,
|
static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
|
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,
|
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_interface, 1);
|
||||||
wl_seat_add_listener(seat, &seat_listener, NULL);
|
wl_seat_add_listener(seat, &seat_listener, NULL);
|
||||||
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
||||||
layer_shell = wl_registry_bind(
|
layer_shell = wl_registry_bind(registry, name,
|
||||||
registry, name, &zwlr_layer_shell_v1_interface, 1);
|
&zwlr_layer_shell_v1_interface, version < 4 ? version : 4);
|
||||||
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
||||||
xdg_wm_base = wl_registry_bind(
|
xdg_wm_base = wl_registry_bind(
|
||||||
registry, name, &xdg_wm_base_interface, 1);
|
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) {
|
int main(int argc, char **argv) {
|
||||||
wlr_log_init(WLR_DEBUG, NULL);
|
|
||||||
char *namespace = "wlroots";
|
char *namespace = "wlroots";
|
||||||
int exclusive_zone = 0;
|
int exclusive_zone = 0;
|
||||||
int32_t margin_right = 0, margin_bottom = 0, margin_left = 0;
|
int32_t margin_right = 0, margin_bottom = 0, margin_left = 0;
|
||||||
bool found;
|
bool found;
|
||||||
int c;
|
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) {
|
switch (c) {
|
||||||
case 'o':
|
case 'o':
|
||||||
output = atoi(optarg);
|
output = atoi(optarg);
|
||||||
|
@ -558,9 +556,29 @@ int main(int argc, char **argv) {
|
||||||
case 'n':
|
case 'n':
|
||||||
animate = true;
|
animate = true;
|
||||||
break;
|
break;
|
||||||
case 'k':
|
case 'k': {
|
||||||
keyboard_interactive = true;
|
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;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -610,9 +628,7 @@ int main(int argc, char **argv) {
|
||||||
cursor_surface = wl_compositor_create_surface(compositor);
|
cursor_surface = wl_compositor_create_surface(compositor);
|
||||||
assert(cursor_surface);
|
assert(cursor_surface);
|
||||||
|
|
||||||
EGLint attribs[] = { EGL_ALPHA_SIZE, 8, EGL_NONE };
|
egl_init(display);
|
||||||
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display,
|
|
||||||
attribs, WL_SHM_FORMAT_ARGB8888);
|
|
||||||
|
|
||||||
wl_surface = wl_compositor_create_surface(compositor);
|
wl_surface = wl_compositor_create_surface(compositor);
|
||||||
assert(wl_surface);
|
assert(wl_surface);
|
||||||
|
@ -634,8 +650,9 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(wl_surface, width, height);
|
egl_window = wl_egl_window_create(wl_surface, width, height);
|
||||||
assert(egl_window);
|
assert(egl_window);
|
||||||
egl_surface = wlr_egl_create_surface(&egl, egl_window);
|
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
||||||
assert(egl_surface);
|
egl_display, egl_config, egl_window, NULL);
|
||||||
|
assert(egl_surface != EGL_NO_SURFACE);
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
draw();
|
draw();
|
||||||
|
|
|
@ -1,85 +1,127 @@
|
||||||
threads = dependency('threads')
|
threads = dependency('threads')
|
||||||
|
wayland_egl = dependency('wayland-egl')
|
||||||
wayland_cursor = dependency('wayland-cursor')
|
wayland_cursor = dependency('wayland-cursor')
|
||||||
libpng = dependency('libpng', required: false)
|
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
|
# These versions correspond to ffmpeg 4.0
|
||||||
libavutil = dependency('libavutil', version: '>=56.14.100', required: false)
|
libavutil = dependency('libavutil', version: '>=56.14.100', required: false, disabler: true)
|
||||||
libavcodec = dependency('libavcodec', version: '>=58.18.100', required: false)
|
libavcodec = dependency('libavcodec', version: '>=58.18.100', required: false, disabler: true)
|
||||||
libavformat = dependency('libavformat', version: '>=58.12.100', required: false)
|
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
|
# epoll is a separate library in FreeBSD
|
||||||
if host_machine.system() == 'freebsd'
|
if host_machine.system() == 'freebsd'
|
||||||
libepoll = [dependency('epoll-shim')]
|
libepoll = dependency('epoll-shim')
|
||||||
else
|
else
|
||||||
libepoll = []
|
libepoll = dependency('', required: false)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Small hack until https://github.com/mesonbuild/meson/pull/3386/ is merged
|
|
||||||
foreach dep : ['libpng', 'libavutil', 'libavcodec', 'libavformat']
|
|
||||||
if not get_variable(dep).found()
|
|
||||||
set_variable(dep, disabler())
|
|
||||||
endif
|
|
||||||
endforeach
|
|
||||||
|
|
||||||
if not cc.has_header('libavutil/hwcontext_drm.h', dependencies: libavutil)
|
if not cc.has_header('libavutil/hwcontext_drm.h', dependencies: libavutil)
|
||||||
libavutil = disabler()
|
libavutil = disabler()
|
||||||
endif
|
endif
|
||||||
|
|
||||||
examples = {
|
compositors = {
|
||||||
'simple': {
|
'simple': {
|
||||||
'src': 'simple.c',
|
'src': 'simple.c',
|
||||||
'dep': [wlroots],
|
|
||||||
},
|
},
|
||||||
'pointer': {
|
'pointer': {
|
||||||
'src': 'pointer.c',
|
'src': 'pointer.c',
|
||||||
'dep': [wlroots],
|
|
||||||
},
|
},
|
||||||
'touch': {
|
'touch': {
|
||||||
'src': ['touch.c', 'cat.c'],
|
'src': ['touch.c', 'cat.c'],
|
||||||
'dep': [wlroots],
|
|
||||||
},
|
},
|
||||||
'tablet': {
|
'tablet': {
|
||||||
'src': 'tablet.c',
|
'src': 'tablet.c',
|
||||||
'dep': [wlroots],
|
|
||||||
},
|
},
|
||||||
'rotation': {
|
'rotation': {
|
||||||
'src': ['rotation.c', 'cat.c'],
|
'src': ['rotation.c', 'cat.c'],
|
||||||
'dep': [wlroots],
|
|
||||||
},
|
},
|
||||||
'multi-pointer': {
|
'multi-pointer': {
|
||||||
'src': 'multi-pointer.c',
|
'src': 'multi-pointer.c',
|
||||||
'dep': [wlroots],
|
|
||||||
},
|
},
|
||||||
'output-layout': {
|
'output-layout': {
|
||||||
'src': ['output-layout.c', 'cat.c'],
|
'src': ['output-layout.c', 'cat.c'],
|
||||||
'dep': [wlroots],
|
|
||||||
},
|
},
|
||||||
|
'fullscreen-shell': {
|
||||||
|
'src': 'fullscreen-shell.c',
|
||||||
|
'proto': ['fullscreen-shell-unstable-v1'],
|
||||||
|
},
|
||||||
|
'quads': {
|
||||||
|
'src': 'quads.c',
|
||||||
|
},
|
||||||
|
'scene-graph': {
|
||||||
|
'src': 'scene-graph.c',
|
||||||
|
'proto': ['xdg-shell'],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
clients = {
|
||||||
'idle': {
|
'idle': {
|
||||||
'src': 'idle.c',
|
'src': 'idle.c',
|
||||||
'dep': [wayland_client, wlr_protos, threads],
|
'dep': threads,
|
||||||
|
'proto': ['kde-idle'],
|
||||||
},
|
},
|
||||||
'idle-inhibit': {
|
'idle-inhibit': {
|
||||||
'src': 'idle-inhibit.c',
|
'src': ['idle-inhibit.c', 'egl_common.c'],
|
||||||
'dep': [wayland_client, wlr_protos, wlroots],
|
'dep': [wayland_egl, egl, glesv2],
|
||||||
|
'proto': [
|
||||||
|
'idle-inhibit-unstable-v1',
|
||||||
|
'xdg-shell',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'keyboard-shortcuts-inhibit': {
|
||||||
|
'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': {
|
'layer-shell': {
|
||||||
'src': 'layer-shell.c',
|
'src': ['layer-shell.c', 'egl_common.c'],
|
||||||
'dep': [wayland_client, wayland_cursor, wlr_protos, wlroots],
|
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
|
||||||
|
'proto': [
|
||||||
|
'wlr-layer-shell-unstable-v1',
|
||||||
|
'xdg-shell',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
'input-inhibitor': {
|
'input-inhibitor': {
|
||||||
'src': 'input-inhibitor.c',
|
'src': ['input-inhibitor.c', 'egl_common.c'],
|
||||||
'dep': [wayland_client, wayland_cursor, wlr_protos, wlroots],
|
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
|
||||||
|
'proto': [
|
||||||
|
'wlr-input-inhibitor-unstable-v1',
|
||||||
|
'xdg-shell',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
'gamma-control': {
|
'gamma-control': {
|
||||||
'src': 'gamma-control.c',
|
'src': 'gamma-control.c',
|
||||||
'dep': [wayland_client, wayland_cursor, wlr_protos, math],
|
'dep': [wayland_cursor, math],
|
||||||
|
'proto': ['wlr-gamma-control-unstable-v1'],
|
||||||
|
},
|
||||||
|
'output-power-management': {
|
||||||
|
'src': 'output-power-management.c',
|
||||||
|
'dep': [wayland_client],
|
||||||
|
'proto': ['wlr-output-power-management-unstable-v1'],
|
||||||
},
|
},
|
||||||
'pointer-constraints': {
|
'pointer-constraints': {
|
||||||
'src': 'pointer-constraints.c',
|
'src': ['pointer-constraints.c', 'egl_common.c'],
|
||||||
'dep': [wayland_client, wlr_protos, wlroots],
|
'dep': [wayland_egl, egl, glesv2],
|
||||||
|
'proto': [
|
||||||
|
'pointer-constraints-unstable-v1',
|
||||||
|
'xdg-shell',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
'relative-pointer': {
|
'relative-pointer': {
|
||||||
'src': 'relative-pointer-unstable-v1.c',
|
'src': ['relative-pointer-unstable-v1.c', 'egl_common.c'],
|
||||||
'dep': [wayland_client, wlr_protos, wlroots],
|
'dep': [wayland_egl, egl, glesv2],
|
||||||
|
'proto': [
|
||||||
|
'pointer-constraints-unstable-v1',
|
||||||
|
'relative-pointer-unstable-v1',
|
||||||
|
'xdg-shell',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
'dmabuf-capture': {
|
'dmabuf-capture': {
|
||||||
'src': 'dmabuf-capture.c',
|
'src': 'dmabuf-capture.c',
|
||||||
|
@ -87,51 +129,92 @@ examples = {
|
||||||
libavcodec,
|
libavcodec,
|
||||||
libavformat,
|
libavformat,
|
||||||
libavutil,
|
libavutil,
|
||||||
drm.partial_dependency(compile_args: true), # <drm_fourcc.h>
|
drm,
|
||||||
threads,
|
threads,
|
||||||
wayland_client,
|
|
||||||
wlr_protos,
|
|
||||||
],
|
],
|
||||||
|
'proto': ['wlr-export-dmabuf-unstable-v1'],
|
||||||
},
|
},
|
||||||
'screencopy': {
|
'screencopy': {
|
||||||
'src': 'screencopy.c',
|
'src': 'screencopy.c',
|
||||||
'dep': [libpng, wayland_client, wlr_protos, rt],
|
'dep': [libpng, rt],
|
||||||
|
'proto': ['wlr-screencopy-unstable-v1'],
|
||||||
|
},
|
||||||
|
'screencopy-dmabuf': {
|
||||||
|
'src': 'screencopy-dmabuf.c',
|
||||||
|
'dep': [libpng, rt, gbm, drm],
|
||||||
|
'proto': [
|
||||||
|
'wlr-screencopy-unstable-v1',
|
||||||
|
'linux-dmabuf-unstable-v1',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
'toplevel-decoration': {
|
'toplevel-decoration': {
|
||||||
'src': 'toplevel-decoration.c',
|
'src': ['toplevel-decoration.c', 'egl_common.c'],
|
||||||
'dep': [wayland_client, wlr_protos, wlroots],
|
'dep': [wayland_egl, egl, glesv2],
|
||||||
|
'proto': [
|
||||||
|
'xdg-decoration-unstable-v1',
|
||||||
|
'xdg-shell',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
'input-method': {
|
'input-method': {
|
||||||
'src': 'input-method.c',
|
'src': 'input-method.c',
|
||||||
'dep': [wayland_client, wlr_protos] + libepoll,
|
'dep': [wayland_egl, libepoll],
|
||||||
|
'proto': [
|
||||||
|
'input-method-unstable-v2',
|
||||||
|
'text-input-unstable-v3',
|
||||||
|
'xdg-shell',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
'text-input': {
|
'text-input': {
|
||||||
'src': 'text-input.c',
|
'src': ['text-input.c', 'egl_common.c'],
|
||||||
'dep': [wayland_cursor, wayland_client, wlr_protos, wlroots],
|
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
|
||||||
|
'proto': [
|
||||||
|
'text-input-unstable-v3',
|
||||||
|
'xdg-shell',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
'foreign-toplevel': {
|
'foreign-toplevel': {
|
||||||
'src': 'foreign-toplevel.c',
|
'src': 'foreign-toplevel.c',
|
||||||
'dep': [wayland_client, wlr_protos, wlroots],
|
'proto': ['wlr-foreign-toplevel-management-unstable-v1'],
|
||||||
},
|
},
|
||||||
'fullscreen-shell': {
|
'virtual-pointer': {
|
||||||
'src': 'fullscreen-shell.c',
|
'src': 'virtual-pointer.c',
|
||||||
'dep': [wlr_protos, wlroots],
|
'proto': ['wlr-virtual-pointer-unstable-v1'],
|
||||||
|
},
|
||||||
|
'input-method-keyboard-grab': {
|
||||||
|
'src': 'input-method-keyboard-grab.c',
|
||||||
|
'dep': xkbcommon,
|
||||||
|
'proto': [
|
||||||
|
'input-method-unstable-v2',
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach name, info : examples
|
foreach name, info : compositors
|
||||||
all_dep_found = true
|
extra_src = []
|
||||||
foreach d : info.get('dep')
|
foreach p : info.get('proto', [])
|
||||||
all_dep_found = all_dep_found and d.found()
|
extra_src += protocols_server_header[p]
|
||||||
endforeach
|
endforeach
|
||||||
if all_dep_found
|
|
||||||
executable(
|
executable(
|
||||||
name,
|
name,
|
||||||
info.get('src'),
|
[info.get('src'), extra_src],
|
||||||
dependencies: info.get('dep'),
|
dependencies: [wlroots, libdrm],
|
||||||
build_by_default: get_option('examples'),
|
include_directories: [wlr_inc, proto_inc],
|
||||||
)
|
build_by_default: get_option('examples'),
|
||||||
else
|
)
|
||||||
warning('Dependencies not satisfied for ' + name)
|
endforeach
|
||||||
endif
|
|
||||||
|
foreach name, info : clients
|
||||||
|
extra_src = []
|
||||||
|
foreach p : info.get('proto')
|
||||||
|
extra_src += protocols_code[p]
|
||||||
|
extra_src += protocols_client_header[p]
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
executable(
|
||||||
|
name,
|
||||||
|
[info.get('src'), extra_src],
|
||||||
|
dependencies: [wayland_client, info.get('dep', [])],
|
||||||
|
build_by_default: get_option('examples'),
|
||||||
|
)
|
||||||
endforeach
|
endforeach
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200112L
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <GLES2/gl2.h>
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -10,13 +9,13 @@
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/render/gles2.h>
|
#include <wlr/render/allocator.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_cursor.h>
|
#include <wlr/types/wlr_cursor.h>
|
||||||
#include <wlr/types/wlr_keyboard.h>
|
#include <wlr/types/wlr_keyboard.h>
|
||||||
#include <wlr/types/wlr_list.h>
|
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
|
#include <wlr/types/wlr_pointer.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <wlr/xcursor.h>
|
#include <wlr/xcursor.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
@ -24,6 +23,8 @@
|
||||||
struct sample_state {
|
struct sample_state {
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
struct wlr_xcursor *xcursor;
|
struct wlr_xcursor *xcursor;
|
||||||
|
struct wlr_renderer *renderer;
|
||||||
|
struct wlr_allocator *allocator;
|
||||||
float default_color[4];
|
float default_color[4];
|
||||||
float clear_color[4];
|
float clear_color[4];
|
||||||
struct wlr_output_layout *layout;
|
struct wlr_output_layout *layout;
|
||||||
|
@ -68,10 +69,10 @@ struct sample_keyboard {
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
};
|
};
|
||||||
|
|
||||||
void configure_cursor(struct wlr_cursor *cursor, struct wlr_input_device *device,
|
static void configure_cursor(struct wlr_cursor *cursor, struct wlr_input_device *device,
|
||||||
struct sample_state *sample) {
|
struct sample_state *sample) {
|
||||||
struct sample_output *output;
|
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
|
// reset mappings
|
||||||
wlr_cursor_map_to_output(cursor, NULL);
|
wlr_cursor_map_to_output(cursor, NULL);
|
||||||
|
@ -88,18 +89,20 @@ void configure_cursor(struct wlr_cursor *cursor, struct wlr_input_device *device
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void output_frame_notify(struct wl_listener *listener, void *data) {
|
static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *output = wl_container_of(listener, output, frame);
|
struct sample_output *output = wl_container_of(listener, output, frame);
|
||||||
struct sample_state *sample = output->sample;
|
struct sample_state *sample = output->sample;
|
||||||
struct wlr_output *wlr_output = output->output;
|
struct wlr_output *wlr_output = output->output;
|
||||||
|
struct wlr_renderer *renderer = sample->renderer;
|
||||||
|
|
||||||
wlr_output_attach_render(wlr_output, NULL);
|
wlr_output_attach_render(wlr_output, NULL);
|
||||||
|
|
||||||
glClearColor(sample->clear_color[0], sample->clear_color[1],
|
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
|
||||||
sample->clear_color[2], sample->clear_color[3]);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
wlr_renderer_clear(renderer, sample->clear_color);
|
||||||
|
|
||||||
wlr_output_render_software_cursors(wlr_output, NULL);
|
wlr_output_render_software_cursors(wlr_output, NULL);
|
||||||
|
wlr_renderer_end(renderer);
|
||||||
wlr_output_commit(wlr_output);
|
wlr_output_commit(wlr_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,27 +130,7 @@ static void cursor_destroy(struct sample_cursor *cursor) {
|
||||||
free(cursor);
|
free(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void input_remove_notify(struct wl_listener *listener, void *data) {
|
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_input_device *device = data;
|
|
||||||
struct sample_cursor *sample_cursor = wl_container_of(listener, sample_cursor, destroy);
|
|
||||||
struct sample_state *sample = sample_cursor->sample;
|
|
||||||
struct sample_cursor *cursor;
|
|
||||||
wl_list_for_each(cursor, &sample->cursors, link) {
|
|
||||||
if (cursor->device == device) {
|
|
||||||
cursor_destroy(cursor);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
struct sample_pointer *pointer;
|
|
||||||
wl_list_for_each(pointer, &sample->pointers, link) {
|
|
||||||
if (pointer->device == device) {
|
|
||||||
free(pointer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void output_remove_notify(struct wl_listener *listener, void *data) {
|
|
||||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
||||||
struct sample_state *sample = sample_output->sample;
|
struct sample_state *sample = sample_output->sample;
|
||||||
wl_list_remove(&sample_output->frame.link);
|
wl_list_remove(&sample_output->frame.link);
|
||||||
|
@ -161,14 +144,13 @@ void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void new_output_notify(struct wl_listener *listener, void *data) {
|
static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_output *output = data;
|
struct wlr_output *output = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
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 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->output = output;
|
||||||
sample_output->sample = sample;
|
sample_output->sample = sample;
|
||||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||||
|
@ -178,7 +160,6 @@ void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
|
|
||||||
wlr_output_layout_add_auto(sample->layout, output);
|
wlr_output_layout_add_auto(sample->layout, output);
|
||||||
|
|
||||||
|
|
||||||
struct sample_cursor *cursor;
|
struct sample_cursor *cursor;
|
||||||
wl_list_for_each(cursor, &sample->cursors, link) {
|
wl_list_for_each(cursor, &sample->cursors, link) {
|
||||||
configure_cursor(cursor->cursor, cursor->device, sample);
|
configure_cursor(cursor->cursor, cursor->device, sample);
|
||||||
|
@ -191,9 +172,16 @@ void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
cursor->cursor->y);
|
cursor->cursor->y);
|
||||||
}
|
}
|
||||||
wl_list_insert(&sample->outputs, &sample_output->link);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
||||||
struct sample_state *sample = keyboard->sample;
|
struct sample_state *sample = keyboard->sample;
|
||||||
struct wlr_event_keyboard_key *event = data;
|
struct wlr_event_keyboard_key *event = data;
|
||||||
|
@ -209,14 +197,14 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
||||||
wl_list_remove(&keyboard->destroy.link);
|
wl_list_remove(&keyboard->destroy.link);
|
||||||
wl_list_remove(&keyboard->key.link);
|
wl_list_remove(&keyboard->key.link);
|
||||||
free(keyboard);
|
free(keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void new_input_notify(struct wl_listener *listener, void *data) {
|
static void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_input_device *device = data;
|
struct wlr_input_device *device = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
|
@ -228,18 +216,12 @@ void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||||
keyboard->key.notify = keyboard_key_notify;
|
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);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||||
exit(1);
|
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);
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
if (!keymap) {
|
if (!keymap) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||||
|
@ -289,10 +271,14 @@ int main(int argc, char *argv[]) {
|
||||||
.clear_color = { 0.25f, 0.25f, 0.25f, 1 },
|
.clear_color = { 0.25f, 0.25f, 0.25f, 1 },
|
||||||
.display = display,
|
.display = display,
|
||||||
};
|
};
|
||||||
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
||||||
if (!wlr) {
|
if (!wlr) {
|
||||||
exit(1);
|
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.cursors);
|
||||||
wl_list_init(&state.pointers);
|
wl_list_init(&state.pointers);
|
||||||
wl_list_init(&state.outputs);
|
wl_list_init(&state.outputs);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200112L
|
||||||
#include <GLES2/gl2.h>
|
#include <drm_fourcc.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -11,12 +11,14 @@
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
|
#include <wlr/render/allocator.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_keyboard.h>
|
#include <wlr/types/wlr_keyboard.h>
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/types/wlr_input_device.h>
|
#include <wlr/types/wlr_input_device.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
#include <wlr/types/wlr_output.h>
|
#include <wlr/types/wlr_output.h>
|
||||||
|
#include <wlr/util/box.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
#include "cat.h"
|
#include "cat.h"
|
||||||
|
@ -26,6 +28,7 @@ struct sample_state {
|
||||||
struct wl_listener new_output;
|
struct wl_listener new_output;
|
||||||
struct wl_listener new_input;
|
struct wl_listener new_input;
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
|
struct wlr_allocator *allocator;
|
||||||
struct wlr_texture *cat_texture;
|
struct wlr_texture *cat_texture;
|
||||||
struct wlr_output_layout *layout;
|
struct wlr_output_layout *layout;
|
||||||
float x_offs, y_offs;
|
float x_offs, y_offs;
|
||||||
|
@ -106,7 +109,7 @@ static void animate_cat(struct sample_state *sample,
|
||||||
sample->ts_last = ts;
|
sample->ts_last = ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
void output_frame_notify(struct wl_listener *listener, void *data) {
|
static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *output = wl_container_of(listener, output, frame);
|
struct sample_output *output = wl_container_of(listener, output, frame);
|
||||||
struct sample_state *sample = output->sample;
|
struct sample_state *sample = output->sample;
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
|
@ -145,7 +148,7 @@ static void update_velocities(struct sample_state *sample,
|
||||||
sample->y_vel += y_diff;
|
sample->y_vel += y_diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
void output_remove_notify(struct wl_listener *listener, void *data) {
|
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
||||||
struct sample_state *sample = sample_output->sample;
|
struct sample_state *sample = sample_output->sample;
|
||||||
wlr_output_layout_remove(sample->layout, sample_output->output);
|
wlr_output_layout_remove(sample->layout, sample_output->output);
|
||||||
|
@ -154,14 +157,13 @@ void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
free(sample_output);
|
free(sample_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void new_output_notify(struct wl_listener *listener, void *data) {
|
static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_output *output = data;
|
struct wlr_output *output = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
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 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);
|
wlr_output_layout_add_auto(sample->layout, output);
|
||||||
sample_output->output = output;
|
sample_output->output = output;
|
||||||
sample_output->sample = sample;
|
sample_output->sample = sample;
|
||||||
|
@ -169,9 +171,16 @@ void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
sample_output->frame.notify = output_frame_notify;
|
sample_output->frame.notify = output_frame_notify;
|
||||||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
||||||
sample_output->destroy.notify = output_remove_notify;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
||||||
struct sample_state *sample = keyboard->sample;
|
struct sample_state *sample = keyboard->sample;
|
||||||
struct wlr_event_keyboard_key *event = data;
|
struct wlr_event_keyboard_key *event = data;
|
||||||
|
@ -188,7 +197,7 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
// and make this change in pixels/sec^2
|
// and make this change in pixels/sec^2
|
||||||
// Also, key repeat
|
// Also, key repeat
|
||||||
int delta = 75;
|
int delta = 75;
|
||||||
if (event->state == WLR_KEY_PRESSED) {
|
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||||
switch (sym) {
|
switch (sym) {
|
||||||
case XKB_KEY_Left:
|
case XKB_KEY_Left:
|
||||||
update_velocities(sample, -delta, 0);
|
update_velocities(sample, -delta, 0);
|
||||||
|
@ -207,14 +216,14 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
||||||
wl_list_remove(&keyboard->destroy.link);
|
wl_list_remove(&keyboard->destroy.link);
|
||||||
wl_list_remove(&keyboard->key.link);
|
wl_list_remove(&keyboard->key.link);
|
||||||
free(keyboard);
|
free(keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void new_input_notify(struct wl_listener *listener, void *data) {
|
static void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_input_device *device = data;
|
struct wlr_input_device *device = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
|
@ -226,18 +235,12 @@ void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||||
keyboard->key.notify = keyboard_key_notify;
|
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);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||||
exit(1);
|
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);
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
if (!keymap) {
|
if (!keymap) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||||
|
@ -265,7 +268,7 @@ int main(int argc, char *argv[]) {
|
||||||
state.layout = wlr_output_layout_create();
|
state.layout = wlr_output_layout_create();
|
||||||
clock_gettime(CLOCK_MONOTONIC, &state.ts_last);
|
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) {
|
if (!wlr) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -275,11 +278,13 @@ int main(int argc, char *argv[]) {
|
||||||
wl_signal_add(&wlr->events.new_input, &state.new_input);
|
wl_signal_add(&wlr->events.new_input, &state.new_input);
|
||||||
state.new_input.notify = new_input_notify;
|
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,
|
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);
|
cat_tex.pixel_data);
|
||||||
|
|
||||||
|
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
||||||
|
|
||||||
if (!wlr_backend_start(wlr)) {
|
if (!wlr_backend_start(wlr)) {
|
||||||
wlr_log(WLR_ERROR, "Failed to start backend");
|
wlr_log(WLR_ERROR, "Failed to start backend");
|
||||||
wlr_backend_destroy(wlr);
|
wlr_backend_destroy(wlr);
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wayland-client-protocol.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include "wlr-output-power-management-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
struct output {
|
||||||
|
struct wl_output *wl_output;
|
||||||
|
struct zwlr_output_power_v1 *output_power;
|
||||||
|
struct wl_list link;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct wl_list outputs;
|
||||||
|
static struct zwlr_output_power_manager_v1 *output_power_manager = NULL;
|
||||||
|
|
||||||
|
static void output_power_handle_mode(void *data,
|
||||||
|
struct zwlr_output_power_v1 *output_power,
|
||||||
|
enum zwlr_output_power_v1_mode mode) {
|
||||||
|
struct output *output = data;
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case ZWLR_OUTPUT_POWER_V1_MODE_OFF:
|
||||||
|
printf("Output %p disabled\n", output);
|
||||||
|
break;
|
||||||
|
case ZWLR_OUTPUT_POWER_V1_MODE_ON:
|
||||||
|
printf("Output %p enabled\n", output);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_power_handle_failed(void *data,
|
||||||
|
struct zwlr_output_power_v1 *output_power) {
|
||||||
|
fprintf(stderr, "failed to set output power mode\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwlr_output_power_v1_listener output_power_listener = {
|
||||||
|
.mode = output_power_handle_mode,
|
||||||
|
.failed = output_power_handle_failed,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void registry_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 output *output = calloc(1, sizeof(struct output));
|
||||||
|
output->wl_output = wl_registry_bind(registry, name,
|
||||||
|
&wl_output_interface, 1);
|
||||||
|
wl_list_insert(&outputs, &output->link);
|
||||||
|
} else if (strcmp(interface,
|
||||||
|
zwlr_output_power_manager_v1_interface.name) == 0) {
|
||||||
|
output_power_manager = wl_registry_bind(registry, name,
|
||||||
|
&zwlr_output_power_manager_v1_interface, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void registry_handle_global_remove(void *data,
|
||||||
|
struct wl_registry *registry, uint32_t name) {
|
||||||
|
// Who cares?
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_registry_listener registry_listener = {
|
||||||
|
.global = registry_handle_global,
|
||||||
|
.global_remove = registry_handle_global_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char usage[] = "usage: output-power-management [options...]\n"
|
||||||
|
" -h: show this help message\n"
|
||||||
|
" -e: turn outputs on\n"
|
||||||
|
" -d: turn outputs off\n"
|
||||||
|
" -w: continuously watch for power mode changes\n";
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
wl_list_init(&outputs);
|
||||||
|
|
||||||
|
int opt;
|
||||||
|
enum zwlr_output_power_v1_mode mode =
|
||||||
|
ZWLR_OUTPUT_POWER_V1_MODE_ON;
|
||||||
|
bool watch_mode = false;
|
||||||
|
while ((opt = getopt(argc, argv, "edhw")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'e':
|
||||||
|
mode = ZWLR_OUTPUT_POWER_V1_MODE_ON;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
mode = ZWLR_OUTPUT_POWER_V1_MODE_OFF;
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
watch_mode = true;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
default:
|
||||||
|
fprintf(stderr, usage);
|
||||||
|
return opt == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_display *display = wl_display_connect(NULL);
|
||||||
|
if (display == NULL) {
|
||||||
|
fprintf(stderr, "failed to create display\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
if (output_power_manager == NULL) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"compositor doesn't support wlr-output-power-management-unstable-v1\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct output *output;
|
||||||
|
wl_list_for_each(output, &outputs, link) {
|
||||||
|
output->output_power = zwlr_output_power_manager_v1_get_output_power(
|
||||||
|
output_power_manager, output->wl_output);
|
||||||
|
zwlr_output_power_v1_add_listener(output->output_power,
|
||||||
|
&output_power_listener, output);
|
||||||
|
}
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
wl_list_for_each(output, &outputs, link) {
|
||||||
|
zwlr_output_power_v1_set_mode(output->output_power,
|
||||||
|
mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!watch_mode) {
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (wl_display_dispatch(display) != -1) {
|
||||||
|
// nothing to see here, please move along
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
#include <wlr/render/egl.h>
|
#include "egl_common.h"
|
||||||
#include "xdg-shell-client-protocol.h"
|
#include "xdg-shell-client-protocol.h"
|
||||||
#include "pointer-constraints-unstable-v1-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 xdg_wm_base *wm_base = NULL;
|
||||||
static struct zwp_pointer_constraints_v1 *pointer_constraints = NULL;
|
static struct zwp_pointer_constraints_v1 *pointer_constraints = NULL;
|
||||||
|
|
||||||
struct wlr_egl egl;
|
|
||||||
struct wl_egl_window *egl_window;
|
struct wl_egl_window *egl_window;
|
||||||
struct wlr_egl_surface *egl_surface;
|
struct wlr_egl_surface *egl_surface;
|
||||||
struct zwp_locked_pointer_v1* locked_pointer;
|
struct zwp_locked_pointer_v1* locked_pointer;
|
||||||
|
@ -32,7 +31,7 @@ enum {
|
||||||
struct wl_region *regions[3];
|
struct wl_region *regions[3];
|
||||||
|
|
||||||
static void draw(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};
|
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);
|
glClearColor(color[0], color[1], color[2], 1.0);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
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,
|
static void pointer_handle_button(void *data, struct wl_pointer *pointer,
|
||||||
|
@ -198,7 +197,6 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
wl_display_dispatch(display);
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
struct wl_region *disjoint_region = wl_compositor_create_region(compositor);
|
struct wl_region *disjoint_region = wl_compositor_create_region(compositor);
|
||||||
|
@ -212,8 +210,7 @@ int main(int argc, char **argv) {
|
||||||
wl_region_add(joint_region, 256, 256, 256, 256);
|
wl_region_add(joint_region, 256, 256, 256, 256);
|
||||||
regions[REGION_TYPE_JOINT] = joint_region;
|
regions[REGION_TYPE_JOINT] = joint_region;
|
||||||
|
|
||||||
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
egl_init(display);
|
||||||
WL_SHM_FORMAT_ARGB8888);
|
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||||
struct xdg_surface *xdg_surface =
|
struct xdg_surface *xdg_surface =
|
||||||
|
@ -242,7 +239,8 @@ int main(int argc, char **argv) {
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
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);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,15 @@
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/render/gles2.h>
|
#include <wlr/render/allocator.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_cursor.h>
|
#include <wlr/types/wlr_cursor.h>
|
||||||
#include <wlr/types/wlr_keyboard.h>
|
#include <wlr/types/wlr_keyboard.h>
|
||||||
#include <wlr/types/wlr_list.h>
|
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/types/wlr_output_layout.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/types/wlr_xcursor_manager.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
@ -23,6 +25,8 @@
|
||||||
struct sample_state {
|
struct sample_state {
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
struct compositor_state *compositor;
|
struct compositor_state *compositor;
|
||||||
|
struct wlr_renderer *renderer;
|
||||||
|
struct wlr_allocator *allocator;
|
||||||
struct wlr_xcursor_manager *xcursor_manager;
|
struct wlr_xcursor_manager *xcursor_manager;
|
||||||
struct wlr_cursor *cursor;
|
struct wlr_cursor *cursor;
|
||||||
double cur_x, cur_y;
|
double cur_x, cur_y;
|
||||||
|
@ -90,19 +94,19 @@ static void warp_to_touch(struct sample_state *state,
|
||||||
wlr_cursor_warp_absolute(state->cursor, dev, x, y);
|
wlr_cursor_warp_absolute(state->cursor, dev, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void output_frame_notify(struct wl_listener *listener, void *data) {
|
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_output *sample_output = wl_container_of(listener, sample_output, frame);
|
||||||
struct sample_state *state = sample_output->state;
|
struct sample_state *state = sample_output->state;
|
||||||
struct wlr_output *wlr_output = sample_output->output;
|
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);
|
assert(renderer);
|
||||||
|
|
||||||
wlr_output_attach_render(wlr_output, NULL);
|
wlr_output_attach_render(wlr_output, NULL);
|
||||||
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
|
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
|
||||||
wlr_renderer_clear(renderer, state->clear_color);
|
wlr_renderer_clear(renderer, state->clear_color);
|
||||||
wlr_output_render_software_cursors(wlr_output, NULL);
|
wlr_output_render_software_cursors(wlr_output, NULL);
|
||||||
wlr_output_commit(wlr_output);
|
|
||||||
wlr_renderer_end(renderer);
|
wlr_renderer_end(renderer);
|
||||||
|
wlr_output_commit(wlr_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_cursor_motion(struct wl_listener *listener, void *data) {
|
static void handle_cursor_motion(struct wl_listener *listener, void *data) {
|
||||||
|
@ -221,7 +225,7 @@ static void handle_tablet_tool_axis(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
||||||
struct sample_state *sample = keyboard->state;
|
struct sample_state *sample = keyboard->state;
|
||||||
struct wlr_event_keyboard_key *event = data;
|
struct wlr_event_keyboard_key *event = data;
|
||||||
|
@ -237,7 +241,7 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void output_remove_notify(struct wl_listener *listener, void *data) {
|
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
||||||
struct sample_state *sample = sample_output->state;
|
struct sample_state *sample = sample_output->state;
|
||||||
wlr_output_layout_remove(sample->layout, sample_output->output);
|
wlr_output_layout_remove(sample->layout, sample_output->output);
|
||||||
|
@ -246,14 +250,13 @@ void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
free(sample_output);
|
free(sample_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void new_output_notify(struct wl_listener *listener, void *data) {
|
static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_output *output = data;
|
struct wlr_output *output = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
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 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->output = output;
|
||||||
sample_output->state = sample;
|
sample_output->state = sample;
|
||||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||||
|
@ -265,17 +268,24 @@ void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
wlr_xcursor_manager_load(sample->xcursor_manager, output->scale);
|
wlr_xcursor_manager_load(sample->xcursor_manager, output->scale);
|
||||||
wlr_xcursor_manager_set_cursor_image(sample->xcursor_manager, "left_ptr",
|
wlr_xcursor_manager_set_cursor_image(sample->xcursor_manager, "left_ptr",
|
||||||
sample->cursor);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
||||||
wl_list_remove(&keyboard->destroy.link);
|
wl_list_remove(&keyboard->destroy.link);
|
||||||
wl_list_remove(&keyboard->key.link);
|
wl_list_remove(&keyboard->key.link);
|
||||||
free(keyboard);
|
free(keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void new_input_notify(struct wl_listener *listener, void *data) {
|
static void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_input_device *device = data;
|
struct wlr_input_device *device = data;
|
||||||
struct sample_state *state = wl_container_of(listener, state, new_input);
|
struct sample_state *state = wl_container_of(listener, state, new_input);
|
||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
|
@ -293,18 +303,12 @@ void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||||
keyboard->key.notify = keyboard_key_notify;
|
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);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||||
exit(1);
|
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);
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
if (!keymap) {
|
if (!keymap) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||||
|
@ -329,10 +333,14 @@ int main(int argc, char *argv[]) {
|
||||||
.display = display
|
.display = display
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
||||||
if (!wlr) {
|
if (!wlr) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.renderer = wlr_renderer_autocreate(wlr);
|
||||||
|
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
||||||
|
|
||||||
state.cursor = wlr_cursor_create();
|
state.cursor = wlr_cursor_create();
|
||||||
state.layout = wlr_output_layout_create();
|
state.layout = wlr_output_layout_create();
|
||||||
wlr_cursor_attach_output_layout(state.cursor, state.layout);
|
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 <GLES2/gl2.h>
|
||||||
#include <linux/input-event-codes.h>
|
#include <linux/input-event-codes.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
#include <wlr/render/egl.h>
|
#include "egl_common.h"
|
||||||
#include "pointer-constraints-unstable-v1-client-protocol.h"
|
#include "pointer-constraints-unstable-v1-client-protocol.h"
|
||||||
#include "relative-pointer-unstable-v1-client-protocol.h"
|
#include "relative-pointer-unstable-v1-client-protocol.h"
|
||||||
#include "xdg-shell-client-protocol.h"
|
#include "xdg-shell-client-protocol.h"
|
||||||
|
@ -17,7 +17,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct egl_info {
|
struct egl_info {
|
||||||
struct wlr_egl *egl;
|
|
||||||
struct wl_egl_window *egl_window;
|
struct wl_egl_window *egl_window;
|
||||||
struct wlr_egl_surface *egl_surface;
|
struct wlr_egl_surface *egl_surface;
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
|
@ -58,8 +57,8 @@ static struct wl_callback_listener surface_callback_listener = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static void draw_init(struct egl_info *e) {
|
static void draw_init(struct egl_info *e) {
|
||||||
eglMakeCurrent(e->egl->display, e->egl_surface,
|
eglMakeCurrent(egl_display, e->egl_surface,
|
||||||
e->egl_surface, e->egl->context);
|
e->egl_surface, egl_context);
|
||||||
glViewport(0, 0, e->width, e->height);
|
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);
|
e->frame_callback = wl_surface_frame(e->surface);
|
||||||
wl_callback_add_listener(e->frame_callback, &surface_callback_listener, e);
|
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,
|
static void xdg_toplevel_handle_close(void *data,
|
||||||
struct xdg_toplevel *xdg_toplevel) {
|
struct xdg_toplevel *xdg_toplevel) {
|
||||||
struct egl_info *e = data;
|
egl_finish();
|
||||||
wlr_egl_finish(e->egl);
|
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -412,7 +410,6 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
wl_display_dispatch(display);
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
/* Check that all the global interfaces were captured */
|
/* Check that all the global interfaces were captured */
|
||||||
|
@ -441,11 +438,9 @@ int main(int argc, char **argv) {
|
||||||
/* Initialize EGL context */
|
/* Initialize EGL context */
|
||||||
|
|
||||||
struct egl_info *e = calloc(1, sizeof(struct egl_info));
|
struct egl_info *e = calloc(1, sizeof(struct egl_info));
|
||||||
e->egl = calloc(1, sizeof(struct wlr_egl));
|
|
||||||
e->width = e->height = 512;
|
e->width = e->height = 512;
|
||||||
|
|
||||||
wlr_egl_init(e->egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
egl_init(display);
|
||||||
WL_SHM_FORMAT_ARGB8888);
|
|
||||||
|
|
||||||
/* Create the surface and xdg_toplevels, and set listeners */
|
/* Create the surface and xdg_toplevels, and set listeners */
|
||||||
|
|
||||||
|
@ -462,7 +457,8 @@ int main(int argc, char **argv) {
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
|
|
||||||
e->egl_window = wl_egl_window_create(surface, e->width, e->height);
|
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;
|
e->surface = surface;
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200112L
|
||||||
#include <GLES2/gl2.h>
|
#include <drm_fourcc.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -10,11 +10,10 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/render/allocator.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_keyboard.h>
|
#include <wlr/types/wlr_keyboard.h>
|
||||||
#include <wlr/types/wlr_output.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_input_device.h>
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
@ -27,6 +26,7 @@ struct sample_state {
|
||||||
struct wl_listener new_input;
|
struct wl_listener new_input;
|
||||||
struct timespec last_frame;
|
struct timespec last_frame;
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
|
struct wlr_allocator *allocator;
|
||||||
struct wlr_texture *cat_texture;
|
struct wlr_texture *cat_texture;
|
||||||
struct wl_list outputs;
|
struct wl_list outputs;
|
||||||
enum wl_output_transform transform;
|
enum wl_output_transform transform;
|
||||||
|
@ -97,21 +97,20 @@ static void update_velocities(struct sample_state *sample,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void output_remove_notify(struct wl_listener *listener, void *data) {
|
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
||||||
wl_list_remove(&sample_output->frame.link);
|
wl_list_remove(&sample_output->frame.link);
|
||||||
wl_list_remove(&sample_output->destroy.link);
|
wl_list_remove(&sample_output->destroy.link);
|
||||||
free(sample_output);
|
free(sample_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void new_output_notify(struct wl_listener *listener, void *data) {
|
static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_output *output = data;
|
struct wlr_output *output = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
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 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_offs = sample_output->y_offs = 0;
|
||||||
sample_output->x_vel = sample_output->y_vel = 128;
|
sample_output->x_vel = sample_output->y_vel = 128;
|
||||||
|
|
||||||
|
@ -123,9 +122,16 @@ void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
||||||
sample_output->destroy.notify = output_remove_notify;
|
sample_output->destroy.notify = output_remove_notify;
|
||||||
wl_list_insert(&sample->outputs, &sample_output->link);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
||||||
struct sample_state *sample = keyboard->sample;
|
struct sample_state *sample = keyboard->sample;
|
||||||
struct wlr_event_keyboard_key *event = data;
|
struct wlr_event_keyboard_key *event = data;
|
||||||
|
@ -138,7 +144,7 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
if (sym == XKB_KEY_Escape) {
|
if (sym == XKB_KEY_Escape) {
|
||||||
wl_display_terminate(sample->display);
|
wl_display_terminate(sample->display);
|
||||||
}
|
}
|
||||||
if (event->state == WLR_KEY_PRESSED) {
|
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||||
switch (sym) {
|
switch (sym) {
|
||||||
case XKB_KEY_Left:
|
case XKB_KEY_Left:
|
||||||
update_velocities(sample, -16, 0);
|
update_velocities(sample, -16, 0);
|
||||||
|
@ -157,14 +163,14 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
||||||
wl_list_remove(&keyboard->destroy.link);
|
wl_list_remove(&keyboard->destroy.link);
|
||||||
wl_list_remove(&keyboard->key.link);
|
wl_list_remove(&keyboard->key.link);
|
||||||
free(keyboard);
|
free(keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void new_input_notify(struct wl_listener *listener, void *data) {
|
static void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_input_device *device = data;
|
struct wlr_input_device *device = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
|
@ -176,18 +182,12 @@ void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||||
keyboard->key.notify = keyboard_key_notify;
|
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);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||||
exit(1);
|
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);
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
if (!keymap) {
|
if (!keymap) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||||
|
@ -228,7 +228,7 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wlr_log_init(WLR_DEBUG, NULL);
|
wlr_log_init(WLR_DEBUG, NULL);
|
||||||
|
@ -239,7 +239,7 @@ int main(int argc, char *argv[]) {
|
||||||
};
|
};
|
||||||
wl_list_init(&state.outputs);
|
wl_list_init(&state.outputs);
|
||||||
|
|
||||||
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
||||||
if (!wlr) {
|
if (!wlr) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -250,20 +250,22 @@ int main(int argc, char *argv[]) {
|
||||||
state.new_input.notify = new_input_notify;
|
state.new_input.notify = new_input_notify;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
||||||
|
|
||||||
state.renderer = wlr_backend_get_renderer(wlr);
|
state.renderer = wlr_renderer_autocreate(wlr);
|
||||||
if (!state.renderer) {
|
if (!state.renderer) {
|
||||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||||
wlr_backend_destroy(wlr);
|
wlr_backend_destroy(wlr);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
state.cat_texture = wlr_texture_from_pixels(state.renderer,
|
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);
|
cat_tex.pixel_data);
|
||||||
if (!state.cat_texture) {
|
if (!state.cat_texture) {
|
||||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
||||||
|
|
||||||
if (!wlr_backend_start(wlr)) {
|
if (!wlr_backend_start(wlr)) {
|
||||||
wlr_log(WLR_ERROR, "Failed to start backend");
|
wlr_log(WLR_ERROR, "Failed to start backend");
|
||||||
wlr_backend_destroy(wlr);
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,361 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2008 Kristian Høgsberg
|
||||||
|
* Copyright © 2020 Andri Yngvason
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _POSIX_C_SOURCE 200112L
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <png.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <gbm.h>
|
||||||
|
#include <xf86drm.h>
|
||||||
|
#include <drm_fourcc.h>
|
||||||
|
#include <wayland-client-protocol.h>
|
||||||
|
#include "wlr-screencopy-unstable-v1-client-protocol.h"
|
||||||
|
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
struct format {
|
||||||
|
uint32_t format;
|
||||||
|
bool is_bgr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int drm_fd = -1;
|
||||||
|
static struct gbm_device *gbm_device = NULL;
|
||||||
|
|
||||||
|
static struct zwp_linux_dmabuf_v1 *dmabuf = NULL;
|
||||||
|
static struct zwlr_screencopy_manager_v1 *screencopy_manager = NULL;
|
||||||
|
static struct wl_output *output = NULL;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
struct gbm_bo *bo;
|
||||||
|
struct wl_buffer *wl_buffer;
|
||||||
|
int width, height;
|
||||||
|
uint32_t format;
|
||||||
|
bool y_invert;
|
||||||
|
} buffer;
|
||||||
|
|
||||||
|
bool buffer_copy_done = false;
|
||||||
|
static bool have_linux_dmabuf = false;
|
||||||
|
|
||||||
|
// wl_shm_format describes little-endian formats, libpng uses big-endian
|
||||||
|
// formats (so Wayland's ABGR is libpng's RGBA).
|
||||||
|
static const struct format formats[] = {
|
||||||
|
{DRM_FORMAT_XRGB8888, true},
|
||||||
|
{DRM_FORMAT_ARGB8888, true},
|
||||||
|
{DRM_FORMAT_XBGR8888, false},
|
||||||
|
{DRM_FORMAT_ABGR8888, false},
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool find_render_node(char *node, size_t maxlen) {
|
||||||
|
bool r = false;
|
||||||
|
drmDevice *devices[64];
|
||||||
|
|
||||||
|
int n = drmGetDevices2(0, devices, sizeof(devices) / sizeof(devices[0]));
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
drmDevice *dev = devices[i];
|
||||||
|
if (!(dev->available_nodes & (1 << DRM_NODE_RENDER))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(node, dev->nodes[DRM_NODE_RENDER], maxlen - 1);
|
||||||
|
node[maxlen - 1] = '\0';
|
||||||
|
r = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
drmFreeDevices(devices, n);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dmabuf_created(void *data,
|
||||||
|
struct zwp_linux_buffer_params_v1 *params,
|
||||||
|
struct wl_buffer *wl_buffer) {
|
||||||
|
buffer.wl_buffer = wl_buffer;
|
||||||
|
|
||||||
|
zwlr_screencopy_frame_v1_copy(data, buffer.wl_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dmabuf_failed(void *data,
|
||||||
|
struct zwp_linux_buffer_params_v1 *params) {
|
||||||
|
fprintf(stderr, "Failed to create dmabuf\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwp_linux_buffer_params_v1_listener params_listener = {
|
||||||
|
.created = dmabuf_created,
|
||||||
|
.failed = dmabuf_failed,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void frame_handle_buffer(void *data,
|
||||||
|
struct zwlr_screencopy_frame_v1 *frame, uint32_t wl_format,
|
||||||
|
uint32_t width, uint32_t height, uint32_t stride) {
|
||||||
|
// Not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
static void frame_handle_linux_dmabuf(void *data,
|
||||||
|
struct zwlr_screencopy_frame_v1 *frame, uint32_t fourcc,
|
||||||
|
uint32_t width, uint32_t height) {
|
||||||
|
buffer.width = width;
|
||||||
|
buffer.height = height;
|
||||||
|
buffer.format = fourcc;
|
||||||
|
have_linux_dmabuf = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void frame_handle_buffer_done(void *data,
|
||||||
|
struct zwlr_screencopy_frame_v1 *frame) {
|
||||||
|
assert(!buffer.bo);
|
||||||
|
|
||||||
|
if (!have_linux_dmabuf) {
|
||||||
|
fprintf(stderr, "linux-dmabuf is not supported\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.bo = gbm_bo_create(gbm_device, buffer.width, buffer.height,
|
||||||
|
buffer.format,
|
||||||
|
GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING);
|
||||||
|
if (buffer.bo == NULL) {
|
||||||
|
fprintf(stderr, "failed to create GBM buffer object\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct zwp_linux_buffer_params_v1 *params;
|
||||||
|
params = zwp_linux_dmabuf_v1_create_params(dmabuf);
|
||||||
|
assert(params);
|
||||||
|
|
||||||
|
int fd = gbm_bo_get_fd(buffer.bo);
|
||||||
|
uint32_t off = gbm_bo_get_offset(buffer.bo, 0);
|
||||||
|
uint32_t bo_stride = gbm_bo_get_stride(buffer.bo);
|
||||||
|
uint64_t mod = gbm_bo_get_modifier(buffer.bo);
|
||||||
|
zwp_linux_buffer_params_v1_add(params, fd, 0, off, bo_stride, mod >> 32,
|
||||||
|
mod & 0xffffffff);
|
||||||
|
|
||||||
|
zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, frame);
|
||||||
|
|
||||||
|
zwp_linux_buffer_params_v1_create(params, buffer.width, buffer.height,
|
||||||
|
buffer.format, /* flags */ 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void frame_handle_flags(void *data,
|
||||||
|
struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) {
|
||||||
|
buffer.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void frame_handle_ready(void *data,
|
||||||
|
struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi,
|
||||||
|
uint32_t tv_sec_lo, uint32_t tv_nsec) {
|
||||||
|
buffer_copy_done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void frame_handle_failed(void *data,
|
||||||
|
struct zwlr_screencopy_frame_v1 *frame) {
|
||||||
|
fprintf(stderr, "failed to copy frame\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwlr_screencopy_frame_v1_listener frame_listener = {
|
||||||
|
.buffer = frame_handle_buffer,
|
||||||
|
.linux_dmabuf = frame_handle_linux_dmabuf,
|
||||||
|
.buffer_done = frame_handle_buffer_done,
|
||||||
|
.flags = frame_handle_flags,
|
||||||
|
.ready = frame_handle_ready,
|
||||||
|
.failed = frame_handle_failed,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void dmabuf_format(void *data,
|
||||||
|
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format) {
|
||||||
|
// deprecated
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dmabuf_modifier(void *data,
|
||||||
|
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format,
|
||||||
|
uint32_t modifier_hi, uint32_t modifier_lo) {
|
||||||
|
// TODO?
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
|
||||||
|
.format = dmabuf_format,
|
||||||
|
.modifier = dmabuf_modifier,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 && output == NULL) {
|
||||||
|
output = wl_registry_bind(registry, name, &wl_output_interface, 1);
|
||||||
|
} else if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) {
|
||||||
|
dmabuf = wl_registry_bind(registry, name,
|
||||||
|
&zwp_linux_dmabuf_v1_interface, 3);
|
||||||
|
zwp_linux_dmabuf_v1_add_listener(dmabuf, &dmabuf_listener, data);
|
||||||
|
} else if (strcmp(interface,
|
||||||
|
zwlr_screencopy_manager_v1_interface.name) == 0) {
|
||||||
|
screencopy_manager = wl_registry_bind(registry, name,
|
||||||
|
&zwlr_screencopy_manager_v1_interface, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_global_remove(void *data, struct wl_registry *registry,
|
||||||
|
uint32_t name) {
|
||||||
|
// Who cares?
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_registry_listener registry_listener = {
|
||||||
|
.global = handle_global,
|
||||||
|
.global_remove = handle_global_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void write_image(char *filename, uint32_t format, int width,
|
||||||
|
int height, int stride, bool y_invert, png_bytep data) {
|
||||||
|
const struct format *fmt = NULL;
|
||||||
|
for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) {
|
||||||
|
if (formats[i].format == format) {
|
||||||
|
fmt = &formats[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fmt == NULL) {
|
||||||
|
fprintf(stderr, "unsupported format %"PRIu32"\n", format);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *f = fopen(filename, "wb");
|
||||||
|
if (f == NULL) {
|
||||||
|
fprintf(stderr, "failed to open output file\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
png_structp png =
|
||||||
|
png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||||
|
png_infop info = png_create_info_struct(png);
|
||||||
|
|
||||||
|
png_init_io(png, f);
|
||||||
|
|
||||||
|
png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGBA,
|
||||||
|
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
||||||
|
PNG_FILTER_TYPE_DEFAULT);
|
||||||
|
|
||||||
|
if (fmt->is_bgr) {
|
||||||
|
png_set_bgr(png);
|
||||||
|
}
|
||||||
|
|
||||||
|
png_write_info(png, info);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < (size_t)height; ++i) {
|
||||||
|
png_bytep row;
|
||||||
|
if (y_invert) {
|
||||||
|
row = data + (height - i - 1) * stride;
|
||||||
|
} else {
|
||||||
|
row = data + i * stride;
|
||||||
|
}
|
||||||
|
png_write_row(png, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
png_write_end(png, NULL);
|
||||||
|
|
||||||
|
png_destroy_write_struct(&png, &info);
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
char render_node[256];
|
||||||
|
if (!find_render_node(render_node, sizeof(render_node))) {
|
||||||
|
fprintf(stderr, "Failed to find a DRM render node\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Using render node: %s\n", render_node);
|
||||||
|
|
||||||
|
drm_fd = open(render_node, O_RDWR);
|
||||||
|
if (drm_fd < 0) {
|
||||||
|
perror("Failed to open drm render node");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
gbm_device = gbm_create_device(drm_fd);
|
||||||
|
if (!gbm_device) {
|
||||||
|
fprintf(stderr, "Failed to create gbm device\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_display *display = wl_display_connect(NULL);
|
||||||
|
if (display == NULL) {
|
||||||
|
perror("failed to create display");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
if (dmabuf == NULL) {
|
||||||
|
fprintf(stderr, "compositor is missing linux-dmabuf-unstable-v1\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (screencopy_manager == NULL) {
|
||||||
|
fprintf(stderr, "compositor doesn't support wlr-screencopy-unstable-v1\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (output == NULL) {
|
||||||
|
fprintf(stderr, "no output available\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct zwlr_screencopy_frame_v1 *frame =
|
||||||
|
zwlr_screencopy_manager_v1_capture_output(screencopy_manager, 0, output);
|
||||||
|
zwlr_screencopy_frame_v1_add_listener(frame, &frame_listener, NULL);
|
||||||
|
|
||||||
|
while (!buffer_copy_done && wl_display_dispatch(display) != -1) {
|
||||||
|
// This space is intentionally left blank
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t stride = 0;
|
||||||
|
void *map_data = NULL;
|
||||||
|
void *data = gbm_bo_map(buffer.bo, 0, 0, buffer.width, buffer.height,
|
||||||
|
GBM_BO_TRANSFER_READ, &stride, &map_data);
|
||||||
|
if (!data) {
|
||||||
|
perror("Failed to map gbm bo");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_image("wayland-screenshot.png", buffer.format, buffer.width,
|
||||||
|
buffer.height, stride, buffer.y_invert, data);
|
||||||
|
|
||||||
|
gbm_bo_unmap(buffer.bo, map_data);
|
||||||
|
gbm_bo_destroy(buffer.bo);
|
||||||
|
wl_buffer_destroy(buffer.wl_buffer);
|
||||||
|
|
||||||
|
gbm_device_destroy(gbm_device);
|
||||||
|
close(drm_fd);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
|
@ -32,8 +32,10 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <assert.h>
|
||||||
#include <wayland-client-protocol.h>
|
#include <wayland-client-protocol.h>
|
||||||
#include "wlr-screencopy-unstable-v1-client-protocol.h"
|
#include "wlr-screencopy-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
@ -69,7 +71,7 @@ static struct wl_buffer *create_shm_buffer(enum wl_shm_format fmt,
|
||||||
int size = stride * height;
|
int size = stride * height;
|
||||||
|
|
||||||
const char shm_name[] = "/wlroots-screencopy";
|
const char shm_name[] = "/wlroots-screencopy";
|
||||||
int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, 0);
|
int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
fprintf(stderr, "shm_open failed\n");
|
fprintf(stderr, "shm_open failed\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -88,7 +90,7 @@ static struct wl_buffer *create_shm_buffer(enum wl_shm_format fmt,
|
||||||
|
|
||||||
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
if (data == MAP_FAILED) {
|
if (data == MAP_FAILED) {
|
||||||
fprintf(stderr, "mmap failed: %m\n");
|
perror("mmap failed");
|
||||||
close(fd);
|
close(fd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -110,6 +112,9 @@ static void frame_handle_buffer(void *data,
|
||||||
buffer.width = width;
|
buffer.width = width;
|
||||||
buffer.height = height;
|
buffer.height = height;
|
||||||
buffer.stride = stride;
|
buffer.stride = stride;
|
||||||
|
|
||||||
|
// Make sure the buffer is not allocated
|
||||||
|
assert(!buffer.wl_buffer);
|
||||||
buffer.wl_buffer =
|
buffer.wl_buffer =
|
||||||
create_shm_buffer(format, width, height, stride, &buffer.data);
|
create_shm_buffer(format, width, height, stride, &buffer.data);
|
||||||
if (buffer.wl_buffer == NULL) {
|
if (buffer.wl_buffer == NULL) {
|
||||||
|
@ -223,13 +228,12 @@ static void write_image(char *filename, enum wl_shm_format wl_fmt, int width,
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
struct wl_display * display = wl_display_connect(NULL);
|
struct wl_display * display = wl_display_connect(NULL);
|
||||||
if (display == NULL) {
|
if (display == NULL) {
|
||||||
fprintf(stderr, "failed to create display: %m\n");
|
perror("failed to create display");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
wl_display_dispatch(display);
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
if (shm == NULL) {
|
if (shm == NULL) {
|
||||||
|
@ -256,6 +260,7 @@ int main(int argc, char *argv[]) {
|
||||||
write_image("wayland-screenshot.png", buffer.format, buffer.width,
|
write_image("wayland-screenshot.png", buffer.format, buffer.width,
|
||||||
buffer.height, buffer.stride, buffer.y_invert, buffer.data);
|
buffer.height, buffer.stride, buffer.y_invert, buffer.data);
|
||||||
wl_buffer_destroy(buffer.wl_buffer);
|
wl_buffer_destroy(buffer.wl_buffer);
|
||||||
|
munmap(buffer.data, buffer.stride * buffer.height);
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200112L
|
||||||
#include <GLES2/gl2.h>
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -8,8 +7,11 @@
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/backend/session.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_output.h>
|
||||||
#include <wlr/types/wlr_input_device.h>
|
#include <wlr/types/wlr_input_device.h>
|
||||||
|
#include <wlr/types/wlr_keyboard.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
|
||||||
|
@ -17,8 +19,10 @@ struct sample_state {
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
struct wl_listener new_output;
|
struct wl_listener new_output;
|
||||||
struct wl_listener new_input;
|
struct wl_listener new_input;
|
||||||
|
struct wlr_renderer *renderer;
|
||||||
|
struct wlr_allocator *allocator;
|
||||||
struct timespec last_frame;
|
struct timespec last_frame;
|
||||||
float color[3];
|
float color[4];
|
||||||
int dec;
|
int dec;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -36,10 +40,12 @@ struct sample_keyboard {
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
};
|
};
|
||||||
|
|
||||||
void output_frame_notify(struct wl_listener *listener, void *data) {
|
static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output =
|
struct sample_output *sample_output =
|
||||||
wl_container_of(listener, sample_output, frame);
|
wl_container_of(listener, sample_output, frame);
|
||||||
struct sample_state *sample = sample_output->sample;
|
struct sample_state *sample = sample_output->sample;
|
||||||
|
struct wlr_output *wlr_output = sample_output->output;
|
||||||
|
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
|
||||||
|
@ -56,16 +62,18 @@ void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
sample->dec = inc;
|
sample->dec = inc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_output_attach_render(sample_output->output, NULL);
|
wlr_output_attach_render(wlr_output, NULL);
|
||||||
|
|
||||||
glClearColor(sample->color[0], sample->color[1], sample->color[2], 1.0);
|
struct wlr_renderer *renderer = sample->renderer;
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
|
||||||
|
wlr_renderer_clear(renderer, sample->color);
|
||||||
|
wlr_renderer_end(renderer);
|
||||||
|
|
||||||
wlr_output_commit(sample_output->output);
|
wlr_output_commit(wlr_output);
|
||||||
sample->last_frame = now;
|
sample->last_frame = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
void output_remove_notify(struct wl_listener *listener, void *data) {
|
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output =
|
struct sample_output *sample_output =
|
||||||
wl_container_of(listener, sample_output, destroy);
|
wl_container_of(listener, sample_output, destroy);
|
||||||
wlr_log(WLR_DEBUG, "Output removed");
|
wlr_log(WLR_DEBUG, "Output removed");
|
||||||
|
@ -74,26 +82,31 @@ void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
free(sample_output);
|
free(sample_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void new_output_notify(struct wl_listener *listener, void *data) {
|
static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_output *output = data;
|
struct wlr_output *output = data;
|
||||||
struct sample_state *sample =
|
struct sample_state *sample =
|
||||||
wl_container_of(listener, sample, new_output);
|
wl_container_of(listener, sample, new_output);
|
||||||
|
|
||||||
|
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
||||||
|
|
||||||
struct sample_output *sample_output =
|
struct sample_output *sample_output =
|
||||||
calloc(1, sizeof(struct 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->output = output;
|
||||||
sample_output->sample = sample;
|
sample_output->sample = sample;
|
||||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||||
sample_output->frame.notify = output_frame_notify;
|
sample_output->frame.notify = output_frame_notify;
|
||||||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
||||||
sample_output->destroy.notify = output_remove_notify;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
||||||
struct sample_state *sample = keyboard->sample;
|
struct sample_state *sample = keyboard->sample;
|
||||||
struct wlr_event_keyboard_key *event = data;
|
struct wlr_event_keyboard_key *event = data;
|
||||||
|
@ -109,7 +122,7 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard =
|
struct sample_keyboard *keyboard =
|
||||||
wl_container_of(listener, keyboard, destroy);
|
wl_container_of(listener, keyboard, destroy);
|
||||||
wl_list_remove(&keyboard->destroy.link);
|
wl_list_remove(&keyboard->destroy.link);
|
||||||
|
@ -117,7 +130,7 @@ void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
free(keyboard);
|
free(keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void new_input_notify(struct wl_listener *listener, void *data) {
|
static void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_input_device *device = data;
|
struct wlr_input_device *device = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
|
@ -130,18 +143,12 @@ void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||||
keyboard->key.notify = keyboard_key_notify;
|
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);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||||
exit(1);
|
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);
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
if (!keymap) {
|
if (!keymap) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||||
|
@ -160,15 +167,19 @@ int main(void) {
|
||||||
wlr_log_init(WLR_DEBUG, NULL);
|
wlr_log_init(WLR_DEBUG, NULL);
|
||||||
struct wl_display *display = wl_display_create();
|
struct wl_display *display = wl_display_create();
|
||||||
struct sample_state state = {
|
struct sample_state state = {
|
||||||
.color = { 1.0, 0.0, 0.0 },
|
.color = { 1.0, 0.0, 0.0, 1.0 },
|
||||||
.dec = 0,
|
.dec = 0,
|
||||||
.last_frame = { 0 },
|
.last_frame = { 0 },
|
||||||
.display = display
|
.display = display
|
||||||
};
|
};
|
||||||
struct wlr_backend *backend = wlr_backend_autocreate(display, NULL);
|
struct wlr_backend *backend = wlr_backend_autocreate(display);
|
||||||
if (!backend) {
|
if (!backend) {
|
||||||
exit(1);
|
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);
|
wl_signal_add(&backend->events.new_output, &state.new_output);
|
||||||
state.new_output.notify = new_output_notify;
|
state.new_output.notify = new_output_notify;
|
||||||
wl_signal_add(&backend->events.new_input, &state.new_input);
|
wl_signal_add(&backend->events.new_input, &state.new_input);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#define _XOPEN_SOURCE 600
|
#define _XOPEN_SOURCE 600
|
||||||
#include <GLES2/gl2.h>
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -9,19 +8,22 @@
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
|
#include <wlr/render/allocator.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_box.h>
|
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/types/wlr_output.h>
|
#include <wlr/types/wlr_output.h>
|
||||||
#include <wlr/types/wlr_input_device.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_pad.h>
|
||||||
#include <wlr/types/wlr_tablet_tool.h>
|
#include <wlr/types/wlr_tablet_tool.h>
|
||||||
|
#include <wlr/util/box.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
|
||||||
struct sample_state {
|
struct sample_state {
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
|
struct wlr_allocator *allocator;
|
||||||
bool proximity, tap, button;
|
bool proximity, tap, button;
|
||||||
double distance;
|
double distance;
|
||||||
double pressure;
|
double pressure;
|
||||||
|
@ -227,30 +229,36 @@ static void tablet_pad_destroy_notify(struct wl_listener *listener, void *data)
|
||||||
free(pstate);
|
free(pstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void output_remove_notify(struct wl_listener *listener, void *data) {
|
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
||||||
wl_list_remove(&sample_output->frame.link);
|
wl_list_remove(&sample_output->frame.link);
|
||||||
wl_list_remove(&sample_output->destroy.link);
|
wl_list_remove(&sample_output->destroy.link);
|
||||||
free(sample_output);
|
free(sample_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void new_output_notify(struct wl_listener *listener, void *data) {
|
static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_output *output = data;
|
struct wlr_output *output = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
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 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->output = output;
|
||||||
sample_output->sample = sample;
|
sample_output->sample = sample;
|
||||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||||
sample_output->frame.notify = output_frame_notify;
|
sample_output->frame.notify = output_frame_notify;
|
||||||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
||||||
sample_output->destroy.notify = output_remove_notify;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
||||||
struct sample_state *sample = keyboard->sample;
|
struct sample_state *sample = keyboard->sample;
|
||||||
struct wlr_event_keyboard_key *event = data;
|
struct wlr_event_keyboard_key *event = data;
|
||||||
|
@ -266,14 +274,14 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
||||||
wl_list_remove(&keyboard->destroy.link);
|
wl_list_remove(&keyboard->destroy.link);
|
||||||
wl_list_remove(&keyboard->key.link);
|
wl_list_remove(&keyboard->key.link);
|
||||||
free(keyboard);
|
free(keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void new_input_notify(struct wl_listener *listener, void *data) {
|
static void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_input_device *device = data;
|
struct wlr_input_device *device = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
|
@ -285,18 +293,12 @@ void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||||
keyboard->key.notify = keyboard_key_notify;
|
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);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||||
exit(1);
|
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);
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
if (!keymap) {
|
if (!keymap) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||||
|
@ -353,7 +355,7 @@ int main(int argc, char *argv[]) {
|
||||||
};
|
};
|
||||||
wl_list_init(&state.tablet_pads);
|
wl_list_init(&state.tablet_pads);
|
||||||
wl_list_init(&state.tablet_tools);
|
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) {
|
if (!wlr) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -364,11 +366,14 @@ int main(int argc, char *argv[]) {
|
||||||
state.new_input.notify = new_input_notify;
|
state.new_input.notify = new_input_notify;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
||||||
|
|
||||||
state.renderer = wlr_backend_get_renderer(wlr);
|
state.renderer = wlr_renderer_autocreate(wlr);
|
||||||
if (!state.renderer) {
|
if (!state.renderer) {
|
||||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
||||||
|
|
||||||
if (!wlr_backend_start(wlr)) {
|
if (!wlr_backend_start(wlr)) {
|
||||||
wlr_log(WLR_ERROR, "Failed to start backend");
|
wlr_log(WLR_ERROR, "Failed to start backend");
|
||||||
wlr_backend_destroy(wlr);
|
wlr_backend_destroy(wlr);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
#include <wlr/render/egl.h>
|
#include "egl_common.h"
|
||||||
#include "text-input-unstable-v3-client-protocol.h"
|
#include "text-input-unstable-v3-client-protocol.h"
|
||||||
#include "xdg-shell-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_manager_v3 *text_input_manager = NULL;
|
||||||
static struct zwp_text_input_v3 *text_input = NULL;
|
static struct zwp_text_input_v3 *text_input = NULL;
|
||||||
|
|
||||||
struct wlr_egl egl;
|
|
||||||
struct wl_egl_window *egl_window;
|
struct wl_egl_window *egl_window;
|
||||||
struct wlr_egl_surface *egl_surface;
|
struct wlr_egl_surface *egl_surface;
|
||||||
|
|
||||||
static void draw(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};
|
float color[] = {1.0, 1.0, 0.0, 1.0};
|
||||||
color[0] = enabled * 1.0;
|
color[0] = enabled * 1.0;
|
||||||
|
@ -78,7 +77,7 @@ static void draw(void) {
|
||||||
glClearColor(color[0], color[1], color[2], 1.0);
|
glClearColor(color[0], color[1], color[2], 1.0);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
eglSwapBuffers(egl.display, egl_surface);
|
eglSwapBuffers(egl_display, egl_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t utf8_strlen(char *str) {
|
static size_t utf8_strlen(char *str) {
|
||||||
|
@ -344,7 +343,6 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
wl_display_dispatch(display);
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
if (compositor == NULL) {
|
if (compositor == NULL) {
|
||||||
|
@ -364,9 +362,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL);
|
zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL);
|
||||||
|
|
||||||
|
egl_init(display);
|
||||||
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
|
||||||
WL_SHM_FORMAT_ARGB8888);
|
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||||
struct xdg_surface *xdg_surface =
|
struct xdg_surface *xdg_surface =
|
||||||
|
@ -379,7 +375,8 @@ int main(int argc, char **argv) {
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
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);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
#include <wlr/render/egl.h>
|
#include "egl_common.h"
|
||||||
#include "xdg-shell-client-protocol.h"
|
#include "xdg-shell-client-protocol.h"
|
||||||
#include "xdg-decoration-unstable-v1-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 xdg_wm_base *wm_base = NULL;
|
||||||
static struct zxdg_decoration_manager_v1 *decoration_manager = NULL;
|
static struct zxdg_decoration_manager_v1 *decoration_manager = NULL;
|
||||||
|
|
||||||
struct wlr_egl egl;
|
|
||||||
struct wl_egl_window *egl_window;
|
struct wl_egl_window *egl_window;
|
||||||
struct wlr_egl_surface *egl_surface;
|
struct wlr_egl_surface *egl_surface;
|
||||||
|
|
||||||
|
@ -53,7 +52,7 @@ static void request_preferred_mode(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw(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};
|
float color[] = {1.0, 1.0, 0.0, 1.0};
|
||||||
if (current_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE) {
|
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);
|
glClearColor(color[0], color[1], color[2], 1.0);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
eglSwapBuffers(egl.display, egl_surface);
|
eglSwapBuffers(egl_display, egl_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xdg_surface_handle_configure(void *data,
|
static void xdg_surface_handle_configure(void *data,
|
||||||
|
@ -203,7 +202,6 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
wl_display_dispatch(display);
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
if (compositor == NULL) {
|
if (compositor == NULL) {
|
||||||
|
@ -219,8 +217,7 @@ int main(int argc, char **argv) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
egl_init(display);
|
||||||
WL_SHM_FORMAT_ARGB8888);
|
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||||
struct xdg_surface *xdg_surface =
|
struct xdg_surface *xdg_surface =
|
||||||
|
@ -239,7 +236,8 @@ int main(int argc, char **argv) {
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
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);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200112L
|
||||||
#include <GLES2/gl2.h>
|
#include <drm_fourcc.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -10,11 +10,13 @@
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/backend/session.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/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_input_device.h>
|
||||||
|
#include <wlr/types/wlr_keyboard.h>
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
|
#include <wlr/types/wlr_touch.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
#include "cat.h"
|
#include "cat.h"
|
||||||
|
@ -22,6 +24,7 @@
|
||||||
struct sample_state {
|
struct sample_state {
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
|
struct wlr_allocator *allocator;
|
||||||
struct wlr_texture *cat_texture;
|
struct wlr_texture *cat_texture;
|
||||||
struct wl_list touch_points;
|
struct wl_list touch_points;
|
||||||
struct timespec last_frame;
|
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_begin(sample->renderer, wlr_output->width, wlr_output->height);
|
||||||
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
|
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;
|
struct touch_point *p;
|
||||||
wl_list_for_each(p, &sample->touch_points, link) {
|
wl_list_for_each(p, &sample->touch_points, link) {
|
||||||
int x = (int)(p->x * width) - tex_width / 2;
|
int x = (int)(p->x * width) - sample->cat_texture->width / 2;
|
||||||
int y = (int)(p->y * height) - tex_height / 2;
|
int y = (int)(p->y * height) - sample->cat_texture->height / 2;
|
||||||
wlr_render_texture(sample->renderer, sample->cat_texture,
|
wlr_render_texture(sample->renderer, sample->cat_texture,
|
||||||
wlr_output->transform_matrix, x, y, 1.0f);
|
wlr_output->transform_matrix, x, y, 1.0f);
|
||||||
}
|
}
|
||||||
|
@ -140,30 +140,36 @@ static void touch_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
free(tstate);
|
free(tstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void output_remove_notify(struct wl_listener *listener, void *data) {
|
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
||||||
wl_list_remove(&sample_output->frame.link);
|
wl_list_remove(&sample_output->frame.link);
|
||||||
wl_list_remove(&sample_output->destroy.link);
|
wl_list_remove(&sample_output->destroy.link);
|
||||||
free(sample_output);
|
free(sample_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void new_output_notify(struct wl_listener *listener, void *data) {
|
static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_output *output = data;
|
struct wlr_output *output = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
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 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->output = output;
|
||||||
sample_output->sample = sample;
|
sample_output->sample = sample;
|
||||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||||
sample_output->frame.notify = output_frame_notify;
|
sample_output->frame.notify = output_frame_notify;
|
||||||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
||||||
sample_output->destroy.notify = output_remove_notify;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
||||||
struct sample_state *sample = keyboard->sample;
|
struct sample_state *sample = keyboard->sample;
|
||||||
struct wlr_event_keyboard_key *event = data;
|
struct wlr_event_keyboard_key *event = data;
|
||||||
|
@ -179,14 +185,14 @@ void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
||||||
wl_list_remove(&keyboard->destroy.link);
|
wl_list_remove(&keyboard->destroy.link);
|
||||||
wl_list_remove(&keyboard->key.link);
|
wl_list_remove(&keyboard->key.link);
|
||||||
free(keyboard);
|
free(keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void new_input_notify(struct wl_listener *listener, void *data) {
|
static void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_input_device *device = data;
|
struct wlr_input_device *device = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
|
@ -198,18 +204,12 @@ void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||||
keyboard->key.notify = keyboard_key_notify;
|
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);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||||
exit(1);
|
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);
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
if (!keymap) {
|
if (!keymap) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||||
|
@ -248,7 +248,7 @@ int main(int argc, char *argv[]) {
|
||||||
wl_list_init(&state.touch_points);
|
wl_list_init(&state.touch_points);
|
||||||
wl_list_init(&state.touch);
|
wl_list_init(&state.touch);
|
||||||
|
|
||||||
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
||||||
if (!wlr) {
|
if (!wlr) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -259,20 +259,21 @@ int main(int argc, char *argv[]) {
|
||||||
state.new_input.notify = new_input_notify;
|
state.new_input.notify = new_input_notify;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
||||||
|
|
||||||
|
state.renderer = wlr_renderer_autocreate(wlr);
|
||||||
state.renderer = wlr_backend_get_renderer(wlr);
|
|
||||||
if (!state.renderer) {
|
if (!state.renderer) {
|
||||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
state.cat_texture = wlr_texture_from_pixels(state.renderer,
|
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);
|
cat_tex.pixel_data);
|
||||||
if (!state.cat_texture) {
|
if (!state.cat_texture) {
|
||||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
||||||
|
|
||||||
if (!wlr_backend_start(wlr)) {
|
if (!wlr_backend_start(wlr)) {
|
||||||
wlr_log(WLR_ERROR, "Failed to start backend");
|
wlr_log(WLR_ERROR, "Failed to start backend");
|
||||||
wlr_backend_destroy(wlr);
|
wlr_backend_destroy(wlr);
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2019 Josef Gajdusek
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _POSIX_C_SOURCE 200112L
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <wayland-client-protocol.h>
|
||||||
|
#include "wlr-virtual-pointer-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
static struct wl_seat *seat = NULL;
|
||||||
|
static struct zwlr_virtual_pointer_manager_v1 *pointer_manager = NULL;
|
||||||
|
|
||||||
|
static void handle_global(void *data, struct wl_registry *registry,
|
||||||
|
uint32_t name, const char *interface, uint32_t version) {
|
||||||
|
if (strcmp(interface, wl_seat_interface.name) == 0) {
|
||||||
|
seat = wl_registry_bind(registry, name,
|
||||||
|
&wl_seat_interface, version);
|
||||||
|
} else if (strcmp(interface,
|
||||||
|
zwlr_virtual_pointer_manager_v1_interface.name) == 0) {
|
||||||
|
pointer_manager = wl_registry_bind(registry, name,
|
||||||
|
&zwlr_virtual_pointer_manager_v1_interface, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_global_remove(void *data, struct wl_registry *registry,
|
||||||
|
uint32_t name) {
|
||||||
|
// Who cares?
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_registry_listener registry_listener = {
|
||||||
|
.global = handle_global,
|
||||||
|
.global_remove = handle_global_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
if (argc < 2) {
|
||||||
|
fprintf(stderr, "Usage: ./virtual-pointer <subcommand>\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
struct wl_display * display = wl_display_connect(NULL);
|
||||||
|
if (display == NULL) {
|
||||||
|
perror("failed to create display");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
if (pointer_manager == NULL) {
|
||||||
|
fprintf(stderr, "compositor does not support wlr-virtual-pointer-unstable-v1\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct zwlr_virtual_pointer_v1 *pointer =
|
||||||
|
zwlr_virtual_pointer_manager_v1_create_virtual_pointer(
|
||||||
|
pointer_manager, seat);
|
||||||
|
|
||||||
|
const char *cmd = argv[1];
|
||||||
|
if (strcmp(cmd, "motion") == 0) {
|
||||||
|
if (argc < 4) {
|
||||||
|
fprintf(stderr, "Usage: ./virtual-pointer motion <dx> <dy>\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
wl_fixed_t dx = wl_fixed_from_double(atof(argv[2]));
|
||||||
|
wl_fixed_t dy = wl_fixed_from_double(atof(argv[3]));
|
||||||
|
zwlr_virtual_pointer_v1_motion(pointer, 0, dx, dy);
|
||||||
|
} else if (strcmp(cmd, "absolute") == 0) {
|
||||||
|
if (argc < 6) {
|
||||||
|
fprintf(stderr, "Usage: ./virtual-pointer absolute <x> <y> <x_extent> <y_extent>\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
uint32_t x = atoi(argv[2]);
|
||||||
|
uint32_t y = atoi(argv[3]);
|
||||||
|
uint32_t x_extent = atoi(argv[4]);
|
||||||
|
uint32_t y_extent = atoi(argv[5]);
|
||||||
|
zwlr_virtual_pointer_v1_motion_absolute(pointer, 0, x, y, x_extent, y_extent);
|
||||||
|
} else if (strcmp(cmd, "button") == 0) {
|
||||||
|
if (argc < 4) {
|
||||||
|
fprintf(stderr, "Usage: ./virtual-pointer button <button> press|release\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
uint32_t button = atoi(argv[2]);
|
||||||
|
bool press = !!strcmp(argv[3], "release");
|
||||||
|
zwlr_virtual_pointer_v1_button(pointer, 0, button, press);
|
||||||
|
} else if (strcmp(cmd, "axis") == 0) {
|
||||||
|
if (argc < 4) {
|
||||||
|
fprintf(stderr, "Usage: ./virtual-pointer axis <axis> <value>\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
uint32_t axis = atoi(argv[2]);
|
||||||
|
wl_fixed_t value = wl_fixed_from_double(atof(argv[3]));
|
||||||
|
zwlr_virtual_pointer_v1_axis(pointer, 0, axis, value);
|
||||||
|
zwlr_virtual_pointer_v1_axis_stop(pointer, 0, axis);
|
||||||
|
} else if (strcmp(cmd, "axis_discrete") == 0) {
|
||||||
|
if (argc < 5) {
|
||||||
|
fprintf(stderr, "Usage: ./virtual-pointer axis <axis> <value> <value_discrete>\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
uint32_t axis = atoi(argv[2]);
|
||||||
|
wl_fixed_t value = wl_fixed_from_double(atof(argv[3]));
|
||||||
|
uint32_t discrete = atoi(argv[4]);
|
||||||
|
zwlr_virtual_pointer_v1_axis_discrete(pointer, 0, axis, value, discrete);
|
||||||
|
zwlr_virtual_pointer_v1_axis_stop(pointer, 0, axis);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Invalid subcommand\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
zwlr_virtual_pointer_v1_frame(pointer);
|
||||||
|
zwlr_virtual_pointer_v1_destroy(pointer);
|
||||||
|
|
||||||
|
wl_display_flush(display);
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
97
glgen.sh
97
glgen.sh
|
@ -1,97 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# Generates a simple GL/EGL extension function loader.
|
|
||||||
#
|
|
||||||
# The input is a .txt file, with each function to load on its own line.
|
|
||||||
# If a line starts with a -, it is optional, and will not cause the loader
|
|
||||||
# to fail if it can't load the function. You'll need to check if that function
|
|
||||||
# is NULL before using it.
|
|
||||||
|
|
||||||
if [ $# -ne 2 ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
SPEC=$1
|
|
||||||
OUTDIR=$2
|
|
||||||
|
|
||||||
BASE=$(basename "$SPEC" .txt)
|
|
||||||
INCLUDE_GUARD=$(printf %s_%s_H "$OUTDIR" "$BASE" | tr -c [:alnum:] _ | tr [:lower:] [:upper:])
|
|
||||||
|
|
||||||
DECL=""
|
|
||||||
DEFN=""
|
|
||||||
LOADER=""
|
|
||||||
|
|
||||||
DECL_FMT='extern %s %s;'
|
|
||||||
DEFN_FMT='%s %s;'
|
|
||||||
LOADER_FMT='%s = (%s)eglGetProcAddress("%s");'
|
|
||||||
CHECK_FMT='if (!%s) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to load %s");
|
|
||||||
return false;
|
|
||||||
}'
|
|
||||||
|
|
||||||
while read -r COMMAND; do
|
|
||||||
OPTIONAL=0
|
|
||||||
FUNC_PTR_FMT='PFN%sPROC'
|
|
||||||
|
|
||||||
case $COMMAND in
|
|
||||||
-*)
|
|
||||||
OPTIONAL=1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
case $COMMAND in
|
|
||||||
*WL)
|
|
||||||
FUNC_PTR_FMT='PFN%s'
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
COMMAND=${COMMAND#-}
|
|
||||||
FUNC_PTR=$(printf "$FUNC_PTR_FMT" "$COMMAND" | tr [:lower:] [:upper:])
|
|
||||||
|
|
||||||
DECL="$DECL$(printf "\n$DECL_FMT" "$FUNC_PTR" "$COMMAND")"
|
|
||||||
DEFN="$DEFN$(printf "\n$DEFN_FMT" "$FUNC_PTR" "$COMMAND")"
|
|
||||||
LOADER="$LOADER$(printf "\n$LOADER_FMT" "$COMMAND" "$FUNC_PTR" "$COMMAND")"
|
|
||||||
|
|
||||||
if [ $OPTIONAL -eq 0 ]; then
|
|
||||||
LOADER="$LOADER$(printf "\n$CHECK_FMT" "$COMMAND" "$COMMAND")"
|
|
||||||
fi
|
|
||||||
done < "$SPEC"
|
|
||||||
|
|
||||||
cat > "$OUTDIR/$BASE.h" << EOF
|
|
||||||
#ifndef $INCLUDE_GUARD
|
|
||||||
#define $INCLUDE_GUARD
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <wlr/config.h>
|
|
||||||
|
|
||||||
#if !WLR_HAS_X11_BACKEND && !WLR_HAS_XWAYLAND && !defined MESA_EGL_NO_X11_HEADERS
|
|
||||||
#define MESA_EGL_NO_X11_HEADERS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <EGL/egl.h>
|
|
||||||
#include <EGL/eglext.h>
|
|
||||||
#include <GLES2/gl2.h>
|
|
||||||
#include <GLES2/gl2ext.h>
|
|
||||||
|
|
||||||
bool load_$BASE(void);
|
|
||||||
$DECL
|
|
||||||
|
|
||||||
#endif
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat > "$OUTDIR/$BASE.c" << EOF
|
|
||||||
#include <wlr/util/log.h>
|
|
||||||
#include "$BASE.h"
|
|
||||||
$DEFN
|
|
||||||
|
|
||||||
bool load_$BASE(void) {
|
|
||||||
static bool done = false;
|
|
||||||
if (done) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$LOADER
|
|
||||||
|
|
||||||
done = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
EOF
|
|
|
@ -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
|
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef BACKEND_DRM_CVT_H
|
||||||
|
#define BACKEND_DRM_CVT_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <xf86drmMode.h>
|
||||||
|
|
||||||
|
void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay,
|
||||||
|
float vrefresh, bool reduced, bool interlaced);
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,8 +1,6 @@
|
||||||
#ifndef BACKEND_DRM_DRM_H
|
#ifndef BACKEND_DRM_DRM_H
|
||||||
#define BACKEND_DRM_DRM_H
|
#define BACKEND_DRM_DRM_H
|
||||||
|
|
||||||
#include <EGL/egl.h>
|
|
||||||
#include <gbm.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
@ -12,37 +10,37 @@
|
||||||
#include <wlr/backend/drm.h>
|
#include <wlr/backend/drm.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/render/drm_format_set.h>
|
#include <wlr/render/drm_format_set.h>
|
||||||
#include <wlr/render/egl.h>
|
|
||||||
#include <xf86drmMode.h>
|
#include <xf86drmMode.h>
|
||||||
#include "iface.h"
|
#include "backend/drm/iface.h"
|
||||||
#include "properties.h"
|
#include "backend/drm/properties.h"
|
||||||
#include "renderer.h"
|
#include "backend/drm/renderer.h"
|
||||||
|
|
||||||
struct wlr_drm_plane {
|
struct wlr_drm_plane {
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
|
|
||||||
struct wlr_drm_surface surf;
|
/* Only initialized on multi-GPU setups */
|
||||||
struct wlr_drm_surface mgpu_surf;
|
struct wlr_drm_surface mgpu_surf;
|
||||||
|
|
||||||
uint32_t drm_format; // ARGB8888 or XRGB8888
|
/* Buffer to be submitted to the kernel on the next page-flip */
|
||||||
struct wlr_drm_format_set formats;
|
struct wlr_drm_fb *pending_fb;
|
||||||
|
/* Buffer submitted to the kernel, will be presented on next vblank */
|
||||||
|
struct wlr_drm_fb *queued_fb;
|
||||||
|
/* Buffer currently displayed on screen */
|
||||||
|
struct wlr_drm_fb *current_fb;
|
||||||
|
|
||||||
// Only used by cursor
|
struct wlr_drm_format_set formats;
|
||||||
float matrix[9];
|
|
||||||
bool cursor_enabled;
|
|
||||||
int32_t cursor_hotspot_x, cursor_hotspot_y;
|
|
||||||
|
|
||||||
union wlr_drm_plane_props props;
|
union wlr_drm_plane_props props;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_drm_crtc {
|
struct wlr_drm_crtc {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
|
struct wlr_drm_lease *lease;
|
||||||
|
|
||||||
// Atomic modesetting only
|
// Atomic modesetting only
|
||||||
uint32_t mode_id;
|
uint32_t mode_id;
|
||||||
uint32_t gamma_lut;
|
uint32_t gamma_lut;
|
||||||
drmModeAtomicReq *atomic;
|
|
||||||
|
|
||||||
// Legacy only
|
// Legacy only
|
||||||
drmModeCrtc *legacy_crtc;
|
drmModeCrtc *legacy_crtc;
|
||||||
|
@ -50,19 +48,7 @@ struct wlr_drm_crtc {
|
||||||
struct wlr_drm_plane *primary;
|
struct wlr_drm_plane *primary;
|
||||||
struct wlr_drm_plane *cursor;
|
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;
|
union wlr_drm_crtc_props props;
|
||||||
|
|
||||||
struct wl_list connectors;
|
|
||||||
|
|
||||||
uint16_t *gamma_table;
|
|
||||||
size_t gamma_table_size;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_drm_backend {
|
struct wlr_drm_backend {
|
||||||
|
@ -74,6 +60,8 @@ struct wlr_drm_backend {
|
||||||
bool addfb2_modifiers;
|
bool addfb2_modifiers;
|
||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
|
char *name;
|
||||||
|
struct wlr_device *dev;
|
||||||
|
|
||||||
size_t num_crtcs;
|
size_t num_crtcs;
|
||||||
struct wlr_drm_crtc *crtcs;
|
struct wlr_drm_crtc *crtcs;
|
||||||
|
@ -82,16 +70,26 @@ struct wlr_drm_backend {
|
||||||
struct wl_event_source *drm_event;
|
struct wl_event_source *drm_event;
|
||||||
|
|
||||||
struct wl_listener display_destroy;
|
struct wl_listener display_destroy;
|
||||||
struct wl_listener session_signal;
|
struct wl_listener session_destroy;
|
||||||
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 wl_list outputs;
|
||||||
|
|
||||||
struct wlr_drm_renderer renderer;
|
/* Only initialized on multi-GPU setups */
|
||||||
|
struct wlr_drm_renderer mgpu_renderer;
|
||||||
|
|
||||||
struct wlr_session *session;
|
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
|
// Connector is available but no output is plugged in
|
||||||
WLR_DRM_CONN_DISCONNECTED,
|
WLR_DRM_CONN_DISCONNECTED,
|
||||||
// An output just has been plugged in and is waiting for a modeset
|
// An output just has been plugged in and is waiting for a modeset
|
||||||
|
@ -105,34 +103,43 @@ struct wlr_drm_mode {
|
||||||
drmModeModeInfo drm_mode;
|
drmModeModeInfo drm_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_drm_connector {
|
struct wlr_drm_connector_state {
|
||||||
struct wlr_output output;
|
const struct wlr_output_state *base;
|
||||||
|
bool modeset;
|
||||||
|
bool active;
|
||||||
|
drmModeModeInfo mode;
|
||||||
|
};
|
||||||
|
|
||||||
enum wlr_drm_connector_state state;
|
struct wlr_drm_connector {
|
||||||
struct wlr_output_mode *desired_mode;
|
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;
|
bool desired_enabled;
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
|
struct wlr_drm_lease *lease;
|
||||||
|
|
||||||
struct wlr_drm_crtc *crtc;
|
struct wlr_drm_crtc *crtc;
|
||||||
uint32_t possible_crtc;
|
uint32_t possible_crtcs;
|
||||||
|
|
||||||
union wlr_drm_connector_props props;
|
union wlr_drm_connector_props props;
|
||||||
|
|
||||||
uint32_t width, height;
|
bool cursor_enabled;
|
||||||
int32_t cursor_x, cursor_y;
|
int cursor_x, cursor_y;
|
||||||
|
int cursor_width, cursor_height;
|
||||||
|
int cursor_hotspot_x, cursor_hotspot_y;
|
||||||
|
|
||||||
drmModeCrtc *old_crtc;
|
|
||||||
|
|
||||||
bool pageflip_pending;
|
|
||||||
struct wl_event_source *retry_pageflip;
|
|
||||||
struct wl_list link;
|
struct wl_list link;
|
||||||
|
|
||||||
// DMA-BUF to be displayed on next commit
|
/* CRTC ID if a page-flip is pending, zero otherwise.
|
||||||
struct wlr_dmabuf_attributes pending_dmabuf;
|
*
|
||||||
// Buffer submitted to the kernel but not yet displayed
|
* We've asked for a state change in the kernel, and yet to receive a
|
||||||
struct wlr_buffer *pending_buffer;
|
* notification for its completion. Currently, the kernel only has a
|
||||||
// Buffer currently being displayed
|
* queue length of 1, and no way to modify your submissions after
|
||||||
struct wlr_buffer *current_buffer;
|
* they're sent.
|
||||||
|
*/
|
||||||
|
uint32_t pending_page_flip_crtc;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_drm_backend *get_drm_backend_from_backend(
|
struct wlr_drm_backend *get_drm_backend_from_backend(
|
||||||
|
@ -140,13 +147,24 @@ struct wlr_drm_backend *get_drm_backend_from_backend(
|
||||||
bool check_drm_features(struct wlr_drm_backend *drm);
|
bool check_drm_features(struct wlr_drm_backend *drm);
|
||||||
bool init_drm_resources(struct wlr_drm_backend *drm);
|
bool init_drm_resources(struct wlr_drm_backend *drm);
|
||||||
void finish_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);
|
int handle_drm_event(int fd, uint32_t mask, void *data);
|
||||||
bool enable_drm_connector(struct wlr_output *output, bool enable);
|
void destroy_drm_connector(struct wlr_drm_connector *conn);
|
||||||
bool set_drm_connector_gamma(struct wlr_output *output, size_t size,
|
bool drm_connector_commit_state(struct wlr_drm_connector *conn,
|
||||||
const uint16_t *r, const uint16_t *g, const uint16_t *b);
|
const struct wlr_output_state *state);
|
||||||
bool drm_connector_set_mode(struct wlr_output *output,
|
bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn);
|
||||||
struct wlr_output_mode *mode);
|
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
|
#endif
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#ifndef BACKEND_DRM_IFACE_H
|
#ifndef BACKEND_DRM_IFACE_H
|
||||||
#define BACKEND_DRM_IFACE_H
|
#define BACKEND_DRM_IFACE_H
|
||||||
|
|
||||||
#include <gbm.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
|
@ -10,32 +9,20 @@
|
||||||
struct wlr_drm_backend;
|
struct wlr_drm_backend;
|
||||||
struct wlr_drm_connector;
|
struct wlr_drm_connector;
|
||||||
struct wlr_drm_crtc;
|
struct wlr_drm_crtc;
|
||||||
|
struct wlr_drm_connector_state;
|
||||||
|
|
||||||
// Used to provide atomic or legacy DRM functions
|
// Used to provide atomic or legacy DRM functions
|
||||||
struct wlr_drm_interface {
|
struct wlr_drm_interface {
|
||||||
// Enable or disable DPMS for connector
|
// Commit all pending changes on a CRTC.
|
||||||
bool (*conn_enable)(struct wlr_drm_backend *drm,
|
bool (*crtc_commit)(struct wlr_drm_connector *conn,
|
||||||
struct wlr_drm_connector *conn, bool enable);
|
const struct wlr_drm_connector_state *state, uint32_t flags,
|
||||||
// Pageflip on crtc. If mode is non-NULL perform a full modeset using it.
|
bool test_only);
|
||||||
bool (*crtc_pageflip)(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_drm_connector *conn, struct wlr_drm_crtc *crtc,
|
|
||||||
uint32_t fb_id, drmModeModeInfo *mode);
|
|
||||||
// Enable the cursor buffer on crtc. Set bo to NULL to disable
|
|
||||||
bool (*crtc_set_cursor)(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_drm_crtc *crtc, struct gbm_bo *bo);
|
|
||||||
// Move the cursor on crtc
|
|
||||||
bool (*crtc_move_cursor)(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_drm_crtc *crtc, int x, int y);
|
|
||||||
// Set the gamma lut on crtc
|
|
||||||
bool (*crtc_set_gamma)(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_drm_crtc *crtc, size_t size,
|
|
||||||
uint16_t *r, uint16_t *g, uint16_t *b);
|
|
||||||
// Get the gamma lut size of a crtc
|
|
||||||
size_t (*crtc_get_gamma_size)(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_drm_crtc *crtc);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct wlr_drm_interface atomic_iface;
|
extern const struct wlr_drm_interface atomic_iface;
|
||||||
extern const struct wlr_drm_interface legacy_iface;
|
extern const struct wlr_drm_interface legacy_iface;
|
||||||
|
|
||||||
|
bool drm_legacy_crtc_set_gamma(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_crtc *crtc, size_t size, uint16_t *lut);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -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
|
|
@ -16,6 +16,10 @@ union wlr_drm_connector_props {
|
||||||
uint32_t dpms;
|
uint32_t dpms;
|
||||||
uint32_t link_status; // not guaranteed to exist
|
uint32_t link_status; // not guaranteed to exist
|
||||||
uint32_t path;
|
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
|
// atomic-modesetting only
|
||||||
|
|
||||||
|
@ -27,15 +31,14 @@ union wlr_drm_connector_props {
|
||||||
union wlr_drm_crtc_props {
|
union wlr_drm_crtc_props {
|
||||||
struct {
|
struct {
|
||||||
// Neither of these are guaranteed to exist
|
// Neither of these are guaranteed to exist
|
||||||
uint32_t rotation;
|
uint32_t vrr_enabled;
|
||||||
uint32_t scaling_mode;
|
uint32_t gamma_lut;
|
||||||
|
uint32_t gamma_lut_size;
|
||||||
|
|
||||||
// atomic-modesetting only
|
// atomic-modesetting only
|
||||||
|
|
||||||
uint32_t active;
|
uint32_t active;
|
||||||
uint32_t mode_id;
|
uint32_t mode_id;
|
||||||
uint32_t gamma_lut;
|
|
||||||
uint32_t gamma_lut_size;
|
|
||||||
};
|
};
|
||||||
uint32_t props[6];
|
uint32_t props[6];
|
||||||
};
|
};
|
||||||
|
@ -58,8 +61,9 @@ union wlr_drm_plane_props {
|
||||||
uint32_t crtc_h;
|
uint32_t crtc_h;
|
||||||
uint32_t fb_id;
|
uint32_t fb_id;
|
||||||
uint32_t crtc_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,
|
bool get_drm_connector_props(int fd, uint32_t id,
|
||||||
|
@ -69,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);
|
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);
|
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
|
#endif
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
#ifndef BACKEND_DRM_RENDERER_H
|
#ifndef BACKEND_DRM_RENDERER_H
|
||||||
#define BACKEND_DRM_RENDERER_H
|
#define BACKEND_DRM_RENDERER_H
|
||||||
|
|
||||||
#include <EGL/egl.h>
|
|
||||||
#include <gbm.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
|
@ -10,15 +8,13 @@
|
||||||
|
|
||||||
struct wlr_drm_backend;
|
struct wlr_drm_backend;
|
||||||
struct wlr_drm_plane;
|
struct wlr_drm_plane;
|
||||||
|
struct wlr_buffer;
|
||||||
|
|
||||||
struct wlr_drm_renderer {
|
struct wlr_drm_renderer {
|
||||||
int fd;
|
struct wlr_drm_backend *backend;
|
||||||
struct gbm_device *gbm;
|
|
||||||
struct wlr_egl egl;
|
|
||||||
|
|
||||||
uint32_t gbm_format;
|
|
||||||
|
|
||||||
struct wlr_renderer *wlr_rend;
|
struct wlr_renderer *wlr_rend;
|
||||||
|
struct wlr_allocator *allocator;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_drm_surface {
|
struct wlr_drm_surface {
|
||||||
|
@ -27,35 +23,38 @@ struct wlr_drm_surface {
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
uint32_t height;
|
uint32_t height;
|
||||||
|
|
||||||
struct gbm_surface *gbm;
|
struct wlr_swapchain *swapchain;
|
||||||
EGLSurface egl;
|
};
|
||||||
|
|
||||||
struct gbm_bo *front;
|
struct wlr_drm_fb {
|
||||||
struct gbm_bo *back;
|
struct wlr_buffer *wlr_buf;
|
||||||
|
struct wlr_addon addon;
|
||||||
|
struct wlr_drm_backend *backend;
|
||||||
|
struct wl_list link; // wlr_drm_backend.fbs
|
||||||
|
|
||||||
|
uint32_t id;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool init_drm_renderer(struct wlr_drm_backend *drm,
|
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);
|
void finish_drm_renderer(struct wlr_drm_renderer *renderer);
|
||||||
|
|
||||||
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,
|
struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height,
|
||||||
uint32_t format, uint32_t flags);
|
const struct wlr_drm_format *drm_format);
|
||||||
|
|
||||||
bool init_drm_plane_surfaces(struct wlr_drm_plane *plane,
|
bool drm_fb_import(struct wlr_drm_fb **fb, struct wlr_drm_backend *drm,
|
||||||
struct wlr_drm_backend *drm, int32_t width, uint32_t height,
|
struct wlr_buffer *buf, const struct wlr_drm_format_set *formats);
|
||||||
uint32_t format);
|
void drm_fb_destroy(struct wlr_drm_fb *fb);
|
||||||
|
|
||||||
void finish_drm_surface(struct wlr_drm_surface *surf);
|
void drm_fb_clear(struct wlr_drm_fb **fb);
|
||||||
bool make_drm_surface_current(struct wlr_drm_surface *surf, int *buffer_age);
|
void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old);
|
||||||
struct gbm_bo *swap_drm_surface_buffers(struct wlr_drm_surface *surf,
|
|
||||||
pixman_region32_t *damage);
|
struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf,
|
||||||
struct gbm_bo *get_drm_surface_front(struct wlr_drm_surface *surf);
|
struct wlr_buffer *buffer);
|
||||||
void post_drm_surface(struct wlr_drm_surface *surf);
|
|
||||||
struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest,
|
struct wlr_drm_format *drm_plane_pick_render_format(
|
||||||
struct gbm_bo *src);
|
struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer);
|
||||||
struct gbm_bo *import_gbm_bo(struct wlr_drm_renderer *renderer,
|
void drm_plane_finish_surface(struct wlr_drm_plane *plane);
|
||||||
struct wlr_dmabuf_attributes *attribs);
|
|
||||||
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -13,9 +13,6 @@ void parse_edid(struct wlr_output *restrict output, size_t len,
|
||||||
const uint8_t *data);
|
const uint8_t *data);
|
||||||
// Returns the string representation of a DRM output type
|
// Returns the string representation of a DRM output type
|
||||||
const char *conn_get_name(uint32_t type_id);
|
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, uint32_t drm_format,
|
|
||||||
bool with_modifiers);
|
|
||||||
|
|
||||||
// Part of match_obj
|
// Part of match_obj
|
||||||
enum {
|
enum {
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
struct wlr_headless_backend {
|
struct wlr_headless_backend {
|
||||||
struct wlr_backend backend;
|
struct wlr_backend backend;
|
||||||
struct wlr_egl egl;
|
|
||||||
struct wlr_renderer *renderer;
|
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
struct wl_list outputs;
|
struct wl_list outputs;
|
||||||
size_t last_output_num;
|
size_t last_output_num;
|
||||||
|
@ -24,14 +22,13 @@ struct wlr_headless_output {
|
||||||
struct wlr_headless_backend *backend;
|
struct wlr_headless_backend *backend;
|
||||||
struct wl_list link;
|
struct wl_list link;
|
||||||
|
|
||||||
void *egl_surface;
|
|
||||||
struct wl_event_source *frame_timer;
|
struct wl_event_source *frame_timer;
|
||||||
int frame_delay; // ms
|
int frame_delay; // ms
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_headless_input_device {
|
struct wlr_headless_input_device {
|
||||||
struct wlr_input_device wlr_input_device;
|
struct wlr_input_device wlr_input_device;
|
||||||
|
struct wl_list link;
|
||||||
struct wlr_headless_backend *backend;
|
struct wlr_headless_backend *backend;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue