Compare commits
1723 Commits
0.4.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 | |
|
b051bb68c2 | |
|
b1b93c2c7e | |
|
5f78ea20fa | |
|
ebeef0fbe8 | |
|
2285e36b0c | |
|
57babd2e13 | |
|
020a33e057 | |
|
c808613287 | |
|
2ecfc46b93 | |
|
06644575da | |
|
a14d650864 | |
|
8b0f1bc850 | |
|
bf90474b74 | |
|
734c64a6cc | |
|
a20bb38763 | |
|
bd6b348feb | |
|
cdfe836b03 | |
|
fa477c77c4 | |
|
e19f48d1e4 | |
|
4f4d3cf2a2 | |
|
9914784594 | |
|
8d2ea9544b | |
|
540e23d102 | |
|
82f48b8912 | |
|
8d5f27ef25 | |
|
e7f1aa30dd | |
|
217cf18a4b | |
|
39c5d93dea | |
|
68b4a5305e | |
|
0695324de7 | |
|
8f3d73e3a3 | |
|
b400c26b23 | |
|
3fc977d7da | |
|
820800a5ab | |
|
c1be9b6945 | |
|
52037d13f7 | |
|
78d96009e4 | |
|
40d17c1305 | |
|
94f65e354d | |
|
58b2584863 | |
|
913cac1835 | |
|
01f903874b | |
|
4d36cc86eb | |
|
6396710976 | |
|
ee5f98ad49 | |
|
d20aee6c9d | |
|
724b5e1b8d | |
|
ce3e413e83 | |
|
ca45f4490c | |
|
76ef089f52 | |
|
bb05617414 | |
|
9e8f952997 | |
|
415267ac13 | |
|
2d4bc66f11 | |
|
692a16cef7 | |
|
91752e8285 | |
|
28cc1730e8 | |
|
6345000b92 | |
|
df3f0ffbb0 | |
|
c2fb8a84a2 | |
|
1ef0c03a46 | |
|
1d78bae19d | |
|
ded441ffd5 | |
|
edb30a6828 | |
|
fb739b8293 | |
|
d10072e76c | |
|
0b1f9439ba | |
|
46dc4100d6 | |
|
b3f42548d0 | |
|
d80acadfd8 | |
|
5027b23dc2 | |
|
ce3f4c3fe1 | |
|
33127c545b | |
|
06a13203dd | |
|
b85f0cbff9 | |
|
d201fc3506 | |
|
e8057bb60c | |
|
e07ffaa249 | |
|
6c659da98b | |
|
e554c593f9 | |
|
67cd84de45 | |
|
d1766547bd | |
|
44ba603c0e | |
|
ff6b352d75 | |
|
afe7b207d5 | |
|
1d222309b8 | |
|
493804e421 | |
|
6dbdf5db34 | |
|
5c78f1b0d5 | |
|
e516ea4c79 | |
|
96d6fde5dc | |
|
3dec88e455 | |
|
abddd7b4db | |
|
96e9c0f9c8 | |
|
6dfe238ff1 | |
|
a5e32f652b | |
|
0ab1bb623e | |
|
151b7d1d94 | |
|
aa39dbd1e7 | |
|
e04e1ba197 | |
|
461c4f58a6 | |
|
522ddd93f1 | |
|
a68c7c0c8d | |
|
03346cb28f | |
|
90c284bded | |
|
107a1789ea | |
|
947d5ff481 | |
|
292d20e4c1 | |
|
6ae6b5dbb6 | |
|
c0305f4f86 | |
|
cf3b083c32 | |
|
937e0e7937 | |
|
27a87ac586 | |
|
22dd7d3731 | |
|
8cc0859814 | |
|
4e6c17a7c9 | |
|
a656e486f4 | |
|
d7ef3a1f38 | |
|
bde5a1f4ea | |
|
8b1220f5a3 | |
|
1dbece74fb | |
|
57d32d03a8 | |
|
d6e250b389 | |
|
736632ad4e | |
|
a671fc51d2 | |
|
8d3369d70f | |
|
933208837d | |
|
f42816ce3f | |
|
20690346c7 | |
|
5e6766a165 | |
|
31dcecbfa9 | |
|
9a0f8a194c | |
|
23e37e7b1d | |
|
56ceed38bf | |
|
8acbf449cc | |
|
95b22619e0 | |
|
4207f05030 | |
|
d6615e0e84 | |
|
dc5d1d08ef | |
|
7a2f929201 | |
|
1515c56cae | |
|
930e37eae9 | |
|
a800aa3fb4 | |
|
524f5c8425 | |
|
ab3446091b | |
|
fc0ba3ea22 | |
|
81e1489e79 | |
|
8136605cfb | |
|
d71ebde545 | |
|
ee77a65fe3 | |
|
ef68d7d4d1 | |
|
54d6ba78c3 | |
|
e873c652bf | |
|
d695003498 | |
|
0b64ecc162 | |
|
3a233b3fcc | |
|
b6d0de177a | |
|
670c787fa7 | |
|
fd0d7d0907 | |
|
9faea17c73 | |
|
43bd1d807a | |
|
e42178d03f | |
|
c01b81c99c | |
|
09b2833dcd | |
|
08454adada | |
|
17f688735f | |
|
4e614683b7 | |
|
b4f821ca31 | |
|
4281c8c566 | |
|
cd60f40bbb | |
|
4453757fc9 | |
|
6b7f5e4010 | |
|
c97f0eb0f2 | |
|
b135599e5a | |
|
67523fb228 | |
|
408eca7dfa | |
|
c9137cba69 | |
|
aa5c369910 | |
|
2baad6eba6 | |
|
a3c31bb875 | |
|
8363ca8c9f | |
|
6a8f17b5f6 | |
|
680c4c573c | |
|
d02548d87a | |
|
30d3426164 | |
|
9601019192 | |
|
fb106eb979 | |
|
c9b9e48525 | |
|
6a60dafe59 | |
|
c2178d51a8 | |
|
242e9e3bf0 | |
|
bfaf06f04b | |
|
62fd03a7be | |
|
8efeca528f | |
|
755a1c9138 | |
|
2e1dd4ae36 | |
|
e8f012c993 | |
|
2fde5c95d8 | |
|
5445d8aad0 | |
|
4135fafecd | |
|
f8fcd7f06a | |
|
cf9607f282 | |
|
7da9af31e8 | |
|
d3b48dfcae |
|
@ -2,21 +2,35 @@ image: alpine/edge
|
||||||
packages:
|
packages:
|
||||||
- eudev-dev
|
- eudev-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
|
||||||
|
- 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
|
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
|
||||||
|
|
|
@ -2,7 +2,6 @@ image: archlinux
|
||||||
packages:
|
packages:
|
||||||
- clang
|
- clang
|
||||||
- ffmpeg
|
- ffmpeg
|
||||||
- libcap
|
|
||||||
- libinput
|
- libinput
|
||||||
- libxkbcommon
|
- libxkbcommon
|
||||||
- mesa
|
- mesa
|
||||||
|
@ -10,17 +9,37 @@ packages:
|
||||||
- pixman
|
- pixman
|
||||||
- wayland
|
- wayland
|
||||||
- wayland-protocols
|
- wayland-protocols
|
||||||
|
- xcb-util-errors
|
||||||
- xcb-util-image
|
- xcb-util-image
|
||||||
|
- xcb-util-renderutil
|
||||||
|
- 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
|
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
|
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,42 +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
|
||||||
- x11/libX11
|
- graphics/wayland
|
||||||
- x11/libinput
|
- graphics/wayland-protocols
|
||||||
- x11/libxcb
|
- multimedia/ffmpeg
|
||||||
- x11/libxkbcommon
|
- x11/libX11
|
||||||
- x11/pixman
|
- x11/libinput
|
||||||
- x11/xcb-util-errors
|
- x11/libxcb
|
||||||
- x11/xcb-util-wm
|
- x11/libxkbcommon
|
||||||
|
- 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:
|
||||||
- fixup_epoll: |
|
- wlroots: |
|
||||||
cat << 'EOF' | sudo tee /usr/local/libdata/pkgconfig/epoll-shim.pc
|
cd wlroots
|
||||||
prefix=/usr/local
|
meson build --fatal-meson-warnings -Dauto_features=enabled
|
||||||
exec_prefix=\$\{\$prefix\}
|
ninja -C build
|
||||||
libdir=${prefix}/lib
|
sudo ninja -C build install
|
||||||
sharedlibdir=${prefix}/lib
|
- tinywl: |
|
||||||
includedir=${prefix}/include/libepoll-shim
|
cd wlroots/tinywl
|
||||||
Name: epoll-shim
|
gmake
|
||||||
Description: epoll shim implemented using kevent
|
|
||||||
Version: 0
|
|
||||||
Requires:
|
|
||||||
Libs: -L${libdir} -L${sharedlibdir} -lepoll-shim
|
|
||||||
Libs.private: -pthread -lrt
|
|
||||||
Cflags: -I${includedir}
|
|
||||||
EOF
|
|
||||||
- wlroots: |
|
|
||||||
cd wlroots
|
|
||||||
meson build
|
|
||||||
ninja -C build
|
|
||||||
|
|
|
@ -7,4 +7,3 @@ build/
|
||||||
build-*/
|
build-*/
|
||||||
wayland-*-protocol.*
|
wayland-*-protocol.*
|
||||||
wlr-example.ini
|
wlr-example.ini
|
||||||
rootston.ini
|
|
||||||
|
|
|
@ -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,27 +1,39 @@
|
||||||
#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.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
|
||||||
|
|
||||||
|
#define WAIT_SESSION_TIMEOUT 10000 // ms
|
||||||
|
|
||||||
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) {
|
||||||
assert(backend);
|
assert(backend);
|
||||||
|
@ -31,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);
|
||||||
|
@ -50,13 +66,6 @@ void wlr_backend_destroy(struct wlr_backend *backend) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_renderer *wlr_backend_get_renderer(struct wlr_backend *backend) {
|
|
||||||
if (backend->impl->get_renderer) {
|
|
||||||
return backend->impl->get_renderer(backend);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend) {
|
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);
|
||||||
|
@ -64,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);
|
||||||
|
@ -71,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) {
|
||||||
|
@ -87,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;
|
||||||
}
|
}
|
||||||
|
@ -104,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;
|
||||||
}
|
}
|
||||||
|
@ -120,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;
|
||||||
}
|
}
|
||||||
|
@ -134,33 +203,29 @@ static struct wlr_backend *attempt_headless_backend(
|
||||||
return backend;
|
return backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wlr_backend *attempt_noop_backend(struct wl_display *display) {
|
#if WLR_HAS_DRM_BACKEND
|
||||||
struct wlr_backend *backend = wlr_noop_backend_create(display);
|
static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
|
||||||
if (backend == NULL) {
|
struct wlr_backend *backend, struct wlr_session *session) {
|
||||||
|
struct wlr_device *gpus[8];
|
||||||
|
ssize_t num_gpus = wlr_session_find_gpus(session, 8, gpus);
|
||||||
|
if (num_gpus < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to find GPUs");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t outputs = parse_outputs_env("WLR_NOOP_OUTPUTS");
|
if (num_gpus == 0) {
|
||||||
for (size_t i = 0; i < outputs; ++i) {
|
wlr_log(WLR_ERROR, "Found 0 GPUs, cannot create backend");
|
||||||
wlr_noop_add_output(backend);
|
return NULL;
|
||||||
|
} else {
|
||||||
|
wlr_log(WLR_INFO, "Found %zu GPUs", num_gpus);
|
||||||
}
|
}
|
||||||
|
|
||||||
return backend;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
|
|
||||||
struct wlr_backend *backend, struct wlr_session *session,
|
|
||||||
wlr_renderer_create_func_t create_renderer_func) {
|
|
||||||
int gpus[8];
|
|
||||||
size_t num_gpus = wlr_session_find_gpus(session, 8, gpus);
|
|
||||||
struct wlr_backend *primary_drm = NULL;
|
struct wlr_backend *primary_drm = NULL;
|
||||||
wlr_log(WLR_INFO, "Found %zu GPUs", num_gpus);
|
for (size_t i = 0; i < (size_t)num_gpus; ++i) {
|
||||||
|
|
||||||
for (size_t i = 0; i < 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,46 +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);
|
||||||
} 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) {
|
||||||
|
@ -219,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");
|
||||||
|
@ -229,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);
|
||||||
|
@ -254,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) {
|
||||||
|
@ -293,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,266 +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);
|
||||||
if (atom->failed) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret = drmModeAtomicCommit(drm_fd, atom->req, flags, conn);
|
|
||||||
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, conn)) {
|
|
||||||
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);
|
|
||||||
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).
|
|
||||||
// TEMP: This is broken on AMDGPU. Provide a fallback to legacy until they
|
|
||||||
// get it fixed. Ref https://bugs.freedesktop.org/show_bug.cgi?id=107459
|
|
||||||
const char *no_atomic_str = getenv("WLR_DRM_NO_ATOMIC_GAMMA");
|
|
||||||
bool no_atomic = no_atomic_str != NULL && strcmp(no_atomic_str, "1") == 0;
|
|
||||||
if (crtc->props.gamma_lut == 0 || no_atomic) {
|
|
||||||
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,15 +1,14 @@
|
||||||
#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>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server.h>
|
#include <wayland-server-core.h>
|
||||||
#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;
|
||||||
|
}
|
||||||
|
}
|
1993
backend/drm/drm.c
1993
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,78 +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, conn)) {
|
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 ret >= 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
output->adaptive_sync_status = state->base->adaptive_sync_enabled ?
|
||||||
bool legacy_crtc_set_cursor(struct wlr_drm_backend *drm,
|
WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED :
|
||||||
struct wlr_drm_crtc *crtc, struct gbm_bo *bo) {
|
WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED;
|
||||||
if (!crtc || !crtc->cursor) {
|
wlr_drm_conn_log(conn, WLR_DEBUG, "VRR %s",
|
||||||
return true;
|
state->base->adaptive_sync_enabled ? "enabled" : "disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bo) {
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
drmModeFB *drm_fb = drmModeGetFB(drm->fd, cursor_fb->id);
|
||||||
|
if (drm_fb == NULL) {
|
||||||
|
wlr_drm_conn_log_errno(conn, WLR_DEBUG, "Failed to get cursor "
|
||||||
|
"BO handle: drmModeGetFB failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t cursor_handle = drm_fb->handle;
|
||||||
|
uint32_t cursor_width = drm_fb->width;
|
||||||
|
uint32_t cursor_height = drm_fb->height;
|
||||||
|
drmModeFreeFB(drm_fb);
|
||||||
|
|
||||||
|
int ret = drmModeSetCursor(drm->fd, crtc->id, cursor_handle,
|
||||||
|
cursor_width, cursor_height);
|
||||||
|
int set_cursor_errno = errno;
|
||||||
|
if (drmCloseBufferHandle(drm->fd, cursor_handle) != 0) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed");
|
||||||
|
}
|
||||||
|
if (ret != 0) {
|
||||||
|
wlr_drm_conn_log(conn, WLR_DEBUG, "drmModeSetCursor failed: %s",
|
||||||
|
strerror(set_cursor_errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drmModeMoveCursor(drm->fd,
|
||||||
|
crtc->id, conn->cursor_x, conn->cursor_y) != 0) {
|
||||||
|
wlr_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>
|
||||||
|
@ -19,38 +20,44 @@ struct prop_info {
|
||||||
|
|
||||||
static const struct prop_info connector_info[] = {
|
static const struct prop_info connector_info[] = {
|
||||||
#define INDEX(name) (offsetof(union wlr_drm_connector_props, name) / sizeof(uint32_t))
|
#define INDEX(name) (offsetof(union wlr_drm_connector_props, name) / sizeof(uint32_t))
|
||||||
{ "CRTC_ID", INDEX(crtc_id) },
|
{ "CRTC_ID", INDEX(crtc_id) },
|
||||||
{ "DPMS", INDEX(dpms) },
|
{ "DPMS", INDEX(dpms) },
|
||||||
{ "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
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct prop_info crtc_info[] = {
|
static const struct prop_info crtc_info[] = {
|
||||||
#define INDEX(name) (offsetof(union wlr_drm_crtc_props, name) / sizeof(uint32_t))
|
#define INDEX(name) (offsetof(union wlr_drm_crtc_props, name) / sizeof(uint32_t))
|
||||||
{ "ACTIVE", INDEX(active) },
|
{ "ACTIVE", INDEX(active) },
|
||||||
{ "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
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct prop_info plane_info[] = {
|
static const struct prop_info plane_info[] = {
|
||||||
#define INDEX(name) (offsetof(union wlr_drm_plane_props, name) / sizeof(uint32_t))
|
#define INDEX(name) (offsetof(union wlr_drm_plane_props, name) / sizeof(uint32_t))
|
||||||
{ "CRTC_H", INDEX(crtc_h) },
|
{ "CRTC_H", INDEX(crtc_h) },
|
||||||
{ "CRTC_ID", INDEX(crtc_id) },
|
{ "CRTC_ID", INDEX(crtc_id) },
|
||||||
{ "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_ID", INDEX(fb_id) },
|
{ "FB_DAMAGE_CLIPS", INDEX(fb_damage_clips) },
|
||||||
{ "SRC_H", INDEX(src_h) },
|
{ "FB_ID", INDEX(fb_id) },
|
||||||
{ "SRC_W", INDEX(src_w) },
|
{ "IN_FORMATS", INDEX(in_formats) },
|
||||||
{ "SRC_X", INDEX(src_x) },
|
{ "SRC_H", INDEX(src_h) },
|
||||||
{ "SRC_Y", INDEX(src_y) },
|
{ "SRC_W", INDEX(src_w) },
|
||||||
{ "type", INDEX(type) },
|
{ "SRC_X", INDEX(src_x) },
|
||||||
|
{ "SRC_Y", INDEX(src_y) },
|
||||||
|
{ "rotation", INDEX(rotation) },
|
||||||
|
{ "type", INDEX(type) },
|
||||||
#undef INDEX
|
#undef INDEX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -149,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,54 +1,42 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <EGL/egl.h>
|
#include <drm_fourcc.h>
|
||||||
#include <EGL/eglext.h>
|
#include <fcntl.h>
|
||||||
#include <gbm.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) {
|
||||||
|
@ -56,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;
|
||||||
}
|
}
|
||||||
|
@ -72,189 +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);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs) {
|
struct wlr_drm_format *drm_plane_pick_render_format(
|
||||||
memset(attribs, 0, sizeof(struct wlr_dmabuf_attributes));
|
struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer) {
|
||||||
|
const struct wlr_drm_format_set *render_formats =
|
||||||
attribs->n_planes = gbm_bo_get_plane_count(bo);
|
wlr_renderer_get_render_formats(renderer->wlr_rend);
|
||||||
if (attribs->n_planes > WLR_DMABUF_MAX_PLANES) {
|
if (render_formats == NULL) {
|
||||||
return false;
|
wlr_log(WLR_ERROR, "Failed to get render formats");
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
attribs->width = gbm_bo_get_width(bo);
|
const struct wlr_drm_format_set *plane_formats = &plane->formats;
|
||||||
attribs->height = gbm_bo_get_height(bo);
|
|
||||||
attribs->format = gbm_bo_get_format(bo);
|
|
||||||
attribs->modifier = gbm_bo_get_modifier(bo);
|
|
||||||
|
|
||||||
for (int i = 0; i < attribs->n_planes; ++i) {
|
uint32_t fmt = DRM_FORMAT_ARGB8888;
|
||||||
attribs->offset[i] = gbm_bo_get_offset(bo, i);
|
if (!wlr_drm_format_set_get(&plane->formats, fmt)) {
|
||||||
attribs->stride[i] = gbm_bo_get_stride_for_plane(bo, i);
|
const struct wlr_pixel_format_info *format_info =
|
||||||
attribs->fd[i] = gbm_bo_get_fd(bo);
|
drm_get_pixel_format_info(fmt);
|
||||||
if (attribs->fd[i] < 0) {
|
assert(format_info != NULL &&
|
||||||
for (int j = 0; j < i; ++j) {
|
format_info->opaque_substitute != DRM_FORMAT_INVALID);
|
||||||
close(attribs->fd[j]);
|
fmt = format_info->opaque_substitute;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct wlr_drm_format *render_format =
|
||||||
|
wlr_drm_format_set_get(render_formats, fmt);
|
||||||
|
if (render_format == NULL) {
|
||||||
|
wlr_log(WLR_DEBUG, "Renderer doesn't support format 0x%"PRIX32, fmt);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct wlr_drm_format *plane_format =
|
||||||
|
wlr_drm_format_set_get(plane_formats, fmt);
|
||||||
|
if (plane_format == NULL) {
|
||||||
|
wlr_log(WLR_DEBUG, "Plane %"PRIu32" doesn't support format 0x%"PRIX32,
|
||||||
|
plane->id, fmt);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_drm_format *format =
|
||||||
|
wlr_drm_format_intersect(plane_format, render_format);
|
||||||
|
if (format == NULL) {
|
||||||
|
wlr_log(WLR_DEBUG, "Failed to intersect plane and render "
|
||||||
|
"modifiers for format 0x%"PRIX32, fmt);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
void drm_fb_clear(struct wlr_drm_fb **fb_ptr) {
|
||||||
|
if (*fb_ptr == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_drm_fb *fb = *fb_ptr;
|
||||||
|
wlr_buffer_unlock(fb->wlr_buf); // may destroy the buffer
|
||||||
|
|
||||||
|
*fb_ptr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drm_fb_handle_destroy(struct wlr_addon *addon) {
|
||||||
|
struct wlr_drm_fb *fb = wl_container_of(addon, fb, addon);
|
||||||
|
drm_fb_destroy(fb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wlr_addon_interface fb_addon_impl = {
|
||||||
|
.name = "wlr_drm_fb",
|
||||||
|
.destroy = drm_fb_handle_destroy,
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint32_t get_fb_for_bo(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_dmabuf_attributes *dmabuf, uint32_t handles[static 4]) {
|
||||||
|
uint64_t modifiers[4] = {0};
|
||||||
|
for (int i = 0; i < dmabuf->n_planes; i++) {
|
||||||
|
// KMS requires all BO planes to have the same modifier
|
||||||
|
modifiers[i] = dmabuf->modifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t id = 0;
|
||||||
|
if (drm->addfb2_modifiers && dmabuf->modifier != DRM_FORMAT_MOD_INVALID) {
|
||||||
|
if (drmModeAddFB2WithModifiers(drm->fd, dmabuf->width, dmabuf->height,
|
||||||
|
dmabuf->format, handles, dmabuf->stride, dmabuf->offset,
|
||||||
|
modifiers, &id, DRM_MODE_FB_MODIFIERS) != 0) {
|
||||||
|
wlr_log_errno(WLR_DEBUG, "drmModeAddFB2WithModifiers failed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (dmabuf->modifier != DRM_FORMAT_MOD_INVALID &&
|
||||||
|
dmabuf->modifier != DRM_FORMAT_MOD_LINEAR) {
|
||||||
|
wlr_log(WLR_ERROR, "Cannot import DRM framebuffer with explicit "
|
||||||
|
"modifier 0x%"PRIX64, dmabuf->modifier);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = drmModeAddFB2(drm->fd, dmabuf->width, dmabuf->height,
|
||||||
|
dmabuf->format, handles, dmabuf->stride, dmabuf->offset, &id, 0);
|
||||||
|
if (ret != 0 && dmabuf->format == DRM_FORMAT_ARGB8888 &&
|
||||||
|
dmabuf->n_planes == 1 && dmabuf->offset[0] == 0) {
|
||||||
|
// Some big-endian machines don't support drmModeAddFB2. Try a
|
||||||
|
// last-resort fallback for ARGB8888 buffers, like Xorg's
|
||||||
|
// modesetting driver does.
|
||||||
|
wlr_log(WLR_DEBUG, "drmModeAddFB2 failed (%s), falling back to "
|
||||||
|
"legacy drmModeAddFB", strerror(-ret));
|
||||||
|
|
||||||
|
uint32_t depth = 32;
|
||||||
|
uint32_t bpp = 32;
|
||||||
|
ret = drmModeAddFB(drm->fd, dmabuf->width, dmabuf->height, depth,
|
||||||
|
bpp, dmabuf->stride[0], handles[0], &id);
|
||||||
|
if (ret != 0) {
|
||||||
|
wlr_log_errno(WLR_DEBUG, "drmModeAddFB failed");
|
||||||
}
|
}
|
||||||
|
} else if (ret != 0) {
|
||||||
|
wlr_log_errno(WLR_DEBUG, "drmModeAddFB2 failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close_all_bo_handles(struct wlr_drm_backend *drm,
|
||||||
|
uint32_t handles[static 4]) {
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
if (handles[i] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If multiple planes share the same BO handle, avoid double-closing it
|
||||||
|
bool already_closed = false;
|
||||||
|
for (int j = 0; j < i; ++j) {
|
||||||
|
if (handles[i] == handles[j]) {
|
||||||
|
already_closed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (already_closed) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drmCloseBufferHandle(drm->fd, handles[i]) != 0) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drm_poisoned_fb_handle_destroy(struct wlr_addon *addon) {
|
||||||
|
wlr_addon_finish(addon);
|
||||||
|
free(addon);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wlr_addon_interface poisoned_fb_addon_impl = {
|
||||||
|
.name = "wlr_drm_poisoned_fb",
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_buffer_poisoned(drm, buf)) {
|
||||||
|
wlr_log(WLR_DEBUG, "Buffer is poisoned");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_drm_fb *fb = calloc(1, sizeof(*fb));
|
||||||
|
if (!fb) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formats && !wlr_drm_format_set_has(formats, attribs.format,
|
||||||
|
attribs.modifier)) {
|
||||||
|
// The format isn't supported by the plane. Try stripping the alpha
|
||||||
|
// channel, if any.
|
||||||
|
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,52 +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) {
|
|
||||||
uint32_t id = (uintptr_t)gbm_bo_get_user_data(bo);
|
|
||||||
if (id) {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(gbm_bo_get_format(bo) == GBM_FORMAT_ARGB8888);
|
|
||||||
assert(drm_format == DRM_FORMAT_ARGB8888 ||
|
|
||||||
drm_format == DRM_FORMAT_XRGB8888);
|
|
||||||
|
|
||||||
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] = {gbm_bo_get_handle(bo).u32};
|
|
||||||
uint32_t pitches[4] = {gbm_bo_get_stride(bo)};
|
|
||||||
uint32_t offsets[4] = {gbm_bo_get_offset(bo, 0)};
|
|
||||||
|
|
||||||
if (drmModeAddFB2(fd, width, height, drm_format,
|
|
||||||
handles, pitches, 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;
|
||||||
|
@ -78,15 +87,15 @@ struct wlr_input_device *wlr_headless_add_input_device(
|
||||||
wlr_tablet_pad_init(wlr_device->tablet_pad, NULL);
|
wlr_tablet_pad_init(wlr_device->tablet_pad, NULL);
|
||||||
break;
|
break;
|
||||||
case WLR_INPUT_DEVICE_SWITCH:
|
case WLR_INPUT_DEVICE_SWITCH:
|
||||||
wlr_device->lid_switch = calloc(1, sizeof(struct wlr_switch));
|
wlr_device->switch_device = calloc(1, sizeof(struct wlr_switch));
|
||||||
if (wlr_device->lid_switch == NULL) {
|
if (wlr_device->switch_device == NULL) {
|
||||||
wlr_log(WLR_ERROR, "Unable to allocate wlr_switch");
|
wlr_log(WLR_ERROR, "Unable to allocate wlr_switch");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
wlr_switch_init(wlr_device->lid_switch, 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,95 +1,89 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <EGL/egl.h>
|
|
||||||
#include <EGL/eglext.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 void output_transform(struct wlr_output *wlr_output,
|
static bool output_test(struct wlr_output *wlr_output) {
|
||||||
enum wl_output_transform transform) {
|
uint32_t unsupported =
|
||||||
struct wlr_headless_output *output =
|
wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE;
|
||||||
headless_output_from_output(wlr_output);
|
if (unsupported != 0) {
|
||||||
output->wlr_output.transform = transform;
|
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 bool output_make_current(struct wlr_output *wlr_output, int *buffer_age) {
|
static bool output_commit(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);
|
||||||
return wlr_egl_make_current(&output->backend->egl, output->egl_surface,
|
|
||||||
buffer_age);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_swap_buffers(struct wlr_output *wlr_output,
|
if (!output_test(wlr_output)) {
|
||||||
pixman_region32_t *damage) {
|
return false;
|
||||||
// Nothing needs to be done for pbuffers
|
}
|
||||||
wlr_output_send_present(wlr_output, NULL);
|
|
||||||
|
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,
|
|
||||||
.transform = output_transform,
|
|
||||||
.destroy = output_destroy,
|
.destroy = output_destroy,
|
||||||
.make_current = output_make_current,
|
.commit = output_commit,
|
||||||
.swap_buffers = output_swap_buffers,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool wlr_output_is_headless(struct wlr_output *wlr_output) {
|
bool wlr_output_is_headless(struct wlr_output *wlr_output) {
|
||||||
|
@ -119,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-%d",
|
|
||||||
wl_list_length(&backend->outputs) + 1);
|
|
||||||
|
|
||||||
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);
|
||||||
|
@ -152,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,
|
||||||
|
@ -174,8 +175,8 @@ static void handle_device_added(struct wlr_libinput_backend *backend,
|
||||||
if (!wlr_dev) {
|
if (!wlr_dev) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
wlr_dev->lid_switch = create_libinput_switch(libinput_dev);
|
wlr_dev->switch_device = create_libinput_switch(libinput_dev);
|
||||||
if (!wlr_dev->lid_switch) {
|
if (!wlr_dev->switch_device) {
|
||||||
free(wlr_dev);
|
free(wlr_dev);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ void handle_switch_toggle(struct libinput_event *event,
|
||||||
wlr_log(WLR_DEBUG, "Got a switch event for a device with no switch?");
|
wlr_log(WLR_DEBUG, "Got a switch event for a device with no switch?");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
struct libinput_event_switch *sevent =
|
struct libinput_event_switch *sevent =
|
||||||
libinput_event_get_switch_event (event);
|
libinput_event_get_switch_event (event);
|
||||||
struct wlr_event_switch_toggle wlr_event = { 0 };
|
struct wlr_event_switch_toggle wlr_event = { 0 };
|
||||||
wlr_event.device = wlr_dev;
|
wlr_event.device = wlr_dev;
|
||||||
|
@ -51,5 +51,5 @@ void handle_switch_toggle(struct libinput_event *event,
|
||||||
}
|
}
|
||||||
wlr_event.time_msec =
|
wlr_event.time_msec =
|
||||||
usec_to_msec(libinput_event_switch_get_time_usec(sevent));
|
usec_to_msec(libinput_event_switch_get_time_usec(sevent));
|
||||||
wlr_signal_emit_safe(&wlr_dev->lid_switch->events.toggle, &wlr_event);
|
wlr_signal_emit_safe(&wlr_dev->switch_device->events.toggle, &wlr_event);
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ struct wlr_tablet_pad *create_libinput_tablet_pad(
|
||||||
wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_pad");
|
wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_pad");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
wlr_tablet_pad_init(wlr_tablet_pad, NULL);
|
||||||
|
|
||||||
wlr_tablet_pad->button_count =
|
wlr_tablet_pad->button_count =
|
||||||
libinput_device_tablet_pad_get_num_buttons(libinput_dev);
|
libinput_device_tablet_pad_get_num_buttons(libinput_dev);
|
||||||
|
@ -83,17 +84,15 @@ struct wlr_tablet_pad *create_libinput_tablet_pad(
|
||||||
wlr_tablet_pad->strip_count =
|
wlr_tablet_pad->strip_count =
|
||||||
libinput_device_tablet_pad_get_num_strips(libinput_dev);
|
libinput_device_tablet_pad_get_num_strips(libinput_dev);
|
||||||
|
|
||||||
wlr_list_init(&wlr_tablet_pad->paths);
|
|
||||||
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));
|
||||||
|
|
||||||
wl_list_init(&wlr_tablet_pad->groups);
|
|
||||||
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) {
|
||||||
add_pad_group_from_libinput(wlr_tablet_pad, libinput_dev, i);
|
add_pad_group_from_libinput(wlr_tablet_pad, libinput_dev, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_tablet_pad_init(wlr_tablet_pad, NULL);
|
|
||||||
return wlr_tablet_pad;
|
return wlr_tablet_pad;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,9 +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;
|
||||||
|
case LIBINPUT_TABLET_TOOL_TYPE_TOTEM:
|
||||||
|
return WLR_TABLET_TOOL_TYPE_TOTEM;
|
||||||
}
|
}
|
||||||
|
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(
|
||||||
|
@ -159,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.
|
||||||
|
@ -172,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,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;
|
||||||
|
@ -290,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);
|
||||||
|
@ -326,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;
|
||||||
|
|
|
@ -34,7 +34,7 @@ void handle_touch_down(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_touch_get_time_usec(tevent));
|
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
|
||||||
wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
|
wlr_event.touch_id = libinput_event_touch_get_seat_slot(tevent);
|
||||||
wlr_event.x = libinput_event_touch_get_x_transformed(tevent, 1);
|
wlr_event.x = libinput_event_touch_get_x_transformed(tevent, 1);
|
||||||
wlr_event.y = libinput_event_touch_get_y_transformed(tevent, 1);
|
wlr_event.y = libinput_event_touch_get_y_transformed(tevent, 1);
|
||||||
wlr_signal_emit_safe(&wlr_dev->touch->events.down, &wlr_event);
|
wlr_signal_emit_safe(&wlr_dev->touch->events.down, &wlr_event);
|
||||||
|
@ -54,7 +54,7 @@ void handle_touch_up(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_touch_get_time_usec(tevent));
|
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
|
||||||
wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
|
wlr_event.touch_id = libinput_event_touch_get_seat_slot(tevent);
|
||||||
wlr_signal_emit_safe(&wlr_dev->touch->events.up, &wlr_event);
|
wlr_signal_emit_safe(&wlr_dev->touch->events.up, &wlr_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ void handle_touch_motion(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_touch_get_time_usec(tevent));
|
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
|
||||||
wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
|
wlr_event.touch_id = libinput_event_touch_get_seat_slot(tevent);
|
||||||
wlr_event.x = libinput_event_touch_get_x_transformed(tevent, 1);
|
wlr_event.x = libinput_event_touch_get_x_transformed(tevent, 1);
|
||||||
wlr_event.y = libinput_event_touch_get_y_transformed(tevent, 1);
|
wlr_event.y = libinput_event_touch_get_y_transformed(tevent, 1);
|
||||||
wlr_signal_emit_safe(&wlr_dev->touch->events.motion, &wlr_event);
|
wlr_signal_emit_safe(&wlr_dev->touch->events.motion, &wlr_event);
|
||||||
|
@ -92,6 +92,17 @@ void handle_touch_cancel(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_touch_get_time_usec(tevent));
|
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
|
||||||
wlr_event.touch_id = libinput_event_touch_get_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,63 +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/session.c',
|
|
||||||
'wayland/backend.c',
|
|
||||||
'wayland/output.c',
|
|
||||||
'wayland/wl_seat.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
|
||||||
|
|
||||||
subdir('x11')
|
subdir('multi')
|
||||||
|
subdir('wayland')
|
||||||
|
subdir('headless')
|
||||||
|
|
||||||
lib_wlr_backend = static_library(
|
subdir('session')
|
||||||
'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,75 +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 void output_transform(struct wlr_output *wlr_output,
|
|
||||||
enum wl_output_transform transform) {
|
|
||||||
// empty
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_make_current(struct wlr_output *wlr_output, int *buffer_age) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_swap_buffers(struct wlr_output *wlr_output,
|
|
||||||
pixman_region32_t *damage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void output_destroy(struct wlr_output *wlr_output) {
|
|
||||||
struct wlr_noop_output *output =
|
|
||||||
noop_output_from_output(wlr_output);
|
|
||||||
|
|
||||||
wl_list_remove(&output->link);
|
|
||||||
|
|
||||||
free(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wlr_output_impl output_impl = {
|
|
||||||
.transform = output_transform,
|
|
||||||
.destroy = output_destroy,
|
|
||||||
.make_current = output_make_current,
|
|
||||||
.swap_buffers = output_swap_buffers,
|
|
||||||
};
|
|
||||||
|
|
||||||
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-%d",
|
|
||||||
wl_list_length(&backend->outputs) + 1);
|
|
||||||
|
|
||||||
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,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.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,278 +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.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) {
|
|
||||||
int fd = open("/dev/tty", O_RDWR);
|
|
||||||
if (fd == -1) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Cannot open /dev/tty");
|
|
||||||
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 (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,683 +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.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/self", "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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *type = NULL;
|
|
||||||
char *state = NULL;
|
|
||||||
|
|
||||||
// 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,32 +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 <wayland-server.h>
|
#include <unistd.h>
|
||||||
|
#include <time.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;
|
|
||||||
|
|
||||||
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);
|
||||||
|
@ -34,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,38 +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") || !strcmp(env_wlr_session, "systemd")) {
|
|
||||||
#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")) {
|
|
||||||
session = session_direct.create(disp);
|
|
||||||
} else {
|
|
||||||
wlr_log(WLR_ERROR, "WLR_SESSION has an invalid value: %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");
|
||||||
|
@ -116,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);
|
||||||
|
|
||||||
|
@ -132,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,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));
|
||||||
|
@ -171,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);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wlr_device *find_device(struct wlr_session *session, int fd) {
|
void wlr_session_close_file(struct wlr_session *session,
|
||||||
struct wlr_device *dev;
|
struct wlr_device *dev) {
|
||||||
|
if (libseat_close_device(session->seat_handle, dev->device_id) == -1) {
|
||||||
wl_list_for_each(dev, &session->devices, link) {
|
wlr_log_errno(WLR_ERROR, "Failed to close device %d", dev->device_id);
|
||||||
if (dev->fd == fd) {
|
|
||||||
return dev;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
close(dev->fd);
|
||||||
wlr_log(WLR_ERROR, "Tried to use fd %d not opened by session", fd);
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wlr_session_close_file(struct wlr_session *session, int fd) {
|
|
||||||
struct wlr_device *dev = find_device(session, fd);
|
|
||||||
|
|
||||||
session->impl->close(session, 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) {
|
||||||
int fd;
|
|
||||||
|
|
||||||
if (!path) {
|
if (!path) {
|
||||||
return -1;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
drmModeRes *res = drmModeGetResources(fd);
|
if (!drmIsKMS(dev->fd)) {
|
||||||
if (!res) {
|
wlr_log(WLR_DEBUG, "Ignoring '%s': not a KMS device", path);
|
||||||
goto out_fd;
|
wlr_session_close_file(session, dev);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
drmModeFreeResources(res);
|
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;
|
||||||
|
@ -261,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;
|
||||||
|
@ -273,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;
|
||||||
|
@ -334,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;
|
||||||
}
|
}
|
||||||
|
@ -355,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,24 +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 <EGL/egl.h>
|
#include <xf86drm.h>
|
||||||
#include <EGL/eglext.h>
|
|
||||||
#include <wayland-server.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 "drm-client-protocol.h"
|
||||||
|
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
||||||
|
#include "pointer-gestures-unstable-v1-client-protocol.h"
|
||||||
|
#include "presentation-time-client-protocol.h"
|
||||||
|
#include "xdg-activation-v1-client-protocol.h"
|
||||||
|
#include "xdg-decoration-unstable-v1-client-protocol.h"
|
||||||
#include "xdg-shell-client-protocol.h"
|
#include "xdg-shell-client-protocol.h"
|
||||||
|
#include "tablet-unstable-v2-client-protocol.h"
|
||||||
|
#include "relative-pointer-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
struct wlr_wl_linux_dmabuf_feedback_v1 {
|
||||||
|
struct wlr_wl_backend *backend;
|
||||||
|
dev_t main_device_id;
|
||||||
|
struct wlr_wl_linux_dmabuf_v1_table_entry *format_table;
|
||||||
|
size_t format_table_size;
|
||||||
|
|
||||||
|
dev_t tranche_target_device_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_wl_linux_dmabuf_v1_table_entry {
|
||||||
|
uint32_t format;
|
||||||
|
uint32_t pad; /* unused */
|
||||||
|
uint64_t modifier;
|
||||||
|
};
|
||||||
|
|
||||||
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend) {
|
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));
|
||||||
|
@ -29,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,
|
||||||
|
@ -58,26 +92,302 @@ 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)) {
|
||||||
} else if (strcmp(iface, wl_shm_interface.name) == 0) {
|
wl_seat_destroy(wl_seat);
|
||||||
wl->shm = wl_registry_bind(registry, name,
|
}
|
||||||
&wl_shm_interface, 1);
|
|
||||||
} 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);
|
||||||
xdg_wm_base_add_listener(wl->xdg_wm_base, &xdg_wm_base_listener, NULL);
|
xdg_wm_base_add_listener(wl->xdg_wm_base, &xdg_wm_base_listener, NULL);
|
||||||
|
} else if (strcmp(iface, zxdg_decoration_manager_v1_interface.name) == 0) {
|
||||||
|
wl->zxdg_decoration_manager_v1 = wl_registry_bind(registry, name,
|
||||||
|
&zxdg_decoration_manager_v1_interface, 1);
|
||||||
|
} else if (strcmp(iface, zwp_pointer_gestures_v1_interface.name) == 0) {
|
||||||
|
wl->zwp_pointer_gestures_v1 = wl_registry_bind(registry, name,
|
||||||
|
&zwp_pointer_gestures_v1_interface, version < 3 ? version : 3);
|
||||||
|
} else if (strcmp(iface, wp_presentation_interface.name) == 0) {
|
||||||
|
wl->presentation = wl_registry_bind(registry, name,
|
||||||
|
&wp_presentation_interface, 1);
|
||||||
|
wp_presentation_add_listener(wl->presentation,
|
||||||
|
&presentation_listener, wl);
|
||||||
|
} else if (strcmp(iface, zwp_tablet_manager_v2_interface.name) == 0) {
|
||||||
|
wl->tablet_manager = wl_registry_bind(registry, name,
|
||||||
|
&zwp_tablet_manager_v2_interface, 1);
|
||||||
|
} else if (strcmp(iface, zwp_linux_dmabuf_v1_interface.name) == 0 &&
|
||||||
|
version >= 3) {
|
||||||
|
wl->zwp_linux_dmabuf_v1 = wl_registry_bind(registry, name,
|
||||||
|
&zwp_linux_dmabuf_v1_interface, 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,12 +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_add_tablet_seat(wl->tablet_manager, seat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < wl->requested_outputs; ++i) {
|
for (size_t i = 0; i < wl->requested_outputs; ++i) {
|
||||||
|
@ -125,47 +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);
|
||||||
|
|
||||||
|
destroy_wl_seats(wl);
|
||||||
|
if (wl->zxdg_decoration_manager_v1) {
|
||||||
|
zxdg_decoration_manager_v1_destroy(wl->zxdg_decoration_manager_v1);
|
||||||
}
|
}
|
||||||
if (wl->seat) {
|
if (wl->zwp_pointer_gestures_v1) {
|
||||||
wl_seat_destroy(wl->seat);
|
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) {
|
if (wl->shm) {
|
||||||
wl_shm_destroy(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) {
|
||||||
|
@ -179,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));
|
||||||
|
@ -193,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) {
|
||||||
|
@ -205,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,
|
||||||
|
@ -221,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");
|
||||||
|
@ -232,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);
|
||||||
}
|
}
|
||||||
|
@ -271,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,17 +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-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;
|
||||||
|
@ -36,78 +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);
|
return true;
|
||||||
wl_egl_window_resize(output->egl_window, width, height, 0, 0);
|
}
|
||||||
wlr_output_update_custom_mode(&output->wlr_output, width, height, 0);
|
|
||||||
|
void destroy_wl_buffer(struct wlr_wl_buffer *buffer) {
|
||||||
|
if (buffer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_list_remove(&buffer->buffer_destroy.link);
|
||||||
|
wl_list_remove(&buffer->link);
|
||||||
|
wl_buffer_destroy(buffer->wl_buffer);
|
||||||
|
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 =
|
||||||
|
get_wl_output_from_output(wlr_output);
|
||||||
|
|
||||||
|
uint32_t unsupported =
|
||||||
|
wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE;
|
||||||
|
if (unsupported != 0) {
|
||||||
|
wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32,
|
||||||
|
unsupported);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool output_make_current(struct wlr_output *wlr_output,
|
static bool output_commit(struct wlr_output *wlr_output) {
|
||||||
int *buffer_age) {
|
|
||||||
struct wlr_wl_output *output =
|
|
||||||
get_wl_output_from_output(wlr_output);
|
|
||||||
return wlr_egl_make_current(&output->backend->egl, output->egl_surface,
|
|
||||||
buffer_age);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_swap_buffers(struct wlr_output *wlr_output,
|
|
||||||
pixman_region32_t *damage) {
|
|
||||||
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,
|
||||||
if (!wlr_egl_swap_buffers(&output->backend->egl,
|
wlr_output->pending.custom_mode.height,
|
||||||
output->egl_surface, damage)) {
|
wlr_output->pending.custom_mode.refresh)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: if available, use the presentation-time protocol
|
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
|
||||||
wlr_output_send_present(wlr_output, NULL);
|
struct wp_presentation_feedback *wp_feedback = NULL;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_display_flush(output->backend->remote_display);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_transform(struct wlr_output *wlr_output,
|
|
||||||
enum wl_output_transform transform) {
|
|
||||||
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
|
||||||
output->wlr_output.transform = transform;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 =
|
||||||
|
@ -115,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) {
|
||||||
|
@ -170,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);
|
||||||
}
|
}
|
||||||
|
@ -181,17 +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) {
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
@ -202,29 +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,
|
|
||||||
.transform = output_transform,
|
|
||||||
.destroy = output_destroy,
|
.destroy = output_destroy,
|
||||||
.make_current = output_make_current,
|
.test = output_test,
|
||||||
.swap_buffers = output_swap_buffers,
|
.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) {
|
||||||
|
@ -241,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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -263,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,
|
||||||
};
|
};
|
||||||
|
@ -290,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-%d",
|
|
||||||
wl_list_length(&backend->outputs) + 1);
|
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) {
|
||||||
|
@ -314,6 +552,18 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (backend->zxdg_decoration_manager_v1) {
|
||||||
|
output->zxdg_toplevel_decoration_v1 =
|
||||||
|
zxdg_decoration_manager_v1_get_toplevel_decoration(
|
||||||
|
backend->zxdg_decoration_manager_v1, output->xdg_toplevel);
|
||||||
|
if (!output->zxdg_toplevel_decoration_v1) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Could not get xdg toplevel decoration");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
zxdg_toplevel_decoration_v1_set_mode(output->zxdg_toplevel_decoration_v1,
|
||||||
|
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
||||||
|
}
|
||||||
|
|
||||||
wlr_wl_output_set_title(wlr_output, NULL);
|
wlr_wl_output_set_title(wlr_output, NULL);
|
||||||
|
|
||||||
xdg_toplevel_set_app_id(output->xdg_toplevel, "wlroots");
|
xdg_toplevel_set_app_id(output->xdg_toplevel, "wlroots");
|
||||||
|
@ -323,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:
|
||||||
|
@ -376,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;
|
||||||
|
}
|
|
@ -0,0 +1,938 @@
|
||||||
|
#ifndef _POSIX_C_SOURCE
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <wayland-util.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <wlr/interfaces/wlr_tablet_pad.h>
|
||||||
|
#include <wlr/interfaces/wlr_tablet_tool.h>
|
||||||
|
#include <wlr/types/wlr_input_device.h>
|
||||||
|
#include <wlr/interfaces/wlr_input_device.h>
|
||||||
|
|
||||||
|
#include "util/signal.h"
|
||||||
|
#include "util/time.h"
|
||||||
|
#include "wlr/util/log.h"
|
||||||
|
#include "tablet-unstable-v2-client-protocol.h"
|
||||||
|
|
||||||
|
#include "backend/wayland.h"
|
||||||
|
|
||||||
|
struct wlr_wl_tablet_seat {
|
||||||
|
struct zwp_tablet_seat_v2 *tablet_seat;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_wl_tablet_tool {
|
||||||
|
/* static */
|
||||||
|
struct zwp_tablet_tool_v2 *tool;
|
||||||
|
struct wlr_tablet_tool wlr_tool;
|
||||||
|
|
||||||
|
/* semi-static */
|
||||||
|
struct wlr_wl_output *output;
|
||||||
|
struct wlr_wl_input_device *tablet;
|
||||||
|
double pre_x, pre_y;
|
||||||
|
|
||||||
|
/* per frame */
|
||||||
|
double x, y;
|
||||||
|
|
||||||
|
double pressure;
|
||||||
|
double distance;
|
||||||
|
double tilt_x, tilt_y;
|
||||||
|
double rotation;
|
||||||
|
double slider;
|
||||||
|
double wheel_delta;
|
||||||
|
|
||||||
|
bool is_in;
|
||||||
|
bool is_out;
|
||||||
|
|
||||||
|
bool is_up;
|
||||||
|
bool is_down;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_wl_tablet_pad_ring {
|
||||||
|
struct wl_list link; // wlr_wl_tablet_pad_group::rings
|
||||||
|
/* static */
|
||||||
|
struct zwp_tablet_pad_ring_v2 *ring;
|
||||||
|
struct wlr_wl_tablet_pad_group *group;
|
||||||
|
size_t index;
|
||||||
|
|
||||||
|
/* per frame */
|
||||||
|
enum wlr_tablet_pad_ring_source source;
|
||||||
|
double angle;
|
||||||
|
bool stopped;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_wl_tablet_pad_strip {
|
||||||
|
struct wl_list link; // wlr_wl_tablet_pad_group::strips
|
||||||
|
struct zwp_tablet_pad_strip_v2 *strip;
|
||||||
|
struct wlr_wl_tablet_pad_group *group;
|
||||||
|
size_t index;
|
||||||
|
|
||||||
|
enum wlr_tablet_pad_strip_source source;
|
||||||
|
double position;
|
||||||
|
bool stopped;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_wl_tablet_pad_group {
|
||||||
|
struct zwp_tablet_pad_group_v2 *pad_group;
|
||||||
|
struct wlr_tablet_pad *pad;
|
||||||
|
unsigned int mode;
|
||||||
|
|
||||||
|
struct wlr_tablet_pad_group group;
|
||||||
|
|
||||||
|
struct wl_list rings; // wlr_wl_tablet_pad_ring::link
|
||||||
|
struct wl_list strips; // wlr_wl_tablet_pad_strips::link
|
||||||
|
};
|
||||||
|
|
||||||
|
static void handle_tablet_pad_ring_source(void *data,
|
||||||
|
struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2,
|
||||||
|
uint32_t source) {
|
||||||
|
struct wlr_wl_tablet_pad_ring *ring = data;
|
||||||
|
ring->source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_ring_angle(void *data,
|
||||||
|
struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2,
|
||||||
|
wl_fixed_t degrees) {
|
||||||
|
struct wlr_wl_tablet_pad_ring *ring = data;
|
||||||
|
ring->angle = wl_fixed_to_double(degrees);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_ring_stop(void *data,
|
||||||
|
struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2) {
|
||||||
|
struct wlr_wl_tablet_pad_ring *ring = data;
|
||||||
|
ring->stopped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_ring_frame(void *data,
|
||||||
|
struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2,
|
||||||
|
uint32_t time) {
|
||||||
|
struct wlr_wl_tablet_pad_ring *ring = data;
|
||||||
|
|
||||||
|
struct wlr_event_tablet_pad_ring evt = {
|
||||||
|
.time_msec = time,
|
||||||
|
.source = ring->source,
|
||||||
|
.ring = ring->index,
|
||||||
|
.position = ring->angle,
|
||||||
|
.mode = ring->group->mode,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ring->angle >= 0) {
|
||||||
|
wlr_signal_emit_safe(&ring->group->pad->events.ring, &evt);
|
||||||
|
}
|
||||||
|
if (ring->stopped) {
|
||||||
|
evt.position = -1;
|
||||||
|
wlr_signal_emit_safe(&ring->group->pad->events.ring, &evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
ring->angle = -1;
|
||||||
|
ring->stopped = false;
|
||||||
|
ring->source = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwp_tablet_pad_ring_v2_listener tablet_pad_ring_listener = {
|
||||||
|
.source = handle_tablet_pad_ring_source,
|
||||||
|
.angle = handle_tablet_pad_ring_angle,
|
||||||
|
.stop = handle_tablet_pad_ring_stop,
|
||||||
|
.frame = handle_tablet_pad_ring_frame,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void handle_tablet_pad_strip_source(void *data,
|
||||||
|
struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2,
|
||||||
|
uint32_t source) {
|
||||||
|
struct wlr_wl_tablet_pad_strip *strip = data;
|
||||||
|
strip->source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_strip_position(void *data,
|
||||||
|
struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2,
|
||||||
|
uint32_t position) {
|
||||||
|
struct wlr_wl_tablet_pad_strip *strip = data;
|
||||||
|
strip->position = (double) position / 65536.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_strip_stop(void *data,
|
||||||
|
struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2) {
|
||||||
|
struct wlr_wl_tablet_pad_strip *strip = data;
|
||||||
|
strip->stopped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_strip_frame(void *data,
|
||||||
|
struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2,
|
||||||
|
uint32_t time) {
|
||||||
|
struct wlr_wl_tablet_pad_strip *strip = data;
|
||||||
|
|
||||||
|
struct wlr_event_tablet_pad_strip evt = {
|
||||||
|
.time_msec = time,
|
||||||
|
.source = strip->source,
|
||||||
|
.strip = strip->index,
|
||||||
|
.position = strip->position,
|
||||||
|
.mode = strip->group->mode,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (strip->position >= 0) {
|
||||||
|
wlr_signal_emit_safe(&strip->group->pad->events.strip, &evt);
|
||||||
|
}
|
||||||
|
if (strip->stopped) {
|
||||||
|
evt.position = -1;
|
||||||
|
wlr_signal_emit_safe(&strip->group->pad->events.strip, &evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
strip->position = -1;
|
||||||
|
strip->stopped = false;
|
||||||
|
strip->source = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwp_tablet_pad_strip_v2_listener tablet_pad_strip_listener = {
|
||||||
|
.source = handle_tablet_pad_strip_source,
|
||||||
|
.position = handle_tablet_pad_strip_position,
|
||||||
|
.stop = handle_tablet_pad_strip_stop,
|
||||||
|
.frame = handle_tablet_pad_strip_frame,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void handle_tablet_pad_group_buttons(void *data,
|
||||||
|
struct zwp_tablet_pad_group_v2 *pad_group,
|
||||||
|
struct wl_array *buttons) {
|
||||||
|
struct wlr_wl_tablet_pad_group *group = data;
|
||||||
|
|
||||||
|
free(group->group.buttons);
|
||||||
|
group->group.buttons = calloc(1, buttons->size);
|
||||||
|
if (!group->group.buttons) {
|
||||||
|
// FIXME: Add actual error handling
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
group->group.button_count = buttons->size / sizeof(int);
|
||||||
|
memcpy(group->group.buttons, buttons->data, buttons->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_group_modes(void *data,
|
||||||
|
struct zwp_tablet_pad_group_v2 *pad_group, uint32_t modes) {
|
||||||
|
struct wlr_wl_tablet_pad_group *group = data;
|
||||||
|
|
||||||
|
group->group.mode_count = modes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_group_ring(void *data,
|
||||||
|
struct zwp_tablet_pad_group_v2 *pad_group,
|
||||||
|
struct zwp_tablet_pad_ring_v2 *ring) {
|
||||||
|
struct wlr_wl_tablet_pad_group *group = data;
|
||||||
|
struct wlr_wl_tablet_pad_ring *tablet_ring =
|
||||||
|
calloc(1, sizeof(struct wlr_wl_tablet_pad_ring));
|
||||||
|
if (!tablet_ring) {
|
||||||
|
zwp_tablet_pad_ring_v2_destroy(ring);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tablet_ring->index = group->pad->ring_count++;
|
||||||
|
tablet_ring->group = group;
|
||||||
|
zwp_tablet_pad_ring_v2_add_listener(ring, &tablet_pad_ring_listener,
|
||||||
|
tablet_ring);
|
||||||
|
|
||||||
|
group->group.rings = realloc(group->group.rings,
|
||||||
|
++group->group.ring_count * sizeof(unsigned int));
|
||||||
|
group->group.rings[group->group.ring_count - 1] =
|
||||||
|
tablet_ring->index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_group_strip(void *data,
|
||||||
|
struct zwp_tablet_pad_group_v2 *pad_group,
|
||||||
|
struct zwp_tablet_pad_strip_v2 *strip) {
|
||||||
|
struct wlr_wl_tablet_pad_group *group = data;
|
||||||
|
struct wlr_wl_tablet_pad_strip *tablet_strip =
|
||||||
|
calloc(1, sizeof(struct wlr_wl_tablet_pad_strip));
|
||||||
|
if (!tablet_strip) {
|
||||||
|
zwp_tablet_pad_strip_v2_destroy(strip);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tablet_strip->index = group->pad->strip_count++;
|
||||||
|
tablet_strip->group = group;
|
||||||
|
zwp_tablet_pad_strip_v2_add_listener(strip, &tablet_pad_strip_listener,
|
||||||
|
tablet_strip);
|
||||||
|
|
||||||
|
group->group.strips = realloc(group->group.strips,
|
||||||
|
++group->group.strip_count * sizeof(unsigned int));
|
||||||
|
group->group.strips[group->group.strip_count - 1] =
|
||||||
|
tablet_strip->index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_group_done(void *data,
|
||||||
|
struct zwp_tablet_pad_group_v2 *pad_group) {
|
||||||
|
/* Empty for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_group_mode_switch(void *data,
|
||||||
|
struct zwp_tablet_pad_group_v2 *pad_group,
|
||||||
|
uint32_t time, uint32_t serial, uint32_t mode) {
|
||||||
|
struct wlr_wl_tablet_pad_group *group = data;
|
||||||
|
group->mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This isn't in the listener, but keep the naming scheme around since the
|
||||||
|
* other removed functions work like this, and pad sub-resources are just a bit
|
||||||
|
* special */
|
||||||
|
static void handle_tablet_pad_group_removed(
|
||||||
|
struct wlr_wl_tablet_pad_group *group) {
|
||||||
|
|
||||||
|
/* No need to remove the ::link on strips rings as long as we do *not*
|
||||||
|
* wl_list_remove on the wl_groups ring/strip attributes here */
|
||||||
|
struct wlr_wl_tablet_pad_ring *ring, *tmp_ring;
|
||||||
|
wl_list_for_each_safe(ring, tmp_ring, &group->rings, link) {
|
||||||
|
zwp_tablet_pad_ring_v2_destroy(ring->ring);
|
||||||
|
free(ring);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_wl_tablet_pad_strip *strip, *tmp_strip;
|
||||||
|
wl_list_for_each_safe(strip, tmp_strip, &group->strips, link) {
|
||||||
|
zwp_tablet_pad_strip_v2_destroy(strip->strip);
|
||||||
|
free(strip);
|
||||||
|
}
|
||||||
|
|
||||||
|
zwp_tablet_pad_group_v2_destroy(group->pad_group);
|
||||||
|
|
||||||
|
/* While I'm pretty sure we have control over this as well, it's
|
||||||
|
* outside the scope of a single function, so better be safe than
|
||||||
|
* sorry */
|
||||||
|
wl_list_remove(&group->group.link);
|
||||||
|
free(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwp_tablet_pad_group_v2_listener tablet_pad_group_listener = {
|
||||||
|
.buttons = handle_tablet_pad_group_buttons,
|
||||||
|
.modes = handle_tablet_pad_group_modes,
|
||||||
|
.ring = handle_tablet_pad_group_ring,
|
||||||
|
.strip = handle_tablet_pad_group_strip,
|
||||||
|
.done = handle_tablet_pad_group_done,
|
||||||
|
.mode_switch = handle_tablet_pad_group_mode_switch,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void handle_tablet_pad_group(void *data,
|
||||||
|
struct zwp_tablet_pad_v2 *zwp_tablet_pad,
|
||||||
|
struct zwp_tablet_pad_group_v2 *pad_group) {
|
||||||
|
struct wlr_wl_input_device *dev = data;
|
||||||
|
struct wlr_tablet_pad *pad = dev->wlr_input_device.tablet_pad;
|
||||||
|
|
||||||
|
struct wlr_wl_tablet_pad_group *group =
|
||||||
|
calloc(1, sizeof(struct wlr_wl_tablet_pad_group));
|
||||||
|
if (!group) {
|
||||||
|
zwp_tablet_pad_group_v2_destroy(pad_group);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
group->pad_group = pad_group;
|
||||||
|
group->pad = pad;
|
||||||
|
|
||||||
|
wl_list_init(&group->rings);
|
||||||
|
wl_list_init(&group->strips);
|
||||||
|
|
||||||
|
zwp_tablet_pad_group_v2_add_listener(pad_group,
|
||||||
|
&tablet_pad_group_listener, group);
|
||||||
|
|
||||||
|
wl_list_insert(&pad->groups, &group->group.link);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_path(void *data,
|
||||||
|
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
|
||||||
|
const char *path) {
|
||||||
|
struct wlr_wl_input_device *dev = data;
|
||||||
|
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
|
||||||
|
|
||||||
|
char **dst = wl_array_add(&tablet_pad->paths, sizeof(char *));
|
||||||
|
*dst = strdup(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_buttons(void *data,
|
||||||
|
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
|
||||||
|
uint32_t buttons) {
|
||||||
|
struct wlr_wl_input_device *dev = data;
|
||||||
|
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
|
||||||
|
|
||||||
|
tablet_pad->button_count = buttons;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_button(void *data,
|
||||||
|
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
|
||||||
|
uint32_t time, uint32_t button, uint32_t state) {
|
||||||
|
struct wlr_wl_input_device *dev = data;
|
||||||
|
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
|
||||||
|
|
||||||
|
struct wlr_event_tablet_pad_button evt = {
|
||||||
|
.time_msec = time,
|
||||||
|
.button = button,
|
||||||
|
.state = state,
|
||||||
|
.mode = 0,
|
||||||
|
.group = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
wlr_signal_emit_safe(&tablet_pad->events.button, &evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_done(void *data,
|
||||||
|
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2) {
|
||||||
|
struct wlr_wl_input_device *dev = data;
|
||||||
|
|
||||||
|
wlr_signal_emit_safe(&dev->backend->backend.events.new_input,
|
||||||
|
&dev->wlr_input_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_enter(void *data,
|
||||||
|
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
|
||||||
|
uint32_t serial, struct zwp_tablet_v2 *tablet_p,
|
||||||
|
struct wl_surface *surface) {
|
||||||
|
struct wlr_wl_input_device *dev = data;
|
||||||
|
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
|
||||||
|
struct wlr_wl_input_device *tab_dev = zwp_tablet_v2_get_user_data(tablet_p);
|
||||||
|
struct wlr_input_device *tablet = &tab_dev->wlr_input_device;
|
||||||
|
wlr_log(WLR_DEBUG, "Tablet: %p\n", tablet);
|
||||||
|
|
||||||
|
wlr_signal_emit_safe(&tablet_pad->events.attach_tablet, tablet);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_leave(void *data,
|
||||||
|
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
|
||||||
|
uint32_t serial, struct wl_surface *surface) {
|
||||||
|
/* Empty. Probably staying that way, unless we want to create/destroy
|
||||||
|
* tablet on enter/leave events (ehh) */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_pad_removed(void *data,
|
||||||
|
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2) {
|
||||||
|
struct wlr_wl_input_device *dev = data;
|
||||||
|
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
|
||||||
|
|
||||||
|
/* This doesn't free anything, but emits the destroy signal */
|
||||||
|
wlr_input_device_destroy(&dev->wlr_input_device);
|
||||||
|
/* This is a bit ugly, but we need to remove it from our list */
|
||||||
|
wl_list_remove(&dev->link);
|
||||||
|
|
||||||
|
struct wlr_wl_tablet_pad_group *group, *it;
|
||||||
|
wl_list_for_each_safe(group, it, &tablet_pad->groups, group.link) {
|
||||||
|
handle_tablet_pad_group_removed(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This frees */
|
||||||
|
wlr_tablet_pad_destroy(tablet_pad);
|
||||||
|
zwp_tablet_pad_v2_destroy(dev->resource);
|
||||||
|
free(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwp_tablet_pad_v2_listener tablet_pad_listener = {
|
||||||
|
.group = handle_tablet_pad_group,
|
||||||
|
.path = handle_tablet_pad_path,
|
||||||
|
.buttons = handle_tablet_pad_buttons,
|
||||||
|
.button = handle_tablet_pad_button,
|
||||||
|
.done = handle_tablet_pad_done,
|
||||||
|
.enter = handle_tablet_pad_enter,
|
||||||
|
.leave = handle_tablet_pad_leave,
|
||||||
|
.removed = handle_tablet_pad_removed,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void handle_pad_added(void *data,
|
||||||
|
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
|
||||||
|
struct zwp_tablet_pad_v2 *id) {
|
||||||
|
wlr_log(WLR_DEBUG, "New tablet pad");
|
||||||
|
struct wlr_wl_seat *seat = data;
|
||||||
|
struct wlr_wl_input_device *dev = create_wl_input_device(
|
||||||
|
seat, WLR_INPUT_DEVICE_TABLET_PAD);
|
||||||
|
if (!dev) {
|
||||||
|
/* This leaks a couple of server-sent resource ids. iirc this
|
||||||
|
* shouldn't ever be a problem, but it isn't exactly nice
|
||||||
|
* either. */
|
||||||
|
zwp_tablet_pad_v2_destroy(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->resource = id;
|
||||||
|
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
||||||
|
wlr_dev->tablet_pad = calloc(1, sizeof(*wlr_dev->tablet_pad));
|
||||||
|
|
||||||
|
if (!wlr_dev->tablet_pad) {
|
||||||
|
/* This leaks a couple of server-sent resource ids. iirc this
|
||||||
|
* shouldn't ever be a problem, but it isn't exactly nice
|
||||||
|
* either. */
|
||||||
|
free(dev);
|
||||||
|
zwp_tablet_pad_v2_destroy(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wlr_tablet_pad_init(wlr_dev->tablet_pad, NULL);
|
||||||
|
zwp_tablet_pad_v2_add_listener(id, &tablet_pad_listener, dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_done(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id) {
|
||||||
|
/* empty */
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum wlr_tablet_tool_type tablet_type_to_wlr_type(enum zwp_tablet_tool_v2_type type) {
|
||||||
|
switch (type) {
|
||||||
|
case ZWP_TABLET_TOOL_V2_TYPE_PEN:
|
||||||
|
return WLR_TABLET_TOOL_TYPE_PEN;
|
||||||
|
case ZWP_TABLET_TOOL_V2_TYPE_ERASER:
|
||||||
|
return WLR_TABLET_TOOL_TYPE_ERASER;
|
||||||
|
case ZWP_TABLET_TOOL_V2_TYPE_BRUSH:
|
||||||
|
return WLR_TABLET_TOOL_TYPE_BRUSH;
|
||||||
|
case ZWP_TABLET_TOOL_V2_TYPE_PENCIL:
|
||||||
|
return WLR_TABLET_TOOL_TYPE_PENCIL;
|
||||||
|
case ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH:
|
||||||
|
return WLR_TABLET_TOOL_TYPE_AIRBRUSH;
|
||||||
|
case ZWP_TABLET_TOOL_V2_TYPE_MOUSE:
|
||||||
|
return WLR_TABLET_TOOL_TYPE_MOUSE;
|
||||||
|
case ZWP_TABLET_TOOL_V2_TYPE_LENS:
|
||||||
|
return WLR_TABLET_TOOL_TYPE_LENS;
|
||||||
|
case ZWP_TABLET_TOOL_V2_TYPE_FINGER:
|
||||||
|
// unused, see:
|
||||||
|
// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/18
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
abort(); // unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_type(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id,
|
||||||
|
uint32_t tool_type) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
|
||||||
|
tool->wlr_tool.type = tablet_type_to_wlr_type(tool_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_serial(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id,
|
||||||
|
uint32_t high, uint32_t low) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
|
||||||
|
tool->wlr_tool.hardware_serial =
|
||||||
|
((uint64_t) high) << 32 | (uint64_t) low;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_id_wacom(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id,
|
||||||
|
uint32_t high, uint32_t low) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
|
||||||
|
tool->wlr_tool.hardware_wacom =
|
||||||
|
((uint64_t) high) << 32 | (uint64_t) low;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_capability(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id,
|
||||||
|
uint32_t capability) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
|
||||||
|
enum zwp_tablet_tool_v2_capability cap = capability;
|
||||||
|
|
||||||
|
switch (cap) {
|
||||||
|
case ZWP_TABLET_TOOL_V2_CAPABILITY_TILT:
|
||||||
|
tool->wlr_tool.tilt = true;
|
||||||
|
break;
|
||||||
|
case ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE:
|
||||||
|
tool->wlr_tool.pressure = true;
|
||||||
|
break;
|
||||||
|
case ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE:
|
||||||
|
tool->wlr_tool.distance = true;
|
||||||
|
break;
|
||||||
|
case ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION:
|
||||||
|
tool->wlr_tool.rotation = true;
|
||||||
|
break;
|
||||||
|
case ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER:
|
||||||
|
tool->wlr_tool.slider = true;
|
||||||
|
break;
|
||||||
|
case ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL:
|
||||||
|
tool->wlr_tool.wheel = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_proximity_in(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id, uint32_t serial,
|
||||||
|
struct zwp_tablet_v2 *tablet_id,
|
||||||
|
struct wl_surface *surface) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
tool->is_in = true;
|
||||||
|
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,
|
||||||
|
struct zwp_tablet_tool_v2 *id) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
tool->is_out = true;
|
||||||
|
tool->output = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_down(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id,
|
||||||
|
unsigned int serial) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
tool->is_down = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_up(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
tool->is_up = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_motion(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id,
|
||||||
|
wl_fixed_t x, wl_fixed_t y) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
struct wlr_wl_output *output = tool->output;
|
||||||
|
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,
|
||||||
|
struct zwp_tablet_tool_v2 *id,
|
||||||
|
uint32_t pressure) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
tool->pressure = (double) pressure / 65535.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_distance(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id,
|
||||||
|
uint32_t distance) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
tool->distance = (double) distance / 65535.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_tilt(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id,
|
||||||
|
wl_fixed_t x, wl_fixed_t y) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
tool->tilt_x = wl_fixed_to_double(x);
|
||||||
|
tool->tilt_y = wl_fixed_to_double(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_rotation(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id,
|
||||||
|
wl_fixed_t rotation) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
tool->rotation = wl_fixed_to_double(rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_slider(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id,
|
||||||
|
int slider) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
tool->slider = (double) slider / 65535.0;;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This looks wrong :/
|
||||||
|
static void handle_tablet_tool_wheel(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id,
|
||||||
|
wl_fixed_t degree, int clicks) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
tool->wheel_delta = wl_fixed_to_double(degree);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_button(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id,
|
||||||
|
uint32_t serial, uint32_t button, uint32_t state) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
struct wlr_tablet *tablet = tool->tablet->wlr_input_device.tablet;
|
||||||
|
|
||||||
|
struct wlr_event_tablet_tool_button evt = {
|
||||||
|
.device = &tool->tablet->wlr_input_device,
|
||||||
|
.tool = &tool->wlr_tool,
|
||||||
|
.time_msec = get_current_time_msec(),
|
||||||
|
.button = button,
|
||||||
|
.state = state == ZWP_TABLET_TOOL_V2_BUTTON_STATE_RELEASED ?
|
||||||
|
WLR_BUTTON_RELEASED : WLR_BUTTON_PRESSED,
|
||||||
|
};
|
||||||
|
|
||||||
|
wlr_signal_emit_safe(&tablet->events.button, &evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clear_tablet_tool_values(struct wlr_wl_tablet_tool *tool) {
|
||||||
|
tool->is_out = tool->is_in = false;
|
||||||
|
tool->is_up = tool->is_down = false;
|
||||||
|
tool->x = tool->y = NAN;
|
||||||
|
tool->pressure = NAN;
|
||||||
|
tool->distance = NAN;
|
||||||
|
tool->tilt_x = tool->tilt_y = NAN;
|
||||||
|
tool->rotation = NAN;
|
||||||
|
tool->slider = NAN;
|
||||||
|
tool->wheel_delta = NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_frame(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id,
|
||||||
|
uint32_t time) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
if (tool->is_out && tool->is_in) {
|
||||||
|
/* we got a tablet tool coming in and out of proximity before
|
||||||
|
* we could process it. Just ignore anything it did */
|
||||||
|
goto clear_values;
|
||||||
|
}
|
||||||
|
struct wlr_tablet *tablet = tool->tablet->wlr_input_device.tablet;
|
||||||
|
|
||||||
|
if (tool->is_in) {
|
||||||
|
struct wlr_event_tablet_tool_proximity evt = {
|
||||||
|
.device = &tool->tablet->wlr_input_device,
|
||||||
|
.tool = &tool->wlr_tool,
|
||||||
|
.time_msec = time,
|
||||||
|
.x = tool->x,
|
||||||
|
.y = tool->y,
|
||||||
|
.state = WLR_TABLET_TOOL_PROXIMITY_IN,
|
||||||
|
};
|
||||||
|
|
||||||
|
wlr_signal_emit_safe(&tablet->events.proximity, &evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
struct wlr_event_tablet_tool_axis evt = {
|
||||||
|
.device = &tool->tablet->wlr_input_device,
|
||||||
|
.tool = &tool->wlr_tool,
|
||||||
|
.time_msec = time,
|
||||||
|
.updated_axes = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isnan(tool->x) && !tool->is_in) {
|
||||||
|
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_X;
|
||||||
|
evt.x = tool->x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isnan(tool->y) && !tool->is_in) {
|
||||||
|
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_Y;
|
||||||
|
evt.y = tool->y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isnan(tool->pressure)) {
|
||||||
|
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_PRESSURE;
|
||||||
|
evt.pressure = tool->pressure;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isnan(tool->distance)) {
|
||||||
|
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_DISTANCE;
|
||||||
|
evt.distance = tool->distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isnan(tool->tilt_x)) {
|
||||||
|
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_TILT_X;
|
||||||
|
evt.tilt_x = tool->tilt_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isnan(tool->tilt_y)) {
|
||||||
|
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_TILT_Y;
|
||||||
|
evt.tilt_y = tool->tilt_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isnan(tool->rotation)) {
|
||||||
|
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_ROTATION;
|
||||||
|
evt.rotation = tool->rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isnan(tool->slider)) {
|
||||||
|
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_SLIDER;
|
||||||
|
evt.slider = tool->slider;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isnan(tool->wheel_delta)) {
|
||||||
|
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_WHEEL;
|
||||||
|
evt.wheel_delta = tool->wheel_delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evt.updated_axes) {
|
||||||
|
wlr_signal_emit_safe(&tablet->events.axis, &evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This will always send down then up if we got both.
|
||||||
|
* Maybe we should send them right away, in case we get up then both in
|
||||||
|
* series?
|
||||||
|
* Downside: Here we have the frame time, if we sent right away, we
|
||||||
|
* need to generate the time */
|
||||||
|
if (tool->is_down) {
|
||||||
|
struct wlr_event_tablet_tool_tip evt = {
|
||||||
|
.device = &tool->tablet->wlr_input_device,
|
||||||
|
.tool = &tool->wlr_tool,
|
||||||
|
.time_msec = time,
|
||||||
|
.x = tool->x,
|
||||||
|
.y = tool->y,
|
||||||
|
.state = WLR_TABLET_TOOL_TIP_DOWN,
|
||||||
|
};
|
||||||
|
|
||||||
|
wlr_signal_emit_safe(&tablet->events.tip, &evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tool->is_up) {
|
||||||
|
struct wlr_event_tablet_tool_tip evt = {
|
||||||
|
.device = &tool->tablet->wlr_input_device,
|
||||||
|
.tool = &tool->wlr_tool,
|
||||||
|
.time_msec = time,
|
||||||
|
.x = tool->x,
|
||||||
|
.y = tool->y,
|
||||||
|
.state = WLR_TABLET_TOOL_TIP_UP,
|
||||||
|
};
|
||||||
|
|
||||||
|
wlr_signal_emit_safe(&tablet->events.tip, &evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tool->is_out) {
|
||||||
|
struct wlr_event_tablet_tool_proximity evt = {
|
||||||
|
.device = &tool->tablet->wlr_input_device,
|
||||||
|
.tool = &tool->wlr_tool,
|
||||||
|
.time_msec = time,
|
||||||
|
.x = tool->x,
|
||||||
|
.y = tool->y,
|
||||||
|
.state = WLR_TABLET_TOOL_PROXIMITY_OUT,
|
||||||
|
};
|
||||||
|
|
||||||
|
wlr_signal_emit_safe(&tablet->events.proximity, &evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_values:
|
||||||
|
clear_tablet_tool_values(tool);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_tool_removed(void *data,
|
||||||
|
struct zwp_tablet_tool_v2 *id) {
|
||||||
|
struct wlr_wl_tablet_tool *tool = data;
|
||||||
|
|
||||||
|
zwp_tablet_tool_v2_destroy(tool->tool);
|
||||||
|
wlr_signal_emit_safe(&tool->wlr_tool.events.destroy, &tool->wlr_tool);
|
||||||
|
free(tool);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = {
|
||||||
|
.removed = handle_tablet_tool_removed,
|
||||||
|
.done = handle_tablet_tool_done,
|
||||||
|
.type = handle_tablet_tool_type,
|
||||||
|
.hardware_serial = handle_tablet_tool_serial,
|
||||||
|
.hardware_id_wacom = handle_tablet_tool_id_wacom,
|
||||||
|
.capability = handle_tablet_tool_capability,
|
||||||
|
|
||||||
|
.proximity_in = handle_tablet_tool_proximity_in,
|
||||||
|
.proximity_out = handle_tablet_tool_proximity_out,
|
||||||
|
.down = handle_tablet_tool_down,
|
||||||
|
.up = handle_tablet_tool_up,
|
||||||
|
|
||||||
|
.motion = handle_tablet_tool_motion,
|
||||||
|
.pressure = handle_tablet_tool_pressure,
|
||||||
|
.distance = handle_tablet_tool_distance,
|
||||||
|
.tilt = handle_tablet_tool_tilt,
|
||||||
|
.rotation = handle_tablet_tool_rotation,
|
||||||
|
.slider = handle_tablet_tool_slider,
|
||||||
|
.wheel = handle_tablet_tool_wheel,
|
||||||
|
.button = handle_tablet_tool_button,
|
||||||
|
.frame = handle_tablet_tool_frame,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void handle_tool_added(void *data,
|
||||||
|
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
|
||||||
|
struct zwp_tablet_tool_v2 *id) {
|
||||||
|
wlr_log(WLR_DEBUG, "New tablet tool");
|
||||||
|
struct wlr_wl_tablet_tool *tool = calloc(1, sizeof(*tool));
|
||||||
|
if (!tool) {
|
||||||
|
zwp_tablet_tool_v2_destroy(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tool->tool = id;
|
||||||
|
clear_tablet_tool_values(tool);
|
||||||
|
wl_signal_init(&tool->wlr_tool.events.destroy);
|
||||||
|
zwp_tablet_tool_v2_add_listener(id, &tablet_tool_listener, tool);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_name(void *data, struct zwp_tablet_v2 *zwp_tablet_v2,
|
||||||
|
const char *name) {
|
||||||
|
struct wlr_wl_input_device *dev = data;
|
||||||
|
struct wlr_tablet *tablet = dev->wlr_input_device.tablet;
|
||||||
|
|
||||||
|
free(tablet->name);
|
||||||
|
tablet->name = strdup(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_id(void *data, struct zwp_tablet_v2 *zwp_tablet_v2,
|
||||||
|
uint32_t vid, uint32_t pid) {
|
||||||
|
struct wlr_wl_input_device *dev = data;
|
||||||
|
dev->wlr_input_device.vendor = vid;
|
||||||
|
dev->wlr_input_device.product = pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_path(void *data, struct zwp_tablet_v2 *zwp_tablet_v2,
|
||||||
|
const char *path) {
|
||||||
|
struct wlr_wl_input_device *dev = data;
|
||||||
|
struct wlr_tablet *tablet = dev->wlr_input_device.tablet;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
struct wlr_wl_input_device *dev = data;
|
||||||
|
|
||||||
|
wlr_signal_emit_safe(&dev->backend->backend.events.new_input,
|
||||||
|
&dev->wlr_input_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_tablet_removed(void *data,
|
||||||
|
struct zwp_tablet_v2 *zwp_tablet_v2) {
|
||||||
|
struct wlr_wl_input_device *dev = data;
|
||||||
|
|
||||||
|
/* This doesn't free anything, but emits the destroy signal */
|
||||||
|
wlr_input_device_destroy(&dev->wlr_input_device);
|
||||||
|
/* This is a bit ugly, but we need to remove it from our list */
|
||||||
|
wl_list_remove(&dev->link);
|
||||||
|
|
||||||
|
zwp_tablet_v2_destroy(dev->resource);
|
||||||
|
free(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwp_tablet_v2_listener tablet_listener = {
|
||||||
|
.name = handle_tablet_name,
|
||||||
|
.id = handle_tablet_id,
|
||||||
|
.path = handle_tablet_path,
|
||||||
|
.done = handle_tablet_done,
|
||||||
|
.removed = handle_tablet_removed,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void handle_tab_added(void *data,
|
||||||
|
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
|
||||||
|
struct zwp_tablet_v2 *id) {
|
||||||
|
wlr_log(WLR_DEBUG, "New tablet");
|
||||||
|
struct wlr_wl_seat *seat = data;
|
||||||
|
struct wlr_wl_input_device *dev = create_wl_input_device(
|
||||||
|
seat, WLR_INPUT_DEVICE_TABLET_TOOL);
|
||||||
|
|
||||||
|
if (!dev) {
|
||||||
|
zwp_tablet_v2_destroy(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dev->resource = id;
|
||||||
|
|
||||||
|
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
||||||
|
wlr_dev->tablet = calloc(1, sizeof(*wlr_dev->tablet));
|
||||||
|
|
||||||
|
if (!wlr_dev->tablet) {
|
||||||
|
zwp_tablet_v2_destroy(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
zwp_tablet_v2_set_user_data(id, wlr_dev->tablet);
|
||||||
|
wlr_tablet_init(wlr_dev->tablet, NULL);
|
||||||
|
zwp_tablet_v2_add_listener(id, &tablet_listener, dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
|
||||||
|
.tablet_added = handle_tab_added,
|
||||||
|
.tool_added = handle_tool_added,
|
||||||
|
.pad_added = handle_pad_added,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_wl_tablet_seat *wl_add_tablet_seat(
|
||||||
|
struct zwp_tablet_manager_v2 *manager,
|
||||||
|
struct wlr_wl_seat *seat) {
|
||||||
|
struct wlr_wl_tablet_seat *ret =
|
||||||
|
calloc(1, sizeof(struct wlr_wl_tablet_seat));
|
||||||
|
|
||||||
|
if (!(ret->tablet_seat =
|
||||||
|
zwp_tablet_manager_v2_get_tablet_seat(manager, seat->wl_seat))) {
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
zwp_tablet_seat_v2_add_listener(ret->tablet_seat,
|
||||||
|
&tablet_seat_listener, seat);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -1,455 +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 "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() {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static 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);
|
|
||||||
wl_list_remove(&pointer->output_destroy.link);
|
|
||||||
free(pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_pointer_impl pointer_impl = {
|
|
||||||
.destroy = pointer_destroy,
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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.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,7 +71,10 @@ 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) {
|
||||||
wlr_output_update_needs_swap(&output->wlr_output);
|
pixman_region32_union_rect(
|
||||||
|
&output->exposed, &output->exposed,
|
||||||
|
ev->x, ev->y, ev->width, ev->height);
|
||||||
|
wlr_output_update_needs_frame(&output->wlr_output);
|
||||||
}
|
}
|
||||||
break;
|
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>
|
||||||
|
@ -11,13 +13,14 @@
|
||||||
#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/interfaces/wlr_touch.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"
|
||||||
|
|
||||||
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,
|
||||||
|
@ -36,6 +39,7 @@ static void send_button_event(struct wlr_x11_output *output, uint32_t key,
|
||||||
.state = st,
|
.state = st,
|
||||||
};
|
};
|
||||||
wlr_signal_emit_safe(&output->pointer.events.button, &ev);
|
wlr_signal_emit_safe(&output->pointer.events.button, &ev);
|
||||||
|
wlr_signal_emit_safe(&output->pointer.events.frame, &output->pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_axis_event(struct wlr_x11_output *output, int32_t delta,
|
static void send_axis_event(struct wlr_x11_output *output, int32_t delta,
|
||||||
|
@ -65,6 +69,54 @@ static void send_pointer_position_event(struct wlr_x11_output *output,
|
||||||
wlr_signal_emit_safe(&output->pointer.events.frame, &output->pointer);
|
wlr_signal_emit_safe(&output->pointer.events.frame, &output->pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void send_touch_down_event(struct wlr_x11_output *output,
|
||||||
|
int16_t x, int16_t y, int32_t touch_id, xcb_timestamp_t time) {
|
||||||
|
struct wlr_event_touch_down ev = {
|
||||||
|
.device = &output->touch_dev,
|
||||||
|
.time_msec = time,
|
||||||
|
.x = (double)x / output->wlr_output.width,
|
||||||
|
.y = (double)y / output->wlr_output.height,
|
||||||
|
.touch_id = touch_id,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&output->touch.events.down, &ev);
|
||||||
|
wlr_signal_emit_safe(&output->touch.events.frame, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_touch_motion_event(struct wlr_x11_output *output,
|
||||||
|
int16_t x, int16_t y, int32_t touch_id, xcb_timestamp_t time) {
|
||||||
|
struct wlr_event_touch_motion ev = {
|
||||||
|
.device = &output->touch_dev,
|
||||||
|
.time_msec = time,
|
||||||
|
.x = (double)x / output->wlr_output.width,
|
||||||
|
.y = (double)y / output->wlr_output.height,
|
||||||
|
.touch_id = touch_id,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&output->touch.events.motion, &ev);
|
||||||
|
wlr_signal_emit_safe(&output->touch.events.frame, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_touch_up_event(struct wlr_x11_output *output,
|
||||||
|
int32_t touch_id, xcb_timestamp_t time) {
|
||||||
|
struct wlr_event_touch_up ev = {
|
||||||
|
.device = &output->touch_dev,
|
||||||
|
.time_msec = time,
|
||||||
|
.touch_id = touch_id,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&output->touch.events.up, &ev);
|
||||||
|
wlr_signal_emit_safe(&output->touch.events.frame, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wlr_x11_touchpoint *get_touchpoint_from_x11_touch_id(
|
||||||
|
struct wlr_x11_output *output, uint32_t id) {
|
||||||
|
struct wlr_x11_touchpoint *touchpoint;
|
||||||
|
wl_list_for_each(touchpoint, &output->touchpoints, link) {
|
||||||
|
if (touchpoint->x11_id == id) {
|
||||||
|
return touchpoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void handle_x11_xinput_event(struct wlr_x11_backend *x11,
|
void handle_x11_xinput_event(struct wlr_x11_backend *x11,
|
||||||
xcb_ge_generic_event_t *event) {
|
xcb_ge_generic_event_t *event) {
|
||||||
struct wlr_x11_output *output;
|
struct wlr_x11_output *output;
|
||||||
|
@ -74,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;
|
||||||
}
|
}
|
||||||
|
@ -86,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;
|
||||||
}
|
}
|
||||||
|
@ -163,34 +219,68 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
|
||||||
x11->time = ev->time;
|
x11->time = ev->time;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case XCB_INPUT_ENTER: {
|
case XCB_INPUT_TOUCH_BEGIN: {
|
||||||
xcb_input_enter_event_t *ev = (xcb_input_enter_event_t *)event;
|
xcb_input_touch_begin_event_t *ev = (xcb_input_touch_begin_event_t *)event;
|
||||||
|
|
||||||
output = get_x11_output_from_window_id(x11, ev->event);
|
output = get_x11_output_from_window_id(x11, ev->event);
|
||||||
if (!output) {
|
if (!output) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!output->cursor_hidden) {
|
int32_t id = 0;
|
||||||
xcb_xfixes_hide_cursor(x11->xcb, output->win);
|
if (!wl_list_empty(&output->touchpoints)) {
|
||||||
xcb_flush(x11->xcb);
|
struct wlr_x11_touchpoint *last_touchpoint = wl_container_of(
|
||||||
output->cursor_hidden = true;
|
output->touchpoints.next, last_touchpoint, link);
|
||||||
|
id = last_touchpoint->wayland_id + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct wlr_x11_touchpoint *touchpoint = calloc(1, sizeof(struct wlr_x11_touchpoint));
|
||||||
|
touchpoint->x11_id = ev->detail;
|
||||||
|
touchpoint->wayland_id = id;
|
||||||
|
wl_list_init(&touchpoint->link);
|
||||||
|
wl_list_insert(&output->touchpoints, &touchpoint->link);
|
||||||
|
|
||||||
|
send_touch_down_event(output, ev->event_x >> 16,
|
||||||
|
ev->event_y >> 16, touchpoint->wayland_id, ev->time);
|
||||||
|
x11->time = ev->time;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case XCB_INPUT_LEAVE: {
|
case XCB_INPUT_TOUCH_END: {
|
||||||
xcb_input_leave_event_t *ev = (xcb_input_leave_event_t *)event;
|
xcb_input_touch_end_event_t *ev = (xcb_input_touch_end_event_t *)event;
|
||||||
|
|
||||||
output = get_x11_output_from_window_id(x11, ev->event);
|
output = get_x11_output_from_window_id(x11, ev->event);
|
||||||
if (!output) {
|
if (!output) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output->cursor_hidden) {
|
struct wlr_x11_touchpoint *touchpoint = get_touchpoint_from_x11_touch_id(output, ev->detail);
|
||||||
xcb_xfixes_show_cursor(x11->xcb, output->win);
|
if (!touchpoint) {
|
||||||
xcb_flush(x11->xcb);
|
return;
|
||||||
output->cursor_hidden = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
send_touch_up_event(output, touchpoint->wayland_id, ev->time);
|
||||||
|
x11->time = ev->time;
|
||||||
|
|
||||||
|
wl_list_remove(&touchpoint->link);
|
||||||
|
free(touchpoint);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case XCB_INPUT_TOUCH_UPDATE: {
|
||||||
|
xcb_input_touch_update_event_t *ev = (xcb_input_touch_update_event_t *)event;
|
||||||
|
|
||||||
|
output = get_x11_output_from_window_id(x11, ev->event);
|
||||||
|
if (!output) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_x11_touchpoint *touchpoint = get_touchpoint_from_x11_touch_id(output, ev->detail);
|
||||||
|
if (!touchpoint) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
send_touch_motion_event(output, ev->event_x >> 16,
|
||||||
|
ev->event_y >> 16, touchpoint->wayland_id, ev->time);
|
||||||
|
x11->time = ev->time;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,6 +310,14 @@ const struct wlr_pointer_impl pointer_impl = {
|
||||||
.destroy = pointer_destroy,
|
.destroy = pointer_destroy,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void touch_destroy(struct wlr_touch *wlr_touch) {
|
||||||
|
// Don't free the touch, it's on the stack
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct wlr_touch_impl touch_impl = {
|
||||||
|
.destroy = touch_destroy,
|
||||||
|
};
|
||||||
|
|
||||||
void update_x11_pointer_position(struct wlr_x11_output *output,
|
void update_x11_pointer_position(struct wlr_x11_output *output,
|
||||||
xcb_timestamp_t time) {
|
xcb_timestamp_t time) {
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
|
@ -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,23 +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/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) {
|
||||||
|
@ -39,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,
|
||||||
|
@ -75,53 +68,427 @@ static bool output_set_custom_mode(struct wlr_output *wlr_output,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_transform(struct wlr_output *wlr_output,
|
static void destroy_x11_buffer(struct wlr_x11_buffer *buffer);
|
||||||
enum wl_output_transform transform) {
|
|
||||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
|
||||||
output->wlr_output.transform = transform;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
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_make_current(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;
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
if (unsupported != 0) {
|
||||||
|
wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32,
|
||||||
return wlr_egl_make_current(&x11->egl, output->surf, buffer_age);
|
unsupported);
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_swap_buffers(struct wlr_output *wlr_output,
|
|
||||||
pixman_region32_t *damage) {
|
|
||||||
struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output;
|
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
|
||||||
|
|
||||||
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) {
|
||||||
|
assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void destroy_x11_buffer(struct wlr_x11_buffer *buffer) {
|
||||||
|
if (!buffer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wl_list_remove(&buffer->buffer_destroy.link);
|
||||||
|
wl_list_remove(&buffer->link);
|
||||||
|
xcb_free_pixmap(buffer->x11->xcb, buffer->pixmap);
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void buffer_handle_buffer_destroy(struct wl_listener *listener,
|
||||||
|
void *data) {
|
||||||
|
struct wlr_x11_buffer *buffer =
|
||||||
|
wl_container_of(listener, buffer, buffer_destroy);
|
||||||
|
destroy_x11_buffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static xcb_pixmap_t import_dmabuf(struct wlr_x11_output *output,
|
||||||
|
struct wlr_dmabuf_attributes *dmabuf) {
|
||||||
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
||||||
|
if (dmabuf->format != x11->x11_format->drm) {
|
||||||
|
// The pixmap's depth must match the window's depth, otherwise Present
|
||||||
|
// will throw a Match error
|
||||||
|
return XCB_PIXMAP_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// xcb closes the FDs after sending them, so we need to dup them here
|
||||||
|
struct wlr_dmabuf_attributes dup_attrs = {0};
|
||||||
|
if (!wlr_dmabuf_attributes_copy(&dup_attrs, dmabuf)) {
|
||||||
|
return XCB_PIXMAP_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct wlr_x11_format *x11_fmt = x11->x11_format;
|
||||||
|
xcb_pixmap_t pixmap = xcb_generate_id(x11->xcb);
|
||||||
|
|
||||||
|
if (x11->dri3_major_version > 1 || x11->dri3_minor_version >= 2) {
|
||||||
|
if (dmabuf->n_planes > 4) {
|
||||||
|
wlr_dmabuf_attributes_finish(&dup_attrs);
|
||||||
|
return XCB_PIXMAP_NONE;
|
||||||
|
}
|
||||||
|
xcb_dri3_pixmap_from_buffers(x11->xcb, pixmap, output->win,
|
||||||
|
dmabuf->n_planes, dmabuf->width, dmabuf->height, dmabuf->stride[0],
|
||||||
|
dmabuf->offset[0], dmabuf->stride[1], dmabuf->offset[1],
|
||||||
|
dmabuf->stride[2], dmabuf->offset[2], dmabuf->stride[3],
|
||||||
|
dmabuf->offset[3], x11_fmt->depth, x11_fmt->bpp, dmabuf->modifier,
|
||||||
|
dup_attrs.fd);
|
||||||
|
} else {
|
||||||
|
// PixmapFromBuffers requires DRI3 1.2
|
||||||
|
if (dmabuf->n_planes != 1
|
||||||
|
|| dmabuf->modifier != DRM_FORMAT_MOD_INVALID) {
|
||||||
|
wlr_dmabuf_attributes_finish(&dup_attrs);
|
||||||
|
return XCB_PIXMAP_NONE;
|
||||||
|
}
|
||||||
|
xcb_dri3_pixmap_from_buffer(x11->xcb, pixmap, output->win,
|
||||||
|
dmabuf->height * dmabuf->stride[0], dmabuf->width, dmabuf->height,
|
||||||
|
dmabuf->stride[0], x11_fmt->depth, x11_fmt->bpp, dup_attrs.fd[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pixmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
static xcb_pixmap_t import_shm(struct wlr_x11_output *output,
|
||||||
|
struct wlr_shm_attributes *shm) {
|
||||||
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
||||||
|
if (shm->format != x11->x11_format->drm) {
|
||||||
|
// The pixmap's depth must match the window's depth, otherwise Present
|
||||||
|
// will throw a Match error
|
||||||
|
return XCB_PIXMAP_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// xcb closes the FD after sending it
|
||||||
|
int fd = fcntl(shm->fd, F_DUPFD_CLOEXEC, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed");
|
||||||
|
return XCB_PIXMAP_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_shm_seg_t seg = xcb_generate_id(x11->xcb);
|
||||||
|
xcb_shm_attach_fd(x11->xcb, seg, fd, false);
|
||||||
|
|
||||||
|
xcb_pixmap_t pixmap = xcb_generate_id(x11->xcb);
|
||||||
|
xcb_shm_create_pixmap(x11->xcb, pixmap, output->win, shm->width,
|
||||||
|
shm->height, x11->x11_format->depth, seg, shm->offset);
|
||||||
|
|
||||||
|
xcb_shm_detach(x11->xcb, seg);
|
||||||
|
|
||||||
|
return pixmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wlr_x11_buffer *create_x11_buffer(struct wlr_x11_output *output,
|
||||||
|
struct wlr_buffer *wlr_buffer) {
|
||||||
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
xcb_pixmap_t pixmap = XCB_PIXMAP_NONE;
|
||||||
|
|
||||||
|
struct wlr_dmabuf_attributes dmabuf_attrs;
|
||||||
|
struct wlr_shm_attributes shm_attrs;
|
||||||
|
if (wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf_attrs)) {
|
||||||
|
pixmap = import_dmabuf(output, &dmabuf_attrs);
|
||||||
|
} else if (wlr_buffer_get_shm(wlr_buffer, &shm_attrs)) {
|
||||||
|
pixmap = import_shm(output, &shm_attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pixmap == XCB_PIXMAP_NONE) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_x11_buffer *buffer = calloc(1, sizeof(struct wlr_x11_buffer));
|
||||||
|
if (!buffer) {
|
||||||
|
xcb_free_pixmap(x11->xcb, pixmap);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
buffer->buffer = wlr_buffer_lock(wlr_buffer);
|
||||||
|
buffer->pixmap = pixmap;
|
||||||
|
buffer->x11 = x11;
|
||||||
|
wl_list_insert(&output->buffers, &buffer->link);
|
||||||
|
|
||||||
|
buffer->buffer_destroy.notify = buffer_handle_buffer_destroy;
|
||||||
|
wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wlr_x11_buffer *get_or_create_x11_buffer(
|
||||||
|
struct wlr_x11_output *output, struct wlr_buffer *wlr_buffer) {
|
||||||
|
struct wlr_x11_buffer *buffer;
|
||||||
|
wl_list_for_each(buffer, &output->buffers, link) {
|
||||||
|
if (buffer->buffer == wlr_buffer) {
|
||||||
|
wlr_buffer_lock(buffer->buffer);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return create_x11_buffer(output, wlr_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool output_commit_buffer(struct wlr_x11_output *output) {
|
||||||
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
||||||
|
struct wlr_buffer *buffer = output->wlr_output.pending.buffer;
|
||||||
|
struct wlr_x11_buffer *x11_buffer =
|
||||||
|
get_or_create_x11_buffer(output, buffer);
|
||||||
|
if (!x11_buffer) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_xfixes_region_t region = XCB_NONE;
|
||||||
|
if (output->wlr_output.pending.committed & WLR_OUTPUT_STATE_DAMAGE) {
|
||||||
|
pixman_region32_union(&output->exposed, &output->exposed, &output->wlr_output.pending.damage);
|
||||||
|
|
||||||
|
int rects_len = 0;
|
||||||
|
pixman_box32_t *rects = pixman_region32_rectangles(&output->exposed, &rects_len);
|
||||||
|
|
||||||
|
xcb_rectangle_t *xcb_rects = calloc(rects_len, sizeof(xcb_rectangle_t));
|
||||||
|
if (!xcb_rects) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < rects_len; i++) {
|
||||||
|
pixman_box32_t *box = &rects[i];
|
||||||
|
xcb_rects[i] = (struct xcb_rectangle_t){
|
||||||
|
.x = box->x1,
|
||||||
|
.y = box->y1,
|
||||||
|
.width = box->x2 - box->x1,
|
||||||
|
.height = box->y2 - box->y1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
region = xcb_generate_id(x11->xcb);
|
||||||
|
xcb_xfixes_create_region(x11->xcb, region, rects_len, xcb_rects);
|
||||||
|
|
||||||
|
free(xcb_rects);
|
||||||
|
}
|
||||||
|
|
||||||
|
pixman_region32_clear(&output->exposed);
|
||||||
|
|
||||||
|
uint32_t serial = output->wlr_output.commit_seq;
|
||||||
|
uint32_t options = 0;
|
||||||
|
uint64_t target_msc = output->last_msc ? output->last_msc + 1 : 0;
|
||||||
|
xcb_present_pixmap(x11->xcb, output->win, x11_buffer->pixmap, serial,
|
||||||
|
0, region, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, options, target_msc,
|
||||||
|
0, 0, 0, NULL);
|
||||||
|
|
||||||
|
if (region != XCB_NONE) {
|
||||||
|
xcb_xfixes_destroy_region(x11->xcb, region);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error:
|
||||||
|
destroy_x11_buffer(x11_buffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool output_commit(struct wlr_output *wlr_output) {
|
||||||
|
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||||
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
||||||
|
if (!output_test(wlr_output)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
|
||||||
.transform = output_transform,
|
|
||||||
.destroy = output_destroy,
|
.destroy = output_destroy,
|
||||||
.make_current = output_make_current,
|
.test = output_test,
|
||||||
.swap_buffers = output_swap_buffers,
|
.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) {
|
||||||
|
@ -137,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-%d",
|
|
||||||
wl_list_length(&x11->outputs) + 1);
|
|
||||||
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;
|
||||||
|
@ -169,17 +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_TOUCH_BEGIN |
|
||||||
XCB_INPUT_XI_EVENT_MASK_LEAVE,
|
XCB_INPUT_XI_EVENT_MASK_TOUCH_END |
|
||||||
|
XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE,
|
||||||
};
|
};
|
||||||
xcb_input_xi_select_events(x11->xcb, output->win, 1, &xinput_mask.head);
|
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,
|
||||||
|
@ -190,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,
|
||||||
|
@ -204,8 +579,19 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
||||||
output->pointer_dev.pointer = &output->pointer;
|
output->pointer_dev.pointer = &output->pointer;
|
||||||
output->pointer_dev.output_name = strdup(wlr_output->name);
|
output->pointer_dev.output_name = strdup(wlr_output->name);
|
||||||
|
|
||||||
|
wlr_input_device_init(&output->touch_dev, WLR_INPUT_DEVICE_TOUCH,
|
||||||
|
&input_device_impl, "X11 touch", 0, 0);
|
||||||
|
wlr_touch_init(&output->touch, &touch_impl);
|
||||||
|
output->touch_dev.touch = &output->touch;
|
||||||
|
output->touch_dev.output_name = strdup(wlr_output->name);
|
||||||
|
wl_list_init(&output->touchpoints);
|
||||||
|
|
||||||
wlr_signal_emit_safe(&x11->backend.events.new_output, wlr_output);
|
wlr_signal_emit_safe(&x11->backend.events.new_output, wlr_output);
|
||||||
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);
|
||||||
|
|
||||||
|
// Start the rendering loop by requesting the compositor to render a frame
|
||||||
|
wlr_output_schedule_frame(wlr_output);
|
||||||
|
|
||||||
return wlr_output;
|
return wlr_output;
|
||||||
}
|
}
|
||||||
|
@ -213,17 +599,18 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
||||||
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) {
|
||||||
|
@ -245,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,34 +1,54 @@
|
||||||
wlroots reads these environment variables
|
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_DRM_NO_ATOMIC_GAMMA*: set to 1 to use legacy DRM interface for gamma
|
* *WLR_DRM_NO_MODIFIERS*: set to 1 to always allocate planes without modifiers,
|
||||||
control instead of the atomic interface
|
this can fix certain modeset failures because of bandwidth restrictions.
|
||||||
* *WLR_LIBINPUT_NO_DEVICES*: set to 1 to not fail without any input devices
|
|
||||||
* *WLR_BACKENDS*: comma-separated list of backends to use (available backends:
|
## Headless backend
|
||||||
wayland, x11, headless, noop)
|
|
||||||
* *WLR_WL_OUTPUTS*: when using the wayland backend specifies the number of outputs
|
|
||||||
* *WLR_X11_OUTPUTS*: when using the X11 backend specifies the number of outputs
|
|
||||||
* *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
|
||||||
* *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)
|
|
||||||
|
|
||||||
rootston specific
|
## libinput backend
|
||||||
------------------
|
|
||||||
* *XKB_DEFAULT_RULES*, *XKB_DEFAULT_MODEL*, *XKB_DEFAULT_LAYOUT*,
|
|
||||||
*XKB_DEFAULT_VARIANT*, *XKB_DEFAULT_OPTIONS*: xkb setup
|
|
||||||
|
|
||||||
generic
|
* *WLR_LIBINPUT_NO_DEVICES*: set to 1 to not fail without any input devices
|
||||||
-------
|
|
||||||
* *DISPLAY*: if set probe X11 backend in *wlr_backend_autocreate*
|
## Wayland backend
|
||||||
* *WAYLAND_DISPLAY*, *_WAYLAND_DISPLAY*, *WAYLAND_SOCKET*: if set probe Wayland
|
|
||||||
backend in *wlr_backend_autocreate*
|
* *WLR_WL_OUTPUTS*: when using the wayland backend specifies the number of outputs
|
||||||
|
|
||||||
|
## X11 backend
|
||||||
|
|
||||||
|
* *WLR_X11_OUTPUTS*: when using the X11 backend specifies the number of outputs
|
||||||
|
|
||||||
|
## gles2 renderer
|
||||||
|
|
||||||
|
* *WLR_RENDERER_ALLOW_SOFTWARE*: allows the gles2 renderer to use software
|
||||||
|
rendering
|
||||||
|
|
||||||
|
# Generic
|
||||||
|
|
||||||
|
* *DISPLAY*: if set probe X11 backend in `wlr_backend_autocreate`
|
||||||
|
* *WAYLAND_DISPLAY*, *WAYLAND_SOCKET*: if set probe Wayland backend in
|
||||||
|
`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,39 +7,48 @@
|
||||||
#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 3
|
||||||
* Usage:
|
|
||||||
* 1. foreign-toplevel
|
|
||||||
* Prints a list of opened toplevels
|
static void print_help(void) {
|
||||||
* 2. foreign-toplevel -f <id>
|
static const char usage[] =
|
||||||
* Focus the toplevel with the given id
|
"Usage: foreign-toplevel [OPTIONS] ...\n"
|
||||||
* 3. foreign-toplevel -a <id>
|
"Manage and view information about toplevel windows.\n"
|
||||||
* Maximize the toplevel with the given id
|
"\n"
|
||||||
* 4. foreign-toplevel -u <id>
|
" -f <id> focus\n"
|
||||||
* Unmaximize the toplevel with the given id
|
" -s <id> fullscreen\n"
|
||||||
* 5. foreign-toplevel -i <id>
|
" -o <output_id> select output for fullscreen toplevel to appear on. Use this\n"
|
||||||
* Minimize the toplevel with the given id
|
" option with -s. View available outputs with wayland-info.\n"
|
||||||
* 6. foreign-toplevel -r <id>
|
" -S <id> unfullscreen\n"
|
||||||
* Restore(unminimize) the toplevel with the given id
|
" -a <id> maximize\n"
|
||||||
* 7. foreign-toplevel -c <id>
|
" -u <id> unmaximize\n"
|
||||||
* Close the toplevel with the given id
|
" -i <id> minimize\n"
|
||||||
* 8. foreign-toplevel -m
|
" -r <id> restore(unminimize)\n"
|
||||||
* Continuously print changes to the list of opened toplevels.
|
" -c <id> close\n"
|
||||||
* Can be used together with some of the previous options.
|
" -m continuously print changes to the list of opened toplevels\n"
|
||||||
*/
|
" Can be used together with some of the previous options.\n"
|
||||||
|
" -h print help message and quit\n";
|
||||||
|
fprintf(stderr, "%s", usage);
|
||||||
|
}
|
||||||
|
|
||||||
enum toplevel_state_field {
|
enum toplevel_state_field {
|
||||||
TOPLEVEL_STATE_MAXIMIZED = 1,
|
TOPLEVEL_STATE_MAXIMIZED = (1 << 0),
|
||||||
TOPLEVEL_STATE_MINIMIZED = 2,
|
TOPLEVEL_STATE_MINIMIZED = (1 << 1),
|
||||||
TOPLEVEL_STATE_ACTIVATED = 4,
|
TOPLEVEL_STATE_ACTIVATED = (1 << 2),
|
||||||
TOPLEVEL_STATE_INVALID = 8,
|
TOPLEVEL_STATE_FULLSCREEN = (1 << 3),
|
||||||
|
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,
|
||||||
|
@ -64,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,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");
|
||||||
}
|
}
|
||||||
|
@ -102,12 +119,20 @@ static void print_toplevel_state(struct toplevel_v1 *toplevel, bool print_endl)
|
||||||
} else {
|
} else {
|
||||||
printf(" inactive");
|
printf(" inactive");
|
||||||
}
|
}
|
||||||
|
if (toplevel->current.state & TOPLEVEL_STATE_FULLSCREEN) {
|
||||||
|
printf(" fullscreen");
|
||||||
|
}
|
||||||
|
|
||||||
if (print_endl) {
|
if (print_endl) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
@ -152,6 +177,8 @@ static uint32_t array_to_state(struct wl_array *array) {
|
||||||
state |= TOPLEVEL_STATE_MINIMIZED;
|
state |= TOPLEVEL_STATE_MINIMIZED;
|
||||||
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED)
|
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED)
|
||||||
state |= TOPLEVEL_STATE_ACTIVATED;
|
state |= TOPLEVEL_STATE_ACTIVATED;
|
||||||
|
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN)
|
||||||
|
state |= TOPLEVEL_STATE_FULLSCREEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
|
@ -164,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;
|
||||||
|
@ -184,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 = {
|
||||||
|
@ -194,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) {
|
||||||
|
@ -210,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,
|
||||||
|
@ -230,13 +282,16 @@ 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,
|
||||||
&zwlr_foreign_toplevel_manager_v1_interface, 1);
|
&zwlr_foreign_toplevel_manager_v1_interface,
|
||||||
|
WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION);
|
||||||
|
|
||||||
wl_list_init(&toplevel_list);
|
wl_list_init(&toplevel_list);
|
||||||
zwlr_foreign_toplevel_manager_v1_add_listener(toplevel_manager,
|
zwlr_foreign_toplevel_manager_v1_add_listener(toplevel_manager,
|
||||||
|
@ -278,11 +333,11 @@ int main(int argc, char **argv) {
|
||||||
int focus_id = -1, close_id = -1;
|
int focus_id = -1, close_id = -1;
|
||||||
int maximize_id = -1, unmaximize_id = -1;
|
int maximize_id = -1, unmaximize_id = -1;
|
||||||
int minimize_id = -1, restore_id = -1;
|
int minimize_id = -1, restore_id = -1;
|
||||||
|
int fullscreen_id = -1, unfullscreen_id = -1;
|
||||||
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:m")) != -1) {
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'f':
|
case 'f':
|
||||||
focus_id = atoi(optarg);
|
focus_id = atoi(optarg);
|
||||||
|
@ -302,9 +357,26 @@ int main(int argc, char **argv) {
|
||||||
case 'c':
|
case 'c':
|
||||||
close_id = atoi(optarg);
|
close_id = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
fullscreen_id = atoi(optarg);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
unfullscreen_id = atoi(optarg);
|
||||||
|
break;
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,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) {
|
||||||
|
@ -342,6 +413,16 @@ int main(int argc, char **argv) {
|
||||||
if ((toplevel = toplevel_by_id_or_bail(restore_id))) {
|
if ((toplevel = toplevel_by_id_or_bail(restore_id))) {
|
||||||
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 (pref_output_id != UINT32_MAX && pref_output == NULL) {
|
||||||
|
fprintf(stderr, "Could not find output %i\n", pref_output_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
zwlr_foreign_toplevel_handle_v1_set_fullscreen(toplevel->zwlr_toplevel, pref_output);
|
||||||
|
}
|
||||||
|
if ((toplevel = toplevel_by_id_or_bail(unfullscreen_id))) {
|
||||||
|
zwlr_foreign_toplevel_handle_v1_unset_fullscreen(toplevel->zwlr_toplevel);
|
||||||
|
}
|
||||||
if ((toplevel = toplevel_by_id_or_bail(close_id))) {
|
if ((toplevel = toplevel_by_id_or_bail(close_id))) {
|
||||||
zwlr_foreign_toplevel_handle_v1_close(toplevel->zwlr_toplevel);
|
zwlr_foreign_toplevel_handle_v1_close(toplevel->zwlr_toplevel);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,16 +5,17 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server.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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -89,7 +90,7 @@ static void output_handle_frame(struct wl_listener *listener, void *data) {
|
||||||
int width, height;
|
int width, height;
|
||||||
wlr_output_effective_resolution(output->wlr_output, &width, &height);
|
wlr_output_effective_resolution(output->wlr_output, &width, &height);
|
||||||
|
|
||||||
if (!wlr_output_make_current(output->wlr_output, NULL)) {
|
if (!wlr_output_attach_render(output->wlr_output, NULL)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +109,7 @@ static void output_handle_frame(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_renderer_end(renderer);
|
wlr_renderer_end(renderer);
|
||||||
wlr_output_swap_buffers(output->wlr_output, NULL, NULL);
|
wlr_output_commit(output->wlr_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_set_surface(struct fullscreen_output *output,
|
static void output_set_surface(struct fullscreen_output *output,
|
||||||
|
@ -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;
|
||||||
|
}
|
|
@ -176,7 +176,7 @@ static void timer_arm(unsigned seconds) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_updates() {
|
static void do_updates(void) {
|
||||||
printf("Update %d\n", update_stage);
|
printf("Update %d\n", update_stage);
|
||||||
switch (update_stage) {
|
switch (update_stage) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -240,7 +240,7 @@ static void do_updates() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_timer() {
|
static void handle_timer(void) {
|
||||||
printf("Timer dispatched at %d\n", update_stage);
|
printf("Timer dispatched at %d\n", update_stage);
|
||||||
do_updates();
|
do_updates();
|
||||||
}
|
}
|
||||||
|
@ -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,89 +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],
|
|
||||||
},
|
},
|
||||||
'screenshot': {
|
'fullscreen-shell': {
|
||||||
'src': 'screenshot.c',
|
'src': 'fullscreen-shell.c',
|
||||||
'dep': [wayland_client, wlr_protos, rt],
|
'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',
|
||||||
|
@ -91,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,23 +1,21 @@
|
||||||
#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>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-protocol.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wayland-server.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>
|
||||||
|
@ -25,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;
|
||||||
|
@ -69,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);
|
||||||
|
@ -89,19 +89,21 @@ 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_make_current(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_output_swap_buffers(wlr_output, NULL, NULL);
|
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) {
|
||||||
|
@ -128,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);
|
||||||
|
@ -162,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);
|
||||||
|
@ -179,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);
|
||||||
|
@ -192,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;
|
||||||
|
@ -210,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) {
|
||||||
|
@ -229,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");
|
||||||
|
@ -290,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>
|
||||||
|
@ -8,16 +8,17 @@
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-protocol.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wayland-server.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"
|
||||||
|
@ -27,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;
|
||||||
|
@ -107,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;
|
||||||
|
@ -115,7 +117,7 @@ void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
|
|
||||||
struct wlr_output *wlr_output = output->output;
|
struct wlr_output *wlr_output = output->output;
|
||||||
|
|
||||||
wlr_output_make_current(wlr_output, NULL);
|
wlr_output_attach_render(wlr_output, NULL);
|
||||||
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});
|
||||||
|
|
||||||
|
@ -137,7 +139,7 @@ void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_renderer_end(sample->renderer);
|
wlr_renderer_end(sample->renderer);
|
||||||
wlr_output_swap_buffers(wlr_output, NULL, NULL);
|
wlr_output_commit(wlr_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_velocities(struct sample_state *sample,
|
static void update_velocities(struct sample_state *sample,
|
||||||
|
@ -146,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);
|
||||||
|
@ -155,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;
|
||||||
|
@ -170,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;
|
||||||
|
@ -189,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);
|
||||||
|
@ -208,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) {
|
||||||
|
@ -227,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");
|
||||||
|
@ -266,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);
|
||||||
}
|
}
|
||||||
|
@ -276,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);
|
||||||
|
|
||||||
|
|
|
@ -6,17 +6,18 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-protocol.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wayland-server.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>
|
||||||
|
@ -24,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;
|
||||||
|
@ -91,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_make_current(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_swap_buffers(wlr_output, NULL, NULL);
|
|
||||||
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) {
|
||||||
|
@ -222,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;
|
||||||
|
@ -238,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);
|
||||||
|
@ -247,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);
|
||||||
|
@ -266,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) {
|
||||||
|
@ -294,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");
|
||||||
|
@ -330,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>
|
||||||
|
@ -8,14 +8,12 @@
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-protocol.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wayland-server.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>
|
||||||
|
@ -28,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;
|
||||||
|
@ -60,7 +59,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
int32_t width, height;
|
int32_t width, height;
|
||||||
wlr_output_effective_resolution(wlr_output, &width, &height);
|
wlr_output_effective_resolution(wlr_output, &width, &height);
|
||||||
|
|
||||||
wlr_output_make_current(wlr_output, NULL);
|
wlr_output_attach_render(wlr_output, NULL);
|
||||||
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});
|
||||||
|
|
||||||
|
@ -72,7 +71,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_renderer_end(sample->renderer);
|
wlr_renderer_end(sample->renderer);
|
||||||
wlr_output_swap_buffers(wlr_output, NULL, NULL);
|
wlr_output_commit(wlr_output);
|
||||||
|
|
||||||
long ms = (now.tv_sec - sample->last_frame.tv_sec) * 1000 +
|
long ms = (now.tv_sec - sample->last_frame.tv_sec) * 1000 +
|
||||||
(now.tv_nsec - sample->last_frame.tv_nsec) / 1000000;
|
(now.tv_nsec - sample->last_frame.tv_nsec) / 1000000;
|
||||||
|
@ -98,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;
|
||||||
|
|
||||||
|
@ -124,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;
|
||||||
|
@ -139,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);
|
||||||
|
@ -158,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) {
|
||||||
|
@ -177,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");
|
||||||
|
@ -229,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);
|
||||||
|
@ -240,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);
|
||||||
}
|
}
|
||||||
|
@ -251,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,237 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2008 Kristian Høgsberg
|
|
||||||
*
|
|
||||||
* 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 <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include "screenshooter-client-protocol.h"
|
|
||||||
|
|
||||||
static struct wl_shm *shm = NULL;
|
|
||||||
static struct orbital_screenshooter *screenshooter = NULL;
|
|
||||||
static struct wl_list output_list;
|
|
||||||
static bool buffer_copy_done;
|
|
||||||
|
|
||||||
struct screenshooter_output {
|
|
||||||
struct wl_output *output;
|
|
||||||
int width, height;
|
|
||||||
struct wl_list link;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void output_handle_geometry(void *data, struct wl_output *wl_output,
|
|
||||||
int x, int y, int physical_width, int physical_height, int subpixel,
|
|
||||||
const char *make, const char *model, int transform) {
|
|
||||||
// No-op
|
|
||||||
}
|
|
||||||
|
|
||||||
static void output_handle_mode(void *data, struct wl_output *wl_output,
|
|
||||||
uint32_t flags, int width, int height, int refresh) {
|
|
||||||
struct screenshooter_output *output = wl_output_get_user_data(wl_output);
|
|
||||||
|
|
||||||
if (wl_output == output->output && (flags & WL_OUTPUT_MODE_CURRENT)) {
|
|
||||||
output->width = width;
|
|
||||||
output->height = height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void output_handle_done(void *data, struct wl_output *wl_output) {
|
|
||||||
// No-op
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_output_listener output_listener = {
|
|
||||||
.geometry = output_handle_geometry,
|
|
||||||
.mode = output_handle_mode,
|
|
||||||
.done = output_handle_done,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void screenshot_done(void *data, struct orbital_screenshot *screenshot) {
|
|
||||||
buffer_copy_done = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct orbital_screenshot_listener screenshot_listener = {
|
|
||||||
.done = screenshot_done,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
static struct screenshooter_output *output;
|
|
||||||
|
|
||||||
if (strcmp(interface, "wl_output") == 0) {
|
|
||||||
output = calloc(1, sizeof(*output));
|
|
||||||
output->output = wl_registry_bind(registry, name, &wl_output_interface,
|
|
||||||
1);
|
|
||||||
wl_list_insert(&output_list, &output->link);
|
|
||||||
wl_output_add_listener(output->output, &output_listener, output);
|
|
||||||
} else if (strcmp(interface, "wl_shm") == 0) {
|
|
||||||
shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
|
|
||||||
} else if (strcmp(interface, "orbital_screenshooter") == 0) {
|
|
||||||
screenshooter = wl_registry_bind(registry, name,
|
|
||||||
&orbital_screenshooter_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,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct wl_buffer *create_shm_buffer(int width, int height,
|
|
||||||
void **data_out) {
|
|
||||||
int stride = width * 4;
|
|
||||||
int size = stride * height;
|
|
||||||
|
|
||||||
const char shm_name[] = "/wlroots-screenshot";
|
|
||||||
int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, 0);
|
|
||||||
if (fd < 0) {
|
|
||||||
fprintf(stderr, "shm_open failed\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
shm_unlink(shm_name);
|
|
||||||
|
|
||||||
int ret;
|
|
||||||
while ((ret = ftruncate(fd, size)) == EINTR) {
|
|
||||||
// No-op
|
|
||||||
}
|
|
||||||
if (ret < 0) {
|
|
||||||
close(fd);
|
|
||||||
fprintf(stderr, "ftruncate failed\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
||||||
if (data == MAP_FAILED) {
|
|
||||||
fprintf(stderr, "mmap failed: %m\n");
|
|
||||||
close(fd);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
|
|
||||||
close(fd);
|
|
||||||
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height,
|
|
||||||
stride, WL_SHM_FORMAT_XRGB8888);
|
|
||||||
wl_shm_pool_destroy(pool);
|
|
||||||
|
|
||||||
*data_out = data;
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void write_image(const char *filename, int width, int height,
|
|
||||||
void *data) {
|
|
||||||
char size[10 + 1 + 10 + 2 + 1]; // int32_t are max 10 digits
|
|
||||||
sprintf(size, "%dx%d+0", width, height);
|
|
||||||
|
|
||||||
int fd[2];
|
|
||||||
if (pipe(fd) != 0) {
|
|
||||||
fprintf(stderr, "cannot create pipe: %s\n", strerror(errno));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_t child = fork();
|
|
||||||
if (child < 0) {
|
|
||||||
fprintf(stderr, "fork() failed\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
} else if (child != 0) {
|
|
||||||
close(fd[0]);
|
|
||||||
if (write(fd[1], data, 4 * width * height) < 0) {
|
|
||||||
fprintf(stderr, "write() failed: %s\n", strerror(errno));
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
close(fd[1]);
|
|
||||||
waitpid(child, NULL, 0);
|
|
||||||
} else {
|
|
||||||
close(fd[1]);
|
|
||||||
if (dup2(fd[0], 0) != 0) {
|
|
||||||
fprintf(stderr, "cannot dup the pipe\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
close(fd[0]);
|
|
||||||
// We requested WL_SHM_FORMAT_XRGB8888 in little endian, so that's BGRA
|
|
||||||
// in big endian.
|
|
||||||
execlp("convert", "convert", "-depth", "8", "-size", size, "bgra:-",
|
|
||||||
"-alpha", "opaque", filename, NULL);
|
|
||||||
fprintf(stderr, "cannot execute convert\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
struct wl_display * display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
fprintf(stderr, "failed to create display: %m\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_list_init(&output_list);
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_dispatch(display);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (screenshooter == NULL) {
|
|
||||||
fprintf(stderr, "display doesn't support screenshooter\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
struct screenshooter_output *output;
|
|
||||||
wl_list_for_each(output, &output_list, link) {
|
|
||||||
void *data = NULL;
|
|
||||||
struct wl_buffer *buffer =
|
|
||||||
create_shm_buffer(output->width, output->height, &data);
|
|
||||||
if (buffer == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
struct orbital_screenshot *screenshot = orbital_screenshooter_shoot(
|
|
||||||
screenshooter, output->output, buffer);
|
|
||||||
orbital_screenshot_add_listener(screenshot, &screenshot_listener,
|
|
||||||
screenshot);
|
|
||||||
buffer_copy_done = false;
|
|
||||||
while (!buffer_copy_done) {
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
}
|
|
||||||
|
|
||||||
char filename[24 + 10]; // int32_t are max 10 digits
|
|
||||||
snprintf(filename, sizeof(filename), "wayland-screenshot-%d.png", i);
|
|
||||||
|
|
||||||
write_image(filename, output->width, output->height, data);
|
|
||||||
wl_buffer_destroy(buffer);
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
|
@ -1,15 +1,17 @@
|
||||||
#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>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <wayland-server.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_make_current(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_swap_buffers(sample_output->output, NULL, NULL);
|
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,28 +1,29 @@
|
||||||
#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>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-protocol.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wayland-server.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;
|
||||||
|
@ -86,7 +87,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
int32_t width, height;
|
int32_t width, height;
|
||||||
wlr_output_effective_resolution(wlr_output, &width, &height);
|
wlr_output_effective_resolution(wlr_output, &width, &height);
|
||||||
|
|
||||||
wlr_output_make_current(wlr_output, NULL);
|
wlr_output_attach_render(wlr_output, NULL);
|
||||||
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});
|
||||||
|
|
||||||
|
@ -129,7 +130,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_renderer_end(sample->renderer);
|
wlr_renderer_end(sample->renderer);
|
||||||
wlr_output_swap_buffers(wlr_output, NULL, NULL);
|
wlr_output_commit(wlr_output);
|
||||||
sample->last_frame = now;
|
sample->last_frame = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,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;
|
||||||
|
@ -267,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) {
|
||||||
|
@ -286,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");
|
||||||
|
@ -354,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);
|
||||||
}
|
}
|
||||||
|
@ -365,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) {
|
||||||
|
@ -102,7 +101,7 @@ static size_t utf8_offset(char *utf8_str, size_t byte_offset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: would be nicer to have this text display inside the window
|
// TODO: would be nicer to have this text display inside the window
|
||||||
static void show_status() {
|
static void show_status(void) {
|
||||||
printf("State %d:", serial);
|
printf("State %d:", serial);
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
printf(" disabled");
|
printf(" disabled");
|
||||||
|
@ -137,8 +136,7 @@ static void show_status() {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((unsigned)current.preedit.cursor_begin > strlen(preedit_text)
|
if ((unsigned)current.preedit.cursor_begin > strlen(preedit_text)) {
|
||||||
|| (unsigned)current.preedit.cursor_begin > strlen(preedit_text)) {
|
|
||||||
printf("Cursor out of bounds\n");
|
printf("Cursor out of bounds\n");
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
@ -345,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) {
|
||||||
|
@ -365,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 =
|
||||||
|
@ -380,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>
|
||||||
|
@ -7,15 +7,16 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-protocol.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wayland-server.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"
|
||||||
|
@ -23,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;
|
||||||
|
@ -73,23 +75,20 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
int32_t width, height;
|
int32_t width, height;
|
||||||
wlr_output_effective_resolution(wlr_output, &width, &height);
|
wlr_output_effective_resolution(wlr_output, &width, &height);
|
||||||
|
|
||||||
wlr_output_make_current(wlr_output, NULL);
|
wlr_output_attach_render(wlr_output, NULL);
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_renderer_end(sample->renderer);
|
wlr_renderer_end(sample->renderer);
|
||||||
wlr_output_swap_buffers(wlr_output, NULL, NULL);
|
wlr_output_commit(wlr_output);
|
||||||
sample->last_frame = now;
|
sample->last_frame = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,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;
|
||||||
|
@ -180,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) {
|
||||||
|
@ -199,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");
|
||||||
|
@ -249,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);
|
||||||
}
|
}
|
||||||
|
@ -260,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;
|
||||||
|
}
|
92
glgen.sh
92
glgen.sh
|
@ -1,92 +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 <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,67 +1,54 @@
|
||||||
#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>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <wayland-server.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wayland-util.h>
|
#include <wayland-util.h>
|
||||||
#include <wlr/backend/drm.h>
|
#include <wlr/backend/drm.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/render/egl.h>
|
#include <wlr/render/drm_format_set.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;
|
||||||
|
|
||||||
uint32_t possible_crtcs;
|
/* Only initialized on multi-GPU setups */
|
||||||
|
|
||||||
struct wlr_drm_surface surf;
|
|
||||||
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_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;
|
||||||
|
|
||||||
union {
|
struct wlr_drm_plane *primary;
|
||||||
struct {
|
struct wlr_drm_plane *cursor;
|
||||||
struct wlr_drm_plane *overlay;
|
|
||||||
struct wlr_drm_plane *primary;
|
|
||||||
struct wlr_drm_plane *cursor;
|
|
||||||
};
|
|
||||||
struct wlr_drm_plane *planes[3];
|
|
||||||
};
|
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -70,54 +57,45 @@ struct wlr_drm_backend {
|
||||||
struct wlr_drm_backend *parent;
|
struct wlr_drm_backend *parent;
|
||||||
const struct wlr_drm_interface *iface;
|
const struct wlr_drm_interface *iface;
|
||||||
clockid_t clock;
|
clockid_t clock;
|
||||||
|
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;
|
||||||
size_t num_planes;
|
|
||||||
struct wlr_drm_plane *planes;
|
|
||||||
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
size_t num_overlay_planes;
|
|
||||||
size_t num_primary_planes;
|
|
||||||
size_t num_cursor_planes;
|
|
||||||
};
|
|
||||||
size_t num_type_planes[3];
|
|
||||||
};
|
|
||||||
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
struct wlr_drm_plane *overlay_planes;
|
|
||||||
struct wlr_drm_plane *primary_planes;
|
|
||||||
struct wlr_drm_plane *cursor_planes;
|
|
||||||
};
|
|
||||||
struct wlr_drm_plane *type_planes[3];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
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
|
||||||
WLR_DRM_CONN_NEEDS_MODESET,
|
WLR_DRM_CONN_NEEDS_MODESET,
|
||||||
WLR_DRM_CONN_CLEANUP,
|
WLR_DRM_CONN_CLEANUP,
|
||||||
WLR_DRM_CONN_CONNECTED,
|
WLR_DRM_CONN_CONNECTED,
|
||||||
// Connector disappeared, waiting for being destroyed on next page-flip
|
|
||||||
WLR_DRM_CONN_DISAPPEARED,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_drm_mode {
|
struct wlr_drm_mode {
|
||||||
|
@ -125,27 +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;
|
||||||
|
|
||||||
|
/* CRTC ID if a page-flip is pending, zero otherwise.
|
||||||
|
*
|
||||||
|
* We've asked for a state change in the kernel, and yet to receive a
|
||||||
|
* notification for its completion. Currently, the kernel only has a
|
||||||
|
* queue length of 1, and no way to modify your submissions after
|
||||||
|
* they're sent.
|
||||||
|
*/
|
||||||
|
uint32_t pending_page_flip_crtc;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_drm_backend *get_drm_backend_from_backend(
|
struct wlr_drm_backend *get_drm_backend_from_backend(
|
||||||
|
@ -153,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];
|
||||||
};
|
};
|
||||||
|
@ -44,6 +47,7 @@ union wlr_drm_plane_props {
|
||||||
struct {
|
struct {
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
uint32_t rotation; // Not guaranteed to exist
|
uint32_t rotation; // Not guaranteed to exist
|
||||||
|
uint32_t in_formats; // Not guaranteed to exist
|
||||||
|
|
||||||
// atomic-modesetting only
|
// atomic-modesetting only
|
||||||
|
|
||||||
|
@ -57,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[12];
|
uint32_t props[14];
|
||||||
};
|
};
|
||||||
|
|
||||||
bool get_drm_connector_props(int fd, uint32_t id,
|
bool get_drm_connector_props(int fd, uint32_t id,
|
||||||
|
@ -68,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,33 +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);
|
||||||
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs);
|
void drm_plane_finish_surface(struct wlr_drm_plane *plane);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -13,8 +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);
|
|
||||||
|
|
||||||
// Part of match_obj
|
// Part of match_obj
|
||||||
enum {
|
enum {
|
||||||
|
|
|
@ -8,10 +8,9 @@
|
||||||
|
|
||||||
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;
|
||||||
struct wl_list input_devices;
|
struct wl_list input_devices;
|
||||||
struct wl_listener display_destroy;
|
struct wl_listener display_destroy;
|
||||||
bool started;
|
bool started;
|
||||||
|
@ -23,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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include <wlr/backend/libinput.h>
|
#include <wlr/backend/libinput.h>
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
#include <wlr/interfaces/wlr_input_device.h>
|
||||||
#include <wlr/types/wlr_input_device.h>
|
#include <wlr/types/wlr_input_device.h>
|
||||||
#include <wlr/types/wlr_list.h>
|
|
||||||
|
|
||||||
struct wlr_libinput_backend {
|
struct wlr_libinput_backend {
|
||||||
struct wlr_backend backend;
|
struct wlr_backend backend;
|
||||||
|
@ -19,14 +18,15 @@ struct wlr_libinput_backend {
|
||||||
struct wl_event_source *input_event;
|
struct wl_event_source *input_event;
|
||||||
|
|
||||||
struct wl_listener display_destroy;
|
struct wl_listener display_destroy;
|
||||||
|
struct wl_listener session_destroy;
|
||||||
struct wl_listener session_signal;
|
struct wl_listener session_signal;
|
||||||
|
|
||||||
struct wlr_list wlr_device_lists; // list of struct wl_list
|
struct wl_array wlr_device_lists; // struct wl_list *
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_libinput_input_device {
|
struct wlr_libinput_input_device {
|
||||||
struct wlr_input_device wlr_input_device;
|
struct wlr_input_device wlr_input_device;
|
||||||
|
struct wl_list link;
|
||||||
struct libinput_device *handle;
|
struct libinput_device *handle;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -66,6 +66,10 @@ void handle_pointer_pinch_update(struct libinput_event *event,
|
||||||
struct libinput_device *device);
|
struct libinput_device *device);
|
||||||
void handle_pointer_pinch_end(struct libinput_event *event,
|
void handle_pointer_pinch_end(struct libinput_event *event,
|
||||||
struct libinput_device *device);
|
struct libinput_device *device);
|
||||||
|
void handle_pointer_hold_begin(struct libinput_event *event,
|
||||||
|
struct libinput_device *device);
|
||||||
|
void handle_pointer_hold_end(struct libinput_event *event,
|
||||||
|
struct libinput_device *device);
|
||||||
|
|
||||||
struct wlr_switch *create_libinput_switch(
|
struct wlr_switch *create_libinput_switch(
|
||||||
struct libinput_device *device);
|
struct libinput_device *device);
|
||||||
|
@ -82,6 +86,8 @@ void handle_touch_motion(struct libinput_event *event,
|
||||||
struct libinput_device *device);
|
struct libinput_device *device);
|
||||||
void handle_touch_cancel(struct libinput_event *event,
|
void handle_touch_cancel(struct libinput_event *event,
|
||||||
struct libinput_device *device);
|
struct libinput_device *device);
|
||||||
|
void handle_touch_frame(struct libinput_event *event,
|
||||||
|
struct libinput_device *device);
|
||||||
|
|
||||||
struct wlr_tablet *create_libinput_tablet(
|
struct wlr_tablet *create_libinput_tablet(
|
||||||
struct libinput_device *device);
|
struct libinput_device *device);
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
#ifndef BACKEND_NOOP_H
|
|
||||||
#define BACKEND_NOOP_H
|
|
||||||
|
|
||||||
#include <wlr/backend/noop.h>
|
|
||||||
#include <wlr/backend/interface.h>
|
|
||||||
|
|
||||||
struct wlr_noop_backend {
|
|
||||||
struct wlr_backend backend;
|
|
||||||
struct wl_display *display;
|
|
||||||
struct wl_list outputs;
|
|
||||||
bool started;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_noop_output {
|
|
||||||
struct wlr_output wlr_output;
|
|
||||||
|
|
||||||
struct wlr_noop_backend *backend;
|
|
||||||
struct wl_list link;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_noop_backend *noop_backend_from_backend(
|
|
||||||
struct wlr_backend *wlr_backend);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,12 +0,0 @@
|
||||||
#ifndef BACKEND_SESSION_DIRECT_IPC_H
|
|
||||||
#define BACKEND_SESSION_DIRECT_IPC_H
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
int direct_ipc_open(int sock, const char *path);
|
|
||||||
void direct_ipc_setmaster(int sock, int fd);
|
|
||||||
void direct_ipc_dropmaster(int sock, int fd);
|
|
||||||
void direct_ipc_finish(int sock, pid_t pid);
|
|
||||||
int direct_ipc_init(pid_t *pid_out);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef BACKEND_SESSION_SESSION_H
|
||||||
|
#define BACKEND_SESSION_SESSION_H
|
||||||
|
|
||||||
|
struct wlr_session;
|
||||||
|
|
||||||
|
struct wlr_session *libseat_session_create(struct wl_display *disp);
|
||||||
|
void libseat_session_destroy(struct wlr_session *base);
|
||||||
|
int libseat_session_open_device(struct wlr_session *base, const char *path);
|
||||||
|
void libseat_session_close_device(struct wlr_session *base, int fd);
|
||||||
|
bool libseat_change_vt(struct wlr_session *base, unsigned vt);
|
||||||
|
|
||||||
|
void session_init(struct wlr_session *session);
|
||||||
|
|
||||||
|
struct wlr_device *session_open_if_kms(struct wlr_session *restrict session,
|
||||||
|
const char *restrict path);
|
||||||
|
|
||||||
|
#endif
|
|
@ -4,14 +4,12 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-server-core.h>
|
||||||
#include <wayland-server.h>
|
|
||||||
#include <wayland-util.h>
|
|
||||||
|
|
||||||
#include <wlr/backend/wayland.h>
|
#include <wlr/backend/wayland.h>
|
||||||
#include <wlr/render/egl.h>
|
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_box.h>
|
#include <wlr/types/wlr_pointer.h>
|
||||||
|
#include <wlr/render/drm_format_set.h>
|
||||||
|
|
||||||
struct wlr_wl_backend {
|
struct wlr_wl_backend {
|
||||||
struct wlr_backend backend;
|
struct wlr_backend backend;
|
||||||
|
@ -21,22 +19,48 @@ struct wlr_wl_backend {
|
||||||
struct wl_display *local_display;
|
struct wl_display *local_display;
|
||||||
struct wl_list devices;
|
struct wl_list devices;
|
||||||
struct wl_list outputs;
|
struct wl_list outputs;
|
||||||
struct wlr_egl egl;
|
int drm_fd;
|
||||||
struct wlr_renderer *renderer;
|
struct wl_list buffers; // wlr_wl_buffer.link
|
||||||
size_t requested_outputs;
|
size_t requested_outputs;
|
||||||
|
size_t last_output_num;
|
||||||
struct wl_listener local_display_destroy;
|
struct wl_listener local_display_destroy;
|
||||||
|
char *activation_token;
|
||||||
|
|
||||||
/* remote state */
|
/* remote state */
|
||||||
struct wl_display *remote_display;
|
struct wl_display *remote_display;
|
||||||
struct wl_event_source *remote_display_src;
|
struct wl_event_source *remote_display_src;
|
||||||
struct wl_registry *registry;
|
struct wl_registry *registry;
|
||||||
struct wl_compositor *compositor;
|
struct wl_compositor *compositor;
|
||||||
struct xdg_wm_base *xdg_wm_base;
|
struct xdg_wm_base *xdg_wm_base;
|
||||||
|
struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1;
|
||||||
|
struct zwp_pointer_gestures_v1 *zwp_pointer_gestures_v1;
|
||||||
|
struct wp_presentation *presentation;
|
||||||
struct wl_shm *shm;
|
struct wl_shm *shm;
|
||||||
struct wl_seat *seat;
|
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1;
|
||||||
struct wl_pointer *pointer;
|
struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1;
|
||||||
struct wl_keyboard *keyboard;
|
struct wl_list seats; // wlr_wl_seat.link
|
||||||
struct wlr_wl_pointer *current_pointer;
|
struct zwp_tablet_manager_v2 *tablet_manager;
|
||||||
char *seat_name;
|
clockid_t presentation_clock;
|
||||||
|
struct wlr_drm_format_set shm_formats;
|
||||||
|
struct wlr_drm_format_set linux_dmabuf_v1_formats;
|
||||||
|
struct wl_drm *legacy_drm;
|
||||||
|
struct xdg_activation_v1 *activation_v1;
|
||||||
|
char *drm_render_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_wl_buffer {
|
||||||
|
struct wlr_buffer *buffer;
|
||||||
|
struct wl_buffer *wl_buffer;
|
||||||
|
bool released;
|
||||||
|
struct wl_list link; // wlr_wl_backend.buffers
|
||||||
|
struct wl_listener buffer_destroy;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_wl_presentation_feedback {
|
||||||
|
struct wlr_wl_output *output;
|
||||||
|
struct wl_list link;
|
||||||
|
struct wp_presentation_feedback *feedback;
|
||||||
|
uint32_t commit_seq;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_wl_output {
|
struct wlr_wl_output {
|
||||||
|
@ -49,23 +73,25 @@ struct wlr_wl_output {
|
||||||
struct wl_callback *frame_callback;
|
struct wl_callback *frame_callback;
|
||||||
struct xdg_surface *xdg_surface;
|
struct xdg_surface *xdg_surface;
|
||||||
struct xdg_toplevel *xdg_toplevel;
|
struct xdg_toplevel *xdg_toplevel;
|
||||||
struct wl_egl_window *egl_window;
|
struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1;
|
||||||
EGLSurface egl_surface;
|
struct wl_list presentation_feedbacks;
|
||||||
|
|
||||||
uint32_t enter_serial;
|
uint32_t enter_serial;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
struct wlr_wl_pointer *pointer;
|
||||||
struct wl_surface *surface;
|
struct wl_surface *surface;
|
||||||
struct wl_egl_window *egl_window;
|
|
||||||
int32_t hotspot_x, hotspot_y;
|
int32_t hotspot_x, hotspot_y;
|
||||||
int32_t width, height;
|
|
||||||
} cursor;
|
} cursor;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_wl_input_device {
|
struct wlr_wl_input_device {
|
||||||
struct wlr_input_device wlr_input_device;
|
struct wlr_input_device wlr_input_device;
|
||||||
|
struct wl_list link;
|
||||||
|
uint32_t fingers;
|
||||||
|
|
||||||
struct wlr_wl_backend *backend;
|
struct wlr_wl_backend *backend;
|
||||||
|
struct wlr_wl_seat *seat;
|
||||||
void *resource;
|
void *resource;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -74,6 +100,10 @@ struct wlr_wl_pointer {
|
||||||
|
|
||||||
struct wlr_wl_input_device *input_device;
|
struct wlr_wl_input_device *input_device;
|
||||||
struct wl_pointer *wl_pointer;
|
struct wl_pointer *wl_pointer;
|
||||||
|
struct zwp_pointer_gesture_swipe_v1 *gesture_swipe;
|
||||||
|
struct zwp_pointer_gesture_pinch_v1 *gesture_pinch;
|
||||||
|
struct zwp_pointer_gesture_hold_v1 *gesture_hold;
|
||||||
|
struct zwp_relative_pointer_v1 *relative_pointer;
|
||||||
enum wlr_axis_source axis_source;
|
enum wlr_axis_source axis_source;
|
||||||
int32_t axis_discrete;
|
int32_t axis_discrete;
|
||||||
struct wlr_wl_output *output;
|
struct wlr_wl_output *output;
|
||||||
|
@ -81,12 +111,35 @@ struct wlr_wl_pointer {
|
||||||
struct wl_listener output_destroy;
|
struct wl_listener output_destroy;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct wlr_wl_seat {
|
||||||
|
struct wl_seat *wl_seat;
|
||||||
|
|
||||||
|
struct wl_list link; // wlr_wl_backend.seats
|
||||||
|
char *name;
|
||||||
|
struct wl_touch *touch;
|
||||||
|
struct wl_pointer *pointer;
|
||||||
|
struct wl_keyboard *keyboard;
|
||||||
|
|
||||||
|
struct wlr_wl_backend *backend;
|
||||||
|
struct wlr_wl_pointer *active_pointer;
|
||||||
|
};
|
||||||
|
|
||||||
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend);
|
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend);
|
||||||
void update_wl_output_cursor(struct wlr_wl_output *output);
|
void update_wl_output_cursor(struct wlr_wl_output *output);
|
||||||
struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer);
|
struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer);
|
||||||
void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *output);
|
void create_wl_pointer(struct wlr_wl_seat *seat, struct wlr_wl_output *output);
|
||||||
void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *wl);
|
void create_wl_keyboard(struct wlr_wl_seat *seat);
|
||||||
|
void create_wl_touch(struct wlr_wl_seat *seat);
|
||||||
|
struct wlr_wl_input_device *create_wl_input_device(
|
||||||
|
struct wlr_wl_seat *seat, enum wlr_input_device_type type);
|
||||||
|
bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl);
|
||||||
|
void destroy_wl_seats(struct wlr_wl_backend *wl);
|
||||||
|
void destroy_wl_buffer(struct wlr_wl_buffer *buffer);
|
||||||
|
|
||||||
extern const struct wl_seat_listener seat_listener;
|
extern const struct wl_seat_listener seat_listener;
|
||||||
|
|
||||||
|
struct wlr_wl_tablet_seat *wl_add_tablet_seat(
|
||||||
|
struct zwp_tablet_manager_v2 *manager,
|
||||||
|
struct wlr_wl_seat *seat);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue