Compare commits
1059 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
f1a867ea12 | |
![]() |
15589c9997 | |
![]() |
280594354c | |
![]() |
a9377db1a9 | |
![]() |
8f35a9ea8d | |
![]() |
cfe5bf2073 | |
![]() |
09b45a9937 | |
![]() |
d7ad0083a5 | |
![]() |
d2c648f6cc | |
![]() |
13b2c43b2b | |
![]() |
3fd9026a01 | |
![]() |
11d3f27e97 | |
![]() |
8314cdf89d | |
![]() |
6f2cab89d9 | |
![]() |
ad8c947134 | |
![]() |
e597a37429 | |
![]() |
240af64c82 | |
![]() |
585dd069f7 | |
![]() |
4c8544c44f | |
![]() |
a41fac2d75 | |
![]() |
02d43147b5 | |
![]() |
07dcede820 | |
![]() |
4c86653cd5 | |
![]() |
eb7b04e320 | |
![]() |
bdc24ff8c7 | |
![]() |
b35edf8d30 | |
![]() |
60a483471c | |
![]() |
0773db0710 | |
![]() |
9e57469c2f | |
![]() |
f0fad81963 | |
![]() |
7590c6d386 | |
![]() |
5a0ecdba33 | |
![]() |
c5fb21fd8b | |
![]() |
2601d06f5b | |
![]() |
a7470f4b4a | |
![]() |
44ea9ee959 | |
![]() |
059f66af9a | |
![]() |
c958a75e2b | |
![]() |
47ee47dcc8 | |
![]() |
a97c748d1c | |
![]() |
1384a8d3fd | |
![]() |
d6b3baec8f | |
![]() |
53221fd43f | |
![]() |
28b396f44c | |
![]() |
6a2bc79dae | |
![]() |
03eb688818 | |
![]() |
92ceb00b37 | |
![]() |
4453ce7eef | |
![]() |
3b3c75a8e9 | |
![]() |
ad4e27f5ef | |
![]() |
21a0ae31b7 | |
![]() |
a169114fee | |
![]() |
0eb02c8b2c | |
![]() |
b1bd3ff630 | |
![]() |
1dbef2d5ed | |
![]() |
1ee3062e45 | |
![]() |
944be3d55a | |
![]() |
c6ac6d3caa | |
![]() |
6540112bf3 | |
![]() |
7d486ddce7 | |
![]() |
85badd901d | |
![]() |
7eb9f75af2 | |
![]() |
b745f83339 | |
![]() |
9293df2681 | |
![]() |
2fb485d67d | |
![]() |
16f6473f56 | |
![]() |
9bcba5334c | |
![]() |
268d066f4f | |
![]() |
9276d28b3d | |
![]() |
2db79aaca8 | |
![]() |
1a916293dc | |
![]() |
92f1fabe5b | |
![]() |
b107295c03 | |
![]() |
e316694675 | |
![]() |
8a5bae0f86 | |
![]() |
ad31fd6b18 | |
![]() |
18e9adf5b7 | |
![]() |
1dee66a36c | |
![]() |
1b1af29de9 | |
![]() |
940215b624 | |
![]() |
559b0cccf5 | |
![]() |
7fca508619 | |
![]() |
0fc6c6800e | |
![]() |
c761571a43 | |
![]() |
e16069ae77 | |
![]() |
4f5fce8b7a | |
![]() |
170b138026 | |
![]() |
7e2d650c8f | |
![]() |
af16c9b243 | |
![]() |
1a7c94b692 | |
![]() |
ba599a2aa0 | |
![]() |
64bbbde249 | |
![]() |
f89a1f9766 | |
![]() |
a1443cb43e | |
![]() |
2d4bbe917c | |
![]() |
8c067c77f4 | |
![]() |
68b7448847 | |
![]() |
178d49093c | |
![]() |
979ac5bd50 | |
![]() |
29a5541ccf | |
![]() |
e1192a6934 | |
![]() |
d00d00c7fc | |
![]() |
6b4ef4170c | |
![]() |
d595b8f8aa | |
![]() |
55dc891190 | |
![]() |
5c6e51d75e | |
![]() |
927fb42abc | |
![]() |
3461d5bc3a | |
![]() |
e5528e9317 | |
![]() |
786b3ffbae | |
![]() |
07f266e7c7 | |
![]() |
5fe9e9c756 | |
![]() |
8256f8e14c | |
![]() |
5d03d58dc1 | |
![]() |
a7c4175099 | |
![]() |
fef2bfb93f | |
![]() |
ad98069618 | |
![]() |
ec86640f44 | |
![]() |
c2141dc4ef | |
![]() |
f46174453d | |
![]() |
21a45c2700 | |
![]() |
4db2742e96 | |
![]() |
312f88872b | |
![]() |
ad6700dd88 | |
![]() |
6543ab3caa | |
![]() |
c2ca1cc33d | |
![]() |
8fb3348600 | |
![]() |
877d220bfe | |
![]() |
f37c15b0a2 | |
![]() |
6be238e622 | |
![]() |
35a7e30952 | |
![]() |
131c5399b3 | |
![]() |
d65bc400e9 | |
![]() |
1c3b4cc548 | |
![]() |
d6360cc4ce | |
![]() |
64b02cf464 | |
![]() |
6c69f0a4d1 | |
![]() |
f9b9149ba4 | |
![]() |
b202ecf711 | |
![]() |
3c857a3375 | |
![]() |
68d1e2f42a | |
![]() |
70231f2431 | |
![]() |
cd2d5b07d5 | |
![]() |
cfbee55c2a | |
![]() |
61332caa20 | |
![]() |
0c5ee47892 | |
![]() |
4b58f6c207 | |
![]() |
57d8bd6856 | |
![]() |
57f862eb4d | |
![]() |
fa5b3e920b | |
![]() |
cfca23f9b3 | |
![]() |
65b4968c80 | |
![]() |
a6a85466cd | |
![]() |
f6540283d5 | |
![]() |
83502e29a9 | |
![]() |
b2ff465490 | |
![]() |
bffae41983 | |
![]() |
3bf97e87e5 | |
![]() |
0cd6f5cd06 | |
![]() |
6166060f9b | |
![]() |
76739f1f2f | |
![]() |
54473f03ba | |
![]() |
ecb6460277 | |
![]() |
a167657214 | |
![]() |
47d873db94 | |
![]() |
3f48b22f1a | |
![]() |
8c25784070 | |
![]() |
c9627dc60f | |
![]() |
a56b16e2ff | |
![]() |
eea79bddd8 | |
![]() |
7622921de6 | |
![]() |
b4822389c3 | |
![]() |
8c149bb6b7 | |
![]() |
fd746b46e5 | |
![]() |
cadf16b12e | |
![]() |
01a9eb2327 | |
![]() |
d048f9aad8 | |
![]() |
79238b06cf | |
![]() |
79ef1be57b | |
![]() |
8b843e647c | |
![]() |
ee4cf67371 | |
![]() |
1c3a987d74 | |
![]() |
26d9361695 | |
![]() |
31a3961c4c | |
![]() |
a1f30edaf6 | |
![]() |
e84ca82c9a | |
![]() |
f700de56f8 | |
![]() |
40839e3347 | |
![]() |
2ca81bbe32 | |
![]() |
d7772031b1 | |
![]() |
1195936a68 | |
![]() |
f4f0660dbb | |
![]() |
4ab679f2fd | |
![]() |
5d90f232bd | |
![]() |
0ce7a40850 | |
![]() |
2cf36e640f | |
![]() |
cefcdf7db5 | |
![]() |
64ddeff3bd | |
![]() |
ce57af62ec | |
![]() |
ba7b23c41d | |
![]() |
59f1f7ee9f | |
![]() |
c164ff467c | |
![]() |
ec1ebb6fb8 | |
![]() |
1d05f98e4b | |
![]() |
6a59c4defc | |
![]() |
804ecb0e22 | |
![]() |
97c1d335e5 | |
![]() |
09d4f3fda3 | |
![]() |
b0ccacc983 | |
![]() |
fa351403ef | |
![]() |
a76ae07ee1 | |
![]() |
447994581b | |
![]() |
5e122cb6c0 | |
![]() |
ae376766ef | |
![]() |
862dae455a | |
![]() |
d0e07ba3e8 | |
![]() |
669e90325c | |
![]() |
9e3db94718 | |
![]() |
2099409189 | |
![]() |
3f0627172d | |
![]() |
73425b13a4 | |
![]() |
cc031839db | |
![]() |
e678ebef29 | |
![]() |
586a775d5b | |
![]() |
d655b5a994 | |
![]() |
40a9f08dd3 | |
![]() |
1e3d50e3ff | |
![]() |
0c4f46739d | |
![]() |
df9b0bd8f0 | |
![]() |
3ebbe28b64 | |
![]() |
5362105f27 | |
![]() |
572e07b130 | |
![]() |
01d3f22633 | |
![]() |
5e79956933 | |
![]() |
1ab1a5f297 | |
![]() |
14bbe38b31 | |
![]() |
7b547bf61d | |
![]() |
e7e6dee79b | |
![]() |
dd36387c1f | |
![]() |
d737d1dbcb | |
![]() |
84633badc7 | |
![]() |
29122f1df6 | |
![]() |
0e934273bb | |
![]() |
b232e80319 | |
![]() |
1866e70fe3 | |
![]() |
c65f01e5dc | |
![]() |
7ba6c08fa7 | |
![]() |
2e9e6893bb | |
![]() |
0f41239515 | |
![]() |
3465ecac16 | |
![]() |
7bf40c3501 | |
![]() |
79357db409 | |
![]() |
dcd2f7b15a | |
![]() |
202356ba89 | |
![]() |
f4232bc391 | |
![]() |
1ca7524853 | |
![]() |
bc52189e20 | |
![]() |
1d9824d0a2 | |
![]() |
7880f879a3 | |
![]() |
bbf4f46b28 | |
![]() |
f5ac8968d8 | |
![]() |
aae9eb9c65 | |
![]() |
c919c91063 | |
![]() |
35ce8a6208 | |
![]() |
22dffa4714 | |
![]() |
ebe6f58d9a | |
![]() |
0b2596b604 | |
![]() |
b59ba5b9b7 | |
![]() |
de88370577 | |
![]() |
fa895828de | |
![]() |
9af15a979d | |
![]() |
df60eef0db | |
![]() |
856925bcc3 | |
![]() |
de15b87ea7 | |
![]() |
982a2e3a37 | |
![]() |
711c5bb43f | |
![]() |
7710b44f1d | |
![]() |
54f7079100 | |
![]() |
47484bb141 | |
![]() |
96c0d800b8 | |
![]() |
29a93a2e57 | |
![]() |
77794e1333 | |
![]() |
805435d157 | |
![]() |
4ebb21e8c3 | |
![]() |
886f0e852b | |
![]() |
0ce58c7cbf | |
![]() |
9fef6380d4 | |
![]() |
e04ec0ce29 | |
![]() |
b167d1f56a | |
![]() |
8d5ebb5e15 | |
![]() |
22d0631b1b | |
![]() |
589479c24a | |
![]() |
7a2e6bb58e | |
![]() |
d2d92cdfd2 | |
![]() |
a3c763b8ef | |
![]() |
dcda15e5e3 | |
![]() |
653938e456 | |
![]() |
26a0038d0c | |
![]() |
b1ed2bb3e7 | |
![]() |
200e46bec6 | |
![]() |
325daecace | |
![]() |
43c21c390b | |
![]() |
013e69988f | |
![]() |
ba8c52564e | |
![]() |
ef41fa8e8f | |
![]() |
4c144958c7 | |
![]() |
2dd71603dd | |
![]() |
b2e1ede6e5 | |
![]() |
ea5b79dbf6 | |
![]() |
01f3f185d8 | |
![]() |
c41315564c | |
![]() |
46f5951820 | |
![]() |
6ec7e919fb | |
![]() |
24807dfa50 | |
![]() |
d55fa39ebf | |
![]() |
0db46e3966 | |
![]() |
b3f7924539 | |
![]() |
cac430fdbb | |
![]() |
4e01d2c8b7 | |
![]() |
33fe211471 | |
![]() |
14c08422ca | |
![]() |
df9f8f8346 | |
![]() |
fb28d75ad8 | |
![]() |
6a44cce527 | |
![]() |
219f110093 | |
![]() |
4470e82baa | |
![]() |
c8de1aa677 | |
![]() |
11cd27c793 | |
![]() |
5980945ee4 | |
![]() |
38c4980b0d | |
![]() |
6acc652487 | |
![]() |
801fecf821 | |
![]() |
8f0425d91c | |
![]() |
93d810aaad | |
![]() |
a7e26a5770 | |
![]() |
b976c274fd | |
![]() |
aabadcee6d | |
![]() |
04643fa3d8 | |
![]() |
7916352f5b | |
![]() |
cdbe119789 | |
![]() |
b2193f370d | |
![]() |
e4e5c52300 | |
![]() |
55e5d50074 | |
![]() |
354a71855e | |
![]() |
336b2fd2f6 | |
![]() |
26b15feb61 | |
![]() |
20d5f9bc6e | |
![]() |
445567f54e | |
![]() |
3232a63ac1 | |
![]() |
06ba20cc2e | |
![]() |
6b5571b023 | |
![]() |
55cccf3779 | |
![]() |
ce9531d549 | |
![]() |
4e4186aa48 | |
![]() |
a2185d0836 | |
![]() |
5c93c4251b | |
![]() |
75ab5b8353 | |
![]() |
eb331a5df4 | |
![]() |
77cf49aa72 | |
![]() |
f5120e570b | |
![]() |
4037f093db | |
![]() |
e2611d710f | |
![]() |
31f80d18f2 | |
![]() |
2e92fca017 | |
![]() |
69b8c91655 | |
![]() |
351f613ef4 | |
![]() |
e804b45d86 | |
![]() |
4dffc09aaa | |
![]() |
4a70793e2b | |
![]() |
c404c76a8b | |
![]() |
5ff705a124 | |
![]() |
24f4e268a9 | |
![]() |
529ccbbd18 | |
![]() |
068f1c2b54 | |
![]() |
1470cfc790 | |
![]() |
bc29e09d88 | |
![]() |
98aa044508 | |
![]() |
7949032411 | |
![]() |
38694bbe1a | |
![]() |
c14c61f745 | |
![]() |
1f32171ac9 | |
![]() |
1da8b61864 | |
![]() |
fba948b39f | |
![]() |
48197a4f5b | |
![]() |
7bd6b92110 | |
![]() |
eed7943939 | |
![]() |
09b32a1e68 | |
![]() |
9ca7b9b1cf | |
![]() |
26afbfb6fa | |
![]() |
01d224fa22 | |
![]() |
98747246cd | |
![]() |
3ed535dd67 | |
![]() |
bca99d0b4c | |
![]() |
499d006e3a | |
![]() |
2120e18729 | |
![]() |
9ba95ecd5d | |
![]() |
52b92904de | |
![]() |
36001e5ee0 | |
![]() |
1ab5453a90 | |
![]() |
5e94f5429a | |
![]() |
f7a36a43d9 | |
![]() |
344e56dd77 | |
![]() |
99324c12fd | |
![]() |
64e0b693c7 | |
![]() |
b2f181fe80 | |
![]() |
89b6a67232 | |
![]() |
749b80ab49 | |
![]() |
881858f8dd | |
![]() |
1a4606d990 | |
![]() |
bf4c5241a0 | |
![]() |
ab1350b6a6 | |
![]() |
9ca69cf50c | |
![]() |
4c6000b3e1 | |
![]() |
32addf4d59 | |
![]() |
672b662dd2 | |
![]() |
1573d04b5a | |
![]() |
485769ddd9 | |
![]() |
cfe001df6f | |
![]() |
2014f9a382 | |
![]() |
00c1e9850b | |
![]() |
f9e746dff6 | |
![]() |
7d557784a1 | |
![]() |
15e23b825e | |
![]() |
17029cd54f | |
![]() |
d20d84cfa3 | |
![]() |
61ca42cd2c | |
![]() |
c26101a9fe | |
![]() |
786dc09704 | |
![]() |
d9d733924c | |
![]() |
9cd067e4fd | |
![]() |
b29b3db26d | |
![]() |
2dbf2dd995 | |
![]() |
c05a26f5c4 | |
![]() |
693603d87a | |
![]() |
17c857ce66 | |
![]() |
c3c3b903b1 | |
![]() |
608cc0e7f8 | |
![]() |
bd2728fac1 | |
![]() |
9a74d70ba4 | |
![]() |
d9ee6d98ae | |
![]() |
4448e23363 | |
![]() |
9c2035ca63 | |
![]() |
da34191115 | |
![]() |
2b0b04235c | |
![]() |
8c6ebee318 | |
![]() |
38e2bf34cf | |
![]() |
cbab4b46c1 | |
![]() |
baaa1db5d1 | |
![]() |
883acb1f58 | |
![]() |
fdd338f02b | |
![]() |
3d5e10e9bb | |
![]() |
7f20621b25 | |
![]() |
57a79608da | |
![]() |
7c3a27fb43 | |
![]() |
298a8ca8c5 | |
![]() |
7bb250826e | |
![]() |
28d6b5230a | |
![]() |
5272c24301 | |
![]() |
b4e15b28b6 | |
![]() |
4c384d1e9f | |
![]() |
0c49ae5360 | |
![]() |
a4be9859b7 | |
![]() |
4d72263b31 | |
![]() |
20960c2fea | |
![]() |
b5c127519f | |
![]() |
7ac61516b2 | |
![]() |
9165ba3b20 | |
![]() |
5c2314a6bd | |
![]() |
9e9da67fc0 | |
![]() |
c8e0568022 | |
![]() |
4be92db483 | |
![]() |
bd84f1606d | |
![]() |
8d3c06a178 | |
![]() |
d1fdb69946 | |
![]() |
cd2a4de82f | |
![]() |
7894db7504 | |
![]() |
9d3e87bd9d | |
![]() |
ea9691a2f5 | |
![]() |
0bcb9a0ddd | |
![]() |
399a955eb2 | |
![]() |
c310ba782a | |
![]() |
d59451adcf | |
![]() |
5101579eb8 | |
![]() |
8c8c5b9b0e | |
![]() |
8428b4603c | |
![]() |
c6c99c1740 | |
![]() |
686ea6374b | |
![]() |
866afcb94b | |
![]() |
d71243aa07 | |
![]() |
a2cccf0d75 | |
![]() |
9b5ee76683 | |
![]() |
0fef229587 | |
![]() |
6a4a68dea4 | |
![]() |
221c6aaf2b | |
![]() |
08439957d0 | |
![]() |
fb19774730 | |
![]() |
d0b69f3503 | |
![]() |
3c82d5a919 | |
![]() |
831ca28f93 | |
![]() |
5777b3c257 | |
![]() |
8bf1f9203b | |
![]() |
489cd31ee9 | |
![]() |
4f322e24d3 | |
![]() |
232984ebd5 | |
![]() |
111657ad04 | |
![]() |
5c16cdd4ed | |
![]() |
012c64b054 | |
![]() |
236baa63df | |
![]() |
8934a31dad | |
![]() |
442648edc0 | |
![]() |
44b72823c2 | |
![]() |
6412fd2c34 | |
![]() |
b4b9a28b6f | |
![]() |
a7e7923d6b | |
![]() |
630ba5fab4 | |
![]() |
246824aa61 | |
![]() |
60d36c0753 | |
![]() |
fb6ce3707a | |
![]() |
3ebe34bc3f | |
![]() |
75f94c7cd0 | |
![]() |
afbb878fca | |
![]() |
01fa6c0af0 | |
![]() |
7bfb4a93e2 | |
![]() |
701f914081 | |
![]() |
c1a2355d71 | |
![]() |
6b1e2f24f4 | |
![]() |
8892b16626 | |
![]() |
bf6727b384 | |
![]() |
b6d315705b | |
![]() |
29135f188f | |
![]() |
fb7dd07076 | |
![]() |
0897dbff75 | |
![]() |
e13a30f00f | |
![]() |
b2a48d09e7 | |
![]() |
22ef6d9ddf | |
![]() |
0bc3fc18aa | |
![]() |
5fd96620ed | |
![]() |
af3bf939c5 | |
![]() |
2a13a33040 | |
![]() |
e5805fbd62 | |
![]() |
6b25d95f77 | |
![]() |
8cae7f7186 | |
![]() |
e8ae03d799 | |
![]() |
f563bade46 | |
![]() |
0780c0d96b | |
![]() |
645db7fc90 | |
![]() |
2663756ebf | |
![]() |
7db47ac2fd | |
![]() |
78e2c123c3 | |
![]() |
11a5513e78 | |
![]() |
3e4c2b3ec8 | |
![]() |
5d313fdd7b | |
![]() |
2d5a69b1d2 | |
![]() |
aa465efa51 | |
![]() |
1655d6bbc7 | |
![]() |
e68ac506af | |
![]() |
ac9b9a6772 | |
![]() |
b8a59d0bd9 | |
![]() |
6c4551ff46 | |
![]() |
0b4c6f8b3b | |
![]() |
c7b9376ccc | |
![]() |
d6ad0bcc20 | |
![]() |
acb4945cd3 | |
![]() |
9ee6b57d69 | |
![]() |
9a0471a6d5 | |
![]() |
a28676666a | |
![]() |
fa4635e570 | |
![]() |
b2babac3d0 | |
![]() |
09049954b9 | |
![]() |
3a55351211 | |
![]() |
2c7301b3ee | |
![]() |
656313455b | |
![]() |
380795423d | |
![]() |
92d29ed7f0 | |
![]() |
681752ada1 | |
![]() |
609833e880 | |
![]() |
9302e84b95 | |
![]() |
044c9f0154 | |
![]() |
4d659998d4 | |
![]() |
46f3c8509c | |
![]() |
a887b737ef | |
![]() |
2922b8d0a7 | |
![]() |
b4e2241ded | |
![]() |
8a11dde6f2 | |
![]() |
98f0af0bc1 | |
![]() |
f723b11a9a | |
![]() |
ad747daf4f | |
![]() |
670c71f11e | |
![]() |
93326f3e50 | |
![]() |
af7388c8a3 | |
![]() |
3f6a4eb1ea | |
![]() |
47829e6ac3 | |
![]() |
6435adc9d8 | |
![]() |
0bfe7e150a | |
![]() |
422b6c4f0d | |
![]() |
0360ce38e2 | |
![]() |
277701e99f | |
![]() |
c1311abfc1 | |
![]() |
05dc255048 | |
![]() |
2e5d3253d4 | |
![]() |
ad13765348 | |
![]() |
ea4fa87bc6 | |
![]() |
b19426879f | |
![]() |
2f3b05e733 | |
![]() |
179af074eb | |
![]() |
493797ff31 | |
![]() |
7618be3697 | |
![]() |
605401dcfc | |
![]() |
c5cd8d1318 | |
![]() |
5aa2606f04 | |
![]() |
c41da588f6 | |
![]() |
568da29fbf | |
![]() |
a8b9e8d262 | |
![]() |
bb0d1fc45e | |
![]() |
f98477e05b | |
![]() |
1b4a9abbe3 | |
![]() |
feeacf37db | |
![]() |
e763cd9eec | |
![]() |
1b21b08130 | |
![]() |
7ff51ba47c | |
![]() |
f3d9e5c90c | |
![]() |
9bf7c294a8 | |
![]() |
a136a159f9 | |
![]() |
f5cb109a87 | |
![]() |
e2cbed7060 | |
![]() |
18e414443e | |
![]() |
40b8637ab8 | |
![]() |
7078f4e3af | |
![]() |
2103eb8d16 | |
![]() |
7992d01fbd | |
![]() |
b674b458df | |
![]() |
a489bfa12c | |
![]() |
9ca738ffec | |
![]() |
769c63c834 | |
![]() |
5401d6c213 | |
![]() |
34545553b2 | |
![]() |
e4349536a3 | |
![]() |
d1bc39a6ea | |
![]() |
568f9fb666 | |
![]() |
4a42829841 | |
![]() |
1d57c9a5af | |
![]() |
f82542790c | |
![]() |
dcd20922d9 | |
![]() |
0bc60023f5 | |
![]() |
2ad896c71d | |
![]() |
8736e44e1a | |
![]() |
bfd755aee9 | |
![]() |
0c8b3251e5 | |
![]() |
2b8b53a105 | |
![]() |
a33fe84f57 | |
![]() |
d0a6bd3404 | |
![]() |
43e5f17547 | |
![]() |
599020d8da | |
![]() |
1b10dde569 | |
![]() |
da80dd4c84 | |
![]() |
9e77e5038f | |
![]() |
3b28788592 | |
![]() |
918ac8b27e | |
![]() |
b650f55d60 | |
![]() |
2025bf4c6b | |
![]() |
53a61dfac0 | |
![]() |
956390005b | |
![]() |
282d221ee1 | |
![]() |
96139b8af5 | |
![]() |
fdb8d49946 | |
![]() |
2491fb1b2b | |
![]() |
47317a9fc7 | |
![]() |
f0b3964c1a | |
![]() |
181ce590a9 | |
![]() |
f6813258ce | |
![]() |
49852cc096 | |
![]() |
0dc3f4906f | |
![]() |
79b7545840 | |
![]() |
64dc01c640 | |
![]() |
86ede0ecf2 | |
![]() |
d377b5cdab | |
![]() |
bd88801de4 | |
![]() |
e89bd802a2 | |
![]() |
52349491af | |
![]() |
bf1ce85474 | |
![]() |
94121c2a42 | |
![]() |
26a7af5e5f | |
![]() |
e84d5bb666 | |
![]() |
38fae969a9 | |
![]() |
7d7344a983 | |
![]() |
494daab3d5 | |
![]() |
6004ffe228 | |
![]() |
d8e8f952a2 | |
![]() |
bc62e7e094 | |
![]() |
a8a53c651b | |
![]() |
5a32f012c3 | |
![]() |
2ed54ee10f | |
![]() |
6fb8eac890 | |
![]() |
0dd2012d4f | |
![]() |
e40f632c9b | |
![]() |
fe45c99e99 | |
![]() |
191b0e7b6d | |
![]() |
bcba59a7f5 | |
![]() |
916710ea01 | |
![]() |
207bc1d9e6 | |
![]() |
c153fc719a | |
![]() |
f6b605daf2 | |
![]() |
9e70c2a21c | |
![]() |
33e971cc09 | |
![]() |
693cd16b12 | |
![]() |
18628131df | |
![]() |
5f043f2d61 | |
![]() |
36f6f2fb2d | |
![]() |
c11eeabc86 | |
![]() |
765cd82613 | |
![]() |
dd53576fe2 | |
![]() |
af4e12c5fc | |
![]() |
d55f012537 | |
![]() |
183637502d | |
![]() |
d13b6db706 | |
![]() |
677ce4014a | |
![]() |
deb17ded8a | |
![]() |
4d6f6223bc | |
![]() |
329c5de451 | |
![]() |
13c092d671 | |
![]() |
192ec437df | |
![]() |
083318dddd | |
![]() |
32be54b19d | |
![]() |
024db60ccd | |
![]() |
fb36af09cd | |
![]() |
4d5195bf8b | |
![]() |
7468465393 | |
![]() |
d649eab88a | |
![]() |
a8af40ecf7 | |
![]() |
8029c5c7ac | |
![]() |
9cf424993f | |
![]() |
4449315e39 | |
![]() |
10e41fc948 | |
![]() |
56d3c797cc | |
![]() |
7c0a88f34b | |
![]() |
9da0b8543f | |
![]() |
493a4f7280 | |
![]() |
d51b69d6ac | |
![]() |
05a3e2bad9 | |
![]() |
4f008c0231 | |
![]() |
ff4cfe279a | |
![]() |
bb354de90e | |
![]() |
d33ab542db | |
![]() |
904c33556b | |
![]() |
648cfcd66c | |
![]() |
602095ec52 | |
![]() |
b7bdd4f3ff | |
![]() |
8c3332f619 | |
![]() |
564bfafb57 | |
![]() |
039fd39e34 | |
![]() |
080d28b3f9 | |
![]() |
5306823435 | |
![]() |
c5c25a66f7 | |
![]() |
2c445ebf3c | |
![]() |
ade5d72c3a | |
![]() |
c2c19fe91f | |
![]() |
0bd65de375 | |
![]() |
f77f5b2342 | |
![]() |
be3823e826 | |
![]() |
af7fa6a040 | |
![]() |
bb8b51a3ab | |
![]() |
b2450cfe14 | |
![]() |
86f0d9914e | |
![]() |
9e3e567592 | |
![]() |
79dd34b3f9 | |
![]() |
afa450b97a | |
![]() |
4045b3b389 | |
![]() |
6dc908908c | |
![]() |
8fa782f1a8 | |
![]() |
2c280b1bb0 | |
![]() |
86db3b4336 | |
![]() |
3ede995b27 | |
![]() |
64e701fdaf | |
![]() |
c3cda8b62a | |
![]() |
7732f0e56b | |
![]() |
f349d8e132 | |
![]() |
dfa1cbda4f | |
![]() |
86540cd344 | |
![]() |
b3b2bcb369 | |
![]() |
a1ae401240 | |
![]() |
742268bfa1 | |
![]() |
9ea6f311ea | |
![]() |
d69331d186 | |
![]() |
fc92544cb3 | |
![]() |
0991cded05 | |
![]() |
f364f3a923 | |
![]() |
ac9d470ab8 | |
![]() |
7297e62283 | |
![]() |
af12a103ae | |
![]() |
3388b7643c | |
![]() |
3a8ae60f87 | |
![]() |
5d787a4083 | |
![]() |
c6a3d76b92 | |
![]() |
b36894884e | |
![]() |
5244f9f406 | |
![]() |
1b14b7b2f1 | |
![]() |
46ca22f480 | |
![]() |
b7e5d3f232 | |
![]() |
f303ba4b59 | |
![]() |
f650ebe2fc | |
![]() |
1f1c54ca6c | |
![]() |
a917d1885e | |
![]() |
8c543dbe7c | |
![]() |
c5305820d4 | |
![]() |
5386360928 | |
![]() |
8a9cb06b41 | |
![]() |
eab90b6a0a | |
![]() |
b480297913 | |
![]() |
1230c5ef92 | |
![]() |
c6be2b51a9 | |
![]() |
2f4db693e3 | |
![]() |
55ef69645c | |
![]() |
f6447ad188 | |
![]() |
62b55d0463 | |
![]() |
77f3046ae0 | |
![]() |
8f27ee0cb8 | |
![]() |
9abf7a9d61 | |
![]() |
2ca1d3fd4c | |
![]() |
857766eb74 | |
![]() |
a0d437163d | |
![]() |
b02190bf23 | |
![]() |
956451111c | |
![]() |
4585cec7a1 | |
![]() |
daec969720 | |
![]() |
349f9c1fb6 | |
![]() |
9187fe80f4 | |
![]() |
ea8dd5f32c | |
![]() |
4e8f1bdfda | |
![]() |
13e7cac019 | |
![]() |
95d4671a10 | |
![]() |
914aeb32ec | |
![]() |
7c8b5be55a | |
![]() |
31ebdb73a0 | |
![]() |
06db626fc4 | |
![]() |
764f0102fd | |
![]() |
d78dcc6140 | |
![]() |
e974c07001 | |
![]() |
5ad63f21e0 | |
![]() |
a5726e19fd | |
![]() |
cd43199e70 | |
![]() |
540dff30e7 | |
![]() |
6b4018014c | |
![]() |
51fb56773b | |
![]() |
7c4bde3a32 | |
![]() |
6cd615e4df | |
![]() |
83fe158a16 | |
![]() |
4b7874391d | |
![]() |
d3a2e07002 | |
![]() |
1583b2c717 | |
![]() |
6d0f1fedc0 | |
![]() |
f75fb70ddc | |
![]() |
b1908949d0 | |
![]() |
a472c90327 | |
![]() |
c9c84b6859 | |
![]() |
7c2cf9c953 | |
![]() |
cb398ce6fe | |
![]() |
b432c22e76 | |
![]() |
b560ad56b6 | |
![]() |
3714dcc337 | |
![]() |
8086707d1c | |
![]() |
2556378dfa | |
![]() |
9c0947ae15 | |
![]() |
3d21e41bc9 | |
![]() |
14749e91e9 | |
![]() |
1c8bd1be62 | |
![]() |
3118ba5982 | |
![]() |
e7d6e8b217 | |
![]() |
8f7f97148c | |
![]() |
63c1363ca1 | |
![]() |
41aeecbb2a | |
![]() |
e97b6032c2 | |
![]() |
0c03e7ccfc | |
![]() |
69f344a439 | |
![]() |
50fae55821 | |
![]() |
6e4b5839ce | |
![]() |
68f1684031 | |
![]() |
fec39141b5 | |
![]() |
8b29f07dbe | |
![]() |
993f110d59 | |
![]() |
71ade59f4b | |
![]() |
aac5d562fb | |
![]() |
3f2b49be5d | |
![]() |
66a491bda6 | |
![]() |
ba37d18ab9 | |
![]() |
909d83419c | |
![]() |
94e1d6de3e | |
![]() |
7c9fb68b14 | |
![]() |
cc2e6fc96c | |
![]() |
ee782c8a91 | |
![]() |
4d0cd42aef | |
![]() |
79a61deb25 | |
![]() |
e58451fc01 | |
![]() |
92f4a95bb5 | |
![]() |
5896bab86f | |
![]() |
880ce92fb2 | |
![]() |
c733e7a7b6 | |
![]() |
c2749e3acf | |
![]() |
93b9fdd391 | |
![]() |
67b1a88466 | |
![]() |
cc72778d5e | |
![]() |
ae7fb3885b | |
![]() |
795b619704 | |
![]() |
19970bd639 | |
![]() |
5ae170f1d6 | |
![]() |
896a57d3be | |
![]() |
a649be64db | |
![]() |
6db05aaef1 | |
![]() |
9c0085f5a9 | |
![]() |
8d44ece874 | |
![]() |
3a1b9414ed | |
![]() |
120e36da2a | |
![]() |
788f42ddd4 | |
![]() |
def3a50558 | |
![]() |
98dcf82ada | |
![]() |
b1414033ef | |
![]() |
4dd1e91bda | |
![]() |
a64c86b73f | |
![]() |
e23cb52a16 | |
![]() |
b05fa94d32 | |
![]() |
91974b7794 | |
![]() |
d9e96223f4 | |
![]() |
341067d4d7 | |
![]() |
16aaf92782 | |
![]() |
2d399e93f0 | |
![]() |
aaa35d455c | |
![]() |
1eb95982dc | |
![]() |
b21229e59a | |
![]() |
35572904fc | |
![]() |
601d33f294 | |
![]() |
75b08a8fd9 | |
![]() |
ab631c2147 | |
![]() |
53fa2bfbd6 | |
![]() |
0300c17441 | |
![]() |
1b6aa65ee5 | |
![]() |
fe8fe42ea9 | |
![]() |
2d2ee710c8 | |
![]() |
5876269f66 | |
![]() |
91ae3d907e | |
![]() |
2505fd03f1 | |
![]() |
bbc833baa6 | |
![]() |
bb2391133d | |
![]() |
06230e43d6 | |
![]() |
957e73a889 | |
![]() |
daad3b9d7c | |
![]() |
9cdf19c657 | |
![]() |
296d3f31be | |
![]() |
25150633ed | |
![]() |
3bce89553c | |
![]() |
f44484137e | |
![]() |
e01116149b | |
![]() |
a1b47dfa08 | |
![]() |
f065ee6d06 | |
![]() |
3663e1ec13 | |
![]() |
bbb40a9a84 | |
![]() |
adc076322b | |
![]() |
d0a49a6b77 | |
![]() |
8db8dd24bf | |
![]() |
7e095412aa | |
![]() |
3cafc20981 | |
![]() |
7a2d95ddc8 | |
![]() |
575ff68e5b | |
![]() |
01818f683c | |
![]() |
8774a0114c | |
![]() |
acb0dc4c24 | |
![]() |
015a8452cd | |
![]() |
9fa5f7547f | |
![]() |
efde6c8e9e | |
![]() |
fd7a67d92e | |
![]() |
daa8785e34 | |
![]() |
2b684146c7 | |
![]() |
adbf69aab4 | |
![]() |
39a5489bde | |
![]() |
ab41bcf3ad | |
![]() |
9822e75fbf | |
![]() |
7ba7ddfee8 | |
![]() |
0284610eff | |
![]() |
7e8230a596 | |
![]() |
4cc94e9462 | |
![]() |
0ce01ac30f | |
![]() |
d563bc6c86 | |
![]() |
cffacc514d | |
![]() |
428758d33a | |
![]() |
0b36919b7c | |
![]() |
36bec2d941 | |
![]() |
788c186701 | |
![]() |
f6d9b8a0b4 | |
![]() |
f23e0ef05b | |
![]() |
69f8bee0b1 | |
![]() |
d5ca429627 | |
![]() |
da4f4d7e69 | |
![]() |
9f10d5d79f | |
![]() |
f79886765d | |
![]() |
176ae6e692 | |
![]() |
5c4acbdddd | |
![]() |
74a193d383 | |
![]() |
597753047d | |
![]() |
2dba3d30af | |
![]() |
e963ae09cc | |
![]() |
7007599b3f | |
![]() |
42c61dc6a3 | |
![]() |
9c2f12f98f | |
![]() |
e9db9cd7d3 | |
![]() |
1a1eaee13f | |
![]() |
fd2e77317f | |
![]() |
6ab8f179a7 | |
![]() |
ed339de953 | |
![]() |
203c4998bc | |
![]() |
3dd18a543b | |
![]() |
4433fb48dc | |
![]() |
c3d3d83b56 | |
![]() |
2e6619f3d0 | |
![]() |
f665dda192 | |
![]() |
af39179446 | |
![]() |
359f36d6ab | |
![]() |
3b67edf20d | |
![]() |
72c391ebc8 | |
![]() |
09f97c8037 | |
![]() |
b98607c689 | |
![]() |
4f8c941bff | |
![]() |
2d71d7cada | |
![]() |
0ddcc85f68 | |
![]() |
1f74e9a5c5 | |
![]() |
e00a26d72d | |
![]() |
1fd5a6555e | |
![]() |
4f512c6571 | |
![]() |
faab23e914 | |
![]() |
3353ed87ef | |
![]() |
3572e6f0e7 | |
![]() |
c1e09f65c9 | |
![]() |
20bee00681 | |
![]() |
7d1db53f6b | |
![]() |
d8012cf00c | |
![]() |
24168aebdb | |
![]() |
2a07aa238f | |
![]() |
b08a4a1896 | |
![]() |
427ce522a2 | |
![]() |
d4a3d6689e | |
![]() |
56fc8f83ac | |
![]() |
a89d1f8058 | |
![]() |
c59f385996 | |
![]() |
1c15a6dd88 | |
![]() |
260bc0a61d | |
![]() |
451b878bb9 | |
![]() |
36222adb25 | |
![]() |
571e4026ac | |
![]() |
c314705d53 | |
![]() |
bc58d13ee8 | |
![]() |
a879c59a08 | |
![]() |
bc30ca64dc | |
![]() |
4ab504c994 | |
![]() |
68f1570d45 | |
![]() |
3a5ba58a45 | |
![]() |
18ba622c40 | |
![]() |
e427ad73aa | |
![]() |
4d3779f061 | |
![]() |
a05a6d8727 | |
![]() |
66b1462cf8 | |
![]() |
61699e9ed3 | |
![]() |
3f1e57de1c |
|
@ -81,3 +81,6 @@ AllowShortLambdasOnASingleLine: Empty
|
|||
|
||||
# We do not want clang-format to put all arguments on a new line
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
|
||||
# Indent lambdas to the start of the line, not to the start of the lambda
|
||||
LambdaBodyIndentation: OuterScope
|
||||
|
|
|
@ -25,3 +25,4 @@ CMakeLists.txt.user*
|
|||
.idea
|
||||
/cmake-build*
|
||||
.cache
|
||||
.directory
|
||||
|
|
|
@ -5,10 +5,10 @@ include:
|
|||
- project: sysadmin/ci-utilities
|
||||
file:
|
||||
- /gitlab-templates/linux-qt6.yml
|
||||
# - /gitlab-templates/freebsd-qt6.yml
|
||||
- /gitlab-templates/freebsd-qt6.yml
|
||||
|
||||
suse_tumbleweed_qt66_reduced_featureset:
|
||||
extends: suse_tumbleweed_qt66
|
||||
suse_tumbleweed_qt67_reduced_featureset:
|
||||
extends: suse_tumbleweed_qt67
|
||||
script:
|
||||
- git config --global --add safe.directory $CI_PROJECT_DIR
|
||||
- python3 -u ci-utilities/run-ci-build.py --project $CI_PROJECT_NAME --branch $CI_COMMIT_REF_NAME --platform Linux --extra-cmake-args="-DKWIN_BUILD_KCMS=OFF -DKWIN_BUILD_SCREENLOCKER=OFF -DKWIN_BUILD_TABBOX=OFF -DKWIN_BUILD_ACTIVITIES=OFF -DKWIN_BUILD_RUNNERS=OFF -DKWIN_BUILD_NOTIFICATIONS=OFF -DKWIN_BUILD_GLOBALSHORTCUTS=OFF" --skip-publishing
|
||||
- python3 -u ci-utilities/run-ci-build.py --project $CI_PROJECT_NAME --branch $CI_COMMIT_REF_NAME --platform Linux --extra-cmake-args="-DKWIN_BUILD_KCMS=OFF -DKWIN_BUILD_SCREENLOCKER=OFF -DKWIN_BUILD_TABBOX=OFF -DKWIN_BUILD_ACTIVITIES=OFF -DKWIN_BUILD_RUNNERS=OFF -DKWIN_BUILD_NOTIFICATIONS=OFF -DKWIN_BUILD_GLOBALSHORTCUTS=OFF -DKWIN_BUILD_X11=OFF -DKWIN_BUILD_EIS=OFF" --skip-publishing
|
||||
|
|
208
CMakeLists.txt
|
@ -1,14 +1,15 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(PROJECT_VERSION "6.0.4") # Handled by release scripts
|
||||
set(PROJECT_VERSION "6.1.80") # Handled by release scripts
|
||||
project(KWin VERSION ${PROJECT_VERSION})
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(PROJECT_DEP_VERSION "6.1.1")
|
||||
set(QT_MIN_VERSION "6.6.0")
|
||||
set(KF6_MIN_VERSION "6.0.0")
|
||||
set(KF6_MIN_VERSION "6.2.0")
|
||||
set(KDE_COMPILERSETTINGS_LEVEL "5.82")
|
||||
|
||||
find_package(ECM ${KF6_MIN_VERSION} REQUIRED NO_MODULE)
|
||||
|
@ -47,6 +48,9 @@ option(KWIN_BUILD_KCMS "Enable building of KWin configuration modules." ON)
|
|||
option(KWIN_BUILD_NOTIFICATIONS "Enable building of KWin with knotifications support" ON)
|
||||
option(KWIN_BUILD_SCREENLOCKER "Enable building of KWin lockscreen functionality" ON)
|
||||
option(KWIN_BUILD_TABBOX "Enable building of KWin Tabbox functionality" ON)
|
||||
option(KWIN_BUILD_X11 "Enable building kwin_x11 and Xwayland support" ON)
|
||||
option(KWIN_BUILD_GLOBALSHORTCUTS "Enable building of KWin with global shortcuts support" ON)
|
||||
option(KWIN_BUILD_RUNNERS "Enable building of KWin with krunner support" ON)
|
||||
|
||||
find_package(Qt6 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
|
||||
Concurrent
|
||||
|
@ -71,12 +75,13 @@ if (NOT Qt6Test_FOUND)
|
|||
endif()
|
||||
|
||||
if (BUILD_TESTING)
|
||||
pkg_check_modules(PipeWire IMPORTED_TARGET libpipewire-0.3 REQUIRED)
|
||||
find_package(KPipeWire)
|
||||
endif()
|
||||
|
||||
# required frameworks by Core
|
||||
find_package(KF6 ${KF6_MIN_VERSION} REQUIRED COMPONENTS
|
||||
Auth
|
||||
ColorScheme
|
||||
Config
|
||||
ConfigWidgets
|
||||
CoreAddons
|
||||
|
@ -109,14 +114,14 @@ set_package_properties(Threads PROPERTIES
|
|||
TYPE REQUIRED
|
||||
)
|
||||
|
||||
find_package(KWayland ${PROJECT_VERSION} CONFIG)
|
||||
find_package(KWayland ${PROJECT_DEP_VERSION} CONFIG)
|
||||
set_package_properties(KWayland PROPERTIES
|
||||
PURPOSE "Required to build wayland platform plugin and tests"
|
||||
TYPE REQUIRED
|
||||
)
|
||||
|
||||
# optional frameworks
|
||||
find_package(PlasmaActivities ${PROJECT_VERSION} CONFIG)
|
||||
find_package(PlasmaActivities ${PROJECT_DEP_VERSION} CONFIG)
|
||||
set_package_properties(PlasmaActivities PROPERTIES
|
||||
PURPOSE "Enable building of KWin with kactivities support"
|
||||
TYPE OPTIONAL
|
||||
|
@ -130,20 +135,20 @@ set_package_properties(KF6DocTools PROPERTIES
|
|||
)
|
||||
add_feature_info("KF6DocTools" KF6DocTools_FOUND "Enable building documentation")
|
||||
|
||||
find_package(KF6Kirigami2 ${KF6_MIN_VERSION} CONFIG)
|
||||
set_package_properties(KF6Kirigami2 PROPERTIES
|
||||
find_package(KF6Kirigami ${KF6_MIN_VERSION} CONFIG)
|
||||
set_package_properties(KF6Kirigami PROPERTIES
|
||||
DESCRIPTION "A QtQuick based components set"
|
||||
PURPOSE "Required at runtime for several QML effects"
|
||||
TYPE RUNTIME
|
||||
)
|
||||
find_package(Plasma ${PROJECT_VERSION} CONFIG)
|
||||
find_package(Plasma ${PROJECT_DEP_VERSION} CONFIG)
|
||||
set_package_properties(Plasma PROPERTIES
|
||||
DESCRIPTION "A QtQuick based components set"
|
||||
PURPOSE "Required at runtime for several QML effects"
|
||||
TYPE RUNTIME
|
||||
)
|
||||
|
||||
find_package(KDecoration2 ${PROJECT_VERSION} CONFIG)
|
||||
find_package(KDecoration2 ${PROJECT_DEP_VERSION} CONFIG)
|
||||
set_package_properties(KDecoration2 PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Required for server side decoration support"
|
||||
|
@ -187,33 +192,25 @@ if (epoxy_HAS_GLX)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
const int size = 10;
|
||||
int fd = memfd_create(\"test\", MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
||||
ftruncate(fd, size);
|
||||
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL);
|
||||
mmap(nullptr, size, PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
}" HAVE_MEMFD)
|
||||
|
||||
find_package(Wayland 1.22)
|
||||
set_package_properties(Wayland PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Required for building KWin with Wayland support"
|
||||
)
|
||||
if (Wayland_VERSION VERSION_GREATER_EQUAL 1.23)
|
||||
set(HAVE_WL_DISPLAY_SET_DEFAULT_MAX_BUFFER_SIZE 1)
|
||||
else()
|
||||
set(HAVE_WL_DISPLAY_SET_DEFAULT_MAX_BUFFER_SIZE 0)
|
||||
endif()
|
||||
|
||||
find_package(WaylandProtocols 1.32)
|
||||
find_package(WaylandProtocols 1.34)
|
||||
set_package_properties(WaylandProtocols PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Collection of Wayland protocols that add functionality not available in the Wayland core protocol"
|
||||
URL "https://gitlab.freedesktop.org/wayland/wayland-protocols/"
|
||||
)
|
||||
|
||||
find_package(PlasmaWaylandProtocols 1.9.0 CONFIG)
|
||||
find_package(PlasmaWaylandProtocols 1.13.0 CONFIG)
|
||||
set_package_properties(PlasmaWaylandProtocols PROPERTIES
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Collection of Plasma-specific Wayland protocols"
|
||||
|
@ -231,12 +228,67 @@ else()
|
|||
set(HAVE_XKBCOMMON_NO_SECURE_GETENV 0)
|
||||
endif()
|
||||
|
||||
pkg_check_modules(XKBX11 IMPORTED_TARGET xkbcommon-x11 REQUIRED)
|
||||
add_feature_info(XKBX11 XKBX11_FOUND "Required for handling keyboard events in X11 backend")
|
||||
if (KWIN_BUILD_X11)
|
||||
pkg_check_modules(XKBX11 IMPORTED_TARGET xkbcommon-x11 REQUIRED)
|
||||
add_feature_info(XKBX11 XKBX11_FOUND "Required for handling keyboard events in X11 backend")
|
||||
|
||||
# All the required XCB components
|
||||
find_package(XCB 1.10 REQUIRED COMPONENTS
|
||||
COMPOSITE
|
||||
CURSOR
|
||||
DAMAGE
|
||||
DRI3
|
||||
GLX
|
||||
ICCCM
|
||||
IMAGE
|
||||
KEYSYMS
|
||||
PRESENT
|
||||
RANDR
|
||||
RENDER
|
||||
SHAPE
|
||||
SHM
|
||||
SYNC
|
||||
XCB
|
||||
XFIXES
|
||||
XKB
|
||||
XINERAMA
|
||||
XINPUT
|
||||
)
|
||||
set_package_properties(XCB PROPERTIES TYPE REQUIRED)
|
||||
|
||||
find_package(X11_XCB)
|
||||
set_package_properties(X11_XCB PROPERTIES
|
||||
PURPOSE "Required for building X11 windowed backend of kwin_wayland"
|
||||
TYPE OPTIONAL
|
||||
)
|
||||
|
||||
find_package(Xwayland)
|
||||
set_package_properties(Xwayland PROPERTIES
|
||||
URL "https://x.org"
|
||||
DESCRIPTION "Xwayland X server"
|
||||
TYPE RUNTIME
|
||||
PURPOSE "Needed for running kwin_wayland"
|
||||
)
|
||||
set(HAVE_XWAYLAND_LISTENFD ${Xwayland_HAVE_LISTENFD})
|
||||
set(HAVE_XWAYLAND_ENABLE_EI_PORTAL ${Xwayland_HAVE_ENABLE_EI_PORTAL})
|
||||
|
||||
set(HAVE_GLX ${epoxy_HAS_GLX})
|
||||
get_target_property(QT_DISABLED_FEATURES Qt6::Gui QT_DISABLED_PUBLIC_FEATURES)
|
||||
if("xcb_glx_plugin" IN_LIST QT_DISABLED_FEATURES)
|
||||
message(STATUS "Disable GLX because Qt6::Gui was built without xcb_glx_plugin")
|
||||
set(HAVE_GLX false)
|
||||
endif()
|
||||
|
||||
# for kwin internal things
|
||||
set(HAVE_X11_XCB ${X11_XCB_FOUND})
|
||||
endif()
|
||||
|
||||
find_package(Libinput 1.19)
|
||||
set_package_properties(Libinput PROPERTIES TYPE REQUIRED PURPOSE "Required for input handling on Wayland.")
|
||||
|
||||
find_package(Libeis-1.0)
|
||||
set_package_properties(Libeis PROPERTIES TYPE OPTIONAL PURPOSE "Required for emulated input handling.")
|
||||
|
||||
find_package(UDev)
|
||||
set_package_properties(UDev PROPERTIES
|
||||
URL "https://www.freedesktop.org/wiki/Software/systemd/"
|
||||
|
@ -245,7 +297,7 @@ set_package_properties(UDev PROPERTIES
|
|||
PURPOSE "Required for input handling on Wayland."
|
||||
)
|
||||
|
||||
find_package(Libdrm 2.4.112)
|
||||
find_package(Libdrm 2.4.116)
|
||||
set_package_properties(Libdrm PROPERTIES TYPE REQUIRED PURPOSE "Required for drm output on Wayland.")
|
||||
|
||||
find_package(gbm)
|
||||
|
@ -281,35 +333,6 @@ set_package_properties(lcms2 PROPERTIES
|
|||
PURPOSE "Required for the color management system"
|
||||
)
|
||||
|
||||
# All the required XCB components
|
||||
find_package(XCB 1.10 REQUIRED COMPONENTS
|
||||
COMPOSITE
|
||||
CURSOR
|
||||
DAMAGE
|
||||
DRI3
|
||||
GLX
|
||||
ICCCM
|
||||
IMAGE
|
||||
KEYSYMS
|
||||
PRESENT
|
||||
RANDR
|
||||
RENDER
|
||||
SHAPE
|
||||
SHM
|
||||
SYNC
|
||||
XCB
|
||||
XFIXES
|
||||
XKB
|
||||
XINERAMA
|
||||
)
|
||||
set_package_properties(XCB PROPERTIES TYPE REQUIRED)
|
||||
|
||||
find_package(X11_XCB)
|
||||
set_package_properties(X11_XCB PROPERTIES
|
||||
PURPOSE "Required for building X11 windowed backend of kwin_wayland"
|
||||
TYPE OPTIONAL
|
||||
)
|
||||
|
||||
find_package(Freetype)
|
||||
set_package_properties(Freetype PROPERTIES
|
||||
DESCRIPTION "A font rendering engine"
|
||||
|
@ -323,15 +346,6 @@ set_package_properties(Fontconfig PROPERTIES
|
|||
PURPOSE "Needed for KWin's QPA plugin."
|
||||
)
|
||||
|
||||
find_package(Xwayland)
|
||||
set_package_properties(Xwayland PROPERTIES
|
||||
URL "https://x.org"
|
||||
DESCRIPTION "Xwayland X server"
|
||||
TYPE RUNTIME
|
||||
PURPOSE "Needed for running kwin_wayland"
|
||||
)
|
||||
set(HAVE_XWAYLAND_LISTENFD ${Xwayland_HAVE_LISTENFD})
|
||||
|
||||
find_package(Libcap)
|
||||
set_package_properties(Libcap PROPERTIES
|
||||
TYPE OPTIONAL
|
||||
|
@ -355,45 +369,19 @@ set_package_properties(QAccessibilityClient6 PROPERTIES
|
|||
)
|
||||
set(HAVE_ACCESSIBILITY ${QAccessibilityClient6_FOUND})
|
||||
|
||||
option(KWIN_BUILD_GLOBALSHORTCUTS "Enable building of KWin with global shortcuts support" ON)
|
||||
pkg_check_modules(libsystemd IMPORTED_TARGET libsystemd)
|
||||
add_feature_info(libsystemd libsystemd_FOUND "Required for setting up the service watchdog")
|
||||
|
||||
if(KWIN_BUILD_GLOBALSHORTCUTS)
|
||||
find_package(KGlobalAccelD REQUIRED)
|
||||
endif()
|
||||
|
||||
pkg_check_modules(libdisplayinfo IMPORTED_TARGET display-info)
|
||||
if (NOT libdisplayinfo_FOUND)
|
||||
pkg_check_modules(libdisplayinfo REQUIRED IMPORTED_TARGET libdisplay-info)
|
||||
endif()
|
||||
add_feature_info(libdisplayinfo libdisplayinfo_FOUND "EDID and DisplayID library: https://gitlab.freedesktop.org/emersion/libdisplay-info")
|
||||
|
||||
ecm_find_qmlmodule(QtQuick 2.3)
|
||||
ecm_find_qmlmodule(QtQuick.Controls 2.15)
|
||||
ecm_find_qmlmodule(QtQuick.Layouts 1.3)
|
||||
ecm_find_qmlmodule(QtQuick.Window 2.1)
|
||||
ecm_find_qmlmodule(QtMultimedia 5.0)
|
||||
ecm_find_qmlmodule(org.kde.kquickcontrolsaddons 2.0)
|
||||
ecm_find_qmlmodule(org.kde.plasma.core 2.0)
|
||||
ecm_find_qmlmodule(org.kde.plasma.components 2.0)
|
||||
|
||||
########### configure tests ###############
|
||||
cmake_dependent_option(KWIN_BUILD_ACTIVITIES "Enable building of KWin with kactivities support" ON "PlasmaActivities_FOUND" OFF)
|
||||
option(KWIN_BUILD_RUNNERS "Enable building of KWin with krunner support" ON)
|
||||
|
||||
set(HAVE_GLX ${epoxy_HAS_GLX})
|
||||
get_target_property(QT_DISABLED_FEATURES Qt6::Gui QT_DISABLED_PUBLIC_FEATURES)
|
||||
if("xcb_glx_plugin" IN_LIST QT_DISABLED_FEATURES)
|
||||
message(STATUS "Disable GLX because Qt6::Gui was built without xcb_glx_plugin")
|
||||
set(HAVE_GLX false)
|
||||
endif()
|
||||
|
||||
# for kwin internal things
|
||||
set(HAVE_X11_XCB ${X11_XCB_FOUND})
|
||||
|
||||
check_symbol_exists(SCHED_RESET_ON_FORK "sched.h" HAVE_SCHED_RESET_ON_FORK)
|
||||
add_feature_info("SCHED_RESET_ON_FORK"
|
||||
HAVE_SCHED_RESET_ON_FORK
|
||||
"Required for running kwin_wayland with real-time scheduling")
|
||||
|
||||
|
||||
pkg_check_modules(PipeWire IMPORTED_TARGET libpipewire-0.3>=0.3.29)
|
||||
add_feature_info(PipeWire PipeWire_FOUND "Required for Wayland screencasting")
|
||||
|
||||
|
@ -409,7 +397,17 @@ if (KWIN_BUILD_SCREENLOCKER)
|
|||
)
|
||||
endif()
|
||||
|
||||
########### global ###############
|
||||
ecm_find_qmlmodule(QtQuick 2.3)
|
||||
ecm_find_qmlmodule(QtQuick.Controls 2.15)
|
||||
ecm_find_qmlmodule(QtQuick.Layouts 1.3)
|
||||
ecm_find_qmlmodule(QtQuick.Window 2.1)
|
||||
ecm_find_qmlmodule(QtMultimedia 5.0)
|
||||
ecm_find_qmlmodule(org.kde.kquickcontrolsaddons 2.0)
|
||||
ecm_find_qmlmodule(org.kde.plasma.core 2.0)
|
||||
ecm_find_qmlmodule(org.kde.plasma.components 2.0)
|
||||
|
||||
cmake_dependent_option(KWIN_BUILD_ACTIVITIES "Enable building of KWin with kactivities support" ON "PlasmaActivities_FOUND" OFF)
|
||||
cmake_dependent_option(KWIN_BUILD_EIS "Enable building KWin with libeis support" ON "Libeis-1.0_FOUND" OFF)
|
||||
|
||||
include_directories(BEFORE
|
||||
${CMAKE_CURRENT_BINARY_DIR}/src/wayland
|
||||
|
@ -417,6 +415,24 @@ include_directories(BEFORE
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
)
|
||||
|
||||
check_symbol_exists(SCHED_RESET_ON_FORK "sched.h" HAVE_SCHED_RESET_ON_FORK)
|
||||
add_feature_info("SCHED_RESET_ON_FORK"
|
||||
HAVE_SCHED_RESET_ON_FORK
|
||||
"Required for running kwin_wayland with real-time scheduling")
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main() {
|
||||
const int size = 10;
|
||||
int fd = memfd_create(\"test\", MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
||||
ftruncate(fd, size);
|
||||
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL);
|
||||
mmap(nullptr, size, PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
}" HAVE_MEMFD)
|
||||
|
||||
check_cxx_compiler_flag(-Wno-unused-parameter COMPILER_UNUSED_PARAMETER_SUPPORTED)
|
||||
if (COMPILER_UNUSED_PARAMETER_SUPPORTED)
|
||||
add_compile_options(-Wno-unused-parameter)
|
||||
|
|
|
@ -27,9 +27,9 @@ The Breeze decorations theme is not located in the KWin repository, and is in fa
|
|||
|
||||
### Tab Switcher
|
||||
|
||||
The default visual appearance of the tab switcher is not located in the KWin repository, and is in fact part of [Plasma Workspace](https://invent.kde.org/plasma/plasma-workspace), located at `lookandfeel/contents/windowswitcher`.
|
||||
The default visual appearance of the tab switcher is located in `src/tabbox/switchers`.
|
||||
|
||||
Other window switchers usually shipped by default are located in [Plasma Addons](https://invent.kde.org/plasma/kdeplasma-addons), located in the `windowswitchers` directory.
|
||||
Other window switchers usually shipped by default are located in [Plasma Addons](https://invent.kde.org/plasma/kdeplasma-addons), located in the `kwin/windowswitchers` directory.
|
||||
|
||||
### Window Management
|
||||
|
||||
|
@ -53,7 +53,7 @@ Other scripting stuff is located in `src/scripting`.
|
|||
|
||||
KWin's coding conventions are located [here](doc/coding-conventions.md).
|
||||
|
||||
KWin additionally follows [KDE's Frameworks Coding Style]((https://techbase.kde.org/Policies/Frameworks_Coding_Style)).
|
||||
KWin additionally follows [KDE's Frameworks Coding Style](https://community.kde.org/Policies/Frameworks_Coding_Style).
|
||||
|
||||
### Commits
|
||||
|
||||
|
|
|
@ -45,82 +45,97 @@ ecm_mark_as_test(testVirtualDesktops)
|
|||
########################################################
|
||||
# Test ClientMachine
|
||||
########################################################
|
||||
set(testClientMachine_SRCS
|
||||
../src/client_machine.cpp
|
||||
test_client_machine.cpp
|
||||
xcb_scaling_mock.cpp
|
||||
)
|
||||
add_executable(testClientMachine ${testClientMachine_SRCS})
|
||||
set_target_properties(testClientMachine PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW")
|
||||
if(KWIN_BUILD_X11)
|
||||
set(testClientMachine_SRCS
|
||||
../src/client_machine.cpp
|
||||
test_client_machine.cpp
|
||||
xcb_scaling_mock.cpp
|
||||
)
|
||||
add_executable(testClientMachine ${testClientMachine_SRCS})
|
||||
set_target_properties(testClientMachine PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW")
|
||||
|
||||
target_link_libraries(testClientMachine
|
||||
Qt::Concurrent
|
||||
Qt::GuiPrivate
|
||||
Qt::Test
|
||||
Qt::Widgets
|
||||
target_link_libraries(testClientMachine
|
||||
Qt::Concurrent
|
||||
Qt::GuiPrivate
|
||||
Qt::Test
|
||||
Qt::Widgets
|
||||
|
||||
KF6::ConfigCore
|
||||
KF6::WindowSystem
|
||||
KF6::ConfigCore
|
||||
KF6::WindowSystem
|
||||
|
||||
XCB::XCB
|
||||
XCB::XFIXES
|
||||
XCB::XCB
|
||||
XCB::XFIXES
|
||||
|
||||
${X11_X11_LIB} # to make jenkins happy
|
||||
)
|
||||
add_test(NAME kwin-testClientMachine COMMAND testClientMachine)
|
||||
ecm_mark_as_test(testClientMachine)
|
||||
${X11_X11_LIB} # to make jenkins happy
|
||||
)
|
||||
add_test(NAME kwin-testClientMachine COMMAND testClientMachine)
|
||||
ecm_mark_as_test(testClientMachine)
|
||||
|
||||
########################################################
|
||||
# Test XcbWrapper
|
||||
########################################################
|
||||
add_executable(testXcbWrapper test_xcb_wrapper.cpp xcb_scaling_mock.cpp)
|
||||
########################################################
|
||||
# Test XcbWrapper
|
||||
########################################################
|
||||
add_executable(testXcbWrapper test_xcb_wrapper.cpp xcb_scaling_mock.cpp)
|
||||
|
||||
target_link_libraries(testXcbWrapper
|
||||
Qt::GuiPrivate
|
||||
Qt::Test
|
||||
Qt::Widgets
|
||||
target_link_libraries(testXcbWrapper
|
||||
Qt::GuiPrivate
|
||||
Qt::Test
|
||||
Qt::Widgets
|
||||
|
||||
KF6::ConfigCore
|
||||
KF6::WindowSystem
|
||||
KF6::ConfigCore
|
||||
KF6::WindowSystem
|
||||
|
||||
XCB::XCB
|
||||
)
|
||||
add_test(NAME kwin-testXcbWrapper COMMAND testXcbWrapper)
|
||||
ecm_mark_as_test(testXcbWrapper)
|
||||
XCB::XCB
|
||||
)
|
||||
add_test(NAME kwin-testXcbWrapper COMMAND testXcbWrapper)
|
||||
ecm_mark_as_test(testXcbWrapper)
|
||||
|
||||
add_executable(testXcbSizeHints test_xcb_size_hints.cpp xcb_scaling_mock.cpp)
|
||||
set_target_properties(testXcbSizeHints PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW")
|
||||
target_link_libraries(testXcbSizeHints
|
||||
Qt::GuiPrivate
|
||||
Qt::Test
|
||||
Qt::Widgets
|
||||
add_executable(testXcbSizeHints test_xcb_size_hints.cpp xcb_scaling_mock.cpp)
|
||||
set_target_properties(testXcbSizeHints PROPERTIES COMPILE_DEFINITIONS "NO_NONE_WINDOW")
|
||||
target_link_libraries(testXcbSizeHints
|
||||
Qt::GuiPrivate
|
||||
Qt::Test
|
||||
Qt::Widgets
|
||||
|
||||
KF6::ConfigCore
|
||||
KF6::WindowSystem
|
||||
KF6::ConfigCore
|
||||
KF6::WindowSystem
|
||||
|
||||
XCB::ICCCM
|
||||
XCB::XCB
|
||||
)
|
||||
add_test(NAME kwin-testXcbSizeHints COMMAND testXcbSizeHints)
|
||||
ecm_mark_as_test(testXcbSizeHints)
|
||||
XCB::ICCCM
|
||||
XCB::XCB
|
||||
)
|
||||
add_test(NAME kwin-testXcbSizeHints COMMAND testXcbSizeHints)
|
||||
ecm_mark_as_test(testXcbSizeHints)
|
||||
|
||||
########################################################
|
||||
# Test XcbWindow
|
||||
########################################################
|
||||
add_executable(testXcbWindow test_xcb_window.cpp xcb_scaling_mock.cpp)
|
||||
########################################################
|
||||
# Test XcbWindow
|
||||
########################################################
|
||||
add_executable(testXcbWindow test_xcb_window.cpp xcb_scaling_mock.cpp)
|
||||
|
||||
target_link_libraries(testXcbWindow
|
||||
Qt::GuiPrivate
|
||||
Qt::Test
|
||||
Qt::Widgets
|
||||
target_link_libraries(testXcbWindow
|
||||
Qt::GuiPrivate
|
||||
Qt::Test
|
||||
Qt::Widgets
|
||||
|
||||
KF6::ConfigCore
|
||||
KF6::WindowSystem
|
||||
KF6::ConfigCore
|
||||
KF6::WindowSystem
|
||||
|
||||
XCB::XCB
|
||||
)
|
||||
add_test(NAME kwin-testXcbWindow COMMAND testXcbWindow)
|
||||
ecm_mark_as_test(testXcbWindow)
|
||||
XCB::XCB
|
||||
)
|
||||
add_test(NAME kwin-testXcbWindow COMMAND testXcbWindow)
|
||||
ecm_mark_as_test(testXcbWindow)
|
||||
|
||||
########################################################
|
||||
# Test X11 TimestampUpdate
|
||||
########################################################
|
||||
add_executable(testX11TimestampUpdate test_x11_timestamp_update.cpp)
|
||||
target_link_libraries(testX11TimestampUpdate
|
||||
KF6::CoreAddons
|
||||
Qt::Test
|
||||
Qt::GuiPrivate
|
||||
kwin
|
||||
)
|
||||
add_test(NAME kwin-testX11TimestampUpdate COMMAND testX11TimestampUpdate)
|
||||
ecm_mark_as_test(testX11TimestampUpdate)
|
||||
endif()
|
||||
|
||||
########################################################
|
||||
# Test OnScreenNotification
|
||||
|
@ -161,19 +176,6 @@ target_link_libraries(testGestures
|
|||
add_test(NAME kwin-testGestures COMMAND testGestures)
|
||||
ecm_mark_as_test(testGestures)
|
||||
|
||||
########################################################
|
||||
# Test X11 TimestampUpdate
|
||||
########################################################
|
||||
add_executable(testX11TimestampUpdate test_x11_timestamp_update.cpp)
|
||||
target_link_libraries(testX11TimestampUpdate
|
||||
KF6::CoreAddons
|
||||
Qt::Test
|
||||
Qt::GuiPrivate
|
||||
kwin
|
||||
)
|
||||
add_test(NAME kwin-testX11TimestampUpdate COMMAND testX11TimestampUpdate)
|
||||
ecm_mark_as_test(testX11TimestampUpdate)
|
||||
|
||||
set(testOpenGLContextAttributeBuilder_SRCS
|
||||
../src/opengl/abstract_opengl_context_attribute_builder.cpp
|
||||
../src/opengl/egl_context_attribute_builder.cpp
|
||||
|
|
|
@ -8,9 +8,7 @@ set(mockDRM_SRCS
|
|||
../../src/backends/drm/drm_commit_thread.cpp
|
||||
../../src/backends/drm/drm_connector.cpp
|
||||
../../src/backends/drm/drm_crtc.cpp
|
||||
../../src/backends/drm/drm_dmabuf_feedback.cpp
|
||||
../../src/backends/drm/drm_egl_backend.cpp
|
||||
../../src/backends/drm/drm_egl_cursor_layer.cpp
|
||||
../../src/backends/drm/drm_egl_layer.cpp
|
||||
../../src/backends/drm/drm_egl_layer_surface.cpp
|
||||
../../src/backends/drm/drm_gpu.cpp
|
||||
|
@ -39,7 +37,6 @@ target_link_libraries(LibDrmTest
|
|||
KF6::WindowSystem
|
||||
KF6::CoreAddons
|
||||
KF6::I18n
|
||||
XCB::XCB
|
||||
PkgConfig::Libxcvt
|
||||
gbm::gbm
|
||||
Libdrm::Libdrm
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <QSignalSpy>
|
||||
#include <QSize>
|
||||
#include <QTest>
|
||||
|
||||
|
@ -70,6 +71,7 @@ private Q_SLOTS:
|
|||
void testConnectorLifetime();
|
||||
void testModeset_data();
|
||||
void testModeset();
|
||||
void testVrrChange();
|
||||
};
|
||||
|
||||
static void verifyCleanup(MockGpu *mockGpu)
|
||||
|
@ -93,7 +95,7 @@ void DrmTest::testAmsDetection()
|
|||
// gpu without planes should use legacy mode
|
||||
{
|
||||
const auto mockGpu = findPrimaryDevice(0);
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
|
||||
QVERIFY(!gpu->atomicModeSetting());
|
||||
}
|
||||
|
||||
|
@ -101,8 +103,8 @@ void DrmTest::testAmsDetection()
|
|||
{
|
||||
const auto mockGpu = findPrimaryDevice(0);
|
||||
mockGpu->planes << std::make_shared<MockPlane>(mockGpu.get(), PlaneType::Primary, 0);
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
|
||||
gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
|
||||
gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
|
||||
QVERIFY(gpu->atomicModeSetting());
|
||||
}
|
||||
|
||||
|
@ -110,7 +112,7 @@ void DrmTest::testAmsDetection()
|
|||
{
|
||||
const auto mockGpu = findPrimaryDevice(0);
|
||||
mockGpu->deviceCaps[MOCKDRM_DEVICE_CAP_ATOMIC] = 0;
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
|
||||
QVERIFY(!gpu->atomicModeSetting());
|
||||
gpu.reset();
|
||||
verifyCleanup(mockGpu.get());
|
||||
|
@ -131,7 +133,7 @@ void DrmTest::testOutputDetection()
|
|||
const auto session = Session::create(Session::Type::Noop);
|
||||
const auto backend = std::make_unique<DrmBackend>(session.get());
|
||||
const auto renderBackend = backend->createQPainterBackend();
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
|
||||
QVERIFY(gpu->updateOutputs());
|
||||
|
||||
// 3 outputs should be detected, one of them non-desktop
|
||||
|
@ -178,7 +180,7 @@ void DrmTest::testZeroModesHandling()
|
|||
const auto session = Session::create(Session::Type::Noop);
|
||||
const auto backend = std::make_unique<DrmBackend>(session.get());
|
||||
const auto renderBackend = backend->createQPainterBackend();
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
|
||||
|
||||
// connector with zero modes should be ignored
|
||||
conn->modes.clear();
|
||||
|
@ -295,7 +297,7 @@ void DrmTest::testModeGeneration()
|
|||
const auto session = Session::create(Session::Type::Noop);
|
||||
const auto backend = std::make_unique<DrmBackend>(session.get());
|
||||
const auto renderBackend = backend->createQPainterBackend();
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
|
||||
|
||||
QFETCH(QSize, nativeMode);
|
||||
QFETCH(QList<QSize>, expectedModes);
|
||||
|
@ -338,7 +340,7 @@ void DrmTest::testConnectorLifetime()
|
|||
const auto session = Session::create(Session::Type::Noop);
|
||||
const auto backend = std::make_unique<DrmBackend>(session.get());
|
||||
const auto renderBackend = backend->createQPainterBackend();
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
|
||||
|
||||
QVERIFY(gpu->updateOutputs());
|
||||
QCOMPARE(gpu->drmOutputs().size(), 1);
|
||||
|
@ -364,6 +366,8 @@ void DrmTest::testModeset_data()
|
|||
|
||||
void DrmTest::testModeset()
|
||||
{
|
||||
// to reenable, make this part of an integration test, so that kwinApp() isn't nullptr
|
||||
QSKIP("this test needs output pipelines to be enabled by default, which is no longer the case");
|
||||
// test if doing a modeset would succeed
|
||||
QFETCH(int, AMS);
|
||||
const auto mockGpu = findPrimaryDevice(5);
|
||||
|
@ -375,7 +379,7 @@ void DrmTest::testModeset()
|
|||
const auto session = Session::create(Session::Type::Noop);
|
||||
const auto backend = std::make_unique<DrmBackend>(session.get());
|
||||
const auto renderBackend = backend->createQPainterBackend();
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->devNode, mockGpu->fd, 0);
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
|
||||
|
||||
QVERIFY(gpu->updateOutputs());
|
||||
QCOMPARE(gpu->drmOutputs().size(), 1);
|
||||
|
@ -384,12 +388,40 @@ void DrmTest::testModeset()
|
|||
layer->beginFrame();
|
||||
output->renderLoop()->prepareNewFrame();
|
||||
output->renderLoop()->beginPaint();
|
||||
layer->endFrame(infiniteRegion(), infiniteRegion());
|
||||
QVERIFY(output->present(std::make_shared<OutputFrame>(output->renderLoop())));
|
||||
const auto frame = std::make_shared<OutputFrame>(output->renderLoop(), std::chrono::nanoseconds(1'000'000'000'000 / output->refreshRate()));
|
||||
layer->endFrame(infiniteRegion(), infiniteRegion(), frame.get());
|
||||
QVERIFY(output->present(frame));
|
||||
|
||||
gpu.reset();
|
||||
verifyCleanup(mockGpu.get());
|
||||
}
|
||||
|
||||
void DrmTest::testVrrChange()
|
||||
{
|
||||
const auto mockGpu = findPrimaryDevice(5);
|
||||
mockGpu->deviceCaps[MOCKDRM_DEVICE_CAP_ATOMIC] = 1;
|
||||
|
||||
const auto conn = std::make_shared<MockConnector>(mockGpu.get());
|
||||
conn->setVrrCapable(false);
|
||||
mockGpu->connectors.push_back(conn);
|
||||
|
||||
const auto session = Session::create(Session::Type::Noop);
|
||||
const auto backend = std::make_unique<DrmBackend>(session.get());
|
||||
const auto renderBackend = backend->createQPainterBackend();
|
||||
auto gpu = std::make_unique<DrmGpu>(backend.get(), mockGpu->fd, DrmDevice::open(mockGpu->devNode));
|
||||
|
||||
QVERIFY(gpu->updateOutputs());
|
||||
const auto output = gpu->drmOutputs().front();
|
||||
QVERIFY(!(output->capabilities() & Output::Capability::Vrr));
|
||||
|
||||
QSignalSpy capsChanged(output, &Output::capabilitiesChanged);
|
||||
|
||||
conn->setVrrCapable(true);
|
||||
QVERIFY(gpu->updateOutputs());
|
||||
QCOMPARE(gpu->drmOutputs().front(), output);
|
||||
QCOMPARE(capsChanged.count(), 1);
|
||||
QVERIFY(output->capabilities() & Output::Capability::Vrr);
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(DrmTest)
|
||||
#include "drmTest.moc"
|
||||
|
|
|
@ -212,6 +212,14 @@ void MockConnector::addMode(uint32_t width, uint32_t height, float refreshRate,
|
|||
free(modeInfo);
|
||||
}
|
||||
|
||||
void MockConnector::setVrrCapable(bool cap)
|
||||
{
|
||||
auto &prop = *std::ranges::find_if(props, [](const auto &prop) {
|
||||
return prop.name == "vrr_capable";
|
||||
});
|
||||
prop.value = cap ? 1 : 0;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
MockCrtc::MockCrtc(MockGpu *gpu, const std::shared_ptr<MockPlane> &legacyPlane, int pipeIndex, int gamma_size)
|
||||
|
|
|
@ -71,6 +71,7 @@ public:
|
|||
~MockConnector() = default;
|
||||
|
||||
void addMode(uint32_t width, uint32_t height, float refreshRate, bool preferred = false);
|
||||
void setVrrCapable(bool cap);
|
||||
|
||||
drmModeConnection connection;
|
||||
uint32_t type;
|
||||
|
|
|
@ -14,7 +14,7 @@ kwineffects_unit_tests(
|
|||
timelinetest
|
||||
)
|
||||
|
||||
add_executable(kwinglplatformtest kwinglplatformtest.cpp mock_gl.cpp ../../src/opengl/glplatform.cpp ../../src/opengl/openglcontext.cpp ../../src/utils/version.cpp)
|
||||
add_executable(kwinglplatformtest kwinglplatformtest.cpp ../../src/opengl/glplatform.cpp ../../src/utils/version.cpp)
|
||||
add_test(NAME kwineffects-kwinglplatformtest COMMAND kwinglplatformtest)
|
||||
target_link_libraries(kwinglplatformtest Qt::Test Qt::Gui KF6::ConfigCore XCB::XCB)
|
||||
target_link_libraries(kwinglplatformtest Qt::Test Qt::Gui KF6::ConfigCore)
|
||||
ecm_mark_as_test(kwinglplatformtest)
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "mock_gl.h"
|
||||
#include "opengl/glplatform.h"
|
||||
#include <QTest>
|
||||
|
||||
|
@ -18,33 +17,18 @@ Q_DECLARE_METATYPE(KWin::ChipClass)
|
|||
|
||||
using namespace KWin;
|
||||
|
||||
void KWin::cleanupGL()
|
||||
{
|
||||
GLPlatform::cleanup();
|
||||
}
|
||||
|
||||
class GLPlatformTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void cleanup();
|
||||
|
||||
void testDriverToString_data();
|
||||
void testDriverToString();
|
||||
void testChipClassToString_data();
|
||||
void testChipClassToString();
|
||||
void testPriorDetect();
|
||||
void testDetect_data();
|
||||
void testDetect();
|
||||
};
|
||||
|
||||
void GLPlatformTest::cleanup()
|
||||
{
|
||||
cleanupGL();
|
||||
delete s_gl;
|
||||
s_gl = nullptr;
|
||||
}
|
||||
|
||||
void GLPlatformTest::testDriverToString_data()
|
||||
{
|
||||
QTest::addColumn<Driver>("driver");
|
||||
|
@ -142,47 +126,6 @@ void GLPlatformTest::testChipClassToString()
|
|||
QTEST(GLPlatform::chipClassToString(chipClass), "expected");
|
||||
}
|
||||
|
||||
void GLPlatformTest::testPriorDetect()
|
||||
{
|
||||
auto *gl = GLPlatform::instance();
|
||||
QVERIFY(gl);
|
||||
QCOMPARE(gl->supports(GLFeature::LooseBinding), false);
|
||||
|
||||
QCOMPARE(gl->glVersion(), Version());
|
||||
QCOMPARE(gl->glslVersion(), Version());
|
||||
QCOMPARE(gl->mesaVersion(), Version());
|
||||
QCOMPARE(gl->driverVersion(), Version());
|
||||
|
||||
QCOMPARE(gl->driver(), Driver_Unknown);
|
||||
QCOMPARE(gl->chipClass(), UnknownChipClass);
|
||||
|
||||
QCOMPARE(gl->isMesaDriver(), false);
|
||||
QCOMPARE(gl->isRadeon(), false);
|
||||
QCOMPARE(gl->isNvidia(), false);
|
||||
QCOMPARE(gl->isIntel(), false);
|
||||
QCOMPARE(gl->isPanfrost(), false);
|
||||
QCOMPARE(gl->isLima(), false);
|
||||
QCOMPARE(gl->isVideoCore4(), false);
|
||||
QCOMPARE(gl->isVideoCore3D(), false);
|
||||
|
||||
QCOMPARE(gl->isVirtualBox(), false);
|
||||
QCOMPARE(gl->isVMware(), false);
|
||||
|
||||
QCOMPARE(gl->isSoftwareEmulation(), false);
|
||||
QCOMPARE(gl->isVirtualMachine(), false);
|
||||
|
||||
QCOMPARE(gl->glVersionString(), QByteArray());
|
||||
QCOMPARE(gl->glRendererString(), QByteArray());
|
||||
QCOMPARE(gl->glVendorString(), QByteArray());
|
||||
QCOMPARE(gl->glShadingLanguageVersionString(), QByteArray());
|
||||
|
||||
QCOMPARE(gl->isLooseBinding(), false);
|
||||
QCOMPARE(gl->isGLES(), false);
|
||||
QCOMPARE(gl->recommendedCompositor(), QPainterCompositing);
|
||||
QCOMPARE(gl->preferBufferSubData(), false);
|
||||
QCOMPARE(gl->platformInterface(), NoOpenGLPlatformInterface);
|
||||
}
|
||||
|
||||
void GLPlatformTest::testDetect_data()
|
||||
{
|
||||
QTest::addColumn<QString>("configFile");
|
||||
|
@ -222,60 +165,50 @@ void GLPlatformTest::testDetect()
|
|||
QFETCH(QString, configFile);
|
||||
KConfig config(configFile);
|
||||
const KConfigGroup driverGroup = config.group(QStringLiteral("Driver"));
|
||||
s_gl = new MockGL;
|
||||
s_gl->getString.vendor = driverGroup.readEntry("Vendor").toUtf8();
|
||||
s_gl->getString.renderer = driverGroup.readEntry("Renderer").toUtf8();
|
||||
s_gl->getString.version = driverGroup.readEntry("Version").toUtf8();
|
||||
s_gl->getString.shadingLanguageVersion = driverGroup.readEntry("ShadingLanguageVersion").toUtf8();
|
||||
s_gl->getString.extensions = QList<QByteArray>{QByteArrayLiteral("GL_ARB_shader_objects"),
|
||||
QByteArrayLiteral("GL_ARB_fragment_shader"),
|
||||
QByteArrayLiteral("GL_ARB_vertex_shader"),
|
||||
QByteArrayLiteral("GL_ARB_texture_non_power_of_two")};
|
||||
s_gl->getString.extensionsString = QByteArray();
|
||||
|
||||
auto *gl = GLPlatform::instance();
|
||||
QVERIFY(gl);
|
||||
gl->detect(EglPlatformInterface);
|
||||
QCOMPARE(gl->platformInterface(), EglPlatformInterface);
|
||||
const auto version = driverGroup.readEntry("Version").toUtf8();
|
||||
const auto glslVersion = driverGroup.readEntry("ShadingLanguageVersion").toUtf8();
|
||||
const auto renderer = driverGroup.readEntry("Renderer").toUtf8();
|
||||
const auto vendor = driverGroup.readEntry("Vendor").toUtf8();
|
||||
GLPlatform gl(EglPlatformInterface, version, glslVersion, renderer, vendor);
|
||||
QCOMPARE(gl.platformInterface(), EglPlatformInterface);
|
||||
|
||||
const KConfigGroup settingsGroup = config.group(QStringLiteral("Settings"));
|
||||
|
||||
QCOMPARE(gl->supports(GLFeature::LooseBinding), settingsGroup.readEntry("LooseBinding", false));
|
||||
QCOMPARE(gl.isLooseBinding(), settingsGroup.readEntry("LooseBinding", false));
|
||||
|
||||
QCOMPARE(gl->glVersion(), readVersion(settingsGroup, "GLVersion"));
|
||||
QCOMPARE(gl->glslVersion(), readVersion(settingsGroup, "GLSLVersion"));
|
||||
QCOMPARE(gl->mesaVersion(), readVersion(settingsGroup, "MesaVersion"));
|
||||
QCOMPARE(gl.glVersion(), readVersion(settingsGroup, "GLVersion"));
|
||||
QCOMPARE(gl.glslVersion(), readVersion(settingsGroup, "GLSLVersion"));
|
||||
QCOMPARE(gl.mesaVersion(), readVersion(settingsGroup, "MesaVersion"));
|
||||
QEXPECT_FAIL("amd-catalyst-radeonhd-7700M-3.1.13399", "Detects GL version instead of driver version", Continue);
|
||||
QCOMPARE(gl->driverVersion(), readVersion(settingsGroup, "DriverVersion"));
|
||||
QCOMPARE(gl.driverVersion(), readVersion(settingsGroup, "DriverVersion"));
|
||||
|
||||
QCOMPARE(gl->driver(), Driver(settingsGroup.readEntry("Driver", int(Driver_Unknown))));
|
||||
QCOMPARE(gl->chipClass(), ChipClass(settingsGroup.readEntry("ChipClass", int(UnknownChipClass))));
|
||||
QCOMPARE(gl.driver(), Driver(settingsGroup.readEntry("Driver", int(Driver_Unknown))));
|
||||
QCOMPARE(gl.chipClass(), ChipClass(settingsGroup.readEntry("ChipClass", int(UnknownChipClass))));
|
||||
|
||||
QCOMPARE(gl->isMesaDriver(), settingsGroup.readEntry("Mesa", false));
|
||||
QCOMPARE(gl->isRadeon(), settingsGroup.readEntry("Radeon", false));
|
||||
QCOMPARE(gl->isNvidia(), settingsGroup.readEntry("Nvidia", false));
|
||||
QCOMPARE(gl->isIntel(), settingsGroup.readEntry("Intel", false));
|
||||
QCOMPARE(gl->isVirtualBox(), settingsGroup.readEntry("VirtualBox", false));
|
||||
QCOMPARE(gl->isVMware(), settingsGroup.readEntry("VMware", false));
|
||||
QCOMPARE(gl->isAdreno(), settingsGroup.readEntry("Adreno", false));
|
||||
QCOMPARE(gl->isPanfrost(), settingsGroup.readEntry("Panfrost", false));
|
||||
QCOMPARE(gl->isLima(), settingsGroup.readEntry("Lima", false));
|
||||
QCOMPARE(gl->isVideoCore4(), settingsGroup.readEntry("VC4", false));
|
||||
QCOMPARE(gl->isVideoCore3D(), settingsGroup.readEntry("V3D", false));
|
||||
QCOMPARE(gl->isVirgl(), settingsGroup.readEntry("Virgl", false));
|
||||
QCOMPARE(gl.isMesaDriver(), settingsGroup.readEntry("Mesa", false));
|
||||
QCOMPARE(gl.isRadeon(), settingsGroup.readEntry("Radeon", false));
|
||||
QCOMPARE(gl.isNvidia(), settingsGroup.readEntry("Nvidia", false));
|
||||
QCOMPARE(gl.isIntel(), settingsGroup.readEntry("Intel", false));
|
||||
QCOMPARE(gl.isVirtualBox(), settingsGroup.readEntry("VirtualBox", false));
|
||||
QCOMPARE(gl.isVMware(), settingsGroup.readEntry("VMware", false));
|
||||
QCOMPARE(gl.isAdreno(), settingsGroup.readEntry("Adreno", false));
|
||||
QCOMPARE(gl.isPanfrost(), settingsGroup.readEntry("Panfrost", false));
|
||||
QCOMPARE(gl.isLima(), settingsGroup.readEntry("Lima", false));
|
||||
QCOMPARE(gl.isVideoCore4(), settingsGroup.readEntry("VC4", false));
|
||||
QCOMPARE(gl.isVideoCore3D(), settingsGroup.readEntry("V3D", false));
|
||||
QCOMPARE(gl.isVirgl(), settingsGroup.readEntry("Virgl", false));
|
||||
|
||||
QCOMPARE(gl->isSoftwareEmulation(), settingsGroup.readEntry("SoftwareEmulation", false));
|
||||
QCOMPARE(gl->isVirtualMachine(), settingsGroup.readEntry("VirtualMachine", false));
|
||||
QCOMPARE(gl.isVirtualMachine(), settingsGroup.readEntry("VirtualMachine", false));
|
||||
|
||||
QCOMPARE(gl->glVersionString(), s_gl->getString.version);
|
||||
QCOMPARE(gl->glRendererString(), s_gl->getString.renderer);
|
||||
QCOMPARE(gl->glVendorString(), s_gl->getString.vendor);
|
||||
QCOMPARE(gl->glShadingLanguageVersionString(), s_gl->getString.shadingLanguageVersion);
|
||||
QCOMPARE(gl.glVersionString(), version);
|
||||
QCOMPARE(gl.glRendererString(), renderer);
|
||||
QCOMPARE(gl.glVendorString(), vendor);
|
||||
QCOMPARE(gl.glShadingLanguageVersionString(), glslVersion);
|
||||
|
||||
QCOMPARE(gl->isLooseBinding(), settingsGroup.readEntry("LooseBinding", false));
|
||||
QCOMPARE(gl->isGLES(), settingsGroup.readEntry("GLES", false));
|
||||
QCOMPARE(gl->recommendedCompositor(), CompositingType(settingsGroup.readEntry("Compositor", int(NoCompositing))));
|
||||
QCOMPARE(gl->preferBufferSubData(), settingsGroup.readEntry("PreferBufferSubData", false));
|
||||
QCOMPARE(gl.isLooseBinding(), settingsGroup.readEntry("LooseBinding", false));
|
||||
QCOMPARE(gl.recommendedCompositor(), CompositingType(settingsGroup.readEntry("Compositor", int(NoCompositing))));
|
||||
QCOMPARE(gl.preferBufferSubData(), settingsGroup.readEntry("PreferBufferSubData", false));
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(GLPlatformTest)
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "mock_gl.h"
|
||||
#include <epoxy/gl.h>
|
||||
|
||||
MockGL *s_gl = nullptr;
|
||||
|
||||
static const GLubyte *mock_glGetString(GLenum name)
|
||||
{
|
||||
if (!s_gl) {
|
||||
return nullptr;
|
||||
}
|
||||
switch (name) {
|
||||
case GL_VENDOR:
|
||||
return (const GLubyte *)s_gl->getString.vendor.constData();
|
||||
case GL_RENDERER:
|
||||
return (const GLubyte *)s_gl->getString.renderer.constData();
|
||||
case GL_VERSION:
|
||||
return (const GLubyte *)s_gl->getString.version.constData();
|
||||
case GL_EXTENSIONS:
|
||||
return (const GLubyte *)s_gl->getString.extensionsString.constData();
|
||||
case GL_SHADING_LANGUAGE_VERSION:
|
||||
return (const GLubyte *)s_gl->getString.shadingLanguageVersion.constData();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static const GLubyte *mock_glGetStringi(GLenum name, GLuint index)
|
||||
{
|
||||
if (!s_gl) {
|
||||
return nullptr;
|
||||
}
|
||||
if (name == GL_EXTENSIONS && index < uint(s_gl->getString.extensions.count())) {
|
||||
return (const GLubyte *)s_gl->getString.extensions.at(index).constData();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void mock_glGetIntegerv(GLenum pname, GLint *data)
|
||||
{
|
||||
if (pname == GL_NUM_EXTENSIONS) {
|
||||
if (data && s_gl) {
|
||||
*data = s_gl->getString.extensions.count();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PFNGLGETSTRINGPROC epoxy_glGetString = mock_glGetString;
|
||||
PFNGLGETSTRINGIPROC epoxy_glGetStringi = mock_glGetStringi;
|
||||
PFNGLGETINTEGERVPROC epoxy_glGetIntegerv = mock_glGetIntegerv;
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#ifndef MOCK_GL_H
|
||||
#define MOCK_GL_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QList>
|
||||
|
||||
struct MockGL
|
||||
{
|
||||
struct
|
||||
{
|
||||
QByteArray vendor;
|
||||
QByteArray renderer;
|
||||
QByteArray version;
|
||||
QList<QByteArray> extensions;
|
||||
QByteArray extensionsString;
|
||||
QByteArray shadingLanguageVersion;
|
||||
} getString;
|
||||
};
|
||||
|
||||
extern MockGL *s_gl;
|
||||
|
||||
#endif
|
|
@ -25,6 +25,36 @@ qt6_generate_wayland_protocol_client_sources(KWinIntegrationTestFramework
|
|||
${PLASMA_WAYLAND_PROTOCOLS_DIR}/fake-input.xml
|
||||
)
|
||||
|
||||
if (Qt6_VERSION VERSION_LESS "6.7.1")
|
||||
# the qtwaylandscanner macro cannot handle the mismatched file name and <protocol name=""
|
||||
find_package(QtWaylandScanner REQUIRED)
|
||||
if (WaylandProtocols_VERSION VERSION_LESS 1.36)
|
||||
ecm_add_qtwayland_client_protocol(KWinIntegrationTestFramework
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/xdg-dialog/xdg-dialog-v1.xml
|
||||
BASENAME dialog-v1
|
||||
)
|
||||
target_compile_definitions(KWinIntegrationTestFramework PUBLIC
|
||||
-DHAVE_XDG_DIALOG_V1_HEADER=0
|
||||
)
|
||||
else()
|
||||
ecm_add_qtwayland_client_protocol(KWinIntegrationTestFramework
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/xdg-dialog/xdg-dialog-v1.xml
|
||||
BASENAME xdg-dialog-v1
|
||||
)
|
||||
target_compile_definitions(KWinIntegrationTestFramework PUBLIC
|
||||
-DHAVE_XDG_DIALOG_V1_HEADER=1
|
||||
)
|
||||
endif()
|
||||
else()
|
||||
qt6_generate_wayland_protocol_client_sources(KWinIntegrationTestFramework
|
||||
FILES
|
||||
${WaylandProtocols_DATADIR}/staging/xdg-dialog/xdg-dialog-v1.xml
|
||||
)
|
||||
target_compile_definitions(KWinIntegrationTestFramework PUBLIC
|
||||
-DHAVE_XDG_DIALOG_V1_HEADER=1
|
||||
)
|
||||
endif()
|
||||
|
||||
target_sources(KWinIntegrationTestFramework PRIVATE
|
||||
generic_scene_opengl_test.cpp
|
||||
kwin_wayland_test.cpp
|
||||
|
@ -33,20 +63,21 @@ target_sources(KWinIntegrationTestFramework PRIVATE
|
|||
target_link_libraries(KWinIntegrationTestFramework
|
||||
PUBLIC
|
||||
Qt::Test
|
||||
Qt::Concurrent
|
||||
Plasma::KWaylandClient
|
||||
Wayland::Client
|
||||
Libdrm::Libdrm
|
||||
kwin
|
||||
|
||||
PRIVATE
|
||||
# Own libraries
|
||||
KWinXwaylandServerModule
|
||||
|
||||
# Static plugins
|
||||
KWinQpaPlugin
|
||||
KF6WindowSystemKWinPlugin
|
||||
KF6IdleTimeKWinPlugin
|
||||
)
|
||||
if(KWIN_BUILD_X11)
|
||||
target_link_libraries(KWinIntegrationTestFramework PRIVATE KWinXwaylandServerModule)
|
||||
endif()
|
||||
if(TARGET KF6GlobalAccelKWinPlugin)
|
||||
target_link_libraries(KWinIntegrationTestFramework PUBLIC KF6GlobalAccelKWinPlugin)
|
||||
endif()
|
||||
|
@ -60,6 +91,9 @@ function(integrationTest)
|
|||
set(oneValueArgs NAME)
|
||||
set(multiValueArgs SRCS LIBS)
|
||||
cmake_parse_arguments(ARGS "${optionArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
if (NOT KWIN_BUILD_X11 AND ARGS_LIBS MATCHES XCB::)
|
||||
return()
|
||||
endif()
|
||||
add_executable(${ARGS_NAME} ${ARGS_SRCS})
|
||||
target_link_libraries(${ARGS_NAME} KWinIntegrationTestFramework Qt::Test ${ARGS_LIBS})
|
||||
if(${ARGS_BUILTIN_EFFECTS})
|
||||
|
@ -67,12 +101,14 @@ function(integrationTest)
|
|||
endif()
|
||||
add_test(NAME kwin-${ARGS_NAME} COMMAND dbus-run-session ${CMAKE_BINARY_DIR}/bin/${ARGS_NAME})
|
||||
endfunction()
|
||||
|
||||
integrationTest(NAME testDontCrashGlxgears SRCS dont_crash_glxgears.cpp LIBS KF6::I18n KDecoration2::KDecoration)
|
||||
if(KWIN_BUILD_X11)
|
||||
integrationTest(NAME testDontCrashGlxgears SRCS dont_crash_glxgears.cpp LIBS KF6::I18n KDecoration2::KDecoration)
|
||||
endif()
|
||||
if (KWIN_BUILD_SCREENLOCKER)
|
||||
integrationTest(NAME testLockScreen SRCS lockscreen.cpp LIBS KF6::GlobalAccel)
|
||||
endif()
|
||||
integrationTest(NAME testBounceKeys SRCS bounce_keys_test.cpp)
|
||||
integrationTest(NAME testButtonRebind SRCS buttonrebind_test.cpp)
|
||||
integrationTest(NAME testDecorationInput SRCS decoration_input_test.cpp LIBS KDecoration2::KDecoration KDecoration2::KDecoration2Private)
|
||||
integrationTest(NAME testInternalWindow SRCS internal_window.cpp)
|
||||
integrationTest(NAME testTouchInput SRCS touch_input_test.cpp)
|
||||
|
@ -82,15 +118,12 @@ integrationTest(NAME testPlatformCursor SRCS platformcursor.cpp)
|
|||
integrationTest(NAME testDontCrashCancelAnimation SRCS dont_crash_cancel_animation.cpp LIBS KDecoration2::KDecoration)
|
||||
integrationTest(NAME testTransientPlacement SRCS transient_placement.cpp)
|
||||
integrationTest(NAME testDebugConsole SRCS debug_console_test.cpp)
|
||||
integrationTest(NAME testDontCrashEmptyDeco SRCS dont_crash_empty_deco.cpp LIBS KDecoration2::KDecoration)
|
||||
integrationTest(NAME testPlasmaSurface SRCS plasma_surface_test.cpp)
|
||||
integrationTest(NAME testMaximized SRCS maximize_test.cpp LIBS KDecoration2::KDecoration KF6::Package)
|
||||
integrationTest(NAME testXdgShellWindow SRCS xdgshellwindow_test.cpp LIBS KDecoration2::KDecoration)
|
||||
integrationTest(NAME testXwaylandSelections SRCS xwayland_selections_test.cpp)
|
||||
integrationTest(NAME testSceneOpenGL SRCS scene_opengl_test.cpp )
|
||||
integrationTest(NAME testSceneOpenGLES SRCS scene_opengl_es_test.cpp )
|
||||
integrationTest(NAME testScreenChanges SRCS screen_changes_test.cpp)
|
||||
integrationTest(NAME testModiferOnlyShortcut SRCS modifier_only_shortcut_test.cpp LIBS XKB::XKB)
|
||||
if (KWIN_BUILD_TABBOX)
|
||||
integrationTest(NAME testTabBox SRCS tabbox_test.cpp)
|
||||
endif()
|
||||
|
@ -111,7 +144,7 @@ integrationTest(NAME testActivation SRCS activation_test.cpp)
|
|||
integrationTest(NAME testInputMethod SRCS inputmethod_test.cpp LIBS XKB::XKB)
|
||||
integrationTest(NAME testScreens SRCS screens_test.cpp)
|
||||
integrationTest(NAME testScreenEdges SRCS screenedges_test.cpp LIBS XCB::ICCCM)
|
||||
integrationTest(NAME testOutputChanges SRCS outputchanges_test.cpp)
|
||||
integrationTest(NAME testOutputChanges SRCS outputchanges_test.cpp LIBS XCB::ICCCM)
|
||||
integrationTest(NAME testTiles SRCS tiles_test.cpp)
|
||||
integrationTest(NAME testFractionalScaling SRCS fractional_scaling_test.cpp)
|
||||
integrationTest(NAME testMoveResize SRCS move_resize_window_test.cpp LIBS XCB::ICCCM)
|
||||
|
@ -131,7 +164,12 @@ integrationTest(NAME testXwaylandServerRestart SRCS xwaylandserver_restart_test.
|
|||
integrationTest(NAME testFakeInput SRCS fakeinput_test.cpp)
|
||||
integrationTest(NAME testSecurityContext SRCS security_context_test.cpp)
|
||||
integrationTest(NAME testStickyKeys SRCS sticky_keys_test.cpp)
|
||||
integrationTest(NAME testXinerama SRCS xinerama_test.cpp)
|
||||
if(KWIN_BUILD_X11)
|
||||
integrationTest(NAME testDontCrashEmptyDeco SRCS dont_crash_empty_deco.cpp LIBS KDecoration2::KDecoration)
|
||||
integrationTest(NAME testXwaylandSelections SRCS xwayland_selections_test.cpp)
|
||||
integrationTest(NAME testXinerama SRCS xinerama_test.cpp)
|
||||
integrationTest(NAME testX11KeyRead SRCS x11keyread.cpp LIBS XCB::XINPUT)
|
||||
endif()
|
||||
|
||||
qt_add_dbus_interfaces(DBUS_SRCS ${CMAKE_BINARY_DIR}/src/org.kde.kwin.VirtualKeyboard.xml)
|
||||
integrationTest(NAME testVirtualKeyboardDBus SRCS test_virtualkeyboard_dbus.cpp ${DBUS_SRCS})
|
||||
|
@ -140,8 +178,8 @@ if (KWIN_BUILD_GLOBALSHORTCUTS)
|
|||
integrationTest(NAME testGlobalShortcuts SRCS globalshortcuts_test.cpp LIBS XCB::ICCCM KF6::GlobalAccel KF6::I18n XKB::XKB)
|
||||
integrationTest(NAME testKWinBindings SRCS kwinbindings_test.cpp LIBS KF6::I18n)
|
||||
endif()
|
||||
if (TARGET PkgConfig::PipeWire)
|
||||
integrationTest(NAME testScreencasting SRCS screencasting_test.cpp pipewirecore.cpp pipewiresourcestream.cpp LIBS Qt::GuiPrivate PkgConfig::PipeWire)
|
||||
if (TARGET K::KPipeWire)
|
||||
integrationTest(NAME testScreencasting SRCS screencasting_test.cpp LIBS K::KPipeWire)
|
||||
endif()
|
||||
|
||||
if (KWIN_BUILD_ACTIVITIES)
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2024 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "kwin_wayland_test.h"
|
||||
|
||||
#include "pointer_input.h"
|
||||
#include "wayland_server.h"
|
||||
#include "workspace.h"
|
||||
|
||||
#include <KWayland/Client/keyboard.h>
|
||||
#include <KWayland/Client/seat.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
|
||||
using namespace KWin;
|
||||
|
||||
static const QString s_socketName = QStringLiteral("wayland_test_kwin_buttonrebind-0");
|
||||
|
||||
class TestButtonRebind : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void init();
|
||||
void cleanup();
|
||||
void initTestCase();
|
||||
|
||||
void testKey_data();
|
||||
void testKey();
|
||||
|
||||
private:
|
||||
quint32 timestamp = 1;
|
||||
};
|
||||
|
||||
void TestButtonRebind::init()
|
||||
{
|
||||
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat));
|
||||
QVERIFY(Test::waitForWaylandKeyboard());
|
||||
}
|
||||
|
||||
void TestButtonRebind::cleanup()
|
||||
{
|
||||
Test::destroyWaylandConnection();
|
||||
QVERIFY(QFile::remove(QStandardPaths::locate(QStandardPaths::ConfigLocation, QStringLiteral("kcminputrc"))));
|
||||
}
|
||||
|
||||
void TestButtonRebind::initTestCase()
|
||||
{
|
||||
qRegisterMetaType<KWin::Window *>();
|
||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||
QVERIFY(waylandServer()->init(s_socketName));
|
||||
Test::setOutputConfig({
|
||||
QRect(0, 0, 1280, 1024),
|
||||
QRect(1280, 0, 1280, 1024),
|
||||
});
|
||||
kwinApp()->start();
|
||||
QVERIFY(applicationStartedSpy.wait());
|
||||
}
|
||||
|
||||
void TestButtonRebind::testKey_data()
|
||||
{
|
||||
QTest::addColumn<QKeySequence>("boundKeys");
|
||||
QTest::addColumn<QList<quint32>>("expectedKeys");
|
||||
|
||||
QTest::newRow("single key") << QKeySequence(Qt::Key_A) << QList<quint32>{KEY_A};
|
||||
QTest::newRow("single modifier") << QKeySequence(Qt::Key_Control) << QList<quint32>{KEY_LEFTCTRL};
|
||||
QTest::newRow("single modifier plus key") << QKeySequence(Qt::ControlModifier | Qt::Key_N) << QList<quint32>{KEY_LEFTCTRL, KEY_N};
|
||||
QTest::newRow("multiple modifiers plus key") << QKeySequence(Qt::ControlModifier | Qt::MetaModifier | Qt::Key_Y) << QList<quint32>{KEY_LEFTCTRL, KEY_LEFTMETA, KEY_Y};
|
||||
QTest::newRow("delete") << QKeySequence(Qt::Key_Delete) << QList<quint32>{KEY_DELETE};
|
||||
QTest::newRow("keypad delete") << QKeySequence(Qt::KeypadModifier | Qt::Key_Delete) << QList<quint32>{KEY_KPDOT};
|
||||
QTest::newRow("keypad enter") << QKeySequence(Qt::KeypadModifier | Qt::Key_Enter) << QList<quint32>{KEY_KPENTER};
|
||||
}
|
||||
|
||||
void TestButtonRebind::testKey()
|
||||
{
|
||||
KConfigGroup buttonGroup = KSharedConfig::openConfig(QStringLiteral("kcminputrc"))->group(QStringLiteral("ButtonRebinds")).group(QStringLiteral("Mouse"));
|
||||
QFETCH(QKeySequence, boundKeys);
|
||||
buttonGroup.writeEntry("ExtraButton7", QStringList{"Key", boundKeys.toString(QKeySequence::PortableText)}, KConfig::Notify);
|
||||
buttonGroup.sync();
|
||||
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
|
||||
std::unique_ptr<KWayland::Client::Keyboard> keyboard(Test::waylandSeat()->createKeyboard());
|
||||
QSignalSpy enteredSpy(keyboard.get(), &KWayland::Client::Keyboard::entered);
|
||||
QSignalSpy keyChangedSpy(keyboard.get(), &KWayland::Client::Keyboard::keyChanged);
|
||||
QVERIFY(enteredSpy.wait());
|
||||
|
||||
// 0x119 is Qt::ExtraButton7
|
||||
Test::pointerButtonPressed(0x119, timestamp++);
|
||||
|
||||
QVERIFY(keyChangedSpy.wait());
|
||||
QFETCH(QList<quint32>, expectedKeys);
|
||||
QCOMPARE(keyChangedSpy.count(), expectedKeys.count());
|
||||
for (int i = 0; i < keyChangedSpy.count(); i++) {
|
||||
QCOMPARE(keyChangedSpy.at(i).at(0).value<quint32>(), expectedKeys.at(i));
|
||||
QCOMPARE(keyChangedSpy.at(i).at(1).value<KWayland::Client::Keyboard::KeyState>(), KWayland::Client::Keyboard::KeyState::Pressed);
|
||||
}
|
||||
Test::pointerButtonReleased(0x119, timestamp++);
|
||||
}
|
||||
|
||||
WAYLANDTEST_MAIN(TestButtonRebind)
|
||||
#include "buttonrebind_test.moc"
|
|
@ -11,11 +11,14 @@
|
|||
#include "core/output.h"
|
||||
#include "debug_console.h"
|
||||
#include "internalwindow.h"
|
||||
#include "utils/xcbutils.h"
|
||||
#include "wayland_server.h"
|
||||
#include "window.h"
|
||||
#include "workspace.h"
|
||||
|
||||
#if KWIN_BUILD_X11
|
||||
#include "utils/xcbutils.h"
|
||||
#endif
|
||||
|
||||
#include <KWayland/Client/compositor.h>
|
||||
#include <KWayland/Client/connection_thread.h>
|
||||
#include <KWayland/Client/shm_pool.h>
|
||||
|
@ -38,8 +41,10 @@ private Q_SLOTS:
|
|||
void cleanup();
|
||||
void topLevelTest_data();
|
||||
void topLevelTest();
|
||||
#if KWIN_BUILD_X11
|
||||
void testX11Window();
|
||||
void testX11Unmanaged();
|
||||
#endif
|
||||
void testWaylandClient();
|
||||
void testInternalWindow();
|
||||
void testClosingDebugConsole();
|
||||
|
@ -109,6 +114,7 @@ void DebugConsoleTest::topLevelTest()
|
|||
}
|
||||
}
|
||||
|
||||
#if KWIN_BUILD_X11
|
||||
void DebugConsoleTest::testX11Window()
|
||||
{
|
||||
DebugConsoleModel model;
|
||||
|
@ -281,6 +287,7 @@ void DebugConsoleTest::testX11Unmanaged()
|
|||
QVERIFY(!model.hasChildren(unmanagedTopLevelIndex));
|
||||
QVERIFY(!model2.hasChildren(model2.index(1, 0, QModelIndex())));
|
||||
}
|
||||
#endif
|
||||
|
||||
void DebugConsoleTest::testWaylandClient()
|
||||
{
|
||||
|
|
|
@ -52,10 +52,8 @@ private Q_SLOTS:
|
|||
void cleanup();
|
||||
void testAxis_data();
|
||||
void testAxis();
|
||||
void testDoubleClickOnAllDesktops_data();
|
||||
void testDoubleClickOnAllDesktops();
|
||||
void testDoubleClickClose();
|
||||
void testDoubleTap_data();
|
||||
void testDoubleTap();
|
||||
void testHover();
|
||||
void testPressToMove_data();
|
||||
|
@ -72,7 +70,7 @@ private Q_SLOTS:
|
|||
void testTooltipDoesntEatKeyEvents();
|
||||
|
||||
private:
|
||||
std::tuple<Window *, std::unique_ptr<KWayland::Client::Surface>, Test::XdgToplevel *> showWindow();
|
||||
std::tuple<Window *, std::unique_ptr<KWayland::Client::Surface>, std::unique_ptr<Test::XdgToplevel>, std::unique_ptr<Test::XdgToplevelDecorationV1>> showWindow();
|
||||
};
|
||||
|
||||
#define MOTION(target) Test::pointerMotion(target, timestamp++)
|
||||
|
@ -81,23 +79,23 @@ private:
|
|||
|
||||
#define RELEASE Test::pointerButtonReleased(BTN_LEFT, timestamp++)
|
||||
|
||||
std::tuple<Window *, std::unique_ptr<KWayland::Client::Surface>, Test::XdgToplevel *> DecorationInputTest::showWindow()
|
||||
std::tuple<Window *, std::unique_ptr<KWayland::Client::Surface>, std::unique_ptr<Test::XdgToplevel>, std::unique_ptr<Test::XdgToplevelDecorationV1>> DecorationInputTest::showWindow()
|
||||
{
|
||||
#define VERIFY(statement) \
|
||||
if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) \
|
||||
return {nullptr, nullptr, nullptr};
|
||||
return {nullptr, nullptr, nullptr, nullptr};
|
||||
#define COMPARE(actual, expected) \
|
||||
if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
|
||||
return {nullptr, nullptr, nullptr};
|
||||
return {nullptr, nullptr, nullptr, nullptr};
|
||||
|
||||
std::unique_ptr<KWayland::Client::Surface> surface{Test::createSurface()};
|
||||
VERIFY(surface.get());
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly, surface.get());
|
||||
VERIFY(shellSurface);
|
||||
Test::XdgToplevelDecorationV1 *decoration = Test::createXdgToplevelDecorationV1(shellSurface, shellSurface);
|
||||
VERIFY(decoration);
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly);
|
||||
VERIFY(shellSurface.get());
|
||||
std::unique_ptr<Test::XdgToplevelDecorationV1> decoration = Test::createXdgToplevelDecorationV1(shellSurface.get());
|
||||
VERIFY(decoration.get());
|
||||
|
||||
QSignalSpy decorationConfigureRequestedSpy(decoration, &Test::XdgToplevelDecorationV1::configureRequested);
|
||||
QSignalSpy decorationConfigureRequestedSpy(decoration.get(), &Test::XdgToplevelDecorationV1::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
|
||||
decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side);
|
||||
|
@ -114,7 +112,7 @@ std::tuple<Window *, std::unique_ptr<KWayland::Client::Surface>, Test::XdgToplev
|
|||
#undef VERIFY
|
||||
#undef COMPARE
|
||||
|
||||
return {window, std::move(surface), shellSurface};
|
||||
return {window, std::move(surface), std::move(shellSurface), std::move(decoration)};
|
||||
}
|
||||
|
||||
void DecorationInputTest::initTestCase()
|
||||
|
@ -174,7 +172,7 @@ void DecorationInputTest::testAxis()
|
|||
{
|
||||
static constexpr double oneTick = 15;
|
||||
|
||||
const auto [window, surface, shellSurface] = showWindow();
|
||||
const auto [window, surface, shellSurface, decoration] = showWindow();
|
||||
QVERIFY(window);
|
||||
QVERIFY(window->isDecorated());
|
||||
QVERIFY(!window->noBorder());
|
||||
|
@ -211,16 +209,6 @@ void DecorationInputTest::testAxis()
|
|||
QVERIFY(!window->keepAbove());
|
||||
}
|
||||
|
||||
void DecorationInputTest::testDoubleClickOnAllDesktops_data()
|
||||
{
|
||||
QTest::addColumn<QPoint>("decoPoint");
|
||||
QTest::addColumn<Qt::WindowFrameSection>("expectedSection");
|
||||
|
||||
QTest::newRow("topLeft") << QPoint(0, 0) << Qt::TopLeftSection;
|
||||
QTest::newRow("top") << QPoint(250, 0) << Qt::TopSection;
|
||||
QTest::newRow("topRight") << QPoint(499, 0) << Qt::TopRightSection;
|
||||
}
|
||||
|
||||
void KWin::DecorationInputTest::testDoubleClickOnAllDesktops()
|
||||
{
|
||||
KConfigGroup group = kwinApp()->config()->group(QStringLiteral("Windows"));
|
||||
|
@ -228,7 +216,7 @@ void KWin::DecorationInputTest::testDoubleClickOnAllDesktops()
|
|||
group.sync();
|
||||
workspace()->slotReconfigure();
|
||||
|
||||
const auto [window, surface, shellSurface] = showWindow();
|
||||
const auto [window, surface, shellSurface, decoration] = showWindow();
|
||||
QVERIFY(window);
|
||||
QVERIFY(window->isDecorated());
|
||||
QVERIFY(!window->noBorder());
|
||||
|
@ -249,21 +237,6 @@ void KWin::DecorationInputTest::testDoubleClickOnAllDesktops()
|
|||
PRESS;
|
||||
RELEASE;
|
||||
QVERIFY(!window->isOnAllDesktops());
|
||||
|
||||
// test top most deco pixel, BUG: 362860
|
||||
window->move(QPoint(0, 0));
|
||||
QFETCH(QPoint, decoPoint);
|
||||
MOTION(decoPoint);
|
||||
QVERIFY(input()->pointer()->decoration());
|
||||
QCOMPARE(input()->pointer()->decoration()->window(), window);
|
||||
QTEST(input()->pointer()->decoration()->decoration()->sectionUnderMouse(), "expectedSection");
|
||||
// double click
|
||||
PRESS;
|
||||
RELEASE;
|
||||
QVERIFY(!window->isOnAllDesktops());
|
||||
PRESS;
|
||||
RELEASE;
|
||||
QVERIFY(window->isOnAllDesktops());
|
||||
}
|
||||
|
||||
void DecorationInputTest::testDoubleClickClose()
|
||||
|
@ -274,13 +247,13 @@ void DecorationInputTest::testDoubleClickClose()
|
|||
group.sync();
|
||||
workspace()->slotReconfigure();
|
||||
|
||||
auto [window, surface, shellSurface] = showWindow();
|
||||
auto [window, surface, shellSurface, decoration] = showWindow();
|
||||
QVERIFY(window);
|
||||
QVERIFY(window->isDecorated());
|
||||
quint32 timestamp = 1;
|
||||
MOTION(QPoint(window->frameGeometry().center().x(), window->frameMargins().top() / 2.0));
|
||||
|
||||
connect(shellSurface, &Test::XdgToplevel::closeRequested, this, [&surface = surface]() {
|
||||
connect(shellSurface.get(), &Test::XdgToplevel::closeRequested, this, [&surface = surface]() {
|
||||
surface.reset();
|
||||
});
|
||||
|
||||
|
@ -297,16 +270,6 @@ void DecorationInputTest::testDoubleClickClose()
|
|||
window->unref();
|
||||
}
|
||||
|
||||
void DecorationInputTest::testDoubleTap_data()
|
||||
{
|
||||
QTest::addColumn<QPoint>("decoPoint");
|
||||
QTest::addColumn<Qt::WindowFrameSection>("expectedSection");
|
||||
|
||||
QTest::newRow("topLeft") << QPoint(10, 10) << Qt::TopLeftSection;
|
||||
QTest::newRow("top") << QPoint(260, 10) << Qt::TopSection;
|
||||
QTest::newRow("topRight") << QPoint(509, 10) << Qt::TopRightSection;
|
||||
}
|
||||
|
||||
void KWin::DecorationInputTest::testDoubleTap()
|
||||
{
|
||||
KConfigGroup group = kwinApp()->config()->group(QStringLiteral("Windows"));
|
||||
|
@ -314,7 +277,7 @@ void KWin::DecorationInputTest::testDoubleTap()
|
|||
group.sync();
|
||||
workspace()->slotReconfigure();
|
||||
|
||||
const auto [window, surface, shellSurface] = showWindow();
|
||||
const auto [window, surface, shellSurface, decoration] = showWindow();
|
||||
QVERIFY(window);
|
||||
QVERIFY(window->isDecorated());
|
||||
QVERIFY(!window->noBorder());
|
||||
|
@ -335,28 +298,11 @@ void KWin::DecorationInputTest::testDoubleTap()
|
|||
Test::touchDown(0, tapPoint, timestamp++);
|
||||
Test::touchUp(0, timestamp++);
|
||||
QVERIFY(!window->isOnAllDesktops());
|
||||
|
||||
// test top most deco pixel, BUG: 362860
|
||||
//
|
||||
// Not directly at (0, 0), otherwise ScreenEdgeInputFilter catches
|
||||
// event before DecorationEventFilter.
|
||||
window->move(QPoint(10, 10));
|
||||
QFETCH(QPoint, decoPoint);
|
||||
// double click
|
||||
Test::touchDown(0, decoPoint, timestamp++);
|
||||
QVERIFY(input()->touch()->decoration());
|
||||
QCOMPARE(input()->touch()->decoration()->window(), window);
|
||||
QTEST(input()->touch()->decoration()->decoration()->sectionUnderMouse(), "expectedSection");
|
||||
Test::touchUp(0, timestamp++);
|
||||
QVERIFY(!window->isOnAllDesktops());
|
||||
Test::touchDown(0, decoPoint, timestamp++);
|
||||
Test::touchUp(0, timestamp++);
|
||||
QVERIFY(window->isOnAllDesktops());
|
||||
}
|
||||
|
||||
void DecorationInputTest::testHover()
|
||||
{
|
||||
const auto [window, surface, shellSurface] = showWindow();
|
||||
const auto [window, surface, shellSurface, decoration] = showWindow();
|
||||
QVERIFY(window);
|
||||
QVERIFY(window->isDecorated());
|
||||
QVERIFY(!window->noBorder());
|
||||
|
@ -415,7 +361,7 @@ void DecorationInputTest::testPressToMove_data()
|
|||
|
||||
void DecorationInputTest::testPressToMove()
|
||||
{
|
||||
const auto [window, surface, shellSurface] = showWindow();
|
||||
const auto [window, surface, shellSurface, decoration] = showWindow();
|
||||
QVERIFY(window);
|
||||
QVERIFY(window->isDecorated());
|
||||
QVERIFY(!window->noBorder());
|
||||
|
@ -472,7 +418,7 @@ void DecorationInputTest::testTapToMove_data()
|
|||
|
||||
void DecorationInputTest::testTapToMove()
|
||||
{
|
||||
const auto [window, surface, shellSurface] = showWindow();
|
||||
const auto [window, surface, shellSurface, decoration] = showWindow();
|
||||
QVERIFY(window);
|
||||
QVERIFY(window->isDecorated());
|
||||
QVERIFY(!window->noBorder());
|
||||
|
@ -536,7 +482,7 @@ void DecorationInputTest::testResizeOutsideWindow()
|
|||
workspace()->slotReconfigure();
|
||||
|
||||
// now create window
|
||||
const auto [window, surface, shellSurface] = showWindow();
|
||||
const auto [window, surface, shellSurface, decoration] = showWindow();
|
||||
QVERIFY(window);
|
||||
QVERIFY(window->isDecorated());
|
||||
QVERIFY(!window->noBorder());
|
||||
|
@ -630,7 +576,7 @@ void DecorationInputTest::testModifierClickUnrestrictedMove()
|
|||
QCOMPARE(options->commandAll3(), Options::MouseUnrestrictedMove);
|
||||
|
||||
// create a window
|
||||
const auto [window, surface, shellSurface] = showWindow();
|
||||
const auto [window, surface, shellSurface, decoration] = showWindow();
|
||||
QVERIFY(window);
|
||||
QVERIFY(window->isDecorated());
|
||||
QVERIFY(!window->noBorder());
|
||||
|
@ -692,7 +638,7 @@ void DecorationInputTest::testModifierScrollOpacity()
|
|||
group.sync();
|
||||
workspace()->slotReconfigure();
|
||||
|
||||
const auto [window, surface, shellSurface] = showWindow();
|
||||
const auto [window, surface, shellSurface, decoration] = showWindow();
|
||||
QVERIFY(window);
|
||||
QVERIFY(window->isDecorated());
|
||||
QVERIFY(!window->noBorder());
|
||||
|
@ -750,7 +696,7 @@ void DecorationInputTest::testTouchEvents()
|
|||
{
|
||||
// this test verifies that the decoration gets a hover leave event on touch release
|
||||
// see BUG 386231
|
||||
const auto [window, surface, shellSurface] = showWindow();
|
||||
const auto [window, surface, shellSurface, decoration] = showWindow();
|
||||
QVERIFY(window);
|
||||
QVERIFY(window->isDecorated());
|
||||
QVERIFY(!window->noBorder());
|
||||
|
@ -796,7 +742,7 @@ void DecorationInputTest::testTooltipDoesntEatKeyEvents()
|
|||
QVERIFY(keyboard);
|
||||
QSignalSpy enteredSpy(keyboard, &KWayland::Client::Keyboard::entered);
|
||||
|
||||
const auto [window, surface, shellSurface] = showWindow();
|
||||
const auto [window, surface, shellSurface, decoration] = showWindow();
|
||||
QVERIFY(window);
|
||||
QVERIFY(window->isDecorated());
|
||||
QVERIFY(!window->noBorder());
|
||||
|
|
|
@ -131,7 +131,7 @@ void X11DesktopWindowTest::testDesktopWindow()
|
|||
QVERIFY(window);
|
||||
QCOMPARE(window->window(), windowId);
|
||||
QVERIFY(!window->isDecorated());
|
||||
QCOMPARE(window->windowType(), NET::Desktop);
|
||||
QCOMPARE(window->windowType(), WindowType::Desktop);
|
||||
QCOMPARE(window->frameGeometry(), windowGeometry);
|
||||
QVERIFY(window->isDesktop());
|
||||
QCOMPARE(window->depth(), 24);
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
#include "wayland_server.h"
|
||||
#include "window.h"
|
||||
#include "workspace.h"
|
||||
#if KWIN_BUILD_X11
|
||||
#include "x11window.h"
|
||||
#endif
|
||||
|
||||
#include <KDecoration2/Decoration>
|
||||
|
||||
|
@ -85,7 +87,7 @@ void DontCrashCancelAnimationFromAnimationEndedTest::testScript()
|
|||
// create a window
|
||||
std::unique_ptr<KWayland::Client::Surface> surface{Test::createSurface()};
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
// let's render
|
||||
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
|
|
|
@ -182,7 +182,7 @@ void ScriptedEffectsTest::testEffectsHandler()
|
|||
// create a window
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
auto *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
shellSurface->set_title("WindowA");
|
||||
auto *c = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
|
@ -265,7 +265,7 @@ void ScriptedEffectsTest::testAnimations()
|
|||
// animated after window added connect
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
auto *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
shellSurface->set_title("Window 1");
|
||||
auto *c = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
|
@ -371,7 +371,7 @@ void ScriptedEffectsTest::testFullScreenEffect()
|
|||
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
auto *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
shellSurface->set_title("Window 1");
|
||||
auto *c = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
|
@ -433,7 +433,7 @@ void ScriptedEffectsTest::testKeepAlive()
|
|||
// create a window
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
auto *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
auto *c = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(c);
|
||||
|
@ -479,7 +479,7 @@ void ScriptedEffectsTest::testGrab()
|
|||
// create test window
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(window);
|
||||
|
@ -509,7 +509,7 @@ void ScriptedEffectsTest::testGrabAlreadyGrabbedWindow()
|
|||
// create test window
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(window);
|
||||
|
@ -543,7 +543,7 @@ void ScriptedEffectsTest::testGrabAlreadyGrabbedWindowForced()
|
|||
// create test window
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(window);
|
||||
|
@ -572,7 +572,7 @@ void ScriptedEffectsTest::testUngrab()
|
|||
// create test window
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(window);
|
||||
|
@ -614,7 +614,7 @@ void ScriptedEffectsTest::testRedirect()
|
|||
// create test window
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(window);
|
||||
|
@ -690,7 +690,7 @@ void ScriptedEffectsTest::testComplete()
|
|||
// create test window
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(window);
|
||||
|
|
|
@ -42,7 +42,8 @@ private Q_SLOTS:
|
|||
void init();
|
||||
void cleanup();
|
||||
|
||||
void testShow();
|
||||
void testToplevel();
|
||||
void testPopup();
|
||||
};
|
||||
|
||||
void TestFractionalScale::initTestCase()
|
||||
|
@ -87,20 +88,73 @@ void TestFractionalScale::cleanup()
|
|||
Test::destroyWaylandConnection();
|
||||
}
|
||||
|
||||
void TestFractionalScale::testShow()
|
||||
void TestFractionalScale::testToplevel()
|
||||
{
|
||||
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
|
||||
std::unique_ptr<Test::FractionalScaleV1> fractionalScale(Test::createFractionalScaleV1(surface.get()));
|
||||
QSignalSpy fractionalScaleChanged(fractionalScale.get(), &Test::FractionalScaleV1::preferredScaleChanged);
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
|
||||
|
||||
// above call commits the surface and blocks for the configure event. We should have received the scale already
|
||||
// We are sent the value in 120ths
|
||||
QCOMPARE(fractionalScale->preferredScale(), 1.25 * 120);
|
||||
QCOMPARE(fractionalScaleChanged.count(), 1);
|
||||
QCOMPARE(fractionalScale->preferredScale(), std::round(1.25 * 120));
|
||||
|
||||
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(window);
|
||||
QCOMPARE(fractionalScaleChanged.count(), 1);
|
||||
QCOMPARE(fractionalScale->preferredScale(), std::round(1.25 * 120));
|
||||
|
||||
QCOMPARE(fractionalScale->preferredScale(), 1.25 * 120);
|
||||
// move to screen 2
|
||||
window->move(QPoint(1280, 0));
|
||||
|
||||
QVERIFY(Test::waylandSync());
|
||||
QCOMPARE(fractionalScaleChanged.count(), 2);
|
||||
QCOMPARE(fractionalScale->preferredScale(), std::round(2.0 * 120));
|
||||
}
|
||||
|
||||
void TestFractionalScale::testPopup()
|
||||
{
|
||||
std::unique_ptr<KWayland::Client::Surface> toplevelSurface(Test::createSurface());
|
||||
std::unique_ptr<Test::FractionalScaleV1> toplevelFractionalScale(Test::createFractionalScaleV1(toplevelSurface.get()));
|
||||
QSignalSpy toplevelFractionalScaleChanged(toplevelFractionalScale.get(), &Test::FractionalScaleV1::preferredScaleChanged);
|
||||
std::unique_ptr<Test::XdgToplevel> toplevel(Test::createXdgToplevelSurface(toplevelSurface.get()));
|
||||
|
||||
// above call commits the surface and blocks for the configure event. We should have received the scale already
|
||||
// We are sent the value in 120ths
|
||||
QCOMPARE(toplevelFractionalScaleChanged.count(), 1);
|
||||
QCOMPARE(toplevelFractionalScale->preferredScale(), std::round(1.25 * 120));
|
||||
|
||||
auto toplevelWindow = Test::renderAndWaitForShown(toplevelSurface.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(toplevelWindow);
|
||||
QCOMPARE(toplevelFractionalScaleChanged.count(), 1);
|
||||
QCOMPARE(toplevelFractionalScale->preferredScale(), std::round(1.25 * 120));
|
||||
|
||||
std::unique_ptr<KWayland::Client::Surface> popupSurface(Test::createSurface());
|
||||
std::unique_ptr<Test::FractionalScaleV1> popupFractionalScale(Test::createFractionalScaleV1(popupSurface.get()));
|
||||
QSignalSpy popupFractionalScaleChanged(popupFractionalScale.get(), &Test::FractionalScaleV1::preferredScaleChanged);
|
||||
std::unique_ptr<Test::XdgPositioner> positioner(Test::createXdgPositioner());
|
||||
positioner->set_size(10, 10);
|
||||
positioner->set_anchor_rect(10, 10, 10, 10);
|
||||
std::unique_ptr<Test::XdgPopup> popup(Test::createXdgPopupSurface(popupSurface.get(), toplevel->xdgSurface(), positioner.get()));
|
||||
|
||||
// above call commits the surface and blocks for the configure event. We should have received the scale already
|
||||
// We are sent the value in 120ths
|
||||
QCOMPARE(popupFractionalScaleChanged.count(), 1);
|
||||
QCOMPARE(popupFractionalScale->preferredScale(), std::round(1.25 * 120));
|
||||
|
||||
auto popupWindow = Test::renderAndWaitForShown(popupSurface.get(), QSize(10, 10), Qt::cyan);
|
||||
QVERIFY(popupWindow);
|
||||
QCOMPARE(popupFractionalScaleChanged.count(), 1);
|
||||
QCOMPARE(popupFractionalScale->preferredScale(), std::round(1.25 * 120));
|
||||
|
||||
// move the parent to screen 2
|
||||
toplevelWindow->move(QPoint(1280, 0));
|
||||
|
||||
QVERIFY(Test::waylandSync());
|
||||
QCOMPARE(toplevelFractionalScaleChanged.count(), 2);
|
||||
QCOMPARE(toplevelFractionalScale->preferredScale(), std::round(2.0 * 120));
|
||||
QCOMPARE(popupFractionalScaleChanged.count(), 2);
|
||||
QCOMPARE(popupFractionalScale->preferredScale(), std::round(2.0 * 120));
|
||||
}
|
||||
|
||||
WAYLANDTEST_MAIN(TestFractionalScale)
|
||||
|
|
|
@ -101,7 +101,7 @@ void InputStackingOrderTest::testPointerFocusUpdatesOnStackingOrderChange()
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface1 = Test::createSurface();
|
||||
QVERIFY(surface1);
|
||||
Test::XdgToplevel *shellSurface1 = Test::createXdgToplevelSurface(surface1.get(), surface1.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface1 = Test::createXdgToplevelSurface(surface1.get());
|
||||
QVERIFY(shellSurface1);
|
||||
render(surface1.get());
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -110,7 +110,7 @@ void InputStackingOrderTest::testPointerFocusUpdatesOnStackingOrderChange()
|
|||
|
||||
std::unique_ptr<KWayland::Client::Surface> surface2 = Test::createSurface();
|
||||
QVERIFY(surface2);
|
||||
Test::XdgToplevel *shellSurface2 = Test::createXdgToplevelSurface(surface2.get(), surface2.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface2 = Test::createXdgToplevelSurface(surface2.get());
|
||||
QVERIFY(shellSurface2);
|
||||
render(surface2.get());
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
|
|
@ -213,12 +213,12 @@ void InputMethodTest::testEnableDisableV3()
|
|||
kwinApp()->inputMethod()->hide();
|
||||
QVERIFY(!keyboardClient->isShown());
|
||||
|
||||
QSignalSpy windowShownSpy(keyboardClient, &Window::windowShown);
|
||||
QSignalSpy hiddenChangedSpy(keyboardClient, &Window::hiddenChanged);
|
||||
// Force enable the text input object. This is what's done by Gtk.
|
||||
textInputV3->enable();
|
||||
textInputV3->commit();
|
||||
|
||||
windowShownSpy.wait();
|
||||
hiddenChangedSpy.wait();
|
||||
QVERIFY(keyboardClient->isShown());
|
||||
|
||||
// disable text input and ensure that it is not hiding input panel without commit
|
||||
|
@ -361,7 +361,7 @@ void InputMethodTest::testSwitchFocusedSurfaces()
|
|||
|
||||
QList<Window *> windows;
|
||||
std::vector<std::unique_ptr<KWayland::Client::Surface>> surfaces;
|
||||
QList<Test::XdgToplevel *> toplevels;
|
||||
std::vector<std::unique_ptr<Test::XdgToplevel>> toplevels;
|
||||
// We create 3 surfaces
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
|
@ -369,7 +369,7 @@ void InputMethodTest::testSwitchFocusedSurfaces()
|
|||
windows += Test::renderAndWaitForShown(surface.get(), QSize(1280, 1024), Qt::red);
|
||||
QCOMPARE(workspace()->activeWindow(), windows.constLast());
|
||||
surfaces.push_back(std::move(surface));
|
||||
toplevels += shellSurface;
|
||||
toplevels.push_back(std::move(shellSurface));
|
||||
}
|
||||
QCOMPARE(windowAddedSpy.count(), 3);
|
||||
waylandServer()->seat()->setFocusedTextInputSurface(windows.constFirst()->surface());
|
||||
|
@ -388,12 +388,6 @@ void InputMethodTest::testSwitchFocusedSurfaces()
|
|||
waylandServer()->seat()->setFocusedTextInputSurface(windows.first()->surface());
|
||||
QVERIFY(activateSpy.count() || activateSpy.wait());
|
||||
QVERIFY(!kwinApp()->inputMethod()->isActive());
|
||||
|
||||
// Destroy the test window.
|
||||
for (int i = 0; i < windows.count(); ++i) {
|
||||
delete toplevels[i];
|
||||
QVERIFY(Test::waitForWindowClosed(windows[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void InputMethodTest::testV2V3SameClient()
|
||||
|
|
|
@ -500,16 +500,6 @@ void InternalWindowTest::testMove()
|
|||
internalWindow->move(QPoint(10, 20));
|
||||
QCOMPARE(internalWindow->frameGeometry(), QRect(10, 20, 100, 100));
|
||||
QTRY_COMPARE(win.geometry(), QRect(10, 20, 100, 100));
|
||||
|
||||
// now move with a Geometry update blocker
|
||||
{
|
||||
GeometryUpdatesBlocker blocker(internalWindow);
|
||||
internalWindow->move(QPoint(5, 10));
|
||||
// not synced!
|
||||
QCOMPARE(win.geometry(), QRect(10, 20, 100, 100));
|
||||
}
|
||||
// after destroying the blocker it should be synced
|
||||
QTRY_COMPARE(win.geometry(), QRect(5, 10, 100, 100));
|
||||
}
|
||||
|
||||
void InternalWindowTest::testSkipCloseAnimation_data()
|
||||
|
|
|
@ -15,11 +15,14 @@
|
|||
#include "inputmethod.h"
|
||||
#include "placement.h"
|
||||
#include "pluginmanager.h"
|
||||
#include "utils/xcbutils.h"
|
||||
#include "wayland_server.h"
|
||||
#include "workspace.h"
|
||||
|
||||
#if KWIN_BUILD_X11
|
||||
#include "utils/xcbutils.h"
|
||||
#include "xwayland/xwayland.h"
|
||||
#include "xwayland/xwaylandlauncher.h"
|
||||
#endif
|
||||
|
||||
#include <KPluginMetaData>
|
||||
|
||||
|
@ -52,6 +55,7 @@ WaylandTestApplication::WaylandTestApplication(OperationMode mode, int &argc, ch
|
|||
const QStringList configs{
|
||||
QStringLiteral("kaccessrc"),
|
||||
QStringLiteral("kglobalshortcutsrc"),
|
||||
QStringLiteral("kcminputrc"),
|
||||
};
|
||||
for (const QString &config : configs) {
|
||||
if (const QString &fileName = QStandardPaths::locate(QStandardPaths::ConfigLocation, config); !fileName.isEmpty()) {
|
||||
|
@ -79,6 +83,10 @@ WaylandTestApplication::WaylandTestApplication(OperationMode mode, int &argc, ch
|
|||
KConfigGroup windowsGroup = config->group(QStringLiteral("Windows"));
|
||||
windowsGroup.writeEntry("Placement", Placement::policyToString(PlacementSmart));
|
||||
windowsGroup.sync();
|
||||
KConfigGroup edgeBarrierGroup = config->group(QStringLiteral("EdgeBarrier"));
|
||||
edgeBarrierGroup.writeEntry("EdgeBarrier", 0);
|
||||
edgeBarrierGroup.writeEntry("CornerBarrier", false);
|
||||
edgeBarrierGroup.sync();
|
||||
setConfig(config);
|
||||
|
||||
const auto ownPath = libraryPaths().last();
|
||||
|
@ -87,7 +95,7 @@ WaylandTestApplication::WaylandTestApplication(OperationMode mode, int &argc, ch
|
|||
|
||||
setSession(Session::create(Session::Type::Noop));
|
||||
setOutputBackend(std::make_unique<VirtualBackend>());
|
||||
WaylandServer::create(this);
|
||||
m_waylandServer.reset(WaylandServer::create());
|
||||
setProcessStartupEnvironment(QProcessEnvironment::systemEnvironment());
|
||||
}
|
||||
|
||||
|
@ -99,13 +107,16 @@ WaylandTestApplication::~WaylandTestApplication()
|
|||
if (effects) {
|
||||
effects->unloadAllEffects();
|
||||
}
|
||||
#if KWIN_BUILD_X11
|
||||
m_xwayland.reset();
|
||||
#endif
|
||||
destroyVirtualInputDevices();
|
||||
destroyColorManager();
|
||||
destroyWorkspace();
|
||||
destroyInputMethod();
|
||||
destroyCompositor();
|
||||
destroyInput();
|
||||
m_waylandServer.reset();
|
||||
}
|
||||
|
||||
void WaylandTestApplication::createVirtualInputDevices()
|
||||
|
@ -179,10 +190,12 @@ void WaylandTestApplication::continueStartupWithScene()
|
|||
qFatal("Failed to initialize the Wayland server, exiting now");
|
||||
}
|
||||
|
||||
#if KWIN_BUILD_X11
|
||||
if (operationMode() == OperationModeXwayland) {
|
||||
m_xwayland = std::make_unique<Xwl::Xwayland>(this);
|
||||
m_xwayland->init();
|
||||
}
|
||||
#endif
|
||||
|
||||
notifyStarted();
|
||||
}
|
||||
|
@ -202,10 +215,12 @@ Test::VirtualInputDevice *WaylandTestApplication::virtualTouch() const
|
|||
return m_virtualTouch.get();
|
||||
}
|
||||
|
||||
#if KWIN_BUILD_X11
|
||||
XwaylandInterface *WaylandTestApplication::xwayland() const
|
||||
{
|
||||
return m_xwayland.get();
|
||||
}
|
||||
#endif
|
||||
|
||||
Test::FractionalScaleManagerV1::~FractionalScaleManagerV1()
|
||||
{
|
||||
|
@ -224,7 +239,11 @@ int Test::FractionalScaleV1::preferredScale()
|
|||
|
||||
void Test::FractionalScaleV1::wp_fractional_scale_v1_preferred_scale(uint32_t scale)
|
||||
{
|
||||
if (m_preferredScale == scale) {
|
||||
return;
|
||||
}
|
||||
m_preferredScale = scale;
|
||||
Q_EMIT preferredScaleChanged();
|
||||
}
|
||||
|
||||
void Test::setOutputConfig(const QList<QRect> &geometries)
|
||||
|
|
|
@ -34,6 +34,11 @@
|
|||
#include "qwayland-xdg-decoration-unstable-v1.h"
|
||||
#include "qwayland-xdg-shell.h"
|
||||
#include "qwayland-zkde-screencast-unstable-v1.h"
|
||||
#if HAVE_XDG_DIALOG_V1_HEADER
|
||||
#include "qwayland-xdg-dialog-v1.h"
|
||||
#else
|
||||
#include "qwayland-dialog-v1.h"
|
||||
#endif
|
||||
|
||||
namespace KWayland
|
||||
{
|
||||
|
@ -68,10 +73,15 @@ class ScreencastingV1;
|
|||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class WaylandServer;
|
||||
|
||||
#if KWIN_BUILD_X11
|
||||
namespace Xwl
|
||||
{
|
||||
class Xwayland;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace Test
|
||||
{
|
||||
|
@ -93,7 +103,9 @@ public:
|
|||
Test::VirtualInputDevice *virtualPointer() const;
|
||||
Test::VirtualInputDevice *virtualKeyboard() const;
|
||||
Test::VirtualInputDevice *virtualTouch() const;
|
||||
#if KWIN_BUILD_X11
|
||||
XwaylandInterface *xwayland() const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void performStartup() override;
|
||||
|
@ -105,7 +117,10 @@ private:
|
|||
void createVirtualInputDevices();
|
||||
void destroyVirtualInputDevices();
|
||||
|
||||
std::unique_ptr<WaylandServer> m_waylandServer;
|
||||
#if KWIN_BUILD_X11
|
||||
std::unique_ptr<Xwl::Xwayland> m_xwayland;
|
||||
#endif
|
||||
QString m_inputMethodServerToStart;
|
||||
|
||||
std::unique_ptr<Test::VirtualInputDevice> m_virtualPointer;
|
||||
|
@ -474,7 +489,7 @@ protected:
|
|||
|
||||
private:
|
||||
std::unique_ptr<KWayland::Client::Surface> m_inputSurface;
|
||||
QtWayland::zwp_input_panel_surface_v1 *m_inputMethodSurface = nullptr;
|
||||
std::unique_ptr<QtWayland::zwp_input_panel_surface_v1> m_inputMethodSurface;
|
||||
struct ::zwp_input_method_context_v1 *m_context = nullptr;
|
||||
Mode m_mode = Mode::TopLevel;
|
||||
};
|
||||
|
@ -496,8 +511,11 @@ public:
|
|||
protected:
|
||||
void wp_fractional_scale_v1_preferred_scale(uint32_t scale) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void preferredScaleChanged();
|
||||
|
||||
private:
|
||||
int m_preferredScale = 120;
|
||||
uint m_preferredScale = 120;
|
||||
};
|
||||
|
||||
class ScreenEdgeManagerV1 : public QObject, public QtWayland::kde_screen_edge_manager_v1
|
||||
|
@ -542,6 +560,19 @@ public:
|
|||
~SecurityContextManagerV1() override;
|
||||
};
|
||||
|
||||
class XdgWmDialogV1 : public QtWayland::xdg_wm_dialog_v1
|
||||
{
|
||||
public:
|
||||
~XdgWmDialogV1() override;
|
||||
};
|
||||
|
||||
class XdgDialogV1 : public QtWayland::xdg_dialog_v1
|
||||
{
|
||||
public:
|
||||
XdgDialogV1(XdgWmDialogV1 *wm, XdgToplevel *toplevel);
|
||||
~XdgDialogV1() override;
|
||||
};
|
||||
|
||||
enum class AdditionalWaylandInterface {
|
||||
Seat = 1 << 0,
|
||||
PlasmaShell = 1 << 2,
|
||||
|
@ -563,6 +594,7 @@ enum class AdditionalWaylandInterface {
|
|||
CursorShapeV1 = 1 << 18,
|
||||
FakeInput = 1 << 19,
|
||||
SecurityContextManagerV1 = 1 << 20,
|
||||
XdgDialogV1 = 1 << 21,
|
||||
};
|
||||
Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface)
|
||||
|
||||
|
@ -589,7 +621,6 @@ public:
|
|||
void setLeds(LEDs leds) override;
|
||||
|
||||
bool isKeyboard() const override;
|
||||
bool isAlphaNumericKeyboard() const override;
|
||||
bool isPointer() const override;
|
||||
bool isTouchpad() const override;
|
||||
bool isTouch() const override;
|
||||
|
@ -675,13 +706,13 @@ void flushWaylandConnection();
|
|||
bool waylandSync();
|
||||
|
||||
std::unique_ptr<KWayland::Client::Surface> createSurface();
|
||||
KWayland::Client::SubSurface *createSubSurface(KWayland::Client::Surface *surface,
|
||||
KWayland::Client::Surface *parentSurface, QObject *parent = nullptr);
|
||||
std::unique_ptr<KWayland::Client::SubSurface> createSubSurface(KWayland::Client::Surface *surface,
|
||||
KWayland::Client::Surface *parentSurface);
|
||||
|
||||
LayerSurfaceV1 *createLayerSurfaceV1(KWayland::Client::Surface *surface,
|
||||
const QString &scope,
|
||||
KWayland::Client::Output *output = nullptr,
|
||||
LayerShellV1::layer layer = LayerShellV1::layer_top);
|
||||
std::unique_ptr<LayerSurfaceV1> createLayerSurfaceV1(KWayland::Client::Surface *surface,
|
||||
const QString &scope,
|
||||
KWayland::Client::Output *output = nullptr,
|
||||
LayerShellV1::layer layer = LayerShellV1::layer_top);
|
||||
|
||||
TextInputManagerV3 *waylandTextInputManagerV3();
|
||||
|
||||
|
@ -690,28 +721,27 @@ enum class CreationSetup {
|
|||
CreateAndConfigure, /// commit and wait for the configure event, making this surface ready to commit buffers
|
||||
};
|
||||
|
||||
QtWayland::zwp_input_panel_surface_v1 *createInputPanelSurfaceV1(KWayland::Client::Surface *surface,
|
||||
KWayland::Client::Output *output,
|
||||
MockInputMethod::Mode mode);
|
||||
std::unique_ptr<QtWayland::zwp_input_panel_surface_v1> createInputPanelSurfaceV1(KWayland::Client::Surface *surface,
|
||||
KWayland::Client::Output *output,
|
||||
MockInputMethod::Mode mode);
|
||||
|
||||
FractionalScaleV1 *createFractionalScaleV1(KWayland::Client::Surface *surface);
|
||||
std::unique_ptr<FractionalScaleV1> createFractionalScaleV1(KWayland::Client::Surface *surface);
|
||||
|
||||
XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent = nullptr);
|
||||
XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface,
|
||||
CreationSetup configureMode,
|
||||
QObject *parent = nullptr);
|
||||
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface);
|
||||
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface, CreationSetup configureMode);
|
||||
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface, std::function<void(XdgToplevel *toplevel)> setup);
|
||||
|
||||
XdgPositioner *createXdgPositioner();
|
||||
std::unique_ptr<XdgPositioner> createXdgPositioner();
|
||||
|
||||
XdgPopup *createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *parentSurface,
|
||||
XdgPositioner *positioner,
|
||||
CreationSetup configureMode = CreationSetup::CreateAndConfigure,
|
||||
QObject *parent = nullptr);
|
||||
std::unique_ptr<XdgPopup> createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *parentSurface,
|
||||
XdgPositioner *positioner,
|
||||
CreationSetup configureMode = CreationSetup::CreateAndConfigure);
|
||||
|
||||
XdgToplevelDecorationV1 *createXdgToplevelDecorationV1(XdgToplevel *toplevel, QObject *parent = nullptr);
|
||||
IdleInhibitorV1 *createIdleInhibitorV1(KWayland::Client::Surface *surface);
|
||||
AutoHideScreenEdgeV1 *createAutoHideScreenEdgeV1(KWayland::Client::Surface *surface, uint32_t border);
|
||||
CursorShapeDeviceV1 *createCursorShapeDeviceV1(KWayland::Client::Pointer *pointer);
|
||||
std::unique_ptr<XdgToplevelDecorationV1> createXdgToplevelDecorationV1(XdgToplevel *toplevel);
|
||||
std::unique_ptr<IdleInhibitorV1> createIdleInhibitorV1(KWayland::Client::Surface *surface);
|
||||
std::unique_ptr<AutoHideScreenEdgeV1> createAutoHideScreenEdgeV1(KWayland::Client::Surface *surface, uint32_t border);
|
||||
std::unique_ptr<CursorShapeDeviceV1> createCursorShapeDeviceV1(KWayland::Client::Pointer *pointer);
|
||||
std::unique_ptr<XdgDialogV1> createXdgDialogV1(XdgToplevel *toplevel);
|
||||
|
||||
/**
|
||||
* Creates a shared memory buffer of @p size in @p color and attaches it to the @p surface.
|
||||
|
@ -767,13 +797,14 @@ bool renderNodeAvailable();
|
|||
* with X on demand
|
||||
*/
|
||||
|
||||
#if KWIN_BUILD_X11
|
||||
struct XcbConnectionDeleter
|
||||
{
|
||||
void operator()(xcb_connection_t *pointer);
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<xcb_connection_t, XcbConnectionDeleter> XcbConnectionPtr;
|
||||
XcbConnectionPtr createX11Connection();
|
||||
#endif
|
||||
|
||||
MockInputMethod *inputMethod();
|
||||
KWayland::Client::Surface *inputPanelSurface();
|
||||
|
|
|
@ -784,25 +784,24 @@ void LayerShellV1WindowTest::testScreenEdge()
|
|||
QVERIFY(window);
|
||||
QVERIFY(!window->isActive());
|
||||
|
||||
QSignalSpy windowShowSpy(window, &Window::windowShown);
|
||||
QSignalSpy windowHiddenSpy(window, &Window::windowHidden);
|
||||
QSignalSpy hiddenChangedSpy(window, &Window::hiddenChanged);
|
||||
quint32 timestamp = 0;
|
||||
|
||||
// The layer surface will be hidden and shown when the screen edge is activated or deactivated.
|
||||
{
|
||||
screenEdge->activate();
|
||||
QVERIFY(windowHiddenSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(!window->isShown());
|
||||
|
||||
screenEdge->deactivate();
|
||||
QVERIFY(windowShowSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(window->isShown());
|
||||
}
|
||||
|
||||
// The layer surface will be shown when the screen edge is triggered.
|
||||
{
|
||||
screenEdge->activate();
|
||||
QVERIFY(windowHiddenSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(!window->isShown());
|
||||
|
||||
Test::pointerMotion(QPointF(640, 1023), timestamp);
|
||||
|
@ -810,7 +809,7 @@ void LayerShellV1WindowTest::testScreenEdge()
|
|||
Test::pointerMotion(QPointF(640, 1023), timestamp);
|
||||
timestamp += 160;
|
||||
Test::pointerMotion(QPointF(640, 512), timestamp);
|
||||
QVERIFY(windowShowSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(window->isShown());
|
||||
}
|
||||
|
||||
|
@ -818,7 +817,7 @@ void LayerShellV1WindowTest::testScreenEdge()
|
|||
{
|
||||
QSignalSpy approachingSpy(workspace()->screenEdges(), &ScreenEdges::approaching);
|
||||
screenEdge->activate();
|
||||
QVERIFY(windowHiddenSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(!window->isShown());
|
||||
|
||||
Test::pointerMotion(QPointF(640, 1020), timestamp++);
|
||||
|
@ -827,7 +826,7 @@ void LayerShellV1WindowTest::testScreenEdge()
|
|||
QVERIFY(approachingSpy.last().at(1).toReal() != 0.0);
|
||||
|
||||
screenEdge->deactivate();
|
||||
QVERIFY(windowShowSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(window->isShown());
|
||||
QVERIFY(approachingSpy.last().at(1).toReal() == 0.0);
|
||||
|
||||
|
@ -837,11 +836,11 @@ void LayerShellV1WindowTest::testScreenEdge()
|
|||
// The layer surface will be shown when the screen edge is destroyed.
|
||||
{
|
||||
screenEdge->activate();
|
||||
QVERIFY(windowHiddenSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(!window->isShown());
|
||||
|
||||
screenEdge.reset();
|
||||
QVERIFY(windowShowSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(window->isShown());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,12 +67,19 @@ private Q_SLOTS:
|
|||
void testPointerShortcut();
|
||||
void testAxisShortcut_data();
|
||||
void testAxisShortcut();
|
||||
void testKeyboardShortcut();
|
||||
void testKeyboardLockShortcut();
|
||||
void testKeyboardShortcutsDisabledWhenLocked();
|
||||
void testTouch();
|
||||
|
||||
private:
|
||||
struct WindowHandle
|
||||
{
|
||||
Window *window;
|
||||
std::unique_ptr<KWayland::Client::Surface> surface;
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface;
|
||||
};
|
||||
void unlock();
|
||||
std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> showWindow();
|
||||
WindowHandle showWindow();
|
||||
KWayland::Client::ConnectionThread *m_connection = nullptr;
|
||||
KWayland::Client::Compositor *m_compositor = nullptr;
|
||||
KWayland::Client::Seat *m_seat = nullptr;
|
||||
|
@ -147,7 +154,7 @@ void LockScreenTest::unlock()
|
|||
}
|
||||
}
|
||||
|
||||
std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> LockScreenTest::showWindow()
|
||||
LockScreenTest::WindowHandle LockScreenTest::showWindow()
|
||||
{
|
||||
#define VERIFY(statement) \
|
||||
if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) \
|
||||
|
@ -158,8 +165,8 @@ std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> LockScreenTest::
|
|||
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
VERIFY(surface.get());
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
VERIFY(shellSurface);
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
VERIFY(shellSurface.get());
|
||||
// let's render
|
||||
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
|
||||
|
@ -169,7 +176,7 @@ std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> LockScreenTest::
|
|||
#undef VERIFY
|
||||
#undef COMPARE
|
||||
|
||||
return {window, std::move(surface)};
|
||||
return {window, std::move(surface), std::move(shellSurface)};
|
||||
}
|
||||
|
||||
void LockScreenTest::initTestCase()
|
||||
|
@ -235,7 +242,7 @@ void LockScreenTest::testPointer()
|
|||
QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
|
||||
QSignalSpy leftSpy(pointer.get(), &KWayland::Client::Pointer::left);
|
||||
|
||||
auto [window, surface] = showWindow();
|
||||
auto [window, surface, shellSurface] = showWindow();
|
||||
QVERIFY(window);
|
||||
|
||||
// first move cursor into the center of the window
|
||||
|
@ -278,7 +285,7 @@ void LockScreenTest::testPointerButton()
|
|||
QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
|
||||
QSignalSpy buttonChangedSpy(pointer.get(), &KWayland::Client::Pointer::buttonStateChanged);
|
||||
|
||||
auto [window, surface] = showWindow();
|
||||
auto [window, surface, shellSurface] = showWindow();
|
||||
QVERIFY(window);
|
||||
|
||||
// first move cursor into the center of the window
|
||||
|
@ -317,7 +324,7 @@ void LockScreenTest::testPointerAxis()
|
|||
QSignalSpy axisChangedSpy(pointer.get(), &KWayland::Client::Pointer::axisChanged);
|
||||
QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
|
||||
|
||||
auto [window, surface] = showWindow();
|
||||
auto [window, surface, shellSurface] = showWindow();
|
||||
QVERIFY(window);
|
||||
|
||||
// first move cursor into the center of the window
|
||||
|
@ -356,7 +363,7 @@ void LockScreenTest::testKeyboard()
|
|||
QSignalSpy leftSpy(keyboard.get(), &KWayland::Client::Keyboard::left);
|
||||
QSignalSpy keyChangedSpy(keyboard.get(), &KWayland::Client::Keyboard::keyChanged);
|
||||
|
||||
auto [window, surface] = showWindow();
|
||||
auto [window, surface, shellSurface] = showWindow();
|
||||
QVERIFY(window);
|
||||
QVERIFY(enteredSpy.wait());
|
||||
QTRY_COMPARE(enteredSpy.count(), 1);
|
||||
|
@ -553,7 +560,7 @@ void LockScreenTest::testEffectsKeyboardAutorepeat()
|
|||
|
||||
void LockScreenTest::testMoveWindow()
|
||||
{
|
||||
auto [window, surface] = showWindow();
|
||||
auto [window, surface, shellSurface] = showWindow();
|
||||
QVERIFY(window);
|
||||
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
|
||||
quint32 timestamp = 1;
|
||||
|
@ -685,7 +692,10 @@ void LockScreenTest::testAxisShortcut()
|
|||
#undef PERFORM
|
||||
}
|
||||
|
||||
void LockScreenTest::testKeyboardShortcut()
|
||||
/**
|
||||
* This test verifies that keyboard shortcuts are disabled when the screen is locked
|
||||
*/
|
||||
void LockScreenTest::testKeyboardShortcutsDisabledWhenLocked()
|
||||
{
|
||||
#if !KWIN_BUILD_GLOBALSHORTCUTS
|
||||
QSKIP("Can't test shortcuts without shortcuts");
|
||||
|
@ -732,12 +742,49 @@ void LockScreenTest::testKeyboardShortcut()
|
|||
KEYRELEASE(KEY_LEFTALT);
|
||||
}
|
||||
|
||||
/**
|
||||
* This test verifies that the global keyboard shortcut to lock the screen works
|
||||
*/
|
||||
void LockScreenTest::testKeyboardLockShortcut()
|
||||
{
|
||||
#if !KWIN_BUILD_GLOBALSHORTCUTS
|
||||
QSKIP("Can't test shortcuts without shortcuts");
|
||||
return;
|
||||
#endif
|
||||
|
||||
QList<QKeySequence> shortcuts = KGlobalAccel::self()->globalShortcut("ksmserver", "Lock Session");
|
||||
// Verify the shortcut is Meta + L, the default
|
||||
QCOMPARE(shortcuts.first().toString(), QString("Meta+L"));
|
||||
|
||||
// Verify the screen is not locked
|
||||
QVERIFY(!waylandServer()->isScreenLocked());
|
||||
|
||||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
|
||||
// Trigger the shortcut
|
||||
quint32 timestamp = 1;
|
||||
KEYPRESS(KEY_LEFTMETA);
|
||||
KEYPRESS(KEY_L);
|
||||
KEYRELEASE(KEY_L);
|
||||
KEYRELEASE(KEY_LEFTMETA);
|
||||
|
||||
// Verify the screen gets locked
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
Window *window = windowAddedSpy.first().first().value<Window *>();
|
||||
QVERIFY(window);
|
||||
QVERIFY(window->isLockScreen());
|
||||
QTRY_COMPARE(ScreenLocker::KSldApp::self()->lockState(), ScreenLocker::KSldApp::Locked);
|
||||
QVERIFY(waylandServer()->isScreenLocked());
|
||||
|
||||
UNLOCK;
|
||||
}
|
||||
|
||||
void LockScreenTest::testTouch()
|
||||
{
|
||||
auto touch = m_seat->createTouch(m_seat);
|
||||
QVERIFY(touch);
|
||||
QVERIFY(touch->isValid());
|
||||
auto [window, surface] = showWindow();
|
||||
auto [window, surface, shellSurface] = showWindow();
|
||||
QVERIFY(window);
|
||||
QSignalSpy sequenceStartedSpy(touch, &KWayland::Client::Touch::sequenceStarted);
|
||||
QSignalSpy cancelSpy(touch, &KWayland::Client::Touch::sequenceCanceled);
|
||||
|
|
|
@ -1,376 +0,0 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include <config-kwin.h>
|
||||
|
||||
#include "kwin_wayland_test.h"
|
||||
|
||||
#include "input.h"
|
||||
#include "keyboard_input.h"
|
||||
#include "pointer_input.h"
|
||||
#include "wayland_server.h"
|
||||
#include "workspace.h"
|
||||
#include "xkb.h"
|
||||
|
||||
#include <KConfigGroup>
|
||||
|
||||
#include <QDBusConnection>
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
using namespace KWin;
|
||||
|
||||
static const QString s_socketName = QStringLiteral("wayland_test_kwin_modifier_only_shortcut-0");
|
||||
static const QString s_serviceName = QStringLiteral("org.kde.KWin.Test.ModifierOnlyShortcut");
|
||||
static const QString s_path = QStringLiteral("/Test");
|
||||
|
||||
class ModifierOnlyShortcutTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
void testTrigger_data();
|
||||
void testTrigger();
|
||||
void testCapsLock();
|
||||
void testGlobalShortcutsDisabled_data();
|
||||
void testGlobalShortcutsDisabled();
|
||||
};
|
||||
|
||||
class Target : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.kde.KWin.Test.ModifierOnlyShortcut")
|
||||
|
||||
public:
|
||||
Target();
|
||||
~Target() override;
|
||||
|
||||
public Q_SLOTS:
|
||||
Q_SCRIPTABLE void shortcut();
|
||||
|
||||
Q_SIGNALS:
|
||||
void shortcutTriggered();
|
||||
};
|
||||
|
||||
Target::Target()
|
||||
: QObject()
|
||||
{
|
||||
QDBusConnection::sessionBus().registerService(s_serviceName);
|
||||
QDBusConnection::sessionBus().registerObject(s_path, s_serviceName, this, QDBusConnection::ExportScriptableSlots);
|
||||
}
|
||||
|
||||
Target::~Target()
|
||||
{
|
||||
QDBusConnection::sessionBus().unregisterObject(s_path);
|
||||
QDBusConnection::sessionBus().unregisterService(s_serviceName);
|
||||
}
|
||||
|
||||
void Target::shortcut()
|
||||
{
|
||||
Q_EMIT shortcutTriggered();
|
||||
}
|
||||
|
||||
void ModifierOnlyShortcutTest::initTestCase()
|
||||
{
|
||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||
QVERIFY(waylandServer()->init(s_socketName));
|
||||
Test::setOutputConfig({
|
||||
QRect(0, 0, 1280, 1024),
|
||||
QRect(1280, 0, 1280, 1024),
|
||||
});
|
||||
|
||||
kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
|
||||
qputenv("KWIN_XKB_DEFAULT_KEYMAP", "1");
|
||||
qputenv("XKB_DEFAULT_RULES", "evdev");
|
||||
|
||||
kwinApp()->start();
|
||||
QVERIFY(applicationStartedSpy.wait());
|
||||
}
|
||||
|
||||
void ModifierOnlyShortcutTest::init()
|
||||
{
|
||||
workspace()->setActiveOutput(QPoint(640, 512));
|
||||
KWin::input()->pointer()->warp(QPoint(640, 512));
|
||||
}
|
||||
|
||||
void ModifierOnlyShortcutTest::cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
void ModifierOnlyShortcutTest::testTrigger_data()
|
||||
{
|
||||
QTest::addColumn<QStringList>("metaConfig");
|
||||
QTest::addColumn<QStringList>("altConfig");
|
||||
QTest::addColumn<QStringList>("controlConfig");
|
||||
QTest::addColumn<QStringList>("shiftConfig");
|
||||
QTest::addColumn<int>("modifier");
|
||||
QTest::addColumn<QList<int>>("nonTriggeringMods");
|
||||
|
||||
const QStringList trigger = QStringList{s_serviceName, s_path, s_serviceName, QStringLiteral("shortcut")};
|
||||
const QStringList e = QStringList();
|
||||
|
||||
QTest::newRow("leftMeta") << trigger << e << e << e << KEY_LEFTMETA << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
|
||||
QTest::newRow("rightMeta") << trigger << e << e << e << KEY_RIGHTMETA << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
|
||||
QTest::newRow("leftAlt") << e << trigger << e << e << KEY_LEFTALT << QList<int>{KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
|
||||
QTest::newRow("rightAlt") << e << trigger << e << e << KEY_RIGHTALT << QList<int>{KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
|
||||
QTest::newRow("leftControl") << e << e << trigger << e << KEY_LEFTCTRL << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
|
||||
QTest::newRow("rightControl") << e << e << trigger << e << KEY_RIGHTCTRL << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
|
||||
QTest::newRow("leftShift") << e << e << e << trigger << KEY_LEFTSHIFT << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, KEY_RIGHTMETA};
|
||||
QTest::newRow("rightShift") << e << e << e << trigger << KEY_RIGHTSHIFT << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, KEY_RIGHTMETA};
|
||||
}
|
||||
|
||||
void ModifierOnlyShortcutTest::testTrigger()
|
||||
{
|
||||
// this test verifies that modifier only shortcut triggers correctly
|
||||
Target target;
|
||||
QSignalSpy triggeredSpy(&target, &Target::shortcutTriggered);
|
||||
|
||||
KConfigGroup group = kwinApp()->config()->group(QStringLiteral("ModifierOnlyShortcuts"));
|
||||
QFETCH(QStringList, metaConfig);
|
||||
QFETCH(QStringList, altConfig);
|
||||
QFETCH(QStringList, shiftConfig);
|
||||
QFETCH(QStringList, controlConfig);
|
||||
group.writeEntry("Meta", metaConfig);
|
||||
group.writeEntry("Alt", altConfig);
|
||||
group.writeEntry("Shift", shiftConfig);
|
||||
group.writeEntry("Control", controlConfig);
|
||||
group.sync();
|
||||
workspace()->slotReconfigure();
|
||||
|
||||
// configured shortcut should trigger
|
||||
quint32 timestamp = 1;
|
||||
QFETCH(int, modifier);
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 1);
|
||||
|
||||
// the other shortcuts should not trigger
|
||||
QFETCH(QList<int>, nonTriggeringMods);
|
||||
for (auto it = nonTriggeringMods.constBegin(), end = nonTriggeringMods.constEnd(); it != end; it++) {
|
||||
Test::keyboardKeyPressed(*it, timestamp++);
|
||||
Test::keyboardKeyReleased(*it, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 1);
|
||||
}
|
||||
|
||||
// try configured again
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 2);
|
||||
|
||||
// click another key while modifier is held
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::keyboardKeyPressed(KEY_A, timestamp++);
|
||||
Test::keyboardKeyReleased(KEY_A, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 2);
|
||||
|
||||
// release other key after modifier release
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::keyboardKeyPressed(KEY_A, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
Test::keyboardKeyReleased(KEY_A, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 2);
|
||||
|
||||
// press key before pressing modifier
|
||||
Test::keyboardKeyPressed(KEY_A, timestamp++);
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
Test::keyboardKeyReleased(KEY_A, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 2);
|
||||
|
||||
// mouse button pressed before clicking modifier
|
||||
Test::pointerButtonPressed(BTN_LEFT, timestamp++);
|
||||
QCOMPARE(input()->qtButtonStates(), Qt::LeftButton);
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
Test::pointerButtonReleased(BTN_LEFT, timestamp++);
|
||||
QCOMPARE(input()->qtButtonStates(), Qt::NoButton);
|
||||
QCOMPARE(triggeredSpy.count(), 2);
|
||||
|
||||
// mouse button press before mod press, release before mod release
|
||||
Test::pointerButtonPressed(BTN_LEFT, timestamp++);
|
||||
QCOMPARE(input()->qtButtonStates(), Qt::LeftButton);
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::pointerButtonReleased(BTN_LEFT, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
QCOMPARE(input()->qtButtonStates(), Qt::NoButton);
|
||||
QCOMPARE(triggeredSpy.count(), 2);
|
||||
|
||||
// mouse button click while mod is pressed
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::pointerButtonPressed(BTN_LEFT, timestamp++);
|
||||
QCOMPARE(input()->qtButtonStates(), Qt::LeftButton);
|
||||
Test::pointerButtonReleased(BTN_LEFT, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
QCOMPARE(input()->qtButtonStates(), Qt::NoButton);
|
||||
QCOMPARE(triggeredSpy.count(), 2);
|
||||
|
||||
// scroll while mod is pressed
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::pointerAxisVertical(5.0, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 2);
|
||||
|
||||
// same for horizontal
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::pointerAxisHorizontal(5.0, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 2);
|
||||
|
||||
#if KWIN_BUILD_SCREENLOCKER
|
||||
// now try to lock the screen while modifier key is pressed
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
QVERIFY(Test::lockScreen());
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 2);
|
||||
|
||||
// now trigger while screen is locked, should also not work
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 2);
|
||||
|
||||
QVERIFY(Test::unlockScreen());
|
||||
#endif
|
||||
}
|
||||
|
||||
void ModifierOnlyShortcutTest::testCapsLock()
|
||||
{
|
||||
// this test verifies that Capslock does not trigger the shift shortcut
|
||||
// but other shortcuts still trigger even when Capslock is on
|
||||
Target target;
|
||||
QSignalSpy triggeredSpy(&target, &Target::shortcutTriggered);
|
||||
|
||||
KConfigGroup group = kwinApp()->config()->group(QStringLiteral("ModifierOnlyShortcuts"));
|
||||
group.writeEntry("Meta", QStringList());
|
||||
group.writeEntry("Alt", QStringList());
|
||||
group.writeEntry("Shift", QStringList{s_serviceName, s_path, s_serviceName, QStringLiteral("shortcut")});
|
||||
group.writeEntry("Control", QStringList());
|
||||
group.sync();
|
||||
workspace()->slotReconfigure();
|
||||
|
||||
// first test that the normal shortcut triggers
|
||||
quint32 timestamp = 1;
|
||||
const int modifier = KEY_LEFTSHIFT;
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 1);
|
||||
|
||||
// now capslock
|
||||
Test::keyboardKeyPressed(KEY_CAPSLOCK, timestamp++);
|
||||
Test::keyboardKeyReleased(KEY_CAPSLOCK, timestamp++);
|
||||
QCOMPARE(input()->keyboardModifiers(), Qt::NoModifier);
|
||||
QCOMPARE(triggeredSpy.count(), 1);
|
||||
|
||||
// currently caps lock is on
|
||||
// shift still triggers
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
QCOMPARE(input()->keyboardModifiers(), Qt::NoModifier);
|
||||
QCOMPARE(triggeredSpy.count(), 2);
|
||||
|
||||
// meta should also trigger
|
||||
group.writeEntry("Meta", QStringList{s_serviceName, s_path, s_serviceName, QStringLiteral("shortcut")});
|
||||
group.writeEntry("Alt", QStringList());
|
||||
group.writeEntry("Shift", QStringList{});
|
||||
group.writeEntry("Control", QStringList());
|
||||
group.sync();
|
||||
workspace()->slotReconfigure();
|
||||
Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++);
|
||||
QCOMPARE(input()->keyboardModifiers(), Qt::MetaModifier);
|
||||
QCOMPARE(input()->keyboard()->xkb()->modifiersRelevantForGlobalShortcuts(), Qt::MetaModifier);
|
||||
Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 3);
|
||||
|
||||
// set back to shift to ensure we don't trigger with capslock
|
||||
group.writeEntry("Meta", QStringList());
|
||||
group.writeEntry("Alt", QStringList());
|
||||
group.writeEntry("Shift", QStringList{s_serviceName, s_path, s_serviceName, QStringLiteral("shortcut")});
|
||||
group.writeEntry("Control", QStringList());
|
||||
group.sync();
|
||||
workspace()->slotReconfigure();
|
||||
|
||||
// release caps lock
|
||||
Test::keyboardKeyPressed(KEY_CAPSLOCK, timestamp++);
|
||||
Test::keyboardKeyReleased(KEY_CAPSLOCK, timestamp++);
|
||||
QCOMPARE(input()->keyboardModifiers(), Qt::NoModifier);
|
||||
QCOMPARE(triggeredSpy.count(), 3);
|
||||
}
|
||||
|
||||
void ModifierOnlyShortcutTest::testGlobalShortcutsDisabled_data()
|
||||
{
|
||||
QTest::addColumn<QStringList>("metaConfig");
|
||||
QTest::addColumn<QStringList>("altConfig");
|
||||
QTest::addColumn<QStringList>("controlConfig");
|
||||
QTest::addColumn<QStringList>("shiftConfig");
|
||||
QTest::addColumn<int>("modifier");
|
||||
|
||||
const QStringList trigger = QStringList{s_serviceName, s_path, s_serviceName, QStringLiteral("shortcut")};
|
||||
const QStringList e = QStringList();
|
||||
|
||||
QTest::newRow("leftMeta") << trigger << e << e << e << KEY_LEFTMETA;
|
||||
QTest::newRow("rightMeta") << trigger << e << e << e << KEY_RIGHTMETA;
|
||||
QTest::newRow("leftAlt") << e << trigger << e << e << KEY_LEFTALT;
|
||||
QTest::newRow("rightAlt") << e << trigger << e << e << KEY_RIGHTALT;
|
||||
QTest::newRow("leftControl") << e << e << trigger << e << KEY_LEFTCTRL;
|
||||
QTest::newRow("rightControl") << e << e << trigger << e << KEY_RIGHTCTRL;
|
||||
QTest::newRow("leftShift") << e << e << e << trigger << KEY_LEFTSHIFT;
|
||||
QTest::newRow("rightShift") << e << e << e << trigger << KEY_RIGHTSHIFT;
|
||||
}
|
||||
|
||||
void ModifierOnlyShortcutTest::testGlobalShortcutsDisabled()
|
||||
{
|
||||
// this test verifies that when global shortcuts are disabled inside KWin (e.g. through a window rule)
|
||||
// the modifier only shortcuts do not trigger.
|
||||
// see BUG: 370146
|
||||
Target target;
|
||||
QSignalSpy triggeredSpy(&target, &Target::shortcutTriggered);
|
||||
|
||||
KConfigGroup group = kwinApp()->config()->group(QStringLiteral("ModifierOnlyShortcuts"));
|
||||
QFETCH(QStringList, metaConfig);
|
||||
QFETCH(QStringList, altConfig);
|
||||
QFETCH(QStringList, shiftConfig);
|
||||
QFETCH(QStringList, controlConfig);
|
||||
group.writeEntry("Meta", metaConfig);
|
||||
group.writeEntry("Alt", altConfig);
|
||||
group.writeEntry("Shift", shiftConfig);
|
||||
group.writeEntry("Control", controlConfig);
|
||||
group.sync();
|
||||
workspace()->slotReconfigure();
|
||||
|
||||
// trigger once to verify the shortcut works
|
||||
quint32 timestamp = 1;
|
||||
QFETCH(int, modifier);
|
||||
QVERIFY(!workspace()->globalShortcutsDisabled());
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 1);
|
||||
triggeredSpy.clear();
|
||||
|
||||
// now disable global shortcuts
|
||||
workspace()->disableGlobalShortcutsForClient(true);
|
||||
QVERIFY(workspace()->globalShortcutsDisabled());
|
||||
// Should not get triggered
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 0);
|
||||
triggeredSpy.clear();
|
||||
|
||||
// enable again
|
||||
workspace()->disableGlobalShortcutsForClient(false);
|
||||
QVERIFY(!workspace()->globalShortcutsDisabled());
|
||||
// should get triggered again
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 1);
|
||||
}
|
||||
|
||||
WAYLANDTEST_MAIN(ModifierOnlyShortcutTest)
|
||||
#include "modifier_only_shortcut_test.moc"
|
|
@ -54,11 +54,6 @@ private Q_SLOTS:
|
|||
void testPointerMoveEnd_data();
|
||||
void testPointerMoveEnd();
|
||||
void testClientSideMove();
|
||||
void testNetMove();
|
||||
void testAdjustClientGeometryOfHiddenX11Panel_data();
|
||||
void testAdjustClientGeometryOfHiddenX11Panel();
|
||||
void testAdjustClientGeometryOfHiddenWaylandPanel_data();
|
||||
void testAdjustClientGeometryOfHiddenWaylandPanel();
|
||||
void testResizeForVirtualKeyboard_data();
|
||||
void testResizeForVirtualKeyboard();
|
||||
void testResizeForVirtualKeyboardWithMaximize();
|
||||
|
@ -536,234 +531,6 @@ void MoveResizeWindowTest::testClientSideMove()
|
|||
QCOMPARE(pointerEnteredSpy.last().last().toPoint(), QPoint(50, 25));
|
||||
}
|
||||
|
||||
void MoveResizeWindowTest::testNetMove()
|
||||
{
|
||||
// this test verifies that a move request for an X11 window through NET API works
|
||||
// create an xcb window
|
||||
Test::XcbConnectionPtr c = Test::createX11Connection();
|
||||
QVERIFY(!xcb_connection_has_error(c.get()));
|
||||
|
||||
xcb_window_t windowId = xcb_generate_id(c.get());
|
||||
xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
|
||||
0, 0, 100, 100,
|
||||
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
|
||||
xcb_size_hints_t hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
xcb_icccm_size_hints_set_position(&hints, 1, 0, 0);
|
||||
xcb_icccm_size_hints_set_size(&hints, 1, 100, 100);
|
||||
xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
|
||||
// let's set a no-border
|
||||
NETWinInfo winInfo(c.get(), windowId, rootWindow(), NET::WMWindowType, NET::Properties2());
|
||||
winInfo.setWindowType(NET::Override);
|
||||
xcb_map_window(c.get(), windowId);
|
||||
xcb_flush(c.get());
|
||||
|
||||
QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
|
||||
QVERIFY(windowCreatedSpy.wait());
|
||||
X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
|
||||
QVERIFY(window);
|
||||
QCOMPARE(window->window(), windowId);
|
||||
const QRectF origGeo = window->frameGeometry();
|
||||
|
||||
// let's move the cursor outside the window
|
||||
input()->pointer()->warp(workspace()->activeOutput()->geometry().center());
|
||||
QVERIFY(!exclusiveContains(origGeo, Cursors::self()->mouse()->pos()));
|
||||
|
||||
QSignalSpy interactiveMoveResizeStartedSpy(window, &X11Window::interactiveMoveResizeStarted);
|
||||
QSignalSpy interactiveMoveResizeFinishedSpy(window, &X11Window::interactiveMoveResizeFinished);
|
||||
QSignalSpy interactiveMoveResizeSteppedSpy(window, &X11Window::interactiveMoveResizeStepped);
|
||||
QVERIFY(!workspace()->moveResizeWindow());
|
||||
|
||||
// use NETRootInfo to trigger a move request
|
||||
NETRootInfo root(c.get(), NET::Properties());
|
||||
root.moveResizeRequest(windowId, origGeo.center().x(), origGeo.center().y(), NET::Move, XCB_BUTTON_INDEX_1);
|
||||
xcb_flush(c.get());
|
||||
|
||||
QVERIFY(interactiveMoveResizeStartedSpy.wait());
|
||||
QCOMPARE(workspace()->moveResizeWindow(), window);
|
||||
QVERIFY(window->isInteractiveMove());
|
||||
QCOMPARE(window->geometryRestore(), origGeo);
|
||||
QCOMPARE(Cursors::self()->mouse()->pos(), origGeo.center());
|
||||
|
||||
// let's move a step
|
||||
input()->pointer()->warp(Cursors::self()->mouse()->pos() + QPoint(10, 10));
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.first().last(), origGeo.translated(10, 10));
|
||||
|
||||
// let's cancel the move resize again through the net API
|
||||
root.moveResizeRequest(windowId, window->frameGeometry().center().x(), window->frameGeometry().center().y(), NET::MoveResizeCancel, XCB_BUTTON_INDEX_1);
|
||||
xcb_flush(c.get());
|
||||
QVERIFY(interactiveMoveResizeFinishedSpy.wait());
|
||||
|
||||
// and destroy the window again
|
||||
xcb_unmap_window(c.get(), windowId);
|
||||
xcb_destroy_window(c.get(), windowId);
|
||||
xcb_flush(c.get());
|
||||
c.reset();
|
||||
|
||||
QSignalSpy windowClosedSpy(window, &X11Window::closed);
|
||||
QVERIFY(windowClosedSpy.wait());
|
||||
}
|
||||
|
||||
void MoveResizeWindowTest::testAdjustClientGeometryOfHiddenX11Panel_data()
|
||||
{
|
||||
QTest::addColumn<QRect>("panelGeometry");
|
||||
QTest::addColumn<QPoint>("targetPoint");
|
||||
QTest::addColumn<QPoint>("expectedAdjustedPoint");
|
||||
QTest::addColumn<quint32>("hideLocation");
|
||||
|
||||
QTest::newRow("top") << QRect(0, 0, 100, 20) << QPoint(50, 25) << QPoint(50, 20) << 0u;
|
||||
QTest::newRow("bottom") << QRect(0, 1024 - 20, 100, 20) << QPoint(50, 1024 - 25 - 50) << QPoint(50, 1024 - 20 - 50) << 2u;
|
||||
QTest::newRow("left") << QRect(0, 0, 20, 100) << QPoint(25, 50) << QPoint(20, 50) << 3u;
|
||||
QTest::newRow("right") << QRect(1280 - 20, 0, 20, 100) << QPoint(1280 - 25 - 100, 50) << QPoint(1280 - 20 - 100, 50) << 1u;
|
||||
}
|
||||
|
||||
void MoveResizeWindowTest::testAdjustClientGeometryOfHiddenX11Panel()
|
||||
{
|
||||
// this test verifies that auto hiding panels are ignored when adjusting client geometry
|
||||
// see BUG 365892
|
||||
|
||||
// first create our panel
|
||||
Test::XcbConnectionPtr c = Test::createX11Connection();
|
||||
QVERIFY(!xcb_connection_has_error(c.get()));
|
||||
|
||||
xcb_window_t windowId = xcb_generate_id(c.get());
|
||||
QFETCH(QRect, panelGeometry);
|
||||
xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
|
||||
panelGeometry.x(), panelGeometry.y(), panelGeometry.width(), panelGeometry.height(),
|
||||
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
|
||||
xcb_size_hints_t hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
xcb_icccm_size_hints_set_position(&hints, 1, panelGeometry.x(), panelGeometry.y());
|
||||
xcb_icccm_size_hints_set_size(&hints, 1, panelGeometry.width(), panelGeometry.height());
|
||||
xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
|
||||
NETWinInfo winInfo(c.get(), windowId, rootWindow(), NET::WMWindowType, NET::Properties2());
|
||||
winInfo.setWindowType(NET::Dock);
|
||||
xcb_map_window(c.get(), windowId);
|
||||
xcb_flush(c.get());
|
||||
|
||||
QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
|
||||
QVERIFY(windowCreatedSpy.wait());
|
||||
X11Window *panel = windowCreatedSpy.first().first().value<X11Window *>();
|
||||
QVERIFY(panel);
|
||||
QCOMPARE(panel->window(), windowId);
|
||||
QCOMPARE(panel->frameGeometry(), panelGeometry);
|
||||
QVERIFY(panel->isDock());
|
||||
|
||||
// let's create a window
|
||||
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
|
||||
QVERIFY(surface != nullptr);
|
||||
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
|
||||
QVERIFY(shellSurface != nullptr);
|
||||
auto testWindow = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
|
||||
QVERIFY(testWindow);
|
||||
QVERIFY(testWindow->isMovable());
|
||||
// panel is not yet hidden, we should snap against it
|
||||
QFETCH(QPoint, targetPoint);
|
||||
QTEST(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false).toPoint(), "expectedAdjustedPoint");
|
||||
|
||||
// now let's hide the panel
|
||||
QSignalSpy panelHiddenSpy(panel, &Window::windowHidden);
|
||||
QFETCH(quint32, hideLocation);
|
||||
xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->kde_screen_edge_show, XCB_ATOM_CARDINAL, 32, 1, &hideLocation);
|
||||
xcb_flush(c.get());
|
||||
QVERIFY(panelHiddenSpy.wait());
|
||||
|
||||
// now try to snap again
|
||||
QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint);
|
||||
|
||||
// and destroy the panel again
|
||||
xcb_unmap_window(c.get(), windowId);
|
||||
xcb_destroy_window(c.get(), windowId);
|
||||
xcb_flush(c.get());
|
||||
c.reset();
|
||||
|
||||
QSignalSpy panelClosedSpy(panel, &X11Window::closed);
|
||||
QVERIFY(panelClosedSpy.wait());
|
||||
|
||||
// snap once more
|
||||
QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint);
|
||||
|
||||
// and close
|
||||
QSignalSpy windowClosedSpy(testWindow, &Window::closed);
|
||||
shellSurface.reset();
|
||||
surface.reset();
|
||||
QVERIFY(windowClosedSpy.wait());
|
||||
}
|
||||
|
||||
void MoveResizeWindowTest::testAdjustClientGeometryOfHiddenWaylandPanel_data()
|
||||
{
|
||||
QTest::addColumn<uint32_t>("anchor");
|
||||
QTest::addColumn<QRect>("panelGeometry");
|
||||
QTest::addColumn<QPoint>("targetPoint");
|
||||
QTest::addColumn<QPoint>("expectedAdjustedPoint");
|
||||
|
||||
QTest::newRow("top") << uint32_t(Test::LayerSurfaceV1::anchor_top) << QRect(0, 0, 1280, 20) << QPoint(50, 25) << QPoint(50, 20);
|
||||
QTest::newRow("bottom") << uint32_t(Test::LayerSurfaceV1::anchor_bottom) << QRect(0, 1024 - 20, 1280, 20) << QPoint(50, 1024 - 25 - 50) << QPoint(50, 1024 - 20 - 50);
|
||||
QTest::newRow("left") << uint32_t(Test::LayerSurfaceV1::anchor_left) << QRect(0, 0, 20, 1024) << QPoint(25, 50) << QPoint(20, 50);
|
||||
QTest::newRow("right") << uint32_t(Test::LayerSurfaceV1::anchor_right) << QRect(1280 - 20, 0, 20, 1024) << QPoint(1280 - 25 - 100, 50) << QPoint(1280 - 20 - 100, 50);
|
||||
}
|
||||
|
||||
void MoveResizeWindowTest::testAdjustClientGeometryOfHiddenWaylandPanel()
|
||||
{
|
||||
// this test verifies that hidden panels are ignored when adjusting client geometry
|
||||
// see BUG 365892
|
||||
|
||||
// first create our panel
|
||||
std::unique_ptr<KWayland::Client::Surface> panelSurface(Test::createSurface());
|
||||
std::unique_ptr<Test::LayerSurfaceV1> panelShellSurface(Test::createLayerSurfaceV1(panelSurface.get(), QStringLiteral("dock")));
|
||||
QFETCH(QRect, panelGeometry);
|
||||
QFETCH(uint32_t, anchor);
|
||||
panelShellSurface->set_anchor(anchor);
|
||||
panelShellSurface->set_size(panelGeometry.width(), panelGeometry.height());
|
||||
panelSurface->commit(KWayland::Client::Surface::CommitFlag::None);
|
||||
|
||||
// let's render
|
||||
QSignalSpy panelConfigureRequestedSpy(panelShellSurface.get(), &Test::LayerSurfaceV1::configureRequested);
|
||||
QVERIFY(panelConfigureRequestedSpy.wait());
|
||||
auto panel = Test::renderAndWaitForShown(panelSurface.get(), panelConfigureRequestedSpy.last().at(1).toSize(), Qt::blue);
|
||||
QVERIFY(panel);
|
||||
QCOMPARE(panel->frameGeometry(), panelGeometry);
|
||||
QVERIFY(panel->isDock());
|
||||
|
||||
// let's create a window
|
||||
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
|
||||
QVERIFY(surface != nullptr);
|
||||
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
|
||||
QVERIFY(shellSurface != nullptr);
|
||||
auto testWindow = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
|
||||
QVERIFY(testWindow);
|
||||
QVERIFY(testWindow->isMovable());
|
||||
// panel is not yet hidden, we should snap against it
|
||||
QFETCH(QPoint, targetPoint);
|
||||
QTEST(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false).toPoint(), "expectedAdjustedPoint");
|
||||
|
||||
// now let's hide the panel
|
||||
panel->setHidden(true);
|
||||
|
||||
// now try to snap again
|
||||
QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint);
|
||||
|
||||
// and destroy the panel again
|
||||
QSignalSpy panelClosedSpy(panel, &Window::closed);
|
||||
panelShellSurface.reset();
|
||||
panelSurface.reset();
|
||||
QVERIFY(panelClosedSpy.wait());
|
||||
|
||||
// snap once more
|
||||
QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint);
|
||||
|
||||
// and close
|
||||
QSignalSpy windowClosedSpy(testWindow, &Window::closed);
|
||||
shellSurface.reset();
|
||||
surface.reset();
|
||||
QVERIFY(windowClosedSpy.wait());
|
||||
}
|
||||
|
||||
void MoveResizeWindowTest::testResizeForVirtualKeyboard_data()
|
||||
{
|
||||
QTest::addColumn<QRect>("windowRect");
|
||||
|
@ -1039,6 +806,12 @@ void MoveResizeWindowTest::testCancelInteractiveMoveResize()
|
|||
QVERIFY(shellSurface != nullptr);
|
||||
Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(window);
|
||||
QSignalSpy frameGeomtryChangedSpy(window, &Window::frameGeometryChanged);
|
||||
QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
|
||||
// tile / maximize window
|
||||
QFETCH(QuickTileMode, quickTileMode);
|
||||
|
@ -1048,21 +821,20 @@ void MoveResizeWindowTest::testCancelInteractiveMoveResize()
|
|||
} else {
|
||||
window->setQuickTileMode(quickTileMode, true);
|
||||
}
|
||||
QCOMPARE(window->quickTileMode(), quickTileMode);
|
||||
QCOMPARE(window->requestedQuickTileMode(), quickTileMode);
|
||||
QCOMPARE(window->requestedMaximizeMode(), maximizeMode);
|
||||
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue);
|
||||
|
||||
QVERIFY(frameGeomtryChangedSpy.wait());
|
||||
QCOMPARE(window->quickTileMode(), quickTileMode);
|
||||
const QRectF geometry = window->moveResizeGeometry();
|
||||
const QRectF geometryRestore = window->geometryRestore();
|
||||
|
||||
// Start resizing the client.
|
||||
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
|
||||
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
|
||||
|
||||
QCOMPARE(workspace()->moveResizeWindow(), nullptr);
|
||||
QCOMPARE(window->isInteractiveMove(), false);
|
||||
QCOMPARE(window->isInteractiveResize(), false);
|
||||
|
@ -1072,12 +844,22 @@ void MoveResizeWindowTest::testCancelInteractiveMoveResize()
|
|||
QCOMPARE(window->isInteractiveMove(), false);
|
||||
QCOMPARE(window->isInteractiveResize(), true);
|
||||
|
||||
Test::pointerMotionRelative(QPoint(1, 1), 1);
|
||||
Test::pointerMotionRelative(QPoint(-10, -10), 1);
|
||||
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue);
|
||||
QVERIFY(frameGeomtryChangedSpy.wait());
|
||||
QCOMPARE(window->quickTileMode(), QuickTileMode());
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
|
||||
|
||||
// cancel moveresize, all state from before should be restored
|
||||
window->keyPressEvent(Qt::Key::Key_Escape);
|
||||
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue);
|
||||
QVERIFY(frameGeomtryChangedSpy.wait());
|
||||
QCOMPARE(window->moveResizeGeometry(), geometry);
|
||||
QCOMPARE(window->quickTileMode(), quickTileMode);
|
||||
QCOMPARE(window->requestedMaximizeMode(), maximizeMode);
|
||||
|
|
|
@ -26,8 +26,6 @@
|
|||
using namespace KWin;
|
||||
|
||||
static const QString s_socketName = QStringLiteral("wayland_test_kwin_no_global_shortcuts-0");
|
||||
static const QString s_serviceName = QStringLiteral("org.kde.KWin.Test.ModifierOnlyShortcut");
|
||||
static const QString s_path = QStringLiteral("/Test");
|
||||
|
||||
Q_DECLARE_METATYPE(KWin::ElectricBorder)
|
||||
|
||||
|
@ -42,8 +40,6 @@ private Q_SLOTS:
|
|||
void init();
|
||||
void cleanup();
|
||||
|
||||
void testTrigger_data();
|
||||
void testTrigger();
|
||||
void testKGlobalAccel();
|
||||
void testPointerShortcut();
|
||||
void testAxisShortcut_data();
|
||||
|
@ -54,7 +50,6 @@ private Q_SLOTS:
|
|||
class Target : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.kde.KWin.Test.ModifierOnlyShortcut")
|
||||
|
||||
public:
|
||||
Target();
|
||||
|
@ -70,14 +65,10 @@ Q_SIGNALS:
|
|||
Target::Target()
|
||||
: QObject()
|
||||
{
|
||||
QDBusConnection::sessionBus().registerService(s_serviceName);
|
||||
QDBusConnection::sessionBus().registerObject(s_path, s_serviceName, this, QDBusConnection::ExportScriptableSlots);
|
||||
}
|
||||
|
||||
Target::~Target()
|
||||
{
|
||||
QDBusConnection::sessionBus().unregisterObject(s_path);
|
||||
QDBusConnection::sessionBus().unregisterService(s_serviceName);
|
||||
}
|
||||
|
||||
void Target::shortcut()
|
||||
|
@ -89,7 +80,8 @@ void NoGlobalShortcutsTest::initTestCase()
|
|||
{
|
||||
qRegisterMetaType<KWin::ElectricBorder>("ElectricBorder");
|
||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||
QVERIFY(waylandServer()->init(s_socketName, KWin::WaylandServer::InitializationFlag::NoGlobalShortcuts));
|
||||
kwinApp()->setSupportsGlobalShortcuts(false);
|
||||
QVERIFY(waylandServer()->init(s_socketName));
|
||||
Test::setOutputConfig({
|
||||
QRect(0, 0, 1280, 1024),
|
||||
QRect(1280, 0, 1280, 1024),
|
||||
|
@ -113,62 +105,6 @@ void NoGlobalShortcutsTest::cleanup()
|
|||
{
|
||||
}
|
||||
|
||||
void NoGlobalShortcutsTest::testTrigger_data()
|
||||
{
|
||||
QTest::addColumn<QStringList>("metaConfig");
|
||||
QTest::addColumn<QStringList>("altConfig");
|
||||
QTest::addColumn<QStringList>("controlConfig");
|
||||
QTest::addColumn<QStringList>("shiftConfig");
|
||||
QTest::addColumn<int>("modifier");
|
||||
QTest::addColumn<QList<int>>("nonTriggeringMods");
|
||||
|
||||
const QStringList trigger = QStringList{s_serviceName, s_path, s_serviceName, QStringLiteral("shortcut")};
|
||||
const QStringList e = QStringList();
|
||||
|
||||
QTest::newRow("leftMeta") << trigger << e << e << e << KEY_LEFTMETA << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
|
||||
QTest::newRow("rightMeta") << trigger << e << e << e << KEY_RIGHTMETA << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
|
||||
QTest::newRow("leftAlt") << e << trigger << e << e << KEY_LEFTALT << QList<int>{KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
|
||||
QTest::newRow("rightAlt") << e << trigger << e << e << KEY_RIGHTALT << QList<int>{KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
|
||||
QTest::newRow("leftControl") << e << e << trigger << e << KEY_LEFTCTRL << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
|
||||
QTest::newRow("rightControl") << e << e << trigger << e << KEY_RIGHTCTRL << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
|
||||
QTest::newRow("leftShift") << e << e << e << trigger << KEY_LEFTSHIFT << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, KEY_RIGHTMETA};
|
||||
QTest::newRow("rightShift") << e << e << e << trigger << KEY_RIGHTSHIFT << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, KEY_RIGHTMETA};
|
||||
}
|
||||
|
||||
void NoGlobalShortcutsTest::testTrigger()
|
||||
{
|
||||
// test based on ModifierOnlyShortcutTest::testTrigger
|
||||
Target target;
|
||||
QSignalSpy triggeredSpy(&target, &Target::shortcutTriggered);
|
||||
|
||||
KConfigGroup group = kwinApp()->config()->group(QStringLiteral("ModifierOnlyShortcuts"));
|
||||
QFETCH(QStringList, metaConfig);
|
||||
QFETCH(QStringList, altConfig);
|
||||
QFETCH(QStringList, shiftConfig);
|
||||
QFETCH(QStringList, controlConfig);
|
||||
group.writeEntry("Meta", metaConfig);
|
||||
group.writeEntry("Alt", altConfig);
|
||||
group.writeEntry("Shift", shiftConfig);
|
||||
group.writeEntry("Control", controlConfig);
|
||||
group.sync();
|
||||
workspace()->slotReconfigure();
|
||||
|
||||
// configured shortcut should trigger
|
||||
quint32 timestamp = 1;
|
||||
QFETCH(int, modifier);
|
||||
Test::keyboardKeyPressed(modifier, timestamp++);
|
||||
Test::keyboardKeyReleased(modifier, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 0);
|
||||
|
||||
// the other shortcuts should not trigger
|
||||
QFETCH(QList<int>, nonTriggeringMods);
|
||||
for (auto it = nonTriggeringMods.constBegin(), end = nonTriggeringMods.constEnd(); it != end; it++) {
|
||||
Test::keyboardKeyPressed(*it, timestamp++);
|
||||
Test::keyboardKeyReleased(*it, timestamp++);
|
||||
QCOMPARE(triggeredSpy.count(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
void NoGlobalShortcutsTest::testKGlobalAccel()
|
||||
{
|
||||
std::unique_ptr<QAction> action(new QAction(nullptr));
|
||||
|
|
|
@ -10,11 +10,15 @@
|
|||
#include "core/outputbackend.h"
|
||||
#include "core/outputconfiguration.h"
|
||||
#include "pointer_input.h"
|
||||
#include "tiles/tilemanager.h"
|
||||
#include "wayland_server.h"
|
||||
#include "window.h"
|
||||
#include "workspace.h"
|
||||
#include "x11window.h"
|
||||
|
||||
#include <KWayland/Client/surface.h>
|
||||
#include <netwm.h>
|
||||
#include <xcb/xcb_icccm.h>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
|
@ -41,9 +45,16 @@ private Q_SLOTS:
|
|||
void testWindowRestoredAfterEnablingOutput();
|
||||
void testMaximizedWindowRestoredAfterEnablingOutput();
|
||||
void testFullScreenWindowRestoredAfterEnablingOutput();
|
||||
void testQuickTiledWindowRestoredAfterEnablingOutput();
|
||||
void testCustomTiledWindowRestoredAfterEnablingOutput_data();
|
||||
void testCustomTiledWindowRestoredAfterEnablingOutput();
|
||||
void testWindowRestoredAfterChangingScale();
|
||||
void testMaximizeStateRestoredAfterEnablingOutput_data();
|
||||
void testMaximizeStateRestoredAfterEnablingOutput();
|
||||
void testInvalidGeometryRestoreAfterEnablingOutput();
|
||||
void testMaximizedWindowDoesntDisappear_data();
|
||||
void testMaximizedWindowDoesntDisappear();
|
||||
void testXwaylandScaleChange();
|
||||
|
||||
void testWindowNotRestoredAfterMovingWindowAndEnablingOutput();
|
||||
void testLaptopLidClosed();
|
||||
|
@ -459,6 +470,182 @@ void OutputChangesTest::testFullScreenWindowRestoredAfterEnablingOutput()
|
|||
QCOMPARE(window->fullscreenGeometryRestore(), QRectF(1280 + 50, 100, 100, 50));
|
||||
}
|
||||
|
||||
void OutputChangesTest::testQuickTiledWindowRestoredAfterEnablingOutput()
|
||||
{
|
||||
// This test verifies that a quick tiled window will be moved to
|
||||
// its original output and tile when the output is re-enabled
|
||||
|
||||
const auto outputs = kwinApp()->outputBackend()->outputs();
|
||||
|
||||
// Create a window.
|
||||
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
|
||||
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(window);
|
||||
|
||||
// kwin will send a configure event with the actived state.
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
|
||||
// Move the window to the right monitor and tile it to the right.
|
||||
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
|
||||
window->move(QPointF(1280 + 50, 100));
|
||||
window->setQuickTileMode(QuickTileFlag::Right, true);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(1280 / 2, 1024));
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), QSize(1280 / 2, 1024), Qt::blue);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
const QRectF rightQuickTileGeom = QRectF(1280 + 1280 / 2, 0, 1280 / 2, 1024);
|
||||
QCOMPARE(window->frameGeometry(), rightQuickTileGeom);
|
||||
QCOMPARE(window->moveResizeGeometry(), rightQuickTileGeom);
|
||||
QCOMPARE(window->output(), outputs[1]);
|
||||
QCOMPARE(window->quickTileMode(), QuickTileFlag::Right);
|
||||
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::Right);
|
||||
QCOMPARE(window->geometryRestore(), QRectF(1280 + 50, 100, 100, 50));
|
||||
|
||||
// Disable the right output.
|
||||
OutputConfiguration config1;
|
||||
{
|
||||
auto changeSet = config1.changeSet(outputs[1]);
|
||||
changeSet->enabled = false;
|
||||
}
|
||||
workspace()->applyOutputConfiguration(config1);
|
||||
|
||||
// The window will be moved to the left monitor
|
||||
QCOMPARE(window->output(), outputs[0]);
|
||||
|
||||
// Enable the right monitor again
|
||||
OutputConfiguration config2;
|
||||
{
|
||||
auto changeSet = config2.changeSet(outputs[1]);
|
||||
changeSet->enabled = true;
|
||||
}
|
||||
workspace()->applyOutputConfiguration(config2);
|
||||
|
||||
// The window will be moved back to the right monitor, and put in the correct tile
|
||||
QCOMPARE(window->frameGeometry(), rightQuickTileGeom);
|
||||
QCOMPARE(window->moveResizeGeometry(), rightQuickTileGeom);
|
||||
QCOMPARE(window->output(), outputs[1]);
|
||||
QCOMPARE(window->quickTileMode(), QuickTileFlag::Right);
|
||||
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::Right);
|
||||
QCOMPARE(window->geometryRestore(), QRectF(1280 + 50, 100, 100, 50));
|
||||
}
|
||||
|
||||
void OutputChangesTest::testCustomTiledWindowRestoredAfterEnablingOutput_data()
|
||||
{
|
||||
const auto outputs = kwinApp()->outputBackend()->outputs();
|
||||
const size_t tileCount = workspace()->tileManager(outputs[1])->rootTile()->childTiles().size();
|
||||
|
||||
QTest::addColumn<size_t>("tileIndex");
|
||||
for (size_t i = 0; i < tileCount; i++) {
|
||||
QTest::addRow("tile %lu", i) << i;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputChangesTest::testCustomTiledWindowRestoredAfterEnablingOutput()
|
||||
{
|
||||
// This test verifies that a custom tiled window will be moved to
|
||||
// its original output and tile when the output is re-enabled
|
||||
|
||||
const auto outputs = kwinApp()->outputBackend()->outputs();
|
||||
|
||||
// start with only one output
|
||||
{
|
||||
OutputConfiguration config;
|
||||
auto changeSet = config.changeSet(outputs[1]);
|
||||
changeSet->enabled = false;
|
||||
workspace()->applyOutputConfiguration(config);
|
||||
}
|
||||
|
||||
// Create a window.
|
||||
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
|
||||
const auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(window);
|
||||
|
||||
// kwin will send a configure event with the actived state.
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
|
||||
const QRectF originalGeometry = window->moveResizeGeometry();
|
||||
|
||||
// Enable the right output
|
||||
{
|
||||
OutputConfiguration config;
|
||||
auto changeSet = config.changeSet(outputs[1]);
|
||||
changeSet->enabled = true;
|
||||
workspace()->applyOutputConfiguration(config);
|
||||
}
|
||||
|
||||
QFETCH(size_t, tileIndex);
|
||||
const QRectF customTileGeom = workspace()->tileManager(outputs[1])->rootTile()->childTiles()[tileIndex]->windowGeometry();
|
||||
|
||||
// Move the window to the right monitor and put it in the middle tile.
|
||||
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
|
||||
window->move(customTileGeom.topLeft() + QPointF(50, 50));
|
||||
const auto geomBeforeTiling = window->moveResizeGeometry();
|
||||
window->setQuickTileMode(QuickTileFlag::Custom, true);
|
||||
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), customTileGeom.size().toSize());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), customTileGeom.size().toSize(), Qt::blue);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
|
||||
QCOMPARE(window->frameGeometry(), customTileGeom);
|
||||
QCOMPARE(window->moveResizeGeometry(), customTileGeom);
|
||||
QCOMPARE(window->output(), outputs[1]);
|
||||
QCOMPARE(window->quickTileMode(), QuickTileFlag::Custom);
|
||||
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::Custom);
|
||||
QCOMPARE(window->geometryRestore(), geomBeforeTiling);
|
||||
|
||||
// Disable the right output.
|
||||
{
|
||||
OutputConfiguration config;
|
||||
auto changeSet = config.changeSet(outputs[1]);
|
||||
changeSet->enabled = false;
|
||||
workspace()->applyOutputConfiguration(config);
|
||||
}
|
||||
|
||||
// The window will be moved to the left monitor, and the original geometry restored
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), originalGeometry.size().toSize());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), originalGeometry.size().toSize(), Qt::blue);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
|
||||
QCOMPARE(window->frameGeometry(), originalGeometry);
|
||||
QCOMPARE(window->moveResizeGeometry(), originalGeometry);
|
||||
QCOMPARE(window->output(), outputs[0]);
|
||||
QCOMPARE(window->quickTileMode(), QuickTileFlag::None);
|
||||
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::None);
|
||||
|
||||
// Enable the right monitor again
|
||||
{
|
||||
OutputConfiguration config;
|
||||
auto changeSet = config.changeSet(outputs[1]);
|
||||
changeSet->enabled = true;
|
||||
workspace()->applyOutputConfiguration(config);
|
||||
}
|
||||
|
||||
// The window will be moved back to the right monitor, and put in the correct tile
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), customTileGeom.size().toSize());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), customTileGeom.size().toSize(), Qt::blue);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
|
||||
QCOMPARE(window->frameGeometry(), customTileGeom);
|
||||
QCOMPARE(window->moveResizeGeometry(), customTileGeom);
|
||||
QCOMPARE(window->output(), outputs[1]);
|
||||
QCOMPARE(window->quickTileMode(), QuickTileFlag::Custom);
|
||||
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::Custom);
|
||||
QCOMPARE(window->geometryRestore(), geomBeforeTiling);
|
||||
}
|
||||
|
||||
void OutputChangesTest::testWindowRestoredAfterChangingScale()
|
||||
{
|
||||
// This test verifies that a window will be moved to its original position after changing the scale of an output
|
||||
|
@ -502,11 +689,21 @@ void OutputChangesTest::testWindowRestoredAfterChangingScale()
|
|||
QCOMPARE(window->output(), output);
|
||||
}
|
||||
|
||||
void OutputChangesTest::testMaximizeStateRestoredAfterEnablingOutput_data()
|
||||
{
|
||||
QTest::addColumn<MaximizeMode>("maximizeMode");
|
||||
QTest::addRow("Vertical Maximization") << MaximizeMode::MaximizeVertical;
|
||||
QTest::addRow("Horizontal Maximization") << MaximizeMode::MaximizeHorizontal;
|
||||
QTest::addRow("Full Maximization") << MaximizeMode::MaximizeFull;
|
||||
}
|
||||
|
||||
void OutputChangesTest::testMaximizeStateRestoredAfterEnablingOutput()
|
||||
{
|
||||
// This test verifies that the window state will get restored after disabling and enabling an output,
|
||||
// even if its maximize state changed in the process
|
||||
|
||||
QFETCH(MaximizeMode, maximizeMode);
|
||||
|
||||
const auto outputs = kwinApp()->outputBackend()->outputs();
|
||||
|
||||
// Disable the right output
|
||||
|
@ -541,17 +738,16 @@ void OutputChangesTest::testMaximizeStateRestoredAfterEnablingOutput()
|
|||
// Move the window to the right monitor and make it maximized.
|
||||
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
|
||||
window->move(QPointF(1280 + 50, 100));
|
||||
window->maximize(MaximizeFull);
|
||||
window->maximize(maximizeMode);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(1280, 1024));
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), QSize(1280, 1024), Qt::blue);
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::blue);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
|
||||
QCOMPARE(window->moveResizeGeometry(), QRectF(1280, 0, 1280, 1024));
|
||||
const auto maximizedGeometry = window->moveResizeGeometry();
|
||||
QCOMPARE(window->frameGeometry(), maximizedGeometry);
|
||||
QCOMPARE(window->output(), outputs[1]);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeFull);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
|
||||
QCOMPARE(window->maximizeMode(), maximizeMode);
|
||||
QCOMPARE(window->requestedMaximizeMode(), maximizeMode);
|
||||
QCOMPARE(window->geometryRestore(), QRectF(1280 + 50, 100, 100, 50));
|
||||
|
||||
// Disable the right output
|
||||
|
@ -584,15 +780,15 @@ void OutputChangesTest::testMaximizeStateRestoredAfterEnablingOutput()
|
|||
|
||||
// The window will be moved back to the right monitor, maximized and the geometry restore will be updated
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), outputs[1]->geometry().size());
|
||||
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), maximizedGeometry.size());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), outputs[1]->geometry().size(), Qt::blue);
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::blue);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
|
||||
QCOMPARE(window->moveResizeGeometry(), QRectF(1280, 0, 1280, 1024));
|
||||
QCOMPARE(window->frameGeometry(), maximizedGeometry);
|
||||
QCOMPARE(window->moveResizeGeometry(), maximizedGeometry);
|
||||
QCOMPARE(window->output(), outputs[1]);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeFull);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
|
||||
QCOMPARE(window->maximizeMode(), maximizeMode);
|
||||
QCOMPARE(window->requestedMaximizeMode(), maximizeMode);
|
||||
QCOMPARE(window->geometryRestore(), QRectF(1280 + 50, 100, 100, 50));
|
||||
}
|
||||
|
||||
|
@ -689,6 +885,90 @@ void OutputChangesTest::testInvalidGeometryRestoreAfterEnablingOutput()
|
|||
QCOMPARE(window->geometryRestore(), rightGeometryRestore);
|
||||
}
|
||||
|
||||
void OutputChangesTest::testMaximizedWindowDoesntDisappear_data()
|
||||
{
|
||||
QTest::addColumn<MaximizeMode>("maximizeMode");
|
||||
QTest::addRow("Vertical Maximization") << MaximizeMode::MaximizeVertical;
|
||||
QTest::addRow("Horizontal Maximization") << MaximizeMode::MaximizeHorizontal;
|
||||
QTest::addRow("Full Maximization") << MaximizeMode::MaximizeFull;
|
||||
}
|
||||
|
||||
void OutputChangesTest::testMaximizedWindowDoesntDisappear()
|
||||
{
|
||||
// This test verifies that (vertically, horizontally) maximized windows don't get placed out of the screen
|
||||
// when the output they're on gets disabled or removed
|
||||
|
||||
Test::setOutputConfig({
|
||||
Test::OutputInfo{
|
||||
.geometry = QRect(5120 / 3, 1440, 2256 / 1.3, 1504 / 1.3),
|
||||
.scale = 1.3,
|
||||
.internal = true,
|
||||
},
|
||||
Test::OutputInfo{
|
||||
.geometry = QRect(0, 0, 5120, 1440),
|
||||
.scale = 1,
|
||||
.internal = false,
|
||||
},
|
||||
});
|
||||
const auto outputs = kwinApp()->outputBackend()->outputs();
|
||||
QFETCH(MaximizeMode, maximizeMode);
|
||||
|
||||
workspace()->setActiveOutput(outputs[1]);
|
||||
|
||||
// Create a window.
|
||||
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
|
||||
auto window = Test::renderAndWaitForShown(surface.get(), QSize(500, 300), Qt::blue);
|
||||
QVERIFY(window);
|
||||
|
||||
// kwin will send a configure event with the actived state.
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
|
||||
window->move(outputs[1]->geometry().topLeft() + QPoint(3500, 500));
|
||||
const QRectF originalGeometry = window->frameGeometry();
|
||||
QVERIFY(outputs[1]->geometryF().contains(originalGeometry));
|
||||
|
||||
// vertically maximize the window
|
||||
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
|
||||
window->maximize(maximizeMode);
|
||||
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::blue);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
|
||||
QCOMPARE(window->output(), outputs[1]);
|
||||
const auto maximizedGeometry = window->moveResizeGeometry();
|
||||
QCOMPARE(window->frameGeometry(), maximizedGeometry);
|
||||
QCOMPARE(window->maximizeMode(), maximizeMode);
|
||||
QCOMPARE(window->requestedMaximizeMode(), maximizeMode);
|
||||
QCOMPARE(window->geometryRestore(), originalGeometry);
|
||||
|
||||
// Disable the top output
|
||||
{
|
||||
OutputConfiguration config;
|
||||
auto changeSet0 = config.changeSet(outputs[0]);
|
||||
changeSet0->pos = QPoint(0, 0);
|
||||
auto changeSet = config.changeSet(outputs[1]);
|
||||
changeSet->enabled = false;
|
||||
workspace()->applyOutputConfiguration(config);
|
||||
}
|
||||
|
||||
// The window should be moved to the left output
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::blue);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
|
||||
QCOMPARE(window->output(), outputs[0]);
|
||||
QVERIFY(outputs[0]->geometryF().contains(window->frameGeometry()));
|
||||
QVERIFY(outputs[0]->geometryF().contains(window->moveResizeGeometry()));
|
||||
QCOMPARE(window->maximizeMode(), maximizeMode);
|
||||
QCOMPARE(window->requestedMaximizeMode(), maximizeMode);
|
||||
}
|
||||
|
||||
void OutputChangesTest::testLaptopLidClosed()
|
||||
{
|
||||
Test::setOutputConfig({
|
||||
|
@ -728,6 +1008,80 @@ void OutputChangesTest::testLaptopLidClosed()
|
|||
input()->removeInputDevice(lidSwitch.get());
|
||||
}
|
||||
|
||||
static X11Window *createX11Window(xcb_connection_t *connection, const QRect &geometry, std::function<void(xcb_window_t)> setup = {})
|
||||
{
|
||||
xcb_window_t windowId = xcb_generate_id(connection);
|
||||
xcb_create_window(connection, XCB_COPY_FROM_PARENT, windowId, rootWindow(),
|
||||
geometry.x(),
|
||||
geometry.y(),
|
||||
geometry.width(),
|
||||
geometry.height(),
|
||||
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
|
||||
|
||||
xcb_size_hints_t hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
xcb_icccm_size_hints_set_position(&hints, 1, geometry.x(), geometry.y());
|
||||
xcb_icccm_size_hints_set_size(&hints, 1, geometry.width(), geometry.height());
|
||||
xcb_icccm_set_wm_normal_hints(connection, windowId, &hints);
|
||||
|
||||
if (setup) {
|
||||
setup(windowId);
|
||||
}
|
||||
|
||||
xcb_map_window(connection, windowId);
|
||||
xcb_flush(connection);
|
||||
|
||||
QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
|
||||
if (!windowCreatedSpy.wait()) {
|
||||
return nullptr;
|
||||
}
|
||||
return windowCreatedSpy.last().first().value<X11Window *>();
|
||||
}
|
||||
|
||||
void OutputChangesTest::testXwaylandScaleChange()
|
||||
{
|
||||
Test::setOutputConfig({
|
||||
QRect(0, 0, 1280, 1024),
|
||||
QRect(1280, 0, 1280, 1024),
|
||||
});
|
||||
const auto outputs = workspace()->outputs();
|
||||
|
||||
{
|
||||
OutputConfiguration config;
|
||||
config.changeSet(outputs[0])->scale = 2;
|
||||
config.changeSet(outputs[1])->scale = 1;
|
||||
workspace()->applyOutputConfiguration(config);
|
||||
}
|
||||
QCOMPARE(kwinApp()->xwaylandScale(), 2);
|
||||
|
||||
Test::XcbConnectionPtr c = Test::createX11Connection();
|
||||
QVERIFY(!xcb_connection_has_error(c.get()));
|
||||
X11Window *window = createX11Window(c.get(), QRect(0, 0, 100, 200));
|
||||
const QRectF originalGeometry = window->frameGeometry();
|
||||
|
||||
// disable the left output -> window gets moved to the right output
|
||||
{
|
||||
OutputConfiguration config;
|
||||
config.changeSet(outputs[0])->enabled = false;
|
||||
workspace()->applyOutputConfiguration(config);
|
||||
}
|
||||
|
||||
// the window should still have logical size of 100, 200
|
||||
QCOMPARE(kwinApp()->xwaylandScale(), 1);
|
||||
QCOMPARE(window->frameGeometry().size(), originalGeometry.size());
|
||||
|
||||
// enable the left output again
|
||||
{
|
||||
OutputConfiguration config;
|
||||
config.changeSet(outputs[0])->enabled = true;
|
||||
workspace()->applyOutputConfiguration(config);
|
||||
}
|
||||
|
||||
// the window should be back in its original geometry
|
||||
QCOMPARE(kwinApp()->xwaylandScale(), 2);
|
||||
QCOMPARE(window->frameGeometry(), originalGeometry);
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
WAYLANDTEST_MAIN(KWin::OutputChangesTest)
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
SPDX-FileContributor: Jan Grulich <jgrulich@redhat.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "pipewirecore_p.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QSocketNotifier>
|
||||
#include <QThread>
|
||||
#include <QThreadStorage>
|
||||
#include <mutex>
|
||||
#include <spa/utils/result.h>
|
||||
|
||||
pw_core_events PipeWireCore::s_pwCoreEvents = {
|
||||
.version = PW_VERSION_CORE_EVENTS,
|
||||
.info = &PipeWireCore::onCoreInfo,
|
||||
.done = nullptr,
|
||||
.ping = nullptr,
|
||||
.error = &PipeWireCore::onCoreError,
|
||||
.remove_id = nullptr,
|
||||
.bound_id = nullptr,
|
||||
.add_mem = nullptr,
|
||||
.remove_mem = nullptr,
|
||||
};
|
||||
|
||||
PipeWireCore::PipeWireCore()
|
||||
{
|
||||
static std::once_flag pwInitOnce;
|
||||
std::call_once(pwInitOnce, [] {
|
||||
pw_init(nullptr, nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
void PipeWireCore::onCoreError(void *data, uint32_t id, int seq, int res, const char *message)
|
||||
{
|
||||
Q_UNUSED(seq)
|
||||
|
||||
qWarning() << "PipeWire remote error: " << res << message;
|
||||
if (id == PW_ID_CORE) {
|
||||
PipeWireCore *pw = static_cast<PipeWireCore *>(data);
|
||||
Q_EMIT pw->pipewireFailed(QString::fromUtf8(message));
|
||||
|
||||
if (res == -EPIPE) {
|
||||
// Broken pipe, need reconnecting
|
||||
if (pw->m_pwCore) {
|
||||
Q_EMIT pw->pipeBroken();
|
||||
spa_hook_remove(&pw->m_coreListener);
|
||||
pw_core_disconnect(pw->m_pwCore);
|
||||
pw->init_core();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PipeWireCore::onCoreInfo(void *data, const struct pw_core_info *info)
|
||||
{
|
||||
PipeWireCore *pw = static_cast<PipeWireCore *>(data);
|
||||
pw->m_serverVersion = QVersionNumber::fromString(QString::fromUtf8(info->version));
|
||||
}
|
||||
|
||||
PipeWireCore::~PipeWireCore()
|
||||
{
|
||||
if (m_pwMainLoop) {
|
||||
pw_loop_leave(m_pwMainLoop);
|
||||
}
|
||||
|
||||
if (m_pwCore) {
|
||||
pw_core_disconnect(m_pwCore);
|
||||
}
|
||||
|
||||
if (m_pwContext) {
|
||||
pw_context_destroy(m_pwContext);
|
||||
}
|
||||
|
||||
if (m_pwMainLoop) {
|
||||
pw_loop_destroy(m_pwMainLoop);
|
||||
}
|
||||
}
|
||||
|
||||
bool PipeWireCore::init(int fd)
|
||||
{
|
||||
m_pwMainLoop = pw_loop_new(nullptr);
|
||||
pw_loop_enter(m_pwMainLoop);
|
||||
|
||||
QSocketNotifier *notifier = new QSocketNotifier(pw_loop_get_fd(m_pwMainLoop), QSocketNotifier::Read, this);
|
||||
connect(notifier, &QSocketNotifier::activated, this, [this] {
|
||||
int result = pw_loop_iterate(m_pwMainLoop, 0);
|
||||
if (result < 0)
|
||||
qWarning() << "pipewire_loop_iterate failed: " << spa_strerror(result);
|
||||
});
|
||||
|
||||
m_pwContext = pw_context_new(m_pwMainLoop, nullptr, 0);
|
||||
if (!m_pwContext) {
|
||||
qWarning() << "Failed to create PipeWire context";
|
||||
m_error = QStringLiteral("Failed to create PipeWire context");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_fd = fd;
|
||||
|
||||
return init_core();
|
||||
}
|
||||
|
||||
bool PipeWireCore::init_core()
|
||||
{
|
||||
if (m_fd > 0) {
|
||||
m_pwCore = pw_context_connect_fd(m_pwContext, m_fd, nullptr, 0);
|
||||
} else {
|
||||
m_pwCore = pw_context_connect(m_pwContext, nullptr, 0);
|
||||
}
|
||||
if (!m_pwCore) {
|
||||
m_error = QStringLiteral("Failed to connect to PipeWire");
|
||||
qWarning() << "error:" << m_error << m_fd;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pw_loop_iterate(m_pwMainLoop, 0) < 0) {
|
||||
qWarning() << "Failed to start main PipeWire loop";
|
||||
m_error = QStringLiteral("Failed to start main PipeWire loop");
|
||||
return false;
|
||||
}
|
||||
|
||||
pw_core_add_listener(m_pwCore, &m_coreListener, &s_pwCoreEvents, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
QSharedPointer<PipeWireCore> PipeWireCore::fetch(int fd)
|
||||
{
|
||||
static QThreadStorage<QHash<int, QWeakPointer<PipeWireCore>>> global;
|
||||
QSharedPointer<PipeWireCore> ret = global.localData().value(fd).toStrongRef();
|
||||
if (!ret) {
|
||||
ret.reset(new PipeWireCore);
|
||||
if (ret->init(fd)) {
|
||||
global.localData().insert(fd, ret);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString PipeWireCore::error() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
SPDX-FileContributor: Jan Grulich <jgrulich@redhat.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QVersionNumber>
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
class PipeWireCore : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PipeWireCore();
|
||||
|
||||
static void onCoreError(void *data, uint32_t id, int seq, int res, const char *message);
|
||||
static void onCoreInfo(void *data, const struct pw_core_info *info);
|
||||
|
||||
~PipeWireCore();
|
||||
|
||||
bool init(int fd);
|
||||
bool init_core();
|
||||
QString error() const;
|
||||
QVersionNumber serverVersion() const
|
||||
{
|
||||
return m_serverVersion;
|
||||
}
|
||||
|
||||
pw_loop *loop() const
|
||||
{
|
||||
return m_pwMainLoop;
|
||||
}
|
||||
|
||||
pw_core *operator*() const
|
||||
{
|
||||
return m_pwCore;
|
||||
};
|
||||
static QSharedPointer<PipeWireCore> fetch(int fd);
|
||||
|
||||
private:
|
||||
int m_fd = 0;
|
||||
pw_core *m_pwCore = nullptr;
|
||||
pw_context *m_pwContext = nullptr;
|
||||
pw_loop *m_pwMainLoop = nullptr;
|
||||
spa_hook m_coreListener;
|
||||
QString m_error;
|
||||
QVersionNumber m_serverVersion;
|
||||
|
||||
static pw_core_events s_pwCoreEvents;
|
||||
|
||||
Q_SIGNALS:
|
||||
void pipewireFailed(const QString &message);
|
||||
|
||||
/**
|
||||
* Clients should disconnect from the core and reconnect to it on receiving this signal
|
||||
*/
|
||||
void pipeBroken();
|
||||
};
|
|
@ -1,643 +0,0 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2018-2020 Red Hat Inc
|
||||
SPDX-FileCopyrightText: 2020-2021 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
SPDX-FileContributor: Jan Grulich <jgrulich@redhat.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include <epoxy/egl.h>
|
||||
|
||||
#include "pipewirecore_p.h"
|
||||
#include "pipewiresourcestream.h"
|
||||
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <spa/utils/result.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QOpenGLTexture>
|
||||
#include <QSocketNotifier>
|
||||
#include <QThread>
|
||||
#include <QVersionNumber>
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
|
||||
#undef Status
|
||||
|
||||
#if !PW_CHECK_VERSION(0, 3, 29)
|
||||
#define SPA_POD_PROP_FLAG_MANDATORY (1u << 3)
|
||||
#endif
|
||||
#if !PW_CHECK_VERSION(0, 3, 33)
|
||||
#define SPA_POD_PROP_FLAG_DONT_FIXATE (1u << 4)
|
||||
#endif
|
||||
|
||||
#define CURSOR_BPP 4
|
||||
#define CURSOR_META_SIZE(w, h) (sizeof(struct spa_meta_cursor) + sizeof(struct spa_meta_bitmap) + w * h * CURSOR_BPP)
|
||||
|
||||
pw_stream_events pwStreamEvents = {};
|
||||
|
||||
static QImage SpaBufferToQImage(const uchar *data, int width, int height, qsizetype bytesPerLine, spa_video_format format)
|
||||
{
|
||||
switch (format) {
|
||||
case SPA_VIDEO_FORMAT_BGRx:
|
||||
case SPA_VIDEO_FORMAT_BGRA:
|
||||
case SPA_VIDEO_FORMAT_xBGR:
|
||||
case SPA_VIDEO_FORMAT_ABGR: {
|
||||
// This is needed because QImage does not support BGRA
|
||||
// This is obviously a much slower path, it makes sense to avoid it as much as possible
|
||||
return QImage(data, width, height, bytesPerLine, SpaToQImageFormat(format)).rgbSwapped();
|
||||
}
|
||||
case SPA_VIDEO_FORMAT_BGR:
|
||||
case SPA_VIDEO_FORMAT_RGBx:
|
||||
case SPA_VIDEO_FORMAT_RGB:
|
||||
case SPA_VIDEO_FORMAT_RGBA:
|
||||
default:
|
||||
return QImage(data, width, height, bytesPerLine, SpaToQImageFormat(format));
|
||||
}
|
||||
}
|
||||
|
||||
QImage::Format SpaToQImageFormat(quint32 format)
|
||||
{
|
||||
switch (format) {
|
||||
case SPA_VIDEO_FORMAT_BGRx:
|
||||
case SPA_VIDEO_FORMAT_BGRA:
|
||||
return QImage::Format_RGBA8888_Premultiplied; // Handled in SpaBufferToQImage
|
||||
case SPA_VIDEO_FORMAT_ABGR:
|
||||
case SPA_VIDEO_FORMAT_xBGR:
|
||||
return QImage::Format_ARGB32; // Handled in SpaBufferToQImage
|
||||
case SPA_VIDEO_FORMAT_BGR:
|
||||
return QImage::Format_BGR888;
|
||||
case SPA_VIDEO_FORMAT_RGBx:
|
||||
return QImage::Format_RGBX8888;
|
||||
case SPA_VIDEO_FORMAT_RGB:
|
||||
return QImage::Format_RGB888;
|
||||
case SPA_VIDEO_FORMAT_RGBA:
|
||||
return QImage::Format_RGBA8888_Premultiplied;
|
||||
default:
|
||||
qWarning() << "cannot convert spa format to QImage" << format;
|
||||
return QImage::Format_RGB32;
|
||||
}
|
||||
}
|
||||
|
||||
struct PipeWireSourceStreamPrivate
|
||||
{
|
||||
QSharedPointer<PipeWireCore> pwCore;
|
||||
pw_stream *pwStream = nullptr;
|
||||
spa_hook streamListener;
|
||||
|
||||
uint32_t pwNodeId = 0;
|
||||
std::optional<std::chrono::nanoseconds> m_currentPresentationTimestamp;
|
||||
|
||||
QAtomicInt m_stopped = false;
|
||||
pw_stream_state m_state = PW_STREAM_STATE_UNCONNECTED;
|
||||
|
||||
spa_video_info_raw videoFormat;
|
||||
QString m_error;
|
||||
bool m_allowDmaBuf = true;
|
||||
bool m_usingDmaBuf = false;
|
||||
|
||||
QHash<spa_video_format, QList<uint64_t>> m_availableModifiers;
|
||||
spa_source *m_renegotiateEvent = nullptr;
|
||||
|
||||
bool m_withDamage = false;
|
||||
Fraction maxFramerate;
|
||||
};
|
||||
|
||||
static const QVersionNumber pwClientVersion = QVersionNumber::fromString(QString::fromUtf8(pw_get_library_version()));
|
||||
static const QVersionNumber kDmaBufMinVersion = {0, 3, 24};
|
||||
static const QVersionNumber kDmaBufModifierMinVersion = {0, 3, 33};
|
||||
static const QVersionNumber kDropSingleModifierMinVersion = {0, 3, 40};
|
||||
|
||||
uint32_t PipeWireSourceStream::spaVideoFormatToDrmFormat(spa_video_format spa_format)
|
||||
{
|
||||
switch (spa_format) {
|
||||
case SPA_VIDEO_FORMAT_RGBA:
|
||||
return DRM_FORMAT_ABGR8888;
|
||||
case SPA_VIDEO_FORMAT_RGBx:
|
||||
return DRM_FORMAT_XBGR8888;
|
||||
case SPA_VIDEO_FORMAT_BGRA:
|
||||
return DRM_FORMAT_ARGB8888;
|
||||
case SPA_VIDEO_FORMAT_BGRx:
|
||||
return DRM_FORMAT_XRGB8888;
|
||||
case SPA_VIDEO_FORMAT_BGR:
|
||||
return DRM_FORMAT_BGR888;
|
||||
case SPA_VIDEO_FORMAT_RGB:
|
||||
return DRM_FORMAT_RGB888;
|
||||
case SPA_VIDEO_FORMAT_xBGR:
|
||||
return DRM_FORMAT_RGBX8888;
|
||||
case SPA_VIDEO_FORMAT_ABGR:
|
||||
return DRM_FORMAT_RGBA8888;
|
||||
default:
|
||||
qWarning() << "cannot convert spa format to fourcc" << spa_format;
|
||||
return DRM_FORMAT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
static QHash<spa_video_format, QList<uint64_t>> queryDmaBufModifiers(EGLDisplay display, const QList<spa_video_format> &formats)
|
||||
{
|
||||
QHash<spa_video_format, QList<uint64_t>> ret;
|
||||
ret.reserve(formats.size());
|
||||
const bool hasEglImageDmaBufImportExt = epoxy_has_egl_extension(display, "EGL_EXT_image_dma_buf_import");
|
||||
static auto eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)eglGetProcAddress("eglQueryDmaBufModifiersEXT");
|
||||
static auto eglQueryDmaBufFormatsEXT = (PFNEGLQUERYDMABUFFORMATSEXTPROC)eglGetProcAddress("eglQueryDmaBufFormatsEXT");
|
||||
|
||||
EGLint count = 0;
|
||||
EGLBoolean successFormats = eglQueryDmaBufFormatsEXT(display, 0, nullptr, &count);
|
||||
|
||||
QList<uint32_t> drmFormats(count);
|
||||
successFormats &= eglQueryDmaBufFormatsEXT(display, count, reinterpret_cast<EGLint *>(drmFormats.data()), &count);
|
||||
if (!successFormats)
|
||||
qWarning() << "Failed to query DMA-BUF formats.";
|
||||
|
||||
const QList<uint64_t> mods = hasEglImageDmaBufImportExt ? QList<uint64_t>{DRM_FORMAT_MOD_INVALID} : QList<uint64_t>{};
|
||||
if (!eglQueryDmaBufFormatsEXT || !eglQueryDmaBufModifiersEXT || !hasEglImageDmaBufImportExt || !successFormats) {
|
||||
for (spa_video_format format : formats) {
|
||||
ret[format] = mods;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (spa_video_format format : formats) {
|
||||
uint32_t drm_format = PipeWireSourceStream::spaVideoFormatToDrmFormat(format);
|
||||
if (drm_format == DRM_FORMAT_INVALID) {
|
||||
qDebug() << "Failed to find matching DRM format." << format;
|
||||
break;
|
||||
}
|
||||
|
||||
if (std::find(drmFormats.begin(), drmFormats.end(), drm_format) == drmFormats.end()) {
|
||||
qDebug() << "Format " << drm_format << " not supported for modifiers.";
|
||||
ret[format] = mods;
|
||||
break;
|
||||
}
|
||||
|
||||
successFormats = eglQueryDmaBufModifiersEXT(display, drm_format, 0, nullptr, nullptr, &count);
|
||||
if (!successFormats) {
|
||||
qWarning() << "Failed to query DMA-BUF modifier count.";
|
||||
ret[format] = mods;
|
||||
break;
|
||||
}
|
||||
|
||||
QList<uint64_t> modifiers(count);
|
||||
if (count > 0) {
|
||||
if (!eglQueryDmaBufModifiersEXT(display, drm_format, count, modifiers.data(), nullptr, &count)) {
|
||||
qWarning() << "Failed to query DMA-BUF modifiers.";
|
||||
}
|
||||
}
|
||||
|
||||
// Support modifier-less buffers
|
||||
modifiers.push_back(DRM_FORMAT_MOD_INVALID);
|
||||
ret[format] = modifiers;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PipeWireSourceStream::onStreamStateChanged(void *data, pw_stream_state old, pw_stream_state state, const char *error_message)
|
||||
{
|
||||
PipeWireSourceStream *pw = static_cast<PipeWireSourceStream *>(data);
|
||||
qDebug() << "state changed" << pw_stream_state_as_string(old) << "->" << pw_stream_state_as_string(state) << error_message;
|
||||
pw->d->m_state = state;
|
||||
Q_EMIT pw->stateChanged(state, old);
|
||||
|
||||
switch (state) {
|
||||
case PW_STREAM_STATE_ERROR:
|
||||
qWarning() << "Stream error: " << error_message;
|
||||
break;
|
||||
case PW_STREAM_STATE_PAUSED:
|
||||
Q_EMIT pw->streamReady();
|
||||
break;
|
||||
case PW_STREAM_STATE_STREAMING:
|
||||
Q_EMIT pw->startStreaming();
|
||||
break;
|
||||
case PW_STREAM_STATE_CONNECTING:
|
||||
break;
|
||||
case PW_STREAM_STATE_UNCONNECTED:
|
||||
if (!pw->d->m_stopped) {
|
||||
Q_EMIT pw->stopStreaming();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PipeWireSourceStream::onRenegotiate(void *data, uint64_t)
|
||||
{
|
||||
PipeWireSourceStream *pw = static_cast<PipeWireSourceStream *>(data);
|
||||
uint8_t buffer[4096];
|
||||
spa_pod_builder podBuilder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||
auto params = pw->createFormatsParams(podBuilder);
|
||||
pw_stream_update_params(pw->d->pwStream, params.data(), params.size());
|
||||
}
|
||||
|
||||
void PipeWireSourceStream::renegotiateModifierFailed(spa_video_format format, quint64 modifier)
|
||||
{
|
||||
if (d->pwCore->serverVersion() >= kDropSingleModifierMinVersion) {
|
||||
const int removed = d->m_availableModifiers[format].removeAll(modifier);
|
||||
if (removed == 0) {
|
||||
d->m_allowDmaBuf = false;
|
||||
}
|
||||
} else {
|
||||
d->m_allowDmaBuf = false;
|
||||
}
|
||||
qDebug() << "renegotiating, modifier didn't work" << format << modifier << "now only offering" << d->m_availableModifiers[format].count();
|
||||
pw_loop_signal_event(d->pwCore->loop(), d->m_renegotiateEvent);
|
||||
}
|
||||
|
||||
static spa_pod *
|
||||
buildFormat(spa_pod_builder *builder, spa_video_format format, const QList<uint64_t> &modifiers, bool withDontFixate, const Fraction &requestedMaxFramerate)
|
||||
{
|
||||
spa_pod_frame f[2];
|
||||
const spa_rectangle pw_min_screen_bounds{1, 1};
|
||||
const spa_rectangle pw_max_screen_bounds{UINT32_MAX, UINT32_MAX};
|
||||
|
||||
spa_pod_builder_push_object(builder, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
|
||||
spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
|
||||
spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0);
|
||||
spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
|
||||
spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(&pw_min_screen_bounds, &pw_min_screen_bounds, &pw_max_screen_bounds), 0);
|
||||
if (requestedMaxFramerate) {
|
||||
auto defFramerate = SPA_FRACTION(0, 1);
|
||||
auto minFramerate = SPA_FRACTION(1, 1);
|
||||
auto maxFramerate = SPA_FRACTION(requestedMaxFramerate.numerator, requestedMaxFramerate.denominator);
|
||||
spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&defFramerate), 0);
|
||||
spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_CHOICE_RANGE_Fraction(&maxFramerate, &minFramerate, &maxFramerate), 0);
|
||||
}
|
||||
|
||||
if (modifiers.size() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) {
|
||||
// we only support implicit modifiers, use shortpath to skip fixation phase
|
||||
spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY);
|
||||
spa_pod_builder_long(builder, modifiers[0]);
|
||||
} else if (!modifiers.isEmpty()) {
|
||||
// SPA_POD_PROP_FLAG_DONT_FIXATE can be used with PipeWire >= 0.3.33
|
||||
if (withDontFixate) {
|
||||
spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE);
|
||||
} else {
|
||||
spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY);
|
||||
}
|
||||
spa_pod_builder_push_choice(builder, &f[1], SPA_CHOICE_Enum, 0);
|
||||
// mofifiers from the array
|
||||
for (auto it = modifiers.begin(); it != modifiers.end(); it++) {
|
||||
spa_pod_builder_long(builder, *it);
|
||||
if (it == modifiers.begin()) {
|
||||
spa_pod_builder_long(builder, *it);
|
||||
}
|
||||
}
|
||||
spa_pod_builder_pop(builder, &f[1]);
|
||||
}
|
||||
|
||||
return static_cast<spa_pod *>(spa_pod_builder_pop(builder, &f[0]));
|
||||
}
|
||||
|
||||
static const int videoDamageRegionCount = 16;
|
||||
|
||||
void PipeWireSourceStream::onStreamParamChanged(void *data, uint32_t id, const struct spa_pod *format)
|
||||
{
|
||||
if (!format || id != SPA_PARAM_Format) {
|
||||
return;
|
||||
}
|
||||
|
||||
PipeWireSourceStream *pw = static_cast<PipeWireSourceStream *>(data);
|
||||
spa_format_video_raw_parse(format, &pw->d->videoFormat);
|
||||
|
||||
uint8_t paramsBuffer[1024];
|
||||
spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(paramsBuffer, sizeof(paramsBuffer));
|
||||
|
||||
// When SPA_FORMAT_VIDEO_modifier is present we can use DMA-BUFs as
|
||||
// the server announces support for it.
|
||||
// See https://github.com/PipeWire/pipewire/blob/master/doc/dma-buf.dox
|
||||
|
||||
pw->d->m_usingDmaBuf = pw->d->m_allowDmaBuf && spa_pod_find_prop(format, nullptr, SPA_FORMAT_VIDEO_modifier);
|
||||
Q_ASSERT(pw->d->m_allowDmaBuf || !pw->d->m_usingDmaBuf);
|
||||
const auto bufferTypes =
|
||||
pw->d->m_usingDmaBuf ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr) : (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr);
|
||||
|
||||
QVarLengthArray<const spa_pod *> params = {
|
||||
(spa_pod *)spa_pod_builder_add_object(&pod_builder,
|
||||
SPA_TYPE_OBJECT_ParamBuffers,
|
||||
SPA_PARAM_Buffers,
|
||||
SPA_PARAM_BUFFERS_buffers,
|
||||
SPA_POD_CHOICE_RANGE_Int(16, 2, 16),
|
||||
SPA_PARAM_BUFFERS_align,
|
||||
SPA_POD_Int(16),
|
||||
SPA_PARAM_BUFFERS_dataType,
|
||||
SPA_POD_CHOICE_FLAGS_Int(bufferTypes)),
|
||||
(spa_pod *)spa_pod_builder_add_object(&pod_builder,
|
||||
SPA_TYPE_OBJECT_ParamMeta,
|
||||
SPA_PARAM_Meta,
|
||||
SPA_PARAM_META_type,
|
||||
SPA_POD_Id(SPA_META_Header),
|
||||
SPA_PARAM_META_size,
|
||||
SPA_POD_Int(sizeof(struct spa_meta_header))),
|
||||
(spa_pod *)spa_pod_builder_add_object(&pod_builder,
|
||||
SPA_TYPE_OBJECT_ParamMeta,
|
||||
SPA_PARAM_Meta,
|
||||
SPA_PARAM_META_type,
|
||||
SPA_POD_Id(SPA_META_Cursor),
|
||||
SPA_PARAM_META_size,
|
||||
SPA_POD_CHOICE_RANGE_Int(CURSOR_META_SIZE(64, 64), CURSOR_META_SIZE(1, 1), CURSOR_META_SIZE(1024, 1024))),
|
||||
};
|
||||
|
||||
if (pw->d->m_withDamage) {
|
||||
params.append((spa_pod *)spa_pod_builder_add_object(&pod_builder,
|
||||
SPA_TYPE_OBJECT_ParamMeta,
|
||||
SPA_PARAM_Meta,
|
||||
SPA_PARAM_META_type,
|
||||
SPA_POD_Id(SPA_META_VideoDamage),
|
||||
SPA_PARAM_META_size,
|
||||
SPA_POD_CHOICE_RANGE_Int(sizeof(struct spa_meta_region) * videoDamageRegionCount,
|
||||
sizeof(struct spa_meta_region) * 1,
|
||||
sizeof(struct spa_meta_region) * videoDamageRegionCount)));
|
||||
}
|
||||
|
||||
pw_stream_update_params(pw->d->pwStream, params.data(), params.count());
|
||||
Q_EMIT pw->streamParametersChanged();
|
||||
}
|
||||
|
||||
static void onProcess(void *data)
|
||||
{
|
||||
PipeWireSourceStream *stream = static_cast<PipeWireSourceStream *>(data);
|
||||
stream->process();
|
||||
}
|
||||
|
||||
QSize PipeWireSourceStream::size() const
|
||||
{
|
||||
return QSize(d->videoFormat.size.width, d->videoFormat.size.height);
|
||||
}
|
||||
|
||||
pw_stream_state PipeWireSourceStream::state() const
|
||||
{
|
||||
return d->m_state;
|
||||
}
|
||||
|
||||
std::optional<std::chrono::nanoseconds> PipeWireSourceStream::currentPresentationTimestamp() const
|
||||
{
|
||||
return d->m_currentPresentationTimestamp;
|
||||
}
|
||||
|
||||
QString PipeWireSourceStream::error() const
|
||||
{
|
||||
return d->m_error;
|
||||
}
|
||||
|
||||
PipeWireSourceStream::PipeWireSourceStream(QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new PipeWireSourceStreamPrivate)
|
||||
{
|
||||
pwStreamEvents.version = PW_VERSION_STREAM_EVENTS;
|
||||
pwStreamEvents.process = &onProcess;
|
||||
pwStreamEvents.state_changed = &PipeWireSourceStream::onStreamStateChanged;
|
||||
pwStreamEvents.param_changed = &PipeWireSourceStream::onStreamParamChanged;
|
||||
}
|
||||
|
||||
PipeWireSourceStream::~PipeWireSourceStream()
|
||||
{
|
||||
d->m_stopped = true;
|
||||
if (d->m_renegotiateEvent) {
|
||||
pw_loop_destroy_source(d->pwCore->loop(), d->m_renegotiateEvent);
|
||||
}
|
||||
if (d->pwStream) {
|
||||
pw_stream_destroy(d->pwStream);
|
||||
}
|
||||
}
|
||||
|
||||
Fraction PipeWireSourceStream::framerate() const
|
||||
{
|
||||
if (d->pwStream) {
|
||||
return {d->videoFormat.max_framerate.num, d->videoFormat.max_framerate.denom};
|
||||
}
|
||||
|
||||
return {0, 1};
|
||||
}
|
||||
|
||||
void PipeWireSourceStream::setMaxFramerate(const Fraction &framerate)
|
||||
{
|
||||
d->maxFramerate = framerate;
|
||||
|
||||
if (d->pwStream) {
|
||||
pw_loop_signal_event(d->pwCore->loop(), d->m_renegotiateEvent);
|
||||
}
|
||||
}
|
||||
|
||||
uint PipeWireSourceStream::nodeId()
|
||||
{
|
||||
return d->pwNodeId;
|
||||
}
|
||||
|
||||
QList<const spa_pod *> PipeWireSourceStream::createFormatsParams(spa_pod_builder podBuilder)
|
||||
{
|
||||
const auto pwServerVersion = d->pwCore->serverVersion();
|
||||
const QList<spa_video_format> formats = {
|
||||
SPA_VIDEO_FORMAT_RGBx,
|
||||
SPA_VIDEO_FORMAT_RGBA,
|
||||
SPA_VIDEO_FORMAT_BGRx,
|
||||
SPA_VIDEO_FORMAT_BGRA,
|
||||
SPA_VIDEO_FORMAT_RGB,
|
||||
SPA_VIDEO_FORMAT_BGR,
|
||||
SPA_VIDEO_FORMAT_xBGR,
|
||||
SPA_VIDEO_FORMAT_ABGR,
|
||||
};
|
||||
QList<const spa_pod *> params;
|
||||
params.reserve(formats.size() * 2);
|
||||
const EGLDisplay display = static_cast<EGLDisplay>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("egldisplay"));
|
||||
|
||||
d->m_allowDmaBuf = d->m_allowDmaBuf && (pwServerVersion.isNull() || (pwClientVersion >= kDmaBufMinVersion && pwServerVersion >= kDmaBufMinVersion));
|
||||
const bool withDontFixate = d->m_allowDmaBuf && (pwServerVersion.isNull() || (pwClientVersion >= kDmaBufModifierMinVersion && pwServerVersion >= kDmaBufModifierMinVersion));
|
||||
|
||||
if (d->m_availableModifiers.isEmpty()) {
|
||||
d->m_availableModifiers = queryDmaBufModifiers(display, formats);
|
||||
}
|
||||
|
||||
for (auto it = d->m_availableModifiers.constBegin(), itEnd = d->m_availableModifiers.constEnd(); it != itEnd; ++it) {
|
||||
if (d->m_allowDmaBuf && !it->isEmpty()) {
|
||||
params += buildFormat(&podBuilder, it.key(), it.value(), withDontFixate, d->maxFramerate);
|
||||
}
|
||||
|
||||
params += buildFormat(&podBuilder, it.key(), {}, withDontFixate, d->maxFramerate);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
bool PipeWireSourceStream::createStream(uint nodeid, int fd)
|
||||
{
|
||||
d->m_availableModifiers.clear();
|
||||
d->pwCore = PipeWireCore::fetch(fd);
|
||||
if (!d->pwCore->error().isEmpty()) {
|
||||
qDebug() << "received error while creating the stream" << d->pwCore->error();
|
||||
d->m_error = d->pwCore->error();
|
||||
return false;
|
||||
}
|
||||
|
||||
connect(d->pwCore.data(), &PipeWireCore::pipewireFailed, this, &PipeWireSourceStream::coreFailed);
|
||||
|
||||
if (objectName().isEmpty()) {
|
||||
setObjectName(QStringLiteral("plasma-screencast-%1").arg(nodeid));
|
||||
}
|
||||
|
||||
const auto pwServerVersion = d->pwCore->serverVersion();
|
||||
d->pwStream = pw_stream_new(**d->pwCore, objectName().toUtf8().constData(), nullptr);
|
||||
d->pwNodeId = nodeid;
|
||||
pw_stream_add_listener(d->pwStream, &d->streamListener, &pwStreamEvents, this);
|
||||
|
||||
d->m_renegotiateEvent = pw_loop_add_event(d->pwCore->loop(), onRenegotiate, this);
|
||||
|
||||
uint8_t buffer[4096];
|
||||
spa_pod_builder podBuilder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||
auto params = createFormatsParams(podBuilder);
|
||||
pw_stream_flags s = (pw_stream_flags)(PW_STREAM_FLAG_DONT_RECONNECT | PW_STREAM_FLAG_AUTOCONNECT);
|
||||
if (pw_stream_connect(d->pwStream, PW_DIRECTION_INPUT, d->pwNodeId, s, params.data(), params.size()) != 0) {
|
||||
qWarning() << "Could not connect to stream";
|
||||
pw_stream_destroy(d->pwStream);
|
||||
d->pwStream = nullptr;
|
||||
return false;
|
||||
}
|
||||
qDebug() << "created successfully" << nodeid;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PipeWireSourceStream::handleFrame(struct pw_buffer *buffer)
|
||||
{
|
||||
spa_buffer *spaBuffer = buffer->buffer;
|
||||
|
||||
PipeWireFrame frame;
|
||||
frame.format = d->videoFormat.format;
|
||||
|
||||
struct spa_meta_header *h = (struct spa_meta_header *)spa_buffer_find_meta_data(spaBuffer, SPA_META_Header, sizeof(*h));
|
||||
if (h) {
|
||||
d->m_currentPresentationTimestamp = std::chrono::nanoseconds(h->pts);
|
||||
frame.presentationTimestamp = std::chrono::nanoseconds(h->pts);
|
||||
frame.sequential = h->seq;
|
||||
} else {
|
||||
using namespace std::chrono;
|
||||
auto now = system_clock::now();
|
||||
d->m_currentPresentationTimestamp = time_point_cast<nanoseconds>(now).time_since_epoch();
|
||||
frame.presentationTimestamp = d->m_currentPresentationTimestamp;
|
||||
}
|
||||
|
||||
if (spa_meta *vd = spa_buffer_find_meta(spaBuffer, SPA_META_VideoDamage)) {
|
||||
frame.damage = QRegion();
|
||||
spa_meta_region *mr;
|
||||
spa_meta_for_each(mr, vd)
|
||||
{
|
||||
*frame.damage += QRect(mr->region.position.x, mr->region.position.y, mr->region.size.width, mr->region.size.height);
|
||||
}
|
||||
}
|
||||
|
||||
{ // process cursor
|
||||
struct spa_meta_cursor *cursor = static_cast<struct spa_meta_cursor *>(spa_buffer_find_meta_data(spaBuffer, SPA_META_Cursor, sizeof(*cursor)));
|
||||
if (cursor && spa_meta_cursor_is_valid(cursor)) {
|
||||
struct spa_meta_bitmap *bitmap = nullptr;
|
||||
|
||||
if (cursor->bitmap_offset)
|
||||
bitmap = SPA_MEMBER(cursor, cursor->bitmap_offset, struct spa_meta_bitmap);
|
||||
|
||||
QImage cursorTexture;
|
||||
if (bitmap && bitmap->size.width > 0 && bitmap->size.height > 0) {
|
||||
const uint8_t *bitmap_data = SPA_MEMBER(bitmap, bitmap->offset, uint8_t);
|
||||
cursorTexture =
|
||||
SpaBufferToQImage(bitmap_data, bitmap->size.width, bitmap->size.height, bitmap->stride, spa_video_format(bitmap->format));
|
||||
}
|
||||
frame.cursor = {{cursor->position.x, cursor->position.y}, {cursor->hotspot.x, cursor->hotspot.y}, cursorTexture};
|
||||
}
|
||||
}
|
||||
|
||||
if (spaBuffer->datas->chunk->size == 0 || spaBuffer->datas->chunk->flags == SPA_CHUNK_FLAG_CORRUPTED) {
|
||||
// do not get a frame
|
||||
qDebug() << "skipping empty buffer" << spaBuffer->datas->chunk->size << spaBuffer->datas->chunk->flags;
|
||||
} else if (spaBuffer->datas->type == SPA_DATA_MemFd) {
|
||||
uint8_t *map =
|
||||
static_cast<uint8_t *>(mmap(nullptr, spaBuffer->datas->maxsize + spaBuffer->datas->mapoffset, PROT_READ, MAP_PRIVATE, spaBuffer->datas->fd, 0));
|
||||
|
||||
if (map == MAP_FAILED) {
|
||||
qWarning() << "Failed to mmap the memory: " << strerror(errno);
|
||||
return;
|
||||
}
|
||||
QImage img =
|
||||
SpaBufferToQImage(map, d->videoFormat.size.width, d->videoFormat.size.height, spaBuffer->datas->chunk->stride, d->videoFormat.format);
|
||||
frame.image = img.copy();
|
||||
|
||||
munmap(map, spaBuffer->datas->maxsize + spaBuffer->datas->mapoffset);
|
||||
} else if (spaBuffer->datas->type == SPA_DATA_DmaBuf) {
|
||||
DmaBufAttributes attribs;
|
||||
attribs.planes.reserve(spaBuffer->n_datas);
|
||||
attribs.format = spaVideoFormatToDrmFormat(d->videoFormat.format);
|
||||
attribs.modifier = d->videoFormat.modifier;
|
||||
attribs.width = d->videoFormat.size.width;
|
||||
attribs.height = d->videoFormat.size.height;
|
||||
|
||||
for (uint i = 0; i < spaBuffer->n_datas; ++i) {
|
||||
const auto &data = spaBuffer->datas[i];
|
||||
|
||||
DmaBufPlane plane;
|
||||
plane.fd = data.fd;
|
||||
plane.stride = data.chunk->stride;
|
||||
plane.offset = data.chunk->offset;
|
||||
attribs.planes += plane;
|
||||
}
|
||||
Q_ASSERT(!attribs.planes.isEmpty());
|
||||
frame.dmabuf = attribs;
|
||||
} else if (spaBuffer->datas->type == SPA_DATA_MemPtr) {
|
||||
frame.image = SpaBufferToQImage(static_cast<uint8_t *>(spaBuffer->datas->data),
|
||||
d->videoFormat.size.width,
|
||||
d->videoFormat.size.height,
|
||||
spaBuffer->datas->chunk->stride,
|
||||
d->videoFormat.format);
|
||||
} else {
|
||||
if (spaBuffer->datas->type == SPA_ID_INVALID)
|
||||
qWarning() << "invalid buffer type";
|
||||
else
|
||||
qWarning() << "unsupported buffer type" << spaBuffer->datas->type;
|
||||
QImage errorImage(200, 200, QImage::Format_ARGB32_Premultiplied);
|
||||
errorImage.fill(Qt::red);
|
||||
frame.image = errorImage;
|
||||
}
|
||||
|
||||
Q_EMIT frameReceived(frame);
|
||||
}
|
||||
|
||||
void PipeWireSourceStream::coreFailed(const QString &errorMessage)
|
||||
{
|
||||
qDebug() << "received error message" << errorMessage;
|
||||
d->m_error = errorMessage;
|
||||
Q_EMIT stopStreaming();
|
||||
}
|
||||
|
||||
void PipeWireSourceStream::process()
|
||||
{
|
||||
pw_buffer *buf = pw_stream_dequeue_buffer(d->pwStream);
|
||||
if (!buf) {
|
||||
qDebug() << "out of buffers";
|
||||
return;
|
||||
}
|
||||
|
||||
handleFrame(buf);
|
||||
|
||||
pw_stream_queue_buffer(d->pwStream, buf);
|
||||
}
|
||||
|
||||
void PipeWireSourceStream::setActive(bool active)
|
||||
{
|
||||
Q_ASSERT(d->pwStream);
|
||||
pw_stream_set_active(d->pwStream, active);
|
||||
}
|
||||
|
||||
void PipeWireSourceStream::setDamageEnabled(bool withDamage)
|
||||
{
|
||||
d->m_withDamage = withDamage;
|
||||
}
|
||||
|
||||
bool PipeWireSourceStream::usingDmaBuf() const
|
||||
{
|
||||
return d->m_usingDmaBuf;
|
||||
}
|
||||
|
||||
bool PipeWireSourceStream::allowDmaBuf() const
|
||||
{
|
||||
return d->m_allowDmaBuf;
|
||||
}
|
||||
|
||||
void PipeWireSourceStream::setAllowDmaBuf(bool allowed)
|
||||
{
|
||||
d->m_allowDmaBuf = allowed;
|
||||
}
|
||||
|
||||
#include "moc_pipewiresourcestream.cpp"
|
|
@ -1,142 +0,0 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2018-2020 Red Hat Inc
|
||||
SPDX-FileCopyrightText: 2020-2021 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
SPDX-FileContributor: Jan Grulich <jgrulich@redhat.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QHash>
|
||||
#include <QImage>
|
||||
#include <QObject>
|
||||
#include <QPoint>
|
||||
#include <QSharedPointer>
|
||||
#include <QSize>
|
||||
#include <optional>
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <spa/param/format-utils.h>
|
||||
#include <spa/param/props.h>
|
||||
#include <spa/param/video/format-utils.h>
|
||||
|
||||
#undef Status
|
||||
|
||||
class PipeWireCore;
|
||||
struct gbm_device;
|
||||
|
||||
typedef void *EGLDisplay;
|
||||
|
||||
struct DmaBufPlane
|
||||
{
|
||||
int fd; ///< The dmabuf file descriptor
|
||||
uint32_t offset; ///< The offset from the start of buffer
|
||||
uint32_t stride; ///< The distance from the start of a row to the next row in bytes
|
||||
};
|
||||
|
||||
struct DmaBufAttributes
|
||||
{
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
uint32_t format = 0;
|
||||
uint64_t modifier = 0; ///< The layout modifier
|
||||
|
||||
QList<DmaBufPlane> planes;
|
||||
};
|
||||
|
||||
struct PipeWireCursor
|
||||
{
|
||||
QPoint position;
|
||||
QPoint hotspot;
|
||||
QImage texture;
|
||||
bool operator!=(const PipeWireCursor &other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
};
|
||||
bool operator==(const PipeWireCursor &other) const
|
||||
{
|
||||
return position == other.position && hotspot == other.hotspot && texture == other.texture;
|
||||
}
|
||||
};
|
||||
|
||||
struct PipeWireFrame
|
||||
{
|
||||
spa_video_format format;
|
||||
std::optional<int> sequential;
|
||||
std::optional<std::chrono::nanoseconds> presentationTimestamp;
|
||||
std::optional<DmaBufAttributes> dmabuf;
|
||||
std::optional<QImage> image;
|
||||
std::optional<QRegion> damage;
|
||||
std::optional<PipeWireCursor> cursor;
|
||||
};
|
||||
|
||||
struct Fraction
|
||||
{
|
||||
bool operator==(const Fraction &other) const
|
||||
{
|
||||
return numerator == other.numerator && denominator == other.denominator;
|
||||
}
|
||||
explicit operator bool() const
|
||||
{
|
||||
return isValid();
|
||||
}
|
||||
bool isValid() const
|
||||
{
|
||||
return denominator > 0;
|
||||
}
|
||||
quint32 numerator = 0;
|
||||
quint32 denominator = 0;
|
||||
};
|
||||
|
||||
QImage::Format SpaToQImageFormat(quint32 /*spa_video_format*/ format);
|
||||
|
||||
struct PipeWireSourceStreamPrivate;
|
||||
|
||||
class PipeWireSourceStream : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit PipeWireSourceStream(QObject *parent = nullptr);
|
||||
~PipeWireSourceStream();
|
||||
|
||||
Fraction framerate() const;
|
||||
void setMaxFramerate(const Fraction &framerate);
|
||||
uint nodeId();
|
||||
QString error() const;
|
||||
|
||||
QSize size() const;
|
||||
pw_stream_state state() const;
|
||||
bool createStream(uint nodeid, int fd);
|
||||
void setActive(bool active);
|
||||
void setDamageEnabled(bool withDamage);
|
||||
|
||||
void handleFrame(struct pw_buffer *buffer);
|
||||
void process();
|
||||
void renegotiateModifierFailed(spa_video_format format, quint64 modifier);
|
||||
|
||||
std::optional<std::chrono::nanoseconds> currentPresentationTimestamp() const;
|
||||
|
||||
static uint32_t spaVideoFormatToDrmFormat(spa_video_format spa_format);
|
||||
|
||||
bool usingDmaBuf() const;
|
||||
bool allowDmaBuf() const;
|
||||
void setAllowDmaBuf(bool allowed);
|
||||
|
||||
Q_SIGNALS:
|
||||
void streamReady();
|
||||
void startStreaming();
|
||||
void stopStreaming();
|
||||
void streamParametersChanged();
|
||||
void frameReceived(const PipeWireFrame &frame);
|
||||
void stateChanged(pw_stream_state state, pw_stream_state oldState);
|
||||
|
||||
private:
|
||||
static void onStreamParamChanged(void *data, uint32_t id, const struct spa_pod *format);
|
||||
static void onStreamStateChanged(void *data, pw_stream_state old, pw_stream_state state, const char *error_message);
|
||||
static void onRenegotiate(void *data, uint64_t);
|
||||
QList<const spa_pod *> createFormatsParams(spa_pod_builder podBuilder);
|
||||
|
||||
void coreFailed(const QString &errorMessage);
|
||||
QScopedPointer<PipeWireSourceStreamPrivate> d;
|
||||
};
|
|
@ -25,13 +25,6 @@ using namespace KWin;
|
|||
|
||||
static const QString s_socketName = QStringLiteral("wayland_test_kwin_placement-0");
|
||||
|
||||
struct PlaceWindowResult
|
||||
{
|
||||
QSizeF initiallyConfiguredSize;
|
||||
Test::XdgToplevel::States initiallyConfiguredStates;
|
||||
QRectF finalGeometry;
|
||||
};
|
||||
|
||||
class TestPlacement : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -56,12 +49,24 @@ private Q_SLOTS:
|
|||
|
||||
private:
|
||||
void setPlacementPolicy(PlacementPolicy policy);
|
||||
struct WindowHandle
|
||||
{
|
||||
Window *window;
|
||||
std::unique_ptr<KWayland::Client::Surface> surface;
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface;
|
||||
};
|
||||
struct PlaceWindowResult
|
||||
{
|
||||
QSizeF initiallyConfiguredSize;
|
||||
Test::XdgToplevel::States initiallyConfiguredStates;
|
||||
QRectF finalGeometry;
|
||||
};
|
||||
/*
|
||||
* Create a window and return relevant results for testing
|
||||
* defaultSize is the buffer size to use if the compositor returns an empty size in the first configure
|
||||
* event.
|
||||
*/
|
||||
std::pair<PlaceWindowResult, std::unique_ptr<KWayland::Client::Surface>> createAndPlaceWindow(const QSize &defaultSize);
|
||||
std::tuple<PlaceWindowResult, WindowHandle> createAndPlaceWindow(const QSize &defaultSize);
|
||||
};
|
||||
|
||||
void TestPlacement::init()
|
||||
|
@ -105,15 +110,15 @@ void TestPlacement::setPlacementPolicy(PlacementPolicy policy)
|
|||
Workspace::self()->slotReconfigure();
|
||||
}
|
||||
|
||||
std::pair<PlaceWindowResult, std::unique_ptr<KWayland::Client::Surface>> TestPlacement::createAndPlaceWindow(const QSize &defaultSize)
|
||||
std::tuple<TestPlacement::PlaceWindowResult, TestPlacement::WindowHandle> TestPlacement::createAndPlaceWindow(const QSize &defaultSize)
|
||||
{
|
||||
PlaceWindowResult rc;
|
||||
|
||||
// create a new window
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
auto shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly, surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly);
|
||||
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface, &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
||||
surfaceConfigureRequestedSpy.wait();
|
||||
|
@ -131,7 +136,11 @@ std::pair<PlaceWindowResult, std::unique_ptr<KWayland::Client::Surface>> TestPla
|
|||
auto window = Test::renderAndWaitForShown(surface.get(), size.toSize(), Qt::red);
|
||||
|
||||
rc.finalGeometry = window->frameGeometry();
|
||||
return {rc, std::move(surface)};
|
||||
return {rc, WindowHandle{
|
||||
.window = window,
|
||||
.surface = std::move(surface),
|
||||
.shellSurface = std::move(shellSurface),
|
||||
}};
|
||||
}
|
||||
|
||||
void TestPlacement::testPlaceSmart()
|
||||
|
@ -150,11 +159,11 @@ void TestPlacement::testPlaceSmart()
|
|||
|
||||
setPlacementPolicy(PlacementSmart);
|
||||
|
||||
std::vector<std::unique_ptr<KWayland::Client::Surface>> surfaces;
|
||||
std::vector<WindowHandle> handles;
|
||||
|
||||
for (const QRect &desiredGeometry : desiredGeometries) {
|
||||
auto [windowPlacement, surface] = createAndPlaceWindow(QSize(600, 500));
|
||||
surfaces.push_back(std::move(surface));
|
||||
auto [windowPlacement, handle] = createAndPlaceWindow(QSize(600, 500));
|
||||
handles.push_back(std::move(handle));
|
||||
|
||||
// smart placement shouldn't define a size on windows
|
||||
QCOMPARE(windowPlacement.initiallyConfiguredSize, QSize(0, 0));
|
||||
|
@ -181,15 +190,15 @@ void TestPlacement::testPlaceMaximized()
|
|||
QVERIFY(panelConfigureRequestedSpy.wait());
|
||||
Test::renderAndWaitForShown(panelSurface.get(), panelConfigureRequestedSpy.last().at(1).toSize(), Qt::blue);
|
||||
|
||||
std::vector<std::unique_ptr<KWayland::Client::Surface>> surfaces;
|
||||
std::vector<WindowHandle> handles;
|
||||
|
||||
// all windows should be initially maximized with an initial configure size sent
|
||||
for (int i = 0; i < 4; i++) {
|
||||
auto [windowPlacement, surface] = createAndPlaceWindow(QSize(600, 500));
|
||||
auto [windowPlacement, handle] = createAndPlaceWindow(QSize(600, 500));
|
||||
QVERIFY(windowPlacement.initiallyConfiguredStates & Test::XdgToplevel::State::Maximized);
|
||||
QCOMPARE(windowPlacement.initiallyConfiguredSize, QSize(1280, 1024 - 20));
|
||||
QCOMPARE(windowPlacement.finalGeometry, QRect(0, 20, 1280, 1024 - 20)); // under the panel
|
||||
surfaces.push_back(std::move(surface));
|
||||
handles.push_back(std::move(handle));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,14 +217,14 @@ void TestPlacement::testPlaceMaximizedLeavesFullscreen()
|
|||
QVERIFY(panelConfigureRequestedSpy.wait());
|
||||
Test::renderAndWaitForShown(panelSurface.get(), panelConfigureRequestedSpy.last().at(1).toSize(), Qt::blue);
|
||||
|
||||
std::vector<std::unique_ptr<KWayland::Client::Surface>> surfaces;
|
||||
std::vector<WindowHandle> handles;
|
||||
|
||||
// all windows should be initially fullscreen with an initial configure size sent, despite the policy
|
||||
for (int i = 0; i < 4; i++) {
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
auto shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly, surface.get());
|
||||
auto shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly);
|
||||
shellSurface->set_fullscreen(nullptr);
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface, &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
|
@ -230,7 +239,11 @@ void TestPlacement::testPlaceMaximizedLeavesFullscreen()
|
|||
QCOMPARE(initiallyConfiguredSize, QSize(1280, 1024));
|
||||
QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
|
||||
|
||||
surfaces.push_back(std::move(surface));
|
||||
handles.emplace_back(WindowHandle{
|
||||
.window = window,
|
||||
.surface = std::move(surface),
|
||||
.shellSurface = std::move(shellSurface),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -185,7 +185,7 @@ void PlasmaSurfaceTest::testOSDPlacement()
|
|||
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
|
||||
QVERIFY(window);
|
||||
QCOMPARE(window->windowType(), NET::OnScreenDisplay);
|
||||
QCOMPARE(window->windowType(), WindowType::OnScreenDisplay);
|
||||
QVERIFY(window->isOnScreenDisplay());
|
||||
QCOMPARE(window->frameGeometry(), QRect(1280 / 2 - 100 / 2, 2 * 1024 / 3 - 50 / 2, 100, 50));
|
||||
|
||||
|
@ -224,7 +224,7 @@ void PlasmaSurfaceTest::testOSDPlacementManualPosition()
|
|||
|
||||
QVERIFY(window);
|
||||
QVERIFY(!window->isPlaceable());
|
||||
QCOMPARE(window->windowType(), NET::OnScreenDisplay);
|
||||
QCOMPARE(window->windowType(), WindowType::OnScreenDisplay);
|
||||
QVERIFY(window->isOnScreenDisplay());
|
||||
QCOMPARE(window->frameGeometry(), QRect(50, 70, 100, 50));
|
||||
}
|
||||
|
@ -253,7 +253,7 @@ void PlasmaSurfaceTest::testPanelActivate()
|
|||
auto panel = Test::renderAndWaitForShown(surface.get(), QSize(100, 200), Qt::blue);
|
||||
|
||||
QVERIFY(panel);
|
||||
QCOMPARE(panel->windowType(), NET::Dock);
|
||||
QCOMPARE(panel->windowType(), WindowType::Dock);
|
||||
QVERIFY(panel->isDock());
|
||||
QFETCH(bool, active);
|
||||
QCOMPARE(panel->dockWantsInput(), active);
|
||||
|
|
|
@ -38,17 +38,6 @@
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
static PlatformCursorImage loadReferenceThemeCursor_helper(const KXcursorTheme &theme,
|
||||
const QByteArray &name)
|
||||
{
|
||||
const QList<KXcursorSprite> sprites = theme.shape(name);
|
||||
if (sprites.isEmpty()) {
|
||||
return PlatformCursorImage();
|
||||
}
|
||||
|
||||
return PlatformCursorImage(sprites.constFirst().data(), sprites.constFirst().hotspot());
|
||||
}
|
||||
|
||||
static PlatformCursorImage loadReferenceThemeCursor(const QByteArray &name)
|
||||
{
|
||||
const Cursor *pointerCursor = Cursors::self()->mouse();
|
||||
|
@ -58,20 +47,11 @@ static PlatformCursorImage loadReferenceThemeCursor(const QByteArray &name)
|
|||
return PlatformCursorImage();
|
||||
}
|
||||
|
||||
PlatformCursorImage platformCursorImage = loadReferenceThemeCursor_helper(theme, name);
|
||||
if (!platformCursorImage.isNull()) {
|
||||
return platformCursorImage;
|
||||
}
|
||||
ShapeCursorSource source;
|
||||
source.setShape(name);
|
||||
source.setTheme(theme);
|
||||
|
||||
const QList<QByteArray> alternativeNames = Cursor::cursorAlternativeNames(name);
|
||||
for (const QByteArray &alternativeName : alternativeNames) {
|
||||
platformCursorImage = loadReferenceThemeCursor_helper(theme, alternativeName);
|
||||
if (!platformCursorImage.isNull()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return platformCursorImage;
|
||||
return PlatformCursorImage(source.image(), source.hotspot());
|
||||
}
|
||||
|
||||
static PlatformCursorImage loadReferenceThemeCursor(const CursorShape &shape)
|
||||
|
@ -114,6 +94,8 @@ private Q_SLOTS:
|
|||
void testWindowUnderCursorWhileButtonPressed();
|
||||
void testConfineToScreenGeometry_data();
|
||||
void testConfineToScreenGeometry();
|
||||
void testEdgeBarrier_data();
|
||||
void testEdgeBarrier();
|
||||
void testResizeCursor_data();
|
||||
void testResizeCursor();
|
||||
void testMoveCursor();
|
||||
|
@ -165,6 +147,11 @@ void PointerInputTest::init()
|
|||
m_compositor = Test::waylandCompositor();
|
||||
m_seat = Test::waylandSeat();
|
||||
|
||||
auto group = kwinApp()->config()->group(QStringLiteral("EdgeBarrier"));
|
||||
group.writeEntry("EdgeBarrier", 0);
|
||||
group.writeEntry("CornerBarrier", false);
|
||||
group.sync();
|
||||
Workspace::self()->slotReconfigure();
|
||||
workspace()->setActiveOutput(QPoint(640, 512));
|
||||
input()->pointer()->warp(QPoint(640, 512));
|
||||
}
|
||||
|
@ -195,7 +182,7 @@ void PointerInputTest::testWarpingUpdatesFocus()
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
render(surface.get());
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -240,7 +227,7 @@ void PointerInputTest::testWarpingGeneratesPointerMotion()
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
render(surface.get());
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -318,7 +305,7 @@ void PointerInputTest::testUpdateFocusAfterScreenChange()
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
render(surface.get(), QSize(1280, 1024));
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -503,7 +490,7 @@ void PointerInputTest::testModifierClickUnrestrictedMove()
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
render(surface.get());
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -568,10 +555,10 @@ void PointerInputTest::testModifierClickUnrestrictedFullscreenMove()
|
|||
// create a window
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
shellSurface->set_fullscreen(nullptr);
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface, &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
|
@ -623,7 +610,7 @@ void PointerInputTest::testModifierClickUnrestrictedMoveGlobalShortcutsDisabled(
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
render(surface.get());
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -694,7 +681,7 @@ void PointerInputTest::testModifierScrollOpacity()
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
render(surface.get());
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -752,7 +739,7 @@ void PointerInputTest::testModifierScrollOpacityGlobalShortcutsDisabled()
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
render(surface.get());
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -799,7 +786,7 @@ void PointerInputTest::testScrollAction()
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface1 = Test::createSurface();
|
||||
QVERIFY(surface1);
|
||||
Test::XdgToplevel *shellSurface1 = Test::createXdgToplevelSurface(surface1.get(), surface1.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface1 = Test::createXdgToplevelSurface(surface1.get());
|
||||
QVERIFY(shellSurface1);
|
||||
render(surface1.get());
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -807,7 +794,7 @@ void PointerInputTest::testScrollAction()
|
|||
QVERIFY(window1);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface2 = Test::createSurface();
|
||||
QVERIFY(surface2);
|
||||
Test::XdgToplevel *shellSurface2 = Test::createXdgToplevelSurface(surface2.get(), surface2.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface2 = Test::createXdgToplevelSurface(surface2.get());
|
||||
QVERIFY(shellSurface2);
|
||||
render(surface2.get());
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -854,7 +841,7 @@ void PointerInputTest::testFocusFollowsMouse()
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface1 = Test::createSurface();
|
||||
QVERIFY(surface1);
|
||||
Test::XdgToplevel *shellSurface1 = Test::createXdgToplevelSurface(surface1.get(), surface1.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface1 = Test::createXdgToplevelSurface(surface1.get());
|
||||
QVERIFY(shellSurface1);
|
||||
render(surface1.get(), QSize(800, 800));
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -862,7 +849,7 @@ void PointerInputTest::testFocusFollowsMouse()
|
|||
QVERIFY(window1);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface2 = Test::createSurface();
|
||||
QVERIFY(surface2);
|
||||
Test::XdgToplevel *shellSurface2 = Test::createXdgToplevelSurface(surface2.get(), surface2.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface2 = Test::createXdgToplevelSurface(surface2.get());
|
||||
QVERIFY(shellSurface2);
|
||||
render(surface2.get(), QSize(800, 800));
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -936,7 +923,7 @@ void PointerInputTest::testMouseActionInactiveWindow()
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface1 = Test::createSurface();
|
||||
QVERIFY(surface1);
|
||||
Test::XdgToplevel *shellSurface1 = Test::createXdgToplevelSurface(surface1.get(), surface1.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface1 = Test::createXdgToplevelSurface(surface1.get());
|
||||
QVERIFY(shellSurface1);
|
||||
render(surface1.get(), QSize(800, 800));
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -944,7 +931,7 @@ void PointerInputTest::testMouseActionInactiveWindow()
|
|||
QVERIFY(window1);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface2 = Test::createSurface();
|
||||
QVERIFY(surface2);
|
||||
Test::XdgToplevel *shellSurface2 = Test::createXdgToplevelSurface(surface2.get(), surface2.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface2 = Test::createXdgToplevelSurface(surface2.get());
|
||||
QVERIFY(shellSurface2);
|
||||
render(surface2.get(), QSize(800, 800));
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -1021,7 +1008,7 @@ void PointerInputTest::testMouseActionActiveWindow()
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface1 = Test::createSurface();
|
||||
QVERIFY(surface1);
|
||||
Test::XdgToplevel *shellSurface1 = Test::createXdgToplevelSurface(surface1.get(), surface1.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface1 = Test::createXdgToplevelSurface(surface1.get());
|
||||
QVERIFY(shellSurface1);
|
||||
render(surface1.get(), QSize(800, 800));
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -1030,7 +1017,7 @@ void PointerInputTest::testMouseActionActiveWindow()
|
|||
QSignalSpy window1DestroyedSpy(window1, &QObject::destroyed);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface2 = Test::createSurface();
|
||||
QVERIFY(surface2);
|
||||
Test::XdgToplevel *shellSurface2 = Test::createXdgToplevelSurface(surface2.get(), surface2.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface2 = Test::createXdgToplevelSurface(surface2.get());
|
||||
QVERIFY(shellSurface2);
|
||||
render(surface2.get(), QSize(800, 800));
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -1097,7 +1084,7 @@ void PointerInputTest::testCursorImage()
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
render(surface.get());
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -1227,7 +1214,7 @@ void PointerInputTest::testEffectOverrideCursorImage()
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
render(surface.get());
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -1292,7 +1279,7 @@ void PointerInputTest::testPopup()
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
render(surface.get());
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -1317,9 +1304,9 @@ void PointerInputTest::testPopup()
|
|||
positioner->set_gravity(Test::XdgPositioner::gravity_bottom_right);
|
||||
std::unique_ptr<KWayland::Client::Surface> popupSurface = Test::createSurface();
|
||||
QVERIFY(popupSurface);
|
||||
Test::XdgPopup *popupShellSurface = Test::createXdgPopupSurface(popupSurface.get(), shellSurface->xdgSurface(), positioner.get());
|
||||
std::unique_ptr<Test::XdgPopup> popupShellSurface = Test::createXdgPopupSurface(popupSurface.get(), shellSurface->xdgSurface(), positioner.get());
|
||||
QVERIFY(popupShellSurface);
|
||||
QSignalSpy doneReceivedSpy(popupShellSurface, &Test::XdgPopup::doneReceived);
|
||||
QSignalSpy doneReceivedSpy(popupShellSurface.get(), &Test::XdgPopup::doneReceived);
|
||||
popupShellSurface->grab(*Test::waylandSeat(), 0); // FIXME: Serial.
|
||||
render(popupSurface.get(), QSize(100, 50));
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -1398,9 +1385,9 @@ void PointerInputTest::testDecoCancelsPopup()
|
|||
positioner->set_gravity(Test::XdgPositioner::gravity_bottom_right);
|
||||
std::unique_ptr<KWayland::Client::Surface> popupSurface = Test::createSurface();
|
||||
QVERIFY(popupSurface);
|
||||
Test::XdgPopup *popupShellSurface = Test::createXdgPopupSurface(popupSurface.get(), shellSurface->xdgSurface(), positioner.get());
|
||||
std::unique_ptr<Test::XdgPopup> popupShellSurface = Test::createXdgPopupSurface(popupSurface.get(), shellSurface->xdgSurface(), positioner.get());
|
||||
QVERIFY(popupShellSurface);
|
||||
QSignalSpy doneReceivedSpy(popupShellSurface, &Test::XdgPopup::doneReceived);
|
||||
QSignalSpy doneReceivedSpy(popupShellSurface.get(), &Test::XdgPopup::doneReceived);
|
||||
popupShellSurface->grab(*Test::waylandSeat(), 0); // FIXME: Serial.
|
||||
auto popupWindow = Test::renderAndWaitForShown(popupSurface.get(), QSize(100, 50), Qt::red);
|
||||
QVERIFY(popupWindow);
|
||||
|
@ -1435,7 +1422,7 @@ void PointerInputTest::testWindowUnderCursorWhileButtonPressed()
|
|||
QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), surface.get());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(shellSurface);
|
||||
render(surface.get());
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -1458,7 +1445,7 @@ void PointerInputTest::testWindowUnderCursorWhileButtonPressed()
|
|||
positioner->set_gravity(Test::XdgPositioner::gravity_bottom_right);
|
||||
std::unique_ptr<KWayland::Client::Surface> popupSurface = Test::createSurface();
|
||||
QVERIFY(popupSurface);
|
||||
Test::XdgPopup *popupShellSurface = Test::createXdgPopupSurface(popupSurface.get(), shellSurface->xdgSurface(), positioner.get());
|
||||
std::unique_ptr<Test::XdgPopup> popupShellSurface = Test::createXdgPopupSurface(popupSurface.get(), shellSurface->xdgSurface(), positioner.get());
|
||||
QVERIFY(popupShellSurface);
|
||||
render(popupSurface.get(), QSize(99, 49));
|
||||
QVERIFY(windowAddedSpy.wait());
|
||||
|
@ -1562,6 +1549,88 @@ void PointerInputTest::testConfineToScreenGeometry()
|
|||
QCOMPARE(Cursors::self()->mouse()->pos(), expectedPos);
|
||||
}
|
||||
|
||||
void PointerInputTest::testEdgeBarrier_data()
|
||||
{
|
||||
QTest::addColumn<QPoint>("startPos");
|
||||
QTest::addColumn<QList<QPoint>>("movements");
|
||||
QTest::addColumn<int>("targetOutputId");
|
||||
QTest::addColumn<bool>("cornerBarrier");
|
||||
|
||||
// screen layout:
|
||||
//
|
||||
// +----------+----------+---------+
|
||||
// | left | top | right |
|
||||
// +----------+----------+---------+
|
||||
// | bottom |
|
||||
// +----------+
|
||||
//
|
||||
QTest::newRow("move right - barred") << QPoint(1270, 512) << QList<QPoint>{QPoint(20, 0)} << 0 << false;
|
||||
QTest::newRow("move left - barred") << QPoint(1290, 512) << QList<QPoint>{QPoint(-20, 0)} << 1 << false;
|
||||
QTest::newRow("move down - barred") << QPoint(1920, 1014) << QList<QPoint>{QPoint(0, 20)} << 1 << false;
|
||||
QTest::newRow("move up - barred") << QPoint(1920, 1034) << QList<QPoint>{QPoint(0, -20)} << 3 << false;
|
||||
QTest::newRow("move top-right - barred") << QPoint(2550, 1034) << QList<QPoint>{QPoint(20, -20)} << 3 << false;
|
||||
QTest::newRow("move top-left - barred") << QPoint(1290, 1034) << QList<QPoint>{QPoint(-20, -20)} << 3 << false;
|
||||
QTest::newRow("move bottom-right - barred") << QPoint(1270, 1014) << QList<QPoint>{QPoint(20, 20)} << 0 << false;
|
||||
QTest::newRow("move bottom-left - barred") << QPoint(2570, 1014) << QList<QPoint>{QPoint(-20, 20)} << 2 << false;
|
||||
|
||||
QTest::newRow("move right - not barred") << QPoint(1270, 512) << QList<QPoint>{QPoint(100, 0)} << 1 << false;
|
||||
QTest::newRow("move left - not barred") << QPoint(1290, 512) << QList<QPoint>{QPoint(-100, 0)} << 0 << false;
|
||||
QTest::newRow("move down - not barred") << QPoint(1920, 1014) << QList<QPoint>{QPoint(0, 100)} << 3 << false;
|
||||
QTest::newRow("move up - not barred") << QPoint(1920, 1034) << QList<QPoint>{QPoint(0, -100)} << 1 << false;
|
||||
QTest::newRow("move top-right - not barred") << QPoint(2550, 1034) << QList<QPoint>{QPoint(100, -100)} << 2 << false;
|
||||
QTest::newRow("move top-left - not barred") << QPoint(1290, 1034) << QList<QPoint>{QPoint(-100, -100)} << 0 << false;
|
||||
QTest::newRow("move bottom-right - not barred") << QPoint(1270, 1014) << QList<QPoint>{QPoint(100, 100)} << 3 << false;
|
||||
QTest::newRow("move bottom-left - not barred") << QPoint(2570, 1014) << QList<QPoint>{QPoint(-100, 100)} << 3 << false;
|
||||
|
||||
QTest::newRow("move cumulative") << QPoint(1279, 512) << QList<QPoint>{QPoint(24, 0), QPoint(24, 0)} << 1 << false;
|
||||
QTest::newRow("move then idle") << QPoint(1279, 512) << QList<QPoint>{QPoint(24, 0), QPoint(0, 0), QPoint(0, 0), QPoint(3, 0)} << 0 << false;
|
||||
|
||||
QTest::newRow("move top-right - corner barrier") << QPoint(2550, 1034) << QList<QPoint>{QPoint(100, -100)} << 3 << true;
|
||||
QTest::newRow("move top-left - corner barrier") << QPoint(1290, 1034) << QList<QPoint>{QPoint(-100, -100)} << 3 << true;
|
||||
QTest::newRow("move bottom-right - corner barrier") << QPoint(1270, 1014) << QList<QPoint>{QPoint(100, 100)} << 0 << true;
|
||||
QTest::newRow("move bottom-left - corner barrier") << QPoint(2570, 1014) << QList<QPoint>{QPoint(-100, 100)} << 2 << true;
|
||||
}
|
||||
|
||||
void PointerInputTest::testEdgeBarrier()
|
||||
{
|
||||
// setup screen layout
|
||||
const QList<QRect> geometries{
|
||||
QRect(0, 0, 1280, 1024),
|
||||
QRect(1280, 0, 1280, 1024),
|
||||
QRect(2560, 0, 1280, 1024),
|
||||
QRect(1280, 1024, 1280, 1024)};
|
||||
Test::setOutputConfig(geometries);
|
||||
|
||||
const auto outputs = workspace()->outputs();
|
||||
QCOMPARE(outputs.count(), geometries.count());
|
||||
QCOMPARE(outputs[0]->geometry(), geometries.at(0));
|
||||
QCOMPARE(outputs[1]->geometry(), geometries.at(1));
|
||||
QCOMPARE(outputs[2]->geometry(), geometries.at(2));
|
||||
QCOMPARE(outputs[3]->geometry(), geometries.at(3));
|
||||
|
||||
QFETCH(QPoint, startPos);
|
||||
input()->pointer()->warp(startPos);
|
||||
quint32 timestamp = waylandServer()->seat()->timestamp().count() + 5000;
|
||||
Test::pointerMotionRelative(QPoint(0, 0), timestamp);
|
||||
timestamp += 1000;
|
||||
QCOMPARE(Cursors::self()->mouse()->pos(), startPos);
|
||||
|
||||
auto group = kwinApp()->config()->group(QStringLiteral("EdgeBarrier"));
|
||||
group.writeEntry("EdgeBarrier", 25);
|
||||
QFETCH(bool, cornerBarrier);
|
||||
group.writeEntry("CornerBarrier", cornerBarrier);
|
||||
group.sync();
|
||||
workspace()->slotReconfigure();
|
||||
|
||||
QFETCH(QList<QPoint>, movements);
|
||||
for (const auto &movement : movements) {
|
||||
Test::pointerMotionRelative(movement, timestamp);
|
||||
timestamp += 1000;
|
||||
}
|
||||
QFETCH(int, targetOutputId);
|
||||
QCOMPARE(workspace()->outputAt(Cursors::self()->mouse()->pos()), workspace()->outputs().at(targetOutputId));
|
||||
}
|
||||
|
||||
void PointerInputTest::testResizeCursor_data()
|
||||
{
|
||||
QTest::addColumn<Qt::Edges>("edges");
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "effect/globals.h"
|
||||
#include "kwin_wayland_test.h"
|
||||
|
||||
#include "core/output.h"
|
||||
|
@ -14,6 +15,7 @@
|
|||
#include "decorations/settings.h"
|
||||
#include "pointer_input.h"
|
||||
#include "scripting/scripting.h"
|
||||
#include "tiles/tilemanager.h"
|
||||
#include "utils/common.h"
|
||||
#include "wayland_server.h"
|
||||
#include "window.h"
|
||||
|
@ -172,12 +174,15 @@ void QuickTilingTest::testQuickTiling()
|
|||
|
||||
QFETCH(QuickTileMode, mode);
|
||||
QFETCH(QRectF, expectedGeometry);
|
||||
const QuickTileMode oldQuickTileMode = window->quickTileMode();
|
||||
window->setQuickTileMode(mode, true);
|
||||
QCOMPARE(quickTileChangedSpy.count(), 1);
|
||||
|
||||
// at this point the geometry did not yet change
|
||||
QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
|
||||
// but quick tile mode already changed
|
||||
QCOMPARE(window->quickTileMode(), mode);
|
||||
// but requested quick tile mode already changed, proper quickTileMode not yet
|
||||
QCOMPARE(window->requestedQuickTileMode(), mode);
|
||||
// Actual quickTileMOde didn't change yet
|
||||
QCOMPARE(window->quickTileMode(), oldQuickTileMode);
|
||||
|
||||
// but we got requested a new geometry
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
|
@ -191,6 +196,8 @@ void QuickTilingTest::testQuickTiling()
|
|||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(frameGeometryChangedSpy.count(), 1);
|
||||
QCOMPARE(window->frameGeometry(), expectedGeometry);
|
||||
QCOMPARE(quickTileChangedSpy.count(), 1);
|
||||
QCOMPARE(window->quickTileMode(), mode);
|
||||
|
||||
// send window to other screen
|
||||
QList<Output *> outputs = workspace()->outputs();
|
||||
|
@ -200,9 +207,19 @@ void QuickTilingTest::testQuickTiling()
|
|||
// quick tile should not be changed
|
||||
QCOMPARE(window->quickTileMode(), mode);
|
||||
QTEST(window->frameGeometry(), "secondScreen");
|
||||
Tile *tile = workspace()->tileManager(outputs[1])->quickTile(mode);
|
||||
QCOMPARE(window->tile(), tile);
|
||||
|
||||
// now try to toggle again
|
||||
window->setQuickTileMode(mode, true);
|
||||
QTEST(window->requestedQuickTileMode(), "expectedModeAfterToggle");
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
|
||||
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red);
|
||||
QVERIFY(quickTileChangedSpy.wait());
|
||||
QCOMPARE(quickTileChangedSpy.count(), 2);
|
||||
QTEST(window->quickTileMode(), "expectedModeAfterToggle");
|
||||
}
|
||||
|
||||
|
@ -243,13 +260,14 @@ void QuickTilingTest::testQuickMaximizing()
|
|||
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
|
||||
QSignalSpy maximizeChangedSpy(window, &Window::maximizedChanged);
|
||||
|
||||
const QuickTileMode oldQuickTileMode = window->quickTileMode();
|
||||
window->setQuickTileMode(QuickTileFlag::Maximize, true);
|
||||
QCOMPARE(quickTileChangedSpy.count(), 1);
|
||||
|
||||
// at this point the geometry did not yet change
|
||||
QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
|
||||
// but quick tile mode already changed
|
||||
QCOMPARE(window->quickTileMode(), QuickTileFlag::Maximize);
|
||||
// but requested quick tile mode already changed
|
||||
QCOMPARE(window->requestedQuickTileMode(), QuickTileFlag::Maximize);
|
||||
QCOMPARE(window->quickTileMode(), oldQuickTileMode);
|
||||
QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
|
||||
|
||||
// but we got requested a new geometry
|
||||
|
@ -263,19 +281,19 @@ void QuickTilingTest::testQuickMaximizing()
|
|||
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(frameGeometryChangedSpy.count(), 1);
|
||||
QCOMPARE(quickTileChangedSpy.count(), 1);
|
||||
QCOMPARE(window->quickTileMode(), QuickTileFlag::Maximize);
|
||||
QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
|
||||
QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
|
||||
|
||||
// window is now set to maximised
|
||||
QCOMPARE(maximizeChangedSpy.count(), 1);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeFull);
|
||||
QCOMPARE(window->tile(), nullptr);
|
||||
|
||||
// go back to quick tile none
|
||||
QFETCH(QuickTileMode, mode);
|
||||
window->setQuickTileMode(mode, true);
|
||||
QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
|
||||
QCOMPARE(quickTileChangedSpy.count(), 2);
|
||||
QCOMPARE(window->requestedQuickTileMode(), QuickTileMode(QuickTileFlag::None));
|
||||
// geometry not yet changed
|
||||
QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
|
||||
QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
|
||||
|
@ -286,10 +304,12 @@ void QuickTilingTest::testQuickMaximizing()
|
|||
|
||||
// render again
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), QSize(100, 50), Qt::yellow);
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::yellow);
|
||||
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(frameGeometryChangedSpy.count(), 2);
|
||||
QCOMPARE(quickTileChangedSpy.count(), 2);
|
||||
QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
|
||||
QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
|
||||
QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
|
||||
QCOMPARE(maximizeChangedSpy.count(), 2);
|
||||
|
@ -324,6 +344,12 @@ void QuickTilingTest::testQuickTilingKeyboardMove()
|
|||
QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
|
||||
QCOMPARE(window->maximizeMode(), MaximizeRestore);
|
||||
|
||||
// We have to receive a configure event when the window becomes active.
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
|
||||
|
||||
QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
|
||||
|
||||
workspace()->performWindowOperation(window, Options::UnrestrictedMoveOp);
|
||||
|
@ -355,6 +381,11 @@ void QuickTilingTest::testQuickTilingKeyboardMove()
|
|||
QCOMPARE(Cursors::self()->mouse()->pos(), targetPos);
|
||||
QVERIFY(!workspace()->moveResizeWindow());
|
||||
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red);
|
||||
QVERIFY(quickTileChangedSpy.wait());
|
||||
QCOMPARE(quickTileChangedSpy.count(), 1);
|
||||
QTEST(window->quickTileMode(), "expectedMode");
|
||||
}
|
||||
|
@ -406,8 +437,7 @@ void QuickTilingTest::testQuickTilingPointerMove()
|
|||
Test::pointerButtonPressed(BTN_LEFT, timestamp++);
|
||||
Test::pointerMotion(pointerPos, timestamp++);
|
||||
Test::pointerButtonReleased(BTN_LEFT, timestamp++);
|
||||
QCOMPARE(quickTileChangedSpy.count(), 1);
|
||||
QTEST(window->quickTileMode(), "expectedMode");
|
||||
QTEST(window->requestedQuickTileMode(), "expectedMode");
|
||||
QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
|
||||
|
@ -418,6 +448,8 @@ void QuickTilingTest::testQuickTilingPointerMove()
|
|||
Test::render(surface.get(), tileSize, Qt::red);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(window->frameGeometry().size(), tileSize);
|
||||
QCOMPARE(quickTileChangedSpy.count(), 1);
|
||||
QTEST(window->quickTileMode(), "expectedMode");
|
||||
|
||||
// verify that geometry restore is correct after user untiles the window, but changes
|
||||
// their mind and tiles the window again while still holding left button
|
||||
|
@ -426,8 +458,7 @@ void QuickTilingTest::testQuickTilingPointerMove()
|
|||
|
||||
Test::pointerButtonPressed(BTN_LEFT, timestamp++); // untile the window
|
||||
Test::pointerMotion(QPoint(1280, 1024) / 2, timestamp++);
|
||||
QCOMPARE(quickTileChangedSpy.count(), 2);
|
||||
QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
|
||||
QCOMPARE(window->requestedQuickTileMode(), QuickTileMode(QuickTileFlag::None));
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
|
||||
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(100, 50));
|
||||
|
@ -437,15 +468,24 @@ void QuickTilingTest::testQuickTilingPointerMove()
|
|||
Test::render(surface.get(), QSize(100, 50), Qt::red);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(window->frameGeometry().size(), QSize(100, 50));
|
||||
QCOMPARE(quickTileChangedSpy.count(), 2);
|
||||
QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
|
||||
|
||||
Test::pointerMotion(pointerPos, timestamp++); // tile the window again
|
||||
Test::pointerButtonReleased(BTN_LEFT, timestamp++);
|
||||
QCOMPARE(quickTileChangedSpy.count(), 3);
|
||||
QTEST(window->quickTileMode(), "expectedMode");
|
||||
QTEST(window->requestedQuickTileMode(), "expectedMode");
|
||||
QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(surfaceConfigureRequestedSpy.count(), 4);
|
||||
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), tileSize);
|
||||
|
||||
// attach a new image
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), QSize(100, 50), Qt::red);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(window->frameGeometry().size(), QSize(100, 50));
|
||||
QCOMPARE(quickTileChangedSpy.count(), 3);
|
||||
QTEST(window->quickTileMode(), "expectedMode");
|
||||
}
|
||||
|
||||
void QuickTilingTest::testQuickTilingTouchMove_data()
|
||||
|
@ -512,11 +552,17 @@ void QuickTilingTest::testQuickTilingTouchMove()
|
|||
// TODO: we should test both cases with fixed fake decoration for autotests.
|
||||
const bool hasBorders = Workspace::self()->decorationBridge()->settings()->borderSize() != KDecoration2::BorderSize::None;
|
||||
|
||||
QCOMPARE(quickTileChangedSpy.count(), 1);
|
||||
QTEST(window->quickTileMode(), "expectedMode");
|
||||
QTEST(window->requestedQuickTileMode(), "expectedMode");
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QTRY_COMPARE(surfaceConfigureRequestedSpy.count(), hasBorders ? 4 : 3);
|
||||
QCOMPARE(false, toplevelConfigureRequestedSpy.last().first().toSize().isEmpty());
|
||||
|
||||
// attach a new image
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red);
|
||||
QVERIFY(quickTileChangedSpy.wait());
|
||||
QCOMPARE(quickTileChangedSpy.count(), 1);
|
||||
QTEST(window->quickTileMode(), "expectedMode");
|
||||
}
|
||||
|
||||
void QuickTilingTest::testX11QuickTiling_data()
|
||||
|
@ -577,7 +623,6 @@ void QuickTilingTest::testX11QuickTiling()
|
|||
QCOMPARE(window->quickTileMode(), mode);
|
||||
QTEST(window->frameGeometry(), "expectedGeometry");
|
||||
QCOMPARE(window->geometryRestore(), origGeo);
|
||||
QEXPECT_FAIL("maximize", "For maximize we get two changed signals", Continue);
|
||||
QCOMPARE(quickTileChangedSpy.count(), 1);
|
||||
|
||||
// quick tile to same edge again should also act like send to screen
|
||||
|
@ -663,7 +708,6 @@ void QuickTilingTest::testX11QuickTilingAfterVertMaximize()
|
|||
window->setQuickTileMode(mode, true);
|
||||
QCOMPARE(window->quickTileMode(), mode);
|
||||
QTEST(window->frameGeometry(), "expectedGeometry");
|
||||
QEXPECT_FAIL("", "We get two changed events", Continue);
|
||||
QCOMPARE(quickTileChangedSpy.count(), 1);
|
||||
|
||||
// and destroy the window again
|
||||
|
@ -730,6 +774,9 @@ void QuickTilingTest::testShortcut()
|
|||
|
||||
const int numberOfQuickTileActions = shortcutList.count();
|
||||
|
||||
QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
|
||||
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
|
||||
|
||||
for (QString shortcut : shortcutList) {
|
||||
// invoke global shortcut through dbus
|
||||
auto msg = QDBusMessage::createMethodCall(
|
||||
|
@ -739,28 +786,24 @@ void QuickTilingTest::testShortcut()
|
|||
QStringLiteral("invokeShortcut"));
|
||||
msg.setArguments(QList<QVariant>{shortcut});
|
||||
QDBusConnection::sessionBus().asyncCall(msg);
|
||||
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red);
|
||||
QVERIFY(quickTileChangedSpy.wait());
|
||||
}
|
||||
|
||||
QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
|
||||
QCOMPARE(surfaceConfigureRequestedSpy.count(), numberOfQuickTileActions + 1);
|
||||
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), expectedGeometry.size());
|
||||
QCOMPARE(frameGeometryChangedSpy.count(), numberOfQuickTileActions);
|
||||
|
||||
QTRY_COMPARE(quickTileChangedSpy.count(), numberOfQuickTileActions);
|
||||
// at this point the geometry did not yet change
|
||||
QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
|
||||
// but quick tile mode already changed
|
||||
// geometry already changed
|
||||
QCOMPARE(window->frameGeometry(), expectedGeometry);
|
||||
// quick tile mode already changed
|
||||
QTEST(window->quickTileMode(), "expectedMode");
|
||||
|
||||
// but we got requested a new geometry
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
|
||||
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), expectedGeometry.size());
|
||||
|
||||
// attach a new image
|
||||
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), expectedGeometry.size(), Qt::red);
|
||||
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QEXPECT_FAIL("maximize", "Geometry changed called twice for maximize", Continue);
|
||||
QCOMPARE(frameGeometryChangedSpy.count(), 1);
|
||||
QCOMPARE(window->frameGeometry(), expectedGeometry);
|
||||
}
|
||||
|
||||
|
@ -825,16 +868,14 @@ void QuickTilingTest::testScript()
|
|||
QSignalSpy runningChangedSpy(s, &AbstractScript::runningChanged);
|
||||
s->run();
|
||||
|
||||
QVERIFY(quickTileChangedSpy.wait());
|
||||
QCOMPARE(quickTileChangedSpy.count(), 1);
|
||||
|
||||
QVERIFY(runningChangedSpy.wait());
|
||||
QCOMPARE(runningChangedSpy.count(), 1);
|
||||
QCOMPARE(runningChangedSpy.first().first().toBool(), true);
|
||||
|
||||
// at this point the geometry did not yet change
|
||||
QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
|
||||
// but quick tile mode already changed
|
||||
QCOMPARE(window->quickTileMode(), expectedMode);
|
||||
// but requested quick tile mode already changed
|
||||
QCOMPARE(window->requestedQuickTileMode(), expectedMode);
|
||||
|
||||
// but we got requested a new geometry
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
|
@ -846,6 +887,8 @@ void QuickTilingTest::testScript()
|
|||
Test::render(surface.get(), expectedGeometry.size(), Qt::red);
|
||||
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(quickTileChangedSpy.count(), 1);
|
||||
QCOMPARE(window->quickTileMode(), expectedMode);
|
||||
QEXPECT_FAIL("maximize", "Geometry changed called twice for maximize", Continue);
|
||||
QCOMPARE(frameGeometryChangedSpy.count(), 1);
|
||||
QCOMPARE(window->frameGeometry(), expectedGeometry);
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "core/output.h"
|
||||
#include "generic_scene_opengl_test.h"
|
||||
#include "opengl/glplatform.h"
|
||||
#include "pipewiresourcestream.h"
|
||||
#include "pointer_input.h"
|
||||
#include "scene/workspacescene.h"
|
||||
#include "wayland_server.h"
|
||||
|
@ -20,6 +19,7 @@
|
|||
#include <KWayland/Client/output.h>
|
||||
#include <KWayland/Client/subsurface.h>
|
||||
#include <KWayland/Client/surface.h>
|
||||
#include <PipeWireSourceStream>
|
||||
#include <QPainter>
|
||||
#include <QScreen>
|
||||
|
||||
|
@ -110,7 +110,9 @@ std::optional<QImage> ScreencastingTest::oneFrameAndClose(Test::ScreencastingStr
|
|||
|
||||
std::optional<QImage> img;
|
||||
connect(&pwStream, &PipeWireSourceStream::frameReceived, qGuiApp, [&img](const PipeWireFrame &frame) {
|
||||
img = frame.image;
|
||||
if (frame.dataFrame) {
|
||||
img = frame.dataFrame->toImage();
|
||||
}
|
||||
});
|
||||
|
||||
QSignalSpy spy(&pwStream, &PipeWireSourceStream::frameReceived);
|
||||
|
|
|
@ -460,15 +460,14 @@ void ScreenEdgesTest::testKdeNetWmScreenEdgeShow()
|
|||
|
||||
ScreenEdgePropertyMonitor screenEdgeMonitor(c.get(), windowId);
|
||||
QSignalSpy withdrawnSpy(&screenEdgeMonitor, &ScreenEdgePropertyMonitor::withdrawn);
|
||||
QSignalSpy windowShownSpy(window, &Window::windowShown);
|
||||
QSignalSpy windowHiddenSpy(window, &Window::windowHidden);
|
||||
QSignalSpy hiddenChangedSpy(window, &Window::hiddenChanged);
|
||||
quint32 timestamp = 0;
|
||||
|
||||
// The window will be shown when the pointer approaches its reserved screen edge.
|
||||
{
|
||||
enableAutoHide(c.get(), windowId, ElectricBottom);
|
||||
xcb_flush(c.get());
|
||||
QVERIFY(windowHiddenSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(!window->isShown());
|
||||
|
||||
Test::pointerMotion(QPointF(640, 1023), timestamp);
|
||||
|
@ -485,7 +484,7 @@ void ScreenEdgesTest::testKdeNetWmScreenEdgeShow()
|
|||
{
|
||||
enableAutoHide(c.get(), windowId, ElectricBottom);
|
||||
xcb_flush(c.get());
|
||||
QVERIFY(windowHiddenSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(!window->isShown());
|
||||
|
||||
Test::touchDown(0, QPointF(640, 1023), timestamp++);
|
||||
|
@ -499,7 +498,7 @@ void ScreenEdgesTest::testKdeNetWmScreenEdgeShow()
|
|||
{
|
||||
enableAutoHide(c.get(), windowId, ElectricBottom);
|
||||
xcb_flush(c.get());
|
||||
QVERIFY(windowHiddenSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(!window->isShown());
|
||||
|
||||
workspace()->screenEdges()->recreateEdges();
|
||||
|
@ -508,7 +507,7 @@ void ScreenEdgesTest::testKdeNetWmScreenEdgeShow()
|
|||
|
||||
enableAutoHide(c.get(), windowId, ElectricNone);
|
||||
xcb_flush(c.get());
|
||||
QVERIFY(windowShownSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(window->isShown());
|
||||
}
|
||||
|
||||
|
@ -516,12 +515,12 @@ void ScreenEdgesTest::testKdeNetWmScreenEdgeShow()
|
|||
{
|
||||
enableAutoHide(c.get(), windowId, ElectricBottom);
|
||||
xcb_flush(c.get());
|
||||
QVERIFY(windowHiddenSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(!window->isShown());
|
||||
|
||||
enableAutoHide(c.get(), windowId, ElectricNone);
|
||||
xcb_flush(c.get());
|
||||
QVERIFY(windowShownSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(window->isShown());
|
||||
}
|
||||
|
||||
|
@ -530,7 +529,7 @@ void ScreenEdgesTest::testKdeNetWmScreenEdgeShow()
|
|||
QSignalSpy approachingSpy(workspace()->screenEdges(), &ScreenEdges::approaching);
|
||||
enableAutoHide(c.get(), windowId, ElectricBottom);
|
||||
xcb_flush(c.get());
|
||||
QVERIFY(windowHiddenSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(!window->isShown());
|
||||
|
||||
Test::pointerMotion(QPointF(640, 1020), timestamp++);
|
||||
|
@ -540,7 +539,7 @@ void ScreenEdgesTest::testKdeNetWmScreenEdgeShow()
|
|||
|
||||
enableAutoHide(c.get(), windowId, ElectricNone);
|
||||
xcb_flush(c.get());
|
||||
QVERIFY(windowShownSpy.wait());
|
||||
QVERIFY(hiddenChangedSpy.wait());
|
||||
QVERIFY(window->isShown());
|
||||
QVERIFY(approachingSpy.last().at(1).toReal() == 0.0);
|
||||
|
||||
|
|
|
@ -30,10 +30,8 @@ private Q_SLOTS:
|
|||
void initTestCase();
|
||||
void init();
|
||||
void cleanup();
|
||||
void testCurrent_data();
|
||||
void testCurrent();
|
||||
void testCurrentWithFollowsMouse_data();
|
||||
void testCurrentWithFollowsMouse();
|
||||
void testActiveOutputFollowsMouse_data();
|
||||
void testActiveOutputFollowsMouse();
|
||||
void testCurrentPoint_data();
|
||||
void testCurrentPoint();
|
||||
};
|
||||
|
@ -92,30 +90,7 @@ void ScreensTest::cleanup()
|
|||
});
|
||||
}
|
||||
|
||||
void ScreensTest::testCurrent_data()
|
||||
{
|
||||
QTest::addColumn<int>("currentId");
|
||||
|
||||
QTest::newRow("first") << 0;
|
||||
QTest::newRow("second") << 1;
|
||||
}
|
||||
|
||||
void ScreensTest::testCurrent()
|
||||
{
|
||||
QFETCH(int, currentId);
|
||||
Output *output = workspace()->outputs().at(currentId);
|
||||
|
||||
// Disable "active screen follows mouse"
|
||||
auto group = kwinApp()->config()->group(QStringLiteral("Windows"));
|
||||
group.writeEntry("ActiveMouseScreen", false);
|
||||
group.sync();
|
||||
workspace()->slotReconfigure();
|
||||
|
||||
workspace()->setActiveOutput(output);
|
||||
QCOMPARE(workspace()->activeOutput(), output);
|
||||
}
|
||||
|
||||
void ScreensTest::testCurrentWithFollowsMouse_data()
|
||||
void ScreensTest::testActiveOutputFollowsMouse_data()
|
||||
{
|
||||
QTest::addColumn<QList<QRect>>("geometries");
|
||||
QTest::addColumn<QPoint>("cursorPos");
|
||||
|
@ -127,12 +102,12 @@ void ScreensTest::testCurrentWithFollowsMouse_data()
|
|||
QTest::newRow("gap") << QList<QRect>{{QRect{0, 0, 10, 20}, QRect{20, 40, 10, 20}}} << QPoint(15, 30) << 1;
|
||||
}
|
||||
|
||||
void ScreensTest::testCurrentWithFollowsMouse()
|
||||
void ScreensTest::testActiveOutputFollowsMouse()
|
||||
{
|
||||
// Enable "active screen follows mouse"
|
||||
auto group = kwinApp()->config()->group(QStringLiteral("Windows"));
|
||||
group.writeEntry("ActiveMouseScreen", true);
|
||||
group.sync();
|
||||
auto edgeBarrierGroup = kwinApp()->config()->group(QStringLiteral("EdgeBarrier"));
|
||||
edgeBarrierGroup.writeEntry("EdgeBarrier", 0);
|
||||
edgeBarrierGroup.writeEntry("CornerBarrier", false);
|
||||
edgeBarrierGroup.sync();
|
||||
workspace()->slotReconfigure();
|
||||
|
||||
QFETCH(QList<QRect>, geometries);
|
||||
|
|
|
@ -84,7 +84,7 @@ void StackingOrderTest::testTransientIsAboveParent()
|
|||
// Create the parent.
|
||||
std::unique_ptr<KWayland::Client::Surface> parentSurface = Test::createSurface();
|
||||
QVERIFY(parentSurface);
|
||||
std::unique_ptr<Test::XdgToplevel> parentShellSurface(Test::createXdgToplevelSurface(parentSurface.get(), parentSurface.get()));
|
||||
std::unique_ptr<Test::XdgToplevel> parentShellSurface(Test::createXdgToplevelSurface(parentSurface.get()));
|
||||
QVERIFY(parentShellSurface);
|
||||
Window *parent = Test::renderAndWaitForShown(parentSurface.get(), QSize(256, 256), Qt::blue);
|
||||
QVERIFY(parent);
|
||||
|
@ -97,7 +97,7 @@ void StackingOrderTest::testTransientIsAboveParent()
|
|||
// Create the transient.
|
||||
std::unique_ptr<KWayland::Client::Surface> transientSurface = Test::createSurface();
|
||||
QVERIFY(transientSurface);
|
||||
std::unique_ptr<Test::XdgToplevel> transientShellSurface(Test::createXdgToplevelSurface(transientSurface.get(), transientSurface.get()));
|
||||
std::unique_ptr<Test::XdgToplevel> transientShellSurface(Test::createXdgToplevelSurface(transientSurface.get()));
|
||||
QVERIFY(transientShellSurface);
|
||||
transientShellSurface->set_parent(parentShellSurface->object());
|
||||
Window *transient = Test::renderAndWaitForShown(transientSurface.get(), QSize(128, 128), Qt::red);
|
||||
|
@ -123,7 +123,7 @@ void StackingOrderTest::testRaiseTransient()
|
|||
// Create the parent.
|
||||
std::unique_ptr<KWayland::Client::Surface> parentSurface = Test::createSurface();
|
||||
QVERIFY(parentSurface);
|
||||
std::unique_ptr<Test::XdgToplevel> parentShellSurface(Test::createXdgToplevelSurface(parentSurface.get(), parentSurface.get()));
|
||||
std::unique_ptr<Test::XdgToplevel> parentShellSurface(Test::createXdgToplevelSurface(parentSurface.get()));
|
||||
QVERIFY(parentShellSurface);
|
||||
Window *parent = Test::renderAndWaitForShown(parentSurface.get(), QSize(256, 256), Qt::blue);
|
||||
QVERIFY(parent);
|
||||
|
@ -136,7 +136,7 @@ void StackingOrderTest::testRaiseTransient()
|
|||
// Create the transient.
|
||||
std::unique_ptr<KWayland::Client::Surface> transientSurface = Test::createSurface();
|
||||
QVERIFY(transientSurface);
|
||||
std::unique_ptr<Test::XdgToplevel> transientShellSurface(Test::createXdgToplevelSurface(transientSurface.get(), transientSurface.get()));
|
||||
std::unique_ptr<Test::XdgToplevel> transientShellSurface(Test::createXdgToplevelSurface(transientSurface.get()));
|
||||
QVERIFY(transientShellSurface);
|
||||
transientShellSurface->set_parent(parentShellSurface->object());
|
||||
Window *transient = Test::renderAndWaitForShown(transientSurface.get(), QSize(128, 128), Qt::red);
|
||||
|
@ -150,7 +150,7 @@ void StackingOrderTest::testRaiseTransient()
|
|||
// Create a window that doesn't have any relationship to the parent or the transient.
|
||||
std::unique_ptr<KWayland::Client::Surface> anotherSurface = Test::createSurface();
|
||||
QVERIFY(anotherSurface);
|
||||
std::unique_ptr<Test::XdgToplevel> anotherShellSurface(Test::createXdgToplevelSurface(anotherSurface.get(), anotherSurface.get()));
|
||||
std::unique_ptr<Test::XdgToplevel> anotherShellSurface(Test::createXdgToplevelSurface(anotherSurface.get()));
|
||||
QVERIFY(anotherShellSurface);
|
||||
Window *anotherWindow = Test::renderAndWaitForShown(anotherSurface.get(), QSize(128, 128), Qt::green);
|
||||
QVERIFY(anotherWindow);
|
||||
|
@ -200,8 +200,7 @@ void StackingOrderTest::testDeletedTransient()
|
|||
// Create the parent.
|
||||
std::unique_ptr<KWayland::Client::Surface> parentSurface = Test::createSurface();
|
||||
QVERIFY(parentSurface);
|
||||
std::unique_ptr<Test::XdgToplevel>
|
||||
parentShellSurface(Test::createXdgToplevelSurface(parentSurface.get(), parentSurface.get()));
|
||||
std::unique_ptr<Test::XdgToplevel> parentShellSurface(Test::createXdgToplevelSurface(parentSurface.get()));
|
||||
QVERIFY(parentShellSurface);
|
||||
Window *parent = Test::renderAndWaitForShown(parentSurface.get(), QSize(256, 256), Qt::blue);
|
||||
QVERIFY(parent);
|
||||
|
@ -213,7 +212,7 @@ void StackingOrderTest::testDeletedTransient()
|
|||
// Create the first transient.
|
||||
std::unique_ptr<KWayland::Client::Surface> transient1Surface = Test::createSurface();
|
||||
QVERIFY(transient1Surface);
|
||||
std::unique_ptr<Test::XdgToplevel> transient1ShellSurface(Test::createXdgToplevelSurface(transient1Surface.get(), transient1Surface.get()));
|
||||
std::unique_ptr<Test::XdgToplevel> transient1ShellSurface(Test::createXdgToplevelSurface(transient1Surface.get()));
|
||||
QVERIFY(transient1ShellSurface);
|
||||
transient1ShellSurface->set_parent(parentShellSurface->object());
|
||||
Window *transient1 = Test::renderAndWaitForShown(transient1Surface.get(), QSize(128, 128), Qt::red);
|
||||
|
@ -227,7 +226,7 @@ void StackingOrderTest::testDeletedTransient()
|
|||
// Create the second transient.
|
||||
std::unique_ptr<KWayland::Client::Surface> transient2Surface = Test::createSurface();
|
||||
QVERIFY(transient2Surface);
|
||||
std::unique_ptr<Test::XdgToplevel> transient2ShellSurface(Test::createXdgToplevelSurface(transient2Surface.get(), transient2Surface.get()));
|
||||
std::unique_ptr<Test::XdgToplevel> transient2ShellSurface(Test::createXdgToplevelSurface(transient2Surface.get()));
|
||||
QVERIFY(transient2ShellSurface);
|
||||
transient2ShellSurface->set_parent(transient1ShellSurface->object());
|
||||
Window *transient2 = Test::renderAndWaitForShown(transient2Surface.get(), QSize(128, 128), Qt::red);
|
||||
|
@ -517,7 +516,7 @@ void StackingOrderTest::testRaiseGroupTransient()
|
|||
// Create a Wayland window that is not a member of the window group.
|
||||
std::unique_ptr<KWayland::Client::Surface> anotherSurface = Test::createSurface();
|
||||
QVERIFY(anotherSurface);
|
||||
std::unique_ptr<Test::XdgToplevel> anotherShellSurface(Test::createXdgToplevelSurface(anotherSurface.get(), anotherSurface.get()));
|
||||
std::unique_ptr<Test::XdgToplevel> anotherShellSurface(Test::createXdgToplevelSurface(anotherSurface.get()));
|
||||
QVERIFY(anotherShellSurface);
|
||||
Window *anotherWindow = Test::renderAndWaitForShown(anotherSurface.get(), QSize(128, 128), Qt::green);
|
||||
QVERIFY(anotherWindow);
|
||||
|
@ -759,7 +758,7 @@ void StackingOrderTest::testKeepAbove()
|
|||
// Create the first window.
|
||||
std::unique_ptr<KWayland::Client::Surface> surface1 = Test::createSurface();
|
||||
QVERIFY(surface1);
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get(), surface1.get()));
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get()));
|
||||
QVERIFY(shellSurface1);
|
||||
Window *window1 = Test::renderAndWaitForShown(surface1.get(), QSize(128, 128), Qt::green);
|
||||
QVERIFY(window1);
|
||||
|
@ -771,7 +770,7 @@ void StackingOrderTest::testKeepAbove()
|
|||
// Create the second window.
|
||||
std::unique_ptr<KWayland::Client::Surface> surface2 = Test::createSurface();
|
||||
QVERIFY(surface2);
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get(), surface2.get()));
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
|
||||
QVERIFY(shellSurface2);
|
||||
Window *window2 = Test::renderAndWaitForShown(surface2.get(), QSize(128, 128), Qt::green);
|
||||
QVERIFY(window2);
|
||||
|
@ -803,7 +802,7 @@ void StackingOrderTest::testKeepBelow()
|
|||
// Create the first window.
|
||||
std::unique_ptr<KWayland::Client::Surface> surface1 = Test::createSurface();
|
||||
QVERIFY(surface1);
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get(), surface1.get()));
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get()));
|
||||
QVERIFY(shellSurface1);
|
||||
Window *window1 = Test::renderAndWaitForShown(surface1.get(), QSize(128, 128), Qt::green);
|
||||
QVERIFY(window1);
|
||||
|
@ -815,7 +814,7 @@ void StackingOrderTest::testKeepBelow()
|
|||
// Create the second window.
|
||||
std::unique_ptr<KWayland::Client::Surface> surface2 = Test::createSurface();
|
||||
QVERIFY(surface2);
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get(), surface2.get()));
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
|
||||
QVERIFY(shellSurface2);
|
||||
Window *window2 = Test::renderAndWaitForShown(surface2.get(), QSize(128, 128), Qt::green);
|
||||
QVERIFY(window2);
|
||||
|
@ -850,7 +849,7 @@ void StackingOrderTest::testPreserveRelativeWindowStacking()
|
|||
for (int i = 0; i < windowsQuantity; i++) {
|
||||
surfaces[i] = Test::createSurface();
|
||||
QVERIFY(surfaces[i]);
|
||||
shellSurfaces[i] = std::unique_ptr<Test::XdgToplevel>(Test::createXdgToplevelSurface(surfaces[i].get(), surfaces[i].get()));
|
||||
shellSurfaces[i] = Test::createXdgToplevelSurface(surfaces[i].get());
|
||||
QVERIFY(shellSurfaces[i]);
|
||||
}
|
||||
|
||||
|
|
|
@ -367,7 +367,7 @@ void StrutsTest::testX11Struts()
|
|||
QVERIFY(window);
|
||||
QCOMPARE(window->window(), windowId);
|
||||
QVERIFY(!window->isDecorated());
|
||||
QCOMPARE(window->windowType(), NET::Dock);
|
||||
QCOMPARE(window->windowType(), WindowType::Dock);
|
||||
QCOMPARE(window->frameGeometry(), windowGeometry);
|
||||
|
||||
// this should have affected the client area
|
||||
|
@ -479,7 +479,7 @@ void StrutsTest::test363804()
|
|||
QVERIFY(window);
|
||||
QCOMPARE(window->window(), windowId);
|
||||
QVERIFY(!window->isDecorated());
|
||||
QCOMPARE(window->windowType(), NET::Dock);
|
||||
QCOMPARE(window->windowType(), WindowType::Dock);
|
||||
QCOMPARE(window->frameGeometry(), windowGeometry);
|
||||
|
||||
// now verify the actual updated client areas
|
||||
|
@ -557,7 +557,7 @@ void StrutsTest::testLeftScreenSmallerBottomAligned()
|
|||
QVERIFY(window);
|
||||
QCOMPARE(window->window(), windowId);
|
||||
QVERIFY(!window->isDecorated());
|
||||
QCOMPARE(window->windowType(), NET::Dock);
|
||||
QCOMPARE(window->windowType(), WindowType::Dock);
|
||||
QCOMPARE(window->frameGeometry(), windowGeometry);
|
||||
|
||||
// now verify the actual updated client areas
|
||||
|
@ -638,7 +638,7 @@ void StrutsTest::testWindowMoveWithPanelBetweenScreens()
|
|||
QVERIFY(window);
|
||||
QCOMPARE(window->window(), windowId);
|
||||
QVERIFY(!window->isDecorated());
|
||||
QCOMPARE(window->windowType(), NET::Dock);
|
||||
QCOMPARE(window->windowType(), WindowType::Dock);
|
||||
QCOMPARE(window->frameGeometry(), windowGeometry);
|
||||
|
||||
// now verify the actual updated client areas
|
||||
|
|
|
@ -269,6 +269,21 @@ SecurityContextManagerV1::~SecurityContextManagerV1()
|
|||
destroy();
|
||||
}
|
||||
|
||||
XdgWmDialogV1::~XdgWmDialogV1()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
XdgDialogV1::XdgDialogV1(XdgWmDialogV1 *wm, XdgToplevel *toplevel)
|
||||
: QtWayland::xdg_dialog_v1(wm->get_xdg_dialog(toplevel->object()))
|
||||
{
|
||||
}
|
||||
|
||||
XdgDialogV1::~XdgDialogV1()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
static struct
|
||||
{
|
||||
KWayland::Client::ConnectionThread *connection = nullptr;
|
||||
|
@ -302,6 +317,7 @@ static struct
|
|||
CursorShapeManagerV1 *cursorShapeManagerV1 = nullptr;
|
||||
FakeInput *fakeInput = nullptr;
|
||||
SecurityContextManagerV1 *securityContextManagerV1 = nullptr;
|
||||
XdgWmDialogV1 *xdgWmDialogV1;
|
||||
} s_waylandConnection;
|
||||
|
||||
MockInputMethod *inputMethod()
|
||||
|
@ -357,8 +373,7 @@ void MockInputMethod::zwp_input_method_v1_deactivate(struct ::zwp_input_method_c
|
|||
m_inputSurface->release();
|
||||
m_inputSurface->destroy();
|
||||
m_inputSurface.reset();
|
||||
delete m_inputMethodSurface;
|
||||
m_inputMethodSurface = nullptr;
|
||||
m_inputMethodSurface.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -518,6 +533,12 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
|
|||
s_waylandConnection.securityContextManagerV1->init(*registry, name, version);
|
||||
}
|
||||
}
|
||||
if (flags & AdditionalWaylandInterface::XdgDialogV1) {
|
||||
if (interface == xdg_wm_dialog_v1_interface.name) {
|
||||
s_waylandConnection.xdgWmDialogV1 = new XdgWmDialogV1();
|
||||
s_waylandConnection.xdgWmDialogV1->init(*registry, name, version);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
QSignalSpy allAnnounced(registry, &KWayland::Client::Registry::interfacesAnnounced);
|
||||
|
@ -643,6 +664,8 @@ void destroyWaylandConnection()
|
|||
s_waylandConnection.fakeInput = nullptr;
|
||||
delete s_waylandConnection.securityContextManagerV1;
|
||||
s_waylandConnection.securityContextManagerV1 = nullptr;
|
||||
delete s_waylandConnection.xdgWmDialogV1;
|
||||
s_waylandConnection.xdgWmDialogV1 = nullptr;
|
||||
|
||||
delete s_waylandConnection.queue; // Must be destroyed last
|
||||
s_waylandConnection.queue = nullptr;
|
||||
|
@ -905,20 +928,19 @@ std::unique_ptr<KWayland::Client::Surface> createSurface()
|
|||
return s->isValid() ? std::move(s) : nullptr;
|
||||
}
|
||||
|
||||
KWayland::Client::SubSurface *createSubSurface(KWayland::Client::Surface *surface, KWayland::Client::Surface *parentSurface, QObject *parent)
|
||||
std::unique_ptr<KWayland::Client::SubSurface> createSubSurface(KWayland::Client::Surface *surface, KWayland::Client::Surface *parentSurface)
|
||||
{
|
||||
if (!s_waylandConnection.subCompositor) {
|
||||
return nullptr;
|
||||
}
|
||||
auto s = s_waylandConnection.subCompositor->createSubSurface(surface, parentSurface, parent);
|
||||
std::unique_ptr<KWayland::Client::SubSurface> s(s_waylandConnection.subCompositor->createSubSurface(surface, parentSurface));
|
||||
if (!s->isValid()) {
|
||||
delete s;
|
||||
return nullptr;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
LayerSurfaceV1 *createLayerSurfaceV1(KWayland::Client::Surface *surface, const QString &scope, KWayland::Client::Output *output, LayerShellV1::layer layer)
|
||||
std::unique_ptr<LayerSurfaceV1> createLayerSurfaceV1(KWayland::Client::Surface *surface, const QString &scope, KWayland::Client::Output *output, LayerShellV1::layer layer)
|
||||
{
|
||||
LayerShellV1 *shell = s_waylandConnection.layerShellV1;
|
||||
if (!shell) {
|
||||
|
@ -931,22 +953,20 @@ LayerSurfaceV1 *createLayerSurfaceV1(KWayland::Client::Surface *surface, const Q
|
|||
nativeOutput = *output;
|
||||
}
|
||||
|
||||
LayerSurfaceV1 *shellSurface = new LayerSurfaceV1();
|
||||
auto shellSurface = std::make_unique<LayerSurfaceV1>();
|
||||
shellSurface->init(shell->get_layer_surface(*surface, nativeOutput, layer, scope));
|
||||
|
||||
return shellSurface;
|
||||
}
|
||||
|
||||
QtWayland::zwp_input_panel_surface_v1 *createInputPanelSurfaceV1(KWayland::Client::Surface *surface, KWayland::Client::Output *output, MockInputMethod::Mode mode)
|
||||
std::unique_ptr<QtWayland::zwp_input_panel_surface_v1> createInputPanelSurfaceV1(KWayland::Client::Surface *surface, KWayland::Client::Output *output, MockInputMethod::Mode mode)
|
||||
{
|
||||
if (!s_waylandConnection.inputPanelV1) {
|
||||
qWarning() << "Unable to create the input panel surface. The interface input_panel global is not bound";
|
||||
return nullptr;
|
||||
}
|
||||
QtWayland::zwp_input_panel_surface_v1 *s = new QtWayland::zwp_input_panel_surface_v1(s_waylandConnection.inputPanelV1->get_input_panel_surface(*surface));
|
||||
|
||||
auto s = std::make_unique<QtWayland::zwp_input_panel_surface_v1>(s_waylandConnection.inputPanelV1->get_input_panel_surface(*surface));
|
||||
if (!s->isInitialized()) {
|
||||
delete s;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -962,13 +982,13 @@ QtWayland::zwp_input_panel_surface_v1 *createInputPanelSurfaceV1(KWayland::Clien
|
|||
return s;
|
||||
}
|
||||
|
||||
FractionalScaleV1 *createFractionalScaleV1(KWayland::Client::Surface *surface)
|
||||
std::unique_ptr<FractionalScaleV1> createFractionalScaleV1(KWayland::Client::Surface *surface)
|
||||
{
|
||||
if (!s_waylandConnection.fractionalScaleManagerV1) {
|
||||
qWarning() << "Unable to create fractional scale surface. The global is not bound";
|
||||
return nullptr;
|
||||
}
|
||||
auto scale = new FractionalScaleV1();
|
||||
auto scale = std::make_unique<FractionalScaleV1>();
|
||||
scale->init(s_waylandConnection.fractionalScaleManagerV1->get_fractional_scale(*surface));
|
||||
|
||||
return scale;
|
||||
|
@ -984,12 +1004,12 @@ static void waitForConfigured(XdgSurface *shellSurface)
|
|||
shellSurface->ack_configure(surfaceConfigureRequestedSpy.last().first().toUInt());
|
||||
}
|
||||
|
||||
XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent)
|
||||
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface)
|
||||
{
|
||||
return createXdgToplevelSurface(surface, CreationSetup::CreateAndConfigure, parent);
|
||||
return createXdgToplevelSurface(surface, CreationSetup::CreateAndConfigure);
|
||||
}
|
||||
|
||||
XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface, CreationSetup configureMode, QObject *parent)
|
||||
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface, CreationSetup configureMode)
|
||||
{
|
||||
XdgShell *shell = s_waylandConnection.xdgShell;
|
||||
|
||||
|
@ -999,7 +1019,7 @@ XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface, Creati
|
|||
}
|
||||
|
||||
XdgSurface *xdgSurface = new XdgSurface(shell, surface);
|
||||
XdgToplevel *xdgToplevel = new XdgToplevel(xdgSurface, parent);
|
||||
std::unique_ptr<XdgToplevel> xdgToplevel = std::make_unique<XdgToplevel>(xdgSurface);
|
||||
|
||||
if (configureMode == CreationSetup::CreateAndConfigure) {
|
||||
waitForConfigured(xdgSurface);
|
||||
|
@ -1008,7 +1028,25 @@ XdgToplevel *createXdgToplevelSurface(KWayland::Client::Surface *surface, Creati
|
|||
return xdgToplevel;
|
||||
}
|
||||
|
||||
XdgPositioner *createXdgPositioner()
|
||||
std::unique_ptr<XdgToplevel> createXdgToplevelSurface(KWayland::Client::Surface *surface, std::function<void(XdgToplevel *toplevel)> setup)
|
||||
{
|
||||
XdgShell *shell = s_waylandConnection.xdgShell;
|
||||
|
||||
if (!shell) {
|
||||
qWarning() << "Could not create an xdg_toplevel surface because xdg_wm_base global is not bound";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
XdgSurface *xdgSurface = new XdgSurface(shell, surface);
|
||||
std::unique_ptr<XdgToplevel> xdgToplevel = std::make_unique<XdgToplevel>(xdgSurface);
|
||||
|
||||
setup(xdgToplevel.get());
|
||||
waitForConfigured(xdgSurface);
|
||||
|
||||
return xdgToplevel;
|
||||
}
|
||||
|
||||
std::unique_ptr<XdgPositioner> createXdgPositioner()
|
||||
{
|
||||
XdgShell *shell = s_waylandConnection.xdgShell;
|
||||
|
||||
|
@ -1017,11 +1055,10 @@ XdgPositioner *createXdgPositioner()
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return new XdgPositioner(shell);
|
||||
return std::make_unique<XdgPositioner>(shell);
|
||||
}
|
||||
|
||||
XdgPopup *createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *parentSurface, XdgPositioner *positioner,
|
||||
CreationSetup configureMode, QObject *parent)
|
||||
std::unique_ptr<XdgPopup> createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *parentSurface, XdgPositioner *positioner, CreationSetup configureMode)
|
||||
{
|
||||
XdgShell *shell = s_waylandConnection.xdgShell;
|
||||
|
||||
|
@ -1031,7 +1068,7 @@ XdgPopup *createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *
|
|||
}
|
||||
|
||||
XdgSurface *xdgSurface = new XdgSurface(shell, surface);
|
||||
XdgPopup *xdgPopup = new XdgPopup(xdgSurface, parentSurface, positioner, parent);
|
||||
std::unique_ptr<XdgPopup> xdgPopup = std::make_unique<XdgPopup>(xdgSurface, parentSurface, positioner);
|
||||
|
||||
if (configureMode == CreationSetup::CreateAndConfigure) {
|
||||
waitForConfigured(xdgSurface);
|
||||
|
@ -1040,7 +1077,7 @@ XdgPopup *createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *
|
|||
return xdgPopup;
|
||||
}
|
||||
|
||||
XdgToplevelDecorationV1 *createXdgToplevelDecorationV1(XdgToplevel *toplevel, QObject *parent)
|
||||
std::unique_ptr<XdgToplevelDecorationV1> createXdgToplevelDecorationV1(XdgToplevel *toplevel)
|
||||
{
|
||||
XdgDecorationManagerV1 *manager = s_waylandConnection.xdgDecorationManagerV1;
|
||||
|
||||
|
@ -1049,10 +1086,10 @@ XdgToplevelDecorationV1 *createXdgToplevelDecorationV1(XdgToplevel *toplevel, QO
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return new XdgToplevelDecorationV1(manager, toplevel, parent);
|
||||
return std::make_unique<XdgToplevelDecorationV1>(manager, toplevel);
|
||||
}
|
||||
|
||||
IdleInhibitorV1 *createIdleInhibitorV1(KWayland::Client::Surface *surface)
|
||||
std::unique_ptr<IdleInhibitorV1> createIdleInhibitorV1(KWayland::Client::Surface *surface)
|
||||
{
|
||||
IdleInhibitManagerV1 *manager = s_waylandConnection.idleInhibitManagerV1;
|
||||
if (!manager) {
|
||||
|
@ -1060,10 +1097,10 @@ IdleInhibitorV1 *createIdleInhibitorV1(KWayland::Client::Surface *surface)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return new IdleInhibitorV1(manager, surface);
|
||||
return std::make_unique<IdleInhibitorV1>(manager, surface);
|
||||
}
|
||||
|
||||
AutoHideScreenEdgeV1 *createAutoHideScreenEdgeV1(KWayland::Client::Surface *surface, uint32_t border)
|
||||
std::unique_ptr<AutoHideScreenEdgeV1> createAutoHideScreenEdgeV1(KWayland::Client::Surface *surface, uint32_t border)
|
||||
{
|
||||
ScreenEdgeManagerV1 *manager = s_waylandConnection.screenEdgeManagerV1;
|
||||
if (!manager) {
|
||||
|
@ -1071,10 +1108,10 @@ AutoHideScreenEdgeV1 *createAutoHideScreenEdgeV1(KWayland::Client::Surface *surf
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return new AutoHideScreenEdgeV1(manager, surface, border);
|
||||
return std::make_unique<AutoHideScreenEdgeV1>(manager, surface, border);
|
||||
}
|
||||
|
||||
CursorShapeDeviceV1 *createCursorShapeDeviceV1(KWayland::Client::Pointer *pointer)
|
||||
std::unique_ptr<CursorShapeDeviceV1> createCursorShapeDeviceV1(KWayland::Client::Pointer *pointer)
|
||||
{
|
||||
CursorShapeManagerV1 *manager = s_waylandConnection.cursorShapeManagerV1;
|
||||
if (!manager) {
|
||||
|
@ -1082,7 +1119,17 @@ CursorShapeDeviceV1 *createCursorShapeDeviceV1(KWayland::Client::Pointer *pointe
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return new CursorShapeDeviceV1(manager, pointer);
|
||||
return std::make_unique<CursorShapeDeviceV1>(manager, pointer);
|
||||
}
|
||||
|
||||
std::unique_ptr<XdgDialogV1> createXdgDialogV1(XdgToplevel *toplevel)
|
||||
{
|
||||
XdgWmDialogV1 *wm = s_waylandConnection.xdgWmDialogV1;
|
||||
if (!wm) {
|
||||
qWarning() << "Could not create a xdg_dialog_v1 because xdg_wm_dialog_v1 global is not bound";
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_unique<XdgDialogV1>(wm, toplevel);
|
||||
}
|
||||
|
||||
bool waitForWindowClosed(Window *window)
|
||||
|
@ -1183,6 +1230,7 @@ bool renderNodeAvailable()
|
|||
});
|
||||
}
|
||||
|
||||
#if KWIN_BUILD_X11
|
||||
void XcbConnectionDeleter::operator()(xcb_connection_t *pointer)
|
||||
{
|
||||
xcb_disconnect(pointer);
|
||||
|
@ -1200,6 +1248,7 @@ Test::XcbConnectionPtr createX11Connection()
|
|||
e.exec();
|
||||
return Test::XcbConnectionPtr(future.result());
|
||||
}
|
||||
#endif
|
||||
|
||||
WaylandOutputManagementV2::WaylandOutputManagementV2(struct ::wl_registry *registry, int id, int version)
|
||||
: QObject()
|
||||
|
@ -1562,11 +1611,6 @@ bool VirtualInputDevice::isKeyboard() const
|
|||
return m_keyboard;
|
||||
}
|
||||
|
||||
bool VirtualInputDevice::isAlphaNumericKeyboard() const
|
||||
{
|
||||
return m_keyboard;
|
||||
}
|
||||
|
||||
bool VirtualInputDevice::isPointer() const
|
||||
{
|
||||
return m_pointer;
|
||||
|
|
|
@ -46,7 +46,14 @@ private Q_SLOTS:
|
|||
void testGestureDetection();
|
||||
|
||||
private:
|
||||
std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> showWindow(bool decorated = false);
|
||||
struct WindowHandle
|
||||
{
|
||||
Window *window;
|
||||
std::unique_ptr<KWayland::Client::Surface> surface;
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface;
|
||||
std::unique_ptr<Test::XdgToplevelDecorationV1> decoration;
|
||||
};
|
||||
WindowHandle showWindow(bool decorated = false);
|
||||
KWayland::Client::Touch *m_touch = nullptr;
|
||||
};
|
||||
|
||||
|
@ -87,21 +94,22 @@ void TouchInputTest::cleanup()
|
|||
Test::destroyWaylandConnection();
|
||||
}
|
||||
|
||||
std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> TouchInputTest::showWindow(bool decorated)
|
||||
TouchInputTest::WindowHandle TouchInputTest::showWindow(bool decorated)
|
||||
{
|
||||
#define VERIFY(statement) \
|
||||
if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) \
|
||||
return {nullptr, nullptr};
|
||||
return {};
|
||||
#define COMPARE(actual, expected) \
|
||||
if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
|
||||
return {nullptr, nullptr};
|
||||
return {};
|
||||
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
VERIFY(surface.get());
|
||||
Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly, surface.get());
|
||||
VERIFY(shellSurface);
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly);
|
||||
VERIFY(shellSurface.get());
|
||||
std::unique_ptr<Test::XdgToplevelDecorationV1> decoration;
|
||||
if (decorated) {
|
||||
auto decoration = Test::createXdgToplevelDecorationV1(shellSurface, shellSurface);
|
||||
decoration = Test::createXdgToplevelDecorationV1(shellSurface.get());
|
||||
decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side);
|
||||
}
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
|
@ -117,7 +125,7 @@ std::pair<Window *, std::unique_ptr<KWayland::Client::Surface>> TouchInputTest::
|
|||
#undef VERIFY
|
||||
#undef COMPARE
|
||||
|
||||
return {window, std::move(surface)};
|
||||
return {window, std::move(surface), std::move(shellSurface), std::move(decoration)};
|
||||
}
|
||||
|
||||
void TouchInputTest::testTouchHidesCursor()
|
||||
|
@ -155,7 +163,7 @@ void TouchInputTest::testMultipleTouchPoints_data()
|
|||
void TouchInputTest::testMultipleTouchPoints()
|
||||
{
|
||||
QFETCH(bool, decorated);
|
||||
auto [window, surface] = showWindow(decorated);
|
||||
auto [window, surface, shellSurface, decoration] = showWindow(decorated);
|
||||
QCOMPARE(window->isDecorated(), decorated);
|
||||
window->move(QPoint(100, 100));
|
||||
QVERIFY(window);
|
||||
|
@ -210,7 +218,7 @@ void TouchInputTest::testMultipleTouchPoints()
|
|||
|
||||
void TouchInputTest::testCancel()
|
||||
{
|
||||
auto [window, surface] = showWindow();
|
||||
auto [window, surface, shellSurface, decoration] = showWindow();
|
||||
window->move(QPoint(100, 100));
|
||||
QVERIFY(window);
|
||||
QSignalSpy sequenceStartedSpy(m_touch, &KWayland::Client::Touch::sequenceStarted);
|
||||
|
@ -233,9 +241,9 @@ void TouchInputTest::testTouchMouseAction()
|
|||
// this test verifies that a touch down on an inactive window will activate it
|
||||
|
||||
// create two windows
|
||||
auto [c1, surface] = showWindow();
|
||||
auto [c1, surface, shellSurface, decoration] = showWindow();
|
||||
QVERIFY(c1);
|
||||
auto [c2, surface2] = showWindow();
|
||||
auto [c2, surface2, shellSurface2, decoration2] = showWindow();
|
||||
QVERIFY(c2);
|
||||
|
||||
QVERIFY(!c1->isActive());
|
||||
|
|
|
@ -114,7 +114,7 @@ void TransientPlacementTest::testXdgPopup_data()
|
|||
.anchor = Test::XdgPositioner::anchor_none,
|
||||
.gravity = Test::XdgPositioner::gravity_bottom_right,
|
||||
};
|
||||
QTest::newRow("anchorCentre") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorCenter << QRect(550, 550, 200, 200);
|
||||
QTest::newRow("anchorCenter") << QSize(500, 500) << QPoint(300, 300) << layoutAnchorCenter << QRect(550, 550, 200, 200);
|
||||
|
||||
const PopupLayout layoutAnchorTopLeft{
|
||||
.anchorRect = QRect(50, 50, 400, 400),
|
||||
|
@ -189,7 +189,7 @@ void TransientPlacementTest::testXdgPopup_data()
|
|||
.anchor = Test::XdgPositioner::anchor_bottom_right,
|
||||
.gravity = Test::XdgPositioner::gravity_none,
|
||||
};
|
||||
QTest::newRow("gravityCentre") << QSize(500, 500) << QPoint(300, 300) << layoutGravityCenter << QRect(650, 650, 200, 200);
|
||||
QTest::newRow("gravityCenter") << QSize(500, 500) << QPoint(300, 300) << layoutGravityCenter << QRect(650, 650, 200, 200);
|
||||
|
||||
const PopupLayout layoutGravityTopLeft{
|
||||
.anchorRect = QRect(50, 50, 400, 400),
|
||||
|
@ -423,7 +423,7 @@ void TransientPlacementTest::testXdgPopup()
|
|||
|
||||
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
|
||||
QVERIFY(surface);
|
||||
auto parentShellSurface = Test::createXdgToplevelSurface(surface.get(), Test::waylandCompositor());
|
||||
std::unique_ptr<Test::XdgToplevel> parentShellSurface = Test::createXdgToplevelSurface(surface.get());
|
||||
QVERIFY(parentShellSurface);
|
||||
auto parent = Test::renderAndWaitForShown(surface.get(), parentSize, Qt::blue);
|
||||
QVERIFY(parent);
|
||||
|
@ -480,7 +480,7 @@ void TransientPlacementTest::testXdgPopupWithPanel()
|
|||
QVERIFY(dockConfigureRequestedSpy.wait());
|
||||
auto dock = Test::renderAndWaitForShown(dockSurface.get(), dockConfigureRequestedSpy.last().at(1).toSize(), Qt::blue);
|
||||
QVERIFY(dock);
|
||||
QCOMPARE(dock->windowType(), NET::Dock);
|
||||
QCOMPARE(dock->windowType(), WindowType::Dock);
|
||||
QVERIFY(dock->isDock());
|
||||
QCOMPARE(dock->frameGeometry(), QRect(0, output->geometry().height() - 50, 1280, 50));
|
||||
QCOMPARE(dock->hasStrut(), true);
|
||||
|
@ -497,7 +497,7 @@ void TransientPlacementTest::testXdgPopupWithPanel()
|
|||
|
||||
QVERIFY(!parent->isDecorated());
|
||||
parent->move(QPointF(0, output->geometry().height() - 600));
|
||||
parent->keepInArea(workspace()->clientArea(PlacementArea, parent));
|
||||
parent->moveResize(parent->keepInArea(parent->moveResizeGeometry(), workspace()->clientArea(PlacementArea, parent)));
|
||||
QCOMPARE(parent->frameGeometry(), QRect(0, output->geometry().height() - 600 - 50, 800, 600));
|
||||
|
||||
std::unique_ptr<KWayland::Client::Surface> transientSurface(Test::createSurface());
|
||||
|
@ -522,7 +522,7 @@ void TransientPlacementTest::testXdgPopupWithPanel()
|
|||
QVERIFY(Test::waitForWindowClosed(transient));
|
||||
|
||||
// now parent to fullscreen - on fullscreen the panel is ignored
|
||||
QSignalSpy toplevelConfigureRequestedSpy(parentShellSurface, &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy toplevelConfigureRequestedSpy(parentShellSurface.get(), &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(parentShellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
parent->setFullScreen(true);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
|
@ -542,7 +542,7 @@ void TransientPlacementTest::testXdgPopupWithPanel()
|
|||
positioner2->set_size(200, 200);
|
||||
positioner2->set_anchor_rect(anchorRect2.x(), anchorRect2.y(), anchorRect2.width(), anchorRect2.height());
|
||||
positioner2->set_constraint_adjustment(Test::XdgPositioner::constraint_adjustment_slide_x | Test::XdgPositioner::constraint_adjustment_slide_y);
|
||||
transientShellSurface.reset(Test::createXdgPopupSurface(transientSurface.get(), parentShellSurface->xdgSurface(), positioner2.get()));
|
||||
transientShellSurface = Test::createXdgPopupSurface(transientSurface.get(), parentShellSurface->xdgSurface(), positioner2.get());
|
||||
transient = Test::renderAndWaitForShown(transientSurface.get(), QSize(200, 200), Qt::red);
|
||||
QVERIFY(transient);
|
||||
|
||||
|
|
|
@ -9,12 +9,15 @@
|
|||
#include "kwin_wayland_test.h"
|
||||
|
||||
#include "main.h"
|
||||
#include "utils/xcbutils.h"
|
||||
#include "virtualdesktops.h"
|
||||
#include "wayland_server.h"
|
||||
#include "window.h"
|
||||
#include "workspace.h"
|
||||
|
||||
#if KWIN_BUILD_X11
|
||||
#include "utils/xcbutils.h"
|
||||
#endif
|
||||
|
||||
#include <KWayland/Client/surface.h>
|
||||
|
||||
using namespace KWin;
|
||||
|
@ -28,8 +31,9 @@ private Q_SLOTS:
|
|||
void initTestCase();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
#if KWIN_BUILD_X11
|
||||
void testNetCurrentDesktop();
|
||||
#endif
|
||||
void testLastDesktopRemoved();
|
||||
void testWindowOnMultipleDesktops();
|
||||
void testRemoveDesktopWithWindow();
|
||||
|
@ -52,6 +56,7 @@ void VirtualDesktopTest::initTestCase()
|
|||
kwinApp()->start();
|
||||
QVERIFY(applicationStartedSpy.wait());
|
||||
|
||||
#if KWIN_BUILD_X11
|
||||
if (kwinApp()->x11Connection()) {
|
||||
// verify the current desktop x11 property on startup, see BUG: 391034
|
||||
Xcb::Atom currentDesktopAtom("_NET_CURRENT_DESKTOP");
|
||||
|
@ -61,6 +66,7 @@ void VirtualDesktopTest::initTestCase()
|
|||
QCOMPARE(currentDesktop.value(0, &ok), 0);
|
||||
QVERIFY(ok);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void VirtualDesktopTest::init()
|
||||
|
@ -75,6 +81,7 @@ void VirtualDesktopTest::cleanup()
|
|||
Test::destroyWaylandConnection();
|
||||
}
|
||||
|
||||
#if KWIN_BUILD_X11
|
||||
void VirtualDesktopTest::testNetCurrentDesktop()
|
||||
{
|
||||
if (!kwinApp()->x11Connection()) {
|
||||
|
@ -115,6 +122,7 @@ void VirtualDesktopTest::testNetCurrentDesktop()
|
|||
QCOMPARE(currentDesktop.value(0, &ok), 0);
|
||||
QVERIFY(ok);
|
||||
}
|
||||
#endif
|
||||
|
||||
void VirtualDesktopTest::testLastDesktopRemoved()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2024 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "kwin_wayland_test.h"
|
||||
|
||||
#include "input.h"
|
||||
#include "options.h"
|
||||
#include "pointer_input.h"
|
||||
#include "qabstracteventdispatcher.h"
|
||||
#include "qsocketnotifier.h"
|
||||
#include "wayland/keyboard.h"
|
||||
#include "wayland/seat.h"
|
||||
#include "wayland_server.h"
|
||||
#include "workspace.h"
|
||||
#include <KConfigGroup>
|
||||
#include <linux/input.h>
|
||||
|
||||
#define explicit xcb_explicit
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcbext.h>
|
||||
#include <xcb/xinput.h>
|
||||
#include <xcb/xkb.h>
|
||||
#undef explicit
|
||||
|
||||
using namespace KWin;
|
||||
|
||||
static const QString s_socketName = QStringLiteral("wayland_test_kwin_x11-key-read-0");
|
||||
|
||||
enum class State {
|
||||
Press,
|
||||
Release
|
||||
} state;
|
||||
typedef QPair<State, int> KeyAction;
|
||||
Q_DECLARE_METATYPE(KeyAction);
|
||||
|
||||
/*
|
||||
* This tests the "Legacy App Support" feature of allowing X11 apps to get notified of some key press events
|
||||
*/
|
||||
class X11KeyReadTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void initTestCase_data();
|
||||
void initTestCase();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
void testSimpleLetter();
|
||||
void onlyModifier();
|
||||
void letterWithModifier();
|
||||
|
||||
private:
|
||||
QList<KeyAction> recievedX11EventsForInput(const QList<KeyAction> &keyEventsIn);
|
||||
};
|
||||
|
||||
void X11KeyReadTest::initTestCase_data()
|
||||
{
|
||||
QTest::addColumn<XwaylandEavesdropsMode>("operatingMode");
|
||||
QTest::newRow("all") << XwaylandEavesdropsMode::All;
|
||||
QTest::newRow("allWithModifier") << XwaylandEavesdropsMode::AllKeysWithModifier;
|
||||
QTest::newRow("nonCharacter") << XwaylandEavesdropsMode::NonCharacterKeys;
|
||||
QTest::newRow("none") << XwaylandEavesdropsMode::None;
|
||||
}
|
||||
|
||||
void X11KeyReadTest::initTestCase()
|
||||
{
|
||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||
QVERIFY(waylandServer()->init(s_socketName));
|
||||
Test::setOutputConfig({
|
||||
QRect(0, 0, 1280, 1024),
|
||||
});
|
||||
|
||||
qputenv("KWIN_XKB_DEFAULT_KEYMAP", "1");
|
||||
qputenv("XKB_DEFAULT_RULES", "evdev");
|
||||
|
||||
kwinApp()->start();
|
||||
QVERIFY(applicationStartedSpy.wait());
|
||||
|
||||
Test::XcbConnectionPtr c = Test::createX11Connection();
|
||||
QVERIFY(!xcb_connection_has_error(c.get()));
|
||||
}
|
||||
|
||||
void X11KeyReadTest::init()
|
||||
{
|
||||
QFETCH_GLOBAL(XwaylandEavesdropsMode, operatingMode);
|
||||
options->setXwaylandEavesdrops(operatingMode);
|
||||
workspace()->setActiveOutput(QPoint(640, 512));
|
||||
KWin::input()->pointer()->warp(QPoint(640, 512));
|
||||
}
|
||||
|
||||
void X11KeyReadTest::cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
void X11KeyReadTest::testSimpleLetter()
|
||||
{
|
||||
// press a
|
||||
QList<KeyAction> keyEvents = {
|
||||
{State::Press, KEY_A},
|
||||
{State::Release, KEY_A},
|
||||
};
|
||||
auto received = recievedX11EventsForInput(keyEvents);
|
||||
|
||||
QList<KeyAction> expected;
|
||||
QFETCH_GLOBAL(XwaylandEavesdropsMode, operatingMode);
|
||||
switch (operatingMode) {
|
||||
case XwaylandEavesdropsMode::None:
|
||||
case XwaylandEavesdropsMode::NonCharacterKeys:
|
||||
case XwaylandEavesdropsMode::AllKeysWithModifier:
|
||||
expected = {};
|
||||
break;
|
||||
case XwaylandEavesdropsMode::All:
|
||||
expected = keyEvents;
|
||||
break;
|
||||
}
|
||||
|
||||
QCOMPARE(received, expected);
|
||||
}
|
||||
|
||||
void X11KeyReadTest::onlyModifier()
|
||||
{
|
||||
QList<KeyAction> keyEvents = {
|
||||
{State::Press, KEY_LEFTALT},
|
||||
{State::Release, KEY_LEFTALT},
|
||||
};
|
||||
auto received = recievedX11EventsForInput(keyEvents);
|
||||
|
||||
QList<KeyAction> expected;
|
||||
QFETCH_GLOBAL(XwaylandEavesdropsMode, operatingMode);
|
||||
switch (operatingMode) {
|
||||
case XwaylandEavesdropsMode::None:
|
||||
expected = {};
|
||||
break;
|
||||
case XwaylandEavesdropsMode::NonCharacterKeys:
|
||||
case XwaylandEavesdropsMode::AllKeysWithModifier:
|
||||
case XwaylandEavesdropsMode::All:
|
||||
expected = keyEvents;
|
||||
break;
|
||||
}
|
||||
|
||||
QCOMPARE(received, expected);
|
||||
}
|
||||
|
||||
void X11KeyReadTest::letterWithModifier()
|
||||
{
|
||||
QList<KeyAction> keyEvents = {
|
||||
{State::Press, KEY_LEFTALT},
|
||||
{State::Press, KEY_F},
|
||||
{State::Release, KEY_F},
|
||||
{State::Release, KEY_LEFTALT},
|
||||
};
|
||||
auto received = recievedX11EventsForInput(keyEvents);
|
||||
|
||||
QList<KeyAction> expected;
|
||||
QFETCH_GLOBAL(XwaylandEavesdropsMode, operatingMode);
|
||||
switch (operatingMode) {
|
||||
case XwaylandEavesdropsMode::None:
|
||||
expected = {};
|
||||
break;
|
||||
case XwaylandEavesdropsMode::NonCharacterKeys:
|
||||
expected = {
|
||||
{State::Press, KEY_LEFTALT},
|
||||
{State::Release, KEY_LEFTALT},
|
||||
};
|
||||
break;
|
||||
case XwaylandEavesdropsMode::AllKeysWithModifier:
|
||||
case XwaylandEavesdropsMode::All:
|
||||
expected = keyEvents;
|
||||
break;
|
||||
}
|
||||
QCOMPARE(received, expected);
|
||||
}
|
||||
|
||||
class X11EventRecorder :
|
||||
public
|
||||
QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
X11EventRecorder(xcb_connection_t * c);
|
||||
QList<KeyAction> keyEvents() const
|
||||
{
|
||||
return m_keyEvents;
|
||||
}
|
||||
Q_SIGNALS:
|
||||
void fenceReceived();
|
||||
|
||||
private:
|
||||
void processXcbEvents();
|
||||
QList<KeyAction> m_keyEvents;
|
||||
xcb_connection_t *m_connection;
|
||||
QSocketNotifier *m_notifier;
|
||||
};
|
||||
|
||||
X11EventRecorder::X11EventRecorder(xcb_connection_t * c)
|
||||
: QObject()
|
||||
, m_connection(c)
|
||||
, m_notifier(new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this))
|
||||
{
|
||||
struct
|
||||
{
|
||||
xcb_input_event_mask_t head;
|
||||
xcb_input_xi_event_mask_t mask;
|
||||
} mask;
|
||||
mask.head.deviceid = XCB_INPUT_DEVICE_ALL;
|
||||
mask.head.mask_len = sizeof(mask.mask) / sizeof(uint32_t);
|
||||
mask.mask = static_cast<xcb_input_xi_event_mask_t>(
|
||||
XCB_INPUT_XI_EVENT_MASK_KEY_PRESS
|
||||
| XCB_INPUT_XI_EVENT_MASK_KEY_RELEASE
|
||||
| XCB_INPUT_RAW_KEY_PRESS
|
||||
| XCB_INPUT_RAW_KEY_RELEASE);
|
||||
|
||||
xcb_input_xi_select_events(c, kwinApp()->x11RootWindow(), 1, &mask.head);
|
||||
xcb_flush(c);
|
||||
|
||||
connect(m_notifier, &QSocketNotifier::activated, this, &X11EventRecorder::processXcbEvents);
|
||||
connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, &X11EventRecorder::processXcbEvents);
|
||||
connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::awake, this, &X11EventRecorder::processXcbEvents);
|
||||
}
|
||||
|
||||
void X11EventRecorder::processXcbEvents()
|
||||
{
|
||||
xcb_generic_event_t *event;
|
||||
while ((event = xcb_poll_for_event(m_connection))) {
|
||||
u_int8_t responseType = event->response_type & ~0x80;
|
||||
if (responseType == XCB_GE_GENERIC) {
|
||||
auto *geEvent = reinterpret_cast<xcb_ge_generic_event_t *>(event);
|
||||
if (geEvent->event_type == XCB_INPUT_KEY_PRESS || geEvent->event_type == XCB_INPUT_KEY_RELEASE) {
|
||||
|
||||
auto keyEvent = reinterpret_cast<xcb_input_key_press_event_t *>(geEvent);
|
||||
int nativeKeyCode = keyEvent->detail - 0x08;
|
||||
|
||||
if (nativeKeyCode == 0) {
|
||||
Q_EMIT fenceReceived();
|
||||
} else {
|
||||
KeyAction action({geEvent->event_type == XCB_INPUT_KEY_PRESS ? State::Press : State::Release, nativeKeyCode});
|
||||
m_keyEvents << action;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::free(event);
|
||||
}
|
||||
|
||||
xcb_flush(m_connection);
|
||||
}
|
||||
|
||||
QList<KeyAction> X11KeyReadTest::recievedX11EventsForInput(const QList<KeyAction> &keysIn)
|
||||
{
|
||||
quint32 timestamp = 1;
|
||||
Test::XcbConnectionPtr c = Test::createX11Connection();
|
||||
|
||||
X11EventRecorder eventReader(c.get());
|
||||
|
||||
QSignalSpy fenceEventSpy(&eventReader, &X11EventRecorder::fenceReceived);
|
||||
|
||||
for (const KeyAction &action : keysIn) {
|
||||
if (action.first == State::Press) {
|
||||
Test::keyboardKeyPressed(action.second, timestamp++);
|
||||
} else {
|
||||
Test::keyboardKeyReleased(action.second, timestamp++);
|
||||
}
|
||||
Test::flushWaylandConnection();
|
||||
QTest::qWait(5);
|
||||
}
|
||||
// special case, explicitly send key 0, to use as a fence
|
||||
ClientConnection *xwaylandClient = waylandServer()->xWaylandConnection();
|
||||
waylandServer()->seat()->keyboard()->sendKey(0, KeyboardKeyState::Pressed, xwaylandClient);
|
||||
|
||||
Test::flushWaylandConnection();
|
||||
|
||||
bool fenceComplete = fenceEventSpy.wait();
|
||||
Q_ASSERT(fenceComplete);
|
||||
|
||||
return eventReader.keyEvents();
|
||||
}
|
||||
|
||||
WAYLANDTEST_MAIN(X11KeyReadTest)
|
||||
#include "x11keyread.moc"
|
|
@ -175,6 +175,7 @@ private:
|
|||
Window *m_window;
|
||||
std::unique_ptr<KWayland::Client::Surface> m_surface;
|
||||
std::unique_ptr<Test::XdgToplevel> m_shellSurface;
|
||||
std::unique_ptr<Test::XdgToplevelDecorationV1> m_decoration;
|
||||
|
||||
std::unique_ptr<QSignalSpy> m_toplevelConfigureRequestedSpy;
|
||||
std::unique_ptr<QSignalSpy> m_surfaceConfigureRequestedSpy;
|
||||
|
@ -237,15 +238,15 @@ void TestXdgShellWindowRules::createTestWindow(ClientFlags flags)
|
|||
: Test::XdgToplevelDecorationV1::mode_client_side;
|
||||
// Create an xdg surface.
|
||||
m_surface = Test::createSurface();
|
||||
m_shellSurface.reset(Test::createXdgToplevelSurface(m_surface.get(), Test::CreationSetup::CreateOnly, m_surface.get()));
|
||||
Test::XdgToplevelDecorationV1 *decoration = Test::createXdgToplevelDecorationV1(m_shellSurface.get(), m_shellSurface.get());
|
||||
m_shellSurface = Test::createXdgToplevelSurface(m_surface.get(), Test::CreationSetup::CreateOnly);
|
||||
m_decoration = Test::createXdgToplevelDecorationV1(m_shellSurface.get());
|
||||
|
||||
// Add signal watchers
|
||||
m_toplevelConfigureRequestedSpy = std::make_unique<QSignalSpy>(m_shellSurface.get(), &Test::XdgToplevel::configureRequested);
|
||||
m_surfaceConfigureRequestedSpy = std::make_unique<QSignalSpy>(m_shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
|
||||
m_shellSurface->set_app_id(QStringLiteral("org.kde.foo"));
|
||||
decoration->set_mode(decorationMode);
|
||||
m_decoration->set_mode(decorationMode);
|
||||
|
||||
// Wait for the initial configure event
|
||||
m_surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
||||
|
@ -277,6 +278,7 @@ void TestXdgShellWindowRules::destroyTestWindow()
|
|||
{
|
||||
m_surfaceConfigureRequestedSpy.reset();
|
||||
m_toplevelConfigureRequestedSpy.reset();
|
||||
m_decoration.reset();
|
||||
m_shellSurface.reset();
|
||||
m_surface.reset();
|
||||
QVERIFY(Test::waitForWindowClosed(m_window));
|
||||
|
@ -2829,7 +2831,7 @@ void TestXdgShellWindowRules::testScreenDontAffect()
|
|||
QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
|
||||
|
||||
// The user can still move the window to another screen.
|
||||
workspace()->sendWindowToOutput(m_window, outputs.at(1));
|
||||
m_window->sendToOutput(outputs.at(1));
|
||||
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
|
||||
|
||||
destroyTestWindow();
|
||||
|
@ -2848,7 +2850,7 @@ void TestXdgShellWindowRules::testScreenApply()
|
|||
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
|
||||
|
||||
// The user can move the window to another screen.
|
||||
workspace()->sendWindowToOutput(m_window, outputs.at(0));
|
||||
m_window->sendToOutput(outputs.at(0));
|
||||
QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
|
||||
|
||||
destroyTestWindow();
|
||||
|
@ -2866,11 +2868,12 @@ void TestXdgShellWindowRules::testScreenRemember()
|
|||
QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
|
||||
|
||||
// Move the window to the second screen.
|
||||
workspace()->sendWindowToOutput(m_window, outputs.at(1));
|
||||
m_window->sendToOutput(outputs.at(1));
|
||||
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
|
||||
|
||||
// Close and reopen the window.
|
||||
destroyTestWindow();
|
||||
workspace()->setActiveOutput(outputs.at(0));
|
||||
createTestWindow();
|
||||
|
||||
QEXPECT_FAIL("", "Applying a screen rule on a new client fails on Wayland", Continue);
|
||||
|
@ -2892,7 +2895,7 @@ void TestXdgShellWindowRules::testScreenForce()
|
|||
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
|
||||
|
||||
// User should not be able to move the window to another screen.
|
||||
workspace()->sendWindowToOutput(m_window, outputs.at(0));
|
||||
m_window->sendToOutput(outputs.at(0));
|
||||
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
|
||||
|
||||
// Disable the output where the window is on, so the window is moved the other screen
|
||||
|
@ -2934,7 +2937,7 @@ void TestXdgShellWindowRules::testScreenApplyNow()
|
|||
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
|
||||
|
||||
// The user can move the window to another screen.
|
||||
workspace()->sendWindowToOutput(m_window, outputs.at(0));
|
||||
m_window->sendToOutput(outputs.at(0));
|
||||
QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
|
||||
|
||||
// The rule should not be applied again.
|
||||
|
@ -2956,11 +2959,12 @@ void TestXdgShellWindowRules::testScreenForceTemporarily()
|
|||
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
|
||||
|
||||
// User is not allowed to move it
|
||||
workspace()->sendWindowToOutput(m_window, outputs.at(0));
|
||||
m_window->sendToOutput(outputs.at(0));
|
||||
QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
|
||||
|
||||
// Close and reopen the window.
|
||||
destroyTestWindow();
|
||||
workspace()->setActiveOutput(outputs.at(0));
|
||||
createTestWindow();
|
||||
|
||||
// The rule should be discarded now
|
||||
|
|
|
@ -68,11 +68,14 @@ private Q_SLOTS:
|
|||
void testMaximizedToFullscreen_data();
|
||||
void testMaximizedToFullscreen();
|
||||
void testSendMaximizedWindowToAnotherOutput();
|
||||
void testInteractiveMoveUnmaximizeFull();
|
||||
void testInteractiveMoveUnmaximizeInitiallyFull();
|
||||
void testInteractiveMoveUnmaximizeHorizontal();
|
||||
void testInteractiveMoveUnmaximizeVertical();
|
||||
void testFullscreenMultipleOutputs();
|
||||
void testHidden();
|
||||
void testDesktopFileName();
|
||||
void testCaptionSimplified();
|
||||
void testCaptionMultipleWindows();
|
||||
void testUnresponsiveWindow_data();
|
||||
void testUnresponsiveWindow();
|
||||
void testAppMenu();
|
||||
|
@ -101,6 +104,9 @@ private Q_SLOTS:
|
|||
void testMaximizeAndChangeDecorationModeAfterInitialCommit();
|
||||
void testFullScreenAndChangeDecorationModeAfterInitialCommit();
|
||||
void testChangeDecorationModeAfterInitialCommit();
|
||||
void testModal();
|
||||
void testCloseModal();
|
||||
void testCloseInactiveModal();
|
||||
};
|
||||
|
||||
void TestXdgShellWindow::testXdgPopupReactive_data()
|
||||
|
@ -203,7 +209,7 @@ void TestXdgShellWindow::initTestCase()
|
|||
|
||||
void TestXdgShellWindow::init()
|
||||
{
|
||||
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat | Test::AdditionalWaylandInterface::XdgDecorationV1 | Test::AdditionalWaylandInterface::AppMenu));
|
||||
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat | Test::AdditionalWaylandInterface::XdgDecorationV1 | Test::AdditionalWaylandInterface::AppMenu | Test::AdditionalWaylandInterface::XdgDialogV1));
|
||||
QVERIFY(Test::waitForWaylandPointer());
|
||||
|
||||
workspace()->setActiveOutput(QPoint(640, 512));
|
||||
|
@ -475,7 +481,7 @@ void TestXdgShellWindow::testSendFullScreenWindowToAnotherOutput()
|
|||
QCOMPARE(window->output(), outputs[0]);
|
||||
|
||||
// Send the window to another output.
|
||||
workspace()->sendWindowToOutput(window, outputs[1]);
|
||||
window->sendToOutput(outputs[1]);
|
||||
QCOMPARE(window->isFullScreen(), true);
|
||||
QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
|
||||
QCOMPARE(window->fullscreenGeometryRestore(), QRectF(1280 + 10, 20, 100, 50));
|
||||
|
@ -708,52 +714,6 @@ void TestXdgShellWindow::testCaptionSimplified()
|
|||
QCOMPARE(window->caption(), origTitle.simplified());
|
||||
}
|
||||
|
||||
void TestXdgShellWindow::testCaptionMultipleWindows()
|
||||
{
|
||||
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
|
||||
shellSurface->set_title(QStringLiteral("foo"));
|
||||
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(window);
|
||||
QCOMPARE(window->caption(), QStringLiteral("foo"));
|
||||
QCOMPARE(window->captionNormal(), QStringLiteral("foo"));
|
||||
QCOMPARE(window->captionSuffix(), QString());
|
||||
|
||||
std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
|
||||
shellSurface2->set_title(QStringLiteral("foo"));
|
||||
auto c2 = Test::renderAndWaitForShown(surface2.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(c2);
|
||||
QCOMPARE(c2->caption(), QStringLiteral("foo <2>"));
|
||||
QCOMPARE(c2->captionNormal(), QStringLiteral("foo"));
|
||||
QCOMPARE(c2->captionSuffix(), QStringLiteral(" <2>"));
|
||||
|
||||
std::unique_ptr<KWayland::Client::Surface> surface3(Test::createSurface());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface3(Test::createXdgToplevelSurface(surface3.get()));
|
||||
shellSurface3->set_title(QStringLiteral("foo"));
|
||||
auto c3 = Test::renderAndWaitForShown(surface3.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(c3);
|
||||
QCOMPARE(c3->caption(), QStringLiteral("foo <3>"));
|
||||
QCOMPARE(c3->captionNormal(), QStringLiteral("foo"));
|
||||
QCOMPARE(c3->captionSuffix(), QStringLiteral(" <3>"));
|
||||
|
||||
std::unique_ptr<KWayland::Client::Surface> surface4(Test::createSurface());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface4(Test::createXdgToplevelSurface(surface4.get()));
|
||||
shellSurface4->set_title(QStringLiteral("bar"));
|
||||
auto c4 = Test::renderAndWaitForShown(surface4.get(), QSize(100, 50), Qt::blue);
|
||||
QVERIFY(c4);
|
||||
QCOMPARE(c4->caption(), QStringLiteral("bar"));
|
||||
QCOMPARE(c4->captionNormal(), QStringLiteral("bar"));
|
||||
QCOMPARE(c4->captionSuffix(), QString());
|
||||
QSignalSpy captionChangedSpy(c4, &Window::captionChanged);
|
||||
shellSurface4->set_title(QStringLiteral("foo"));
|
||||
QVERIFY(captionChangedSpy.wait());
|
||||
QCOMPARE(captionChangedSpy.count(), 1);
|
||||
QCOMPARE(c4->caption(), QStringLiteral("foo <4>"));
|
||||
QCOMPARE(c4->captionNormal(), QStringLiteral("foo"));
|
||||
QCOMPARE(c4->captionSuffix(), QStringLiteral(" <4>"));
|
||||
}
|
||||
|
||||
void TestXdgShellWindow::testUnresponsiveWindow_data()
|
||||
{
|
||||
QTest::addColumn<QString>("shellInterface"); // see env selection in qwaylandintegration.cpp
|
||||
|
@ -1835,13 +1795,321 @@ void TestXdgShellWindow::testSendMaximizedWindowToAnotherOutput()
|
|||
QCOMPARE(window->output(), outputs[0]);
|
||||
|
||||
// Send the window to another output.
|
||||
workspace()->sendWindowToOutput(window, outputs[1]);
|
||||
window->sendToOutput(outputs[1]);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeFull);
|
||||
QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
|
||||
QCOMPARE(window->geometryRestore(), QRectF(1280 + 10, 20, 100, 50));
|
||||
QCOMPARE(window->output(), outputs[1]);
|
||||
}
|
||||
|
||||
void TestXdgShellWindow::testInteractiveMoveUnmaximizeFull()
|
||||
{
|
||||
// This test verifies that a maximized xdg-toplevel is going to be properly unmaximized when it's dragged.
|
||||
|
||||
// Create the window.
|
||||
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
|
||||
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
|
||||
// Wait for the compositor to send a configure event with the activated state.
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
|
||||
// Make the window maximized.
|
||||
const QRectF originalGeometry = window->frameGeometry();
|
||||
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
|
||||
window->maximize(MaximizeFull);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(window->maximizeMode(), MaximizeFull);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
|
||||
|
||||
// Start interactive move.
|
||||
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
|
||||
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
|
||||
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
|
||||
const qreal xOffset = 0.25;
|
||||
const qreal yOffset = 0.5;
|
||||
quint32 timestamp = 0;
|
||||
Test::pointerMotion(QPointF(window->x() + window->width() * xOffset, window->y() + window->height() * yOffset), timestamp++);
|
||||
window->performMouseCommand(Options::MouseMove, input()->pointer()->pos());
|
||||
QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeFull);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
|
||||
|
||||
// Move the window to unmaximize it.
|
||||
const QRectF maximizedGeometry = window->frameGeometry();
|
||||
Test::pointerMotionRelative(QPointF(0, 100), timestamp++);
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeFull);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), maximizedGeometry);
|
||||
|
||||
// Move the window a tiny bit more.
|
||||
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeFull);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), maximizedGeometry);
|
||||
|
||||
// Render the window at the new size.
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(window->maximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), QRectF(input()->pointer()->pos() - QPointF(originalGeometry.width() * xOffset, originalGeometry.height() * yOffset), originalGeometry.size()));
|
||||
|
||||
// Move the window again.
|
||||
const QRectF normalGeometry = window->frameGeometry();
|
||||
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), normalGeometry.translated(0, 10));
|
||||
|
||||
// Finish interactive move.
|
||||
window->keyPressEvent(Qt::Key_Enter);
|
||||
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
|
||||
}
|
||||
|
||||
void TestXdgShellWindow::testInteractiveMoveUnmaximizeInitiallyFull()
|
||||
{
|
||||
// This test verifies that an initially maximized xdg-toplevel will be properly unmaximized when it's dragged.
|
||||
|
||||
// Create the window.
|
||||
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), [](Test::XdgToplevel *toplevel) {
|
||||
toplevel->set_maximized();
|
||||
}));
|
||||
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
|
||||
// Wait for the compositor to send a configure event with the activated state.
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
|
||||
// Start interactive move.
|
||||
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
|
||||
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
|
||||
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
|
||||
const qreal xOffset = 0.25;
|
||||
const qreal yOffset = 0.5;
|
||||
quint32 timestamp = 0;
|
||||
Test::pointerMotion(QPointF(window->x() + window->width() * xOffset, window->y() + window->height() * yOffset), timestamp++);
|
||||
window->performMouseCommand(Options::MouseMove, input()->pointer()->pos());
|
||||
QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeFull);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
|
||||
|
||||
// Move the window to unmaximize it.
|
||||
const QRectF maximizedGeometry = window->frameGeometry();
|
||||
Test::pointerMotionRelative(QPointF(0, 100), timestamp++);
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeFull);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), maximizedGeometry);
|
||||
|
||||
// Move the window a tiny bit more.
|
||||
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 0);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeFull);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), maximizedGeometry);
|
||||
|
||||
// Render the window at the new size.
|
||||
const QSize restoredSize(100, 50);
|
||||
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(0, 0));
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), restoredSize, Qt::red);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(window->maximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), QRectF(input()->pointer()->pos() - QPointF(restoredSize.width() * xOffset, restoredSize.height() * yOffset), restoredSize));
|
||||
|
||||
// Move the window again.
|
||||
const QRectF normalGeometry = window->frameGeometry();
|
||||
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), normalGeometry.translated(0, 10));
|
||||
|
||||
// Finish interactive move.
|
||||
window->keyPressEvent(Qt::Key_Enter);
|
||||
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
|
||||
}
|
||||
|
||||
void TestXdgShellWindow::testInteractiveMoveUnmaximizeHorizontal()
|
||||
{
|
||||
// This test verifies that a maximized horizontally xdg-toplevel is going to be properly unmaximized when it's dragged horizontally.
|
||||
|
||||
// Create the window.
|
||||
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
|
||||
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
|
||||
// Wait for the compositor to send a configure event with the activated state.
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
|
||||
// Make the window maximized.
|
||||
const QRectF originalGeometry = window->frameGeometry();
|
||||
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
|
||||
window->maximize(MaximizeHorizontal);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeHorizontal);
|
||||
|
||||
// Start interactive move.
|
||||
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
|
||||
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
|
||||
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
|
||||
const qreal xOffset = 0.25;
|
||||
const qreal yOffset = 0.5;
|
||||
quint32 timestamp = 0;
|
||||
Test::pointerMotion(QPointF(window->x() + window->width() * xOffset, window->y() + window->height() * yOffset), timestamp++);
|
||||
window->performMouseCommand(Options::MouseMove, input()->pointer()->pos());
|
||||
QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeHorizontal);
|
||||
|
||||
// Move the window vertically, it's not going to be unmaximized.
|
||||
const QRectF maximizedGeometry = window->frameGeometry();
|
||||
Test::pointerMotionRelative(QPointF(0, 100), timestamp++);
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeHorizontal);
|
||||
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(0, 100));
|
||||
|
||||
// Move the window horizontally.
|
||||
Test::pointerMotionRelative(QPointF(100, 0), timestamp++);
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(0, 100));
|
||||
|
||||
// Move the window to the right a bit more.
|
||||
Test::pointerMotionRelative(QPointF(10, 0), timestamp++);
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(0, 100));
|
||||
|
||||
// Render the window at the new size.
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(window->maximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), QRectF(input()->pointer()->pos() - QPointF(originalGeometry.width() * xOffset, originalGeometry.height() * yOffset), originalGeometry.size()));
|
||||
|
||||
// Move the window again.
|
||||
const QRectF normalGeometry = window->frameGeometry();
|
||||
Test::pointerMotionRelative(QPointF(10, 0), timestamp++);
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), normalGeometry.translated(10, 0));
|
||||
|
||||
// Finish interactive move.
|
||||
window->keyPressEvent(Qt::Key_Enter);
|
||||
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
|
||||
}
|
||||
|
||||
void TestXdgShellWindow::testInteractiveMoveUnmaximizeVertical()
|
||||
{
|
||||
// This test verifies that a maximized vertically xdg-toplevel is going to be properly unmaximized when it's dragged vertically.
|
||||
|
||||
// Create the window.
|
||||
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
|
||||
std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
|
||||
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
|
||||
|
||||
// Wait for the compositor to send a configure event with the activated state.
|
||||
QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
|
||||
QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
|
||||
// Make the window maximized.
|
||||
const QRectF originalGeometry = window->frameGeometry();
|
||||
QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
|
||||
window->maximize(MaximizeVertical);
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(window->maximizeMode(), MaximizeVertical);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeVertical);
|
||||
|
||||
// Start interactive move.
|
||||
QSignalSpy interactiveMoveResizeStartedSpy(window, &Window::interactiveMoveResizeStarted);
|
||||
QSignalSpy interactiveMoveResizeSteppedSpy(window, &Window::interactiveMoveResizeStepped);
|
||||
QSignalSpy interactiveMoveResizeFinishedSpy(window, &Window::interactiveMoveResizeFinished);
|
||||
const qreal xOffset = 0.25;
|
||||
const qreal yOffset = 0.5;
|
||||
quint32 timestamp = 0;
|
||||
Test::pointerMotion(QPointF(window->x() + window->width() * xOffset, window->y() + window->height() * yOffset), timestamp++);
|
||||
window->performMouseCommand(Options::MouseMove, input()->pointer()->pos());
|
||||
QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeVertical);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeVertical);
|
||||
|
||||
// Move the window to the right, it's not going to be unmaximized.
|
||||
const QRectF maximizedGeometry = window->frameGeometry();
|
||||
Test::pointerMotionRelative(QPointF(100, 0), timestamp++);
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeVertical);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeVertical);
|
||||
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(100, 0));
|
||||
|
||||
// Move the window vertically.
|
||||
Test::pointerMotionRelative(QPointF(0, 100), timestamp++);
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeVertical);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(100, 0));
|
||||
|
||||
// Move the window down a bit more.
|
||||
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeVertical);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), maximizedGeometry.translated(100, 0));
|
||||
|
||||
// Render the window at the new size.
|
||||
QVERIFY(surfaceConfigureRequestedSpy.wait());
|
||||
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
|
||||
Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
QCOMPARE(window->maximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), QRectF(input()->pointer()->pos() - QPointF(originalGeometry.width() * xOffset, originalGeometry.height() * yOffset), originalGeometry.size()));
|
||||
|
||||
// Move the window again.
|
||||
const QRectF normalGeometry = window->frameGeometry();
|
||||
Test::pointerMotionRelative(QPointF(0, 10), timestamp++);
|
||||
QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 2);
|
||||
QCOMPARE(window->maximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
|
||||
QCOMPARE(window->frameGeometry(), normalGeometry.translated(0, 10));
|
||||
|
||||
// Finish interactive move.
|
||||
window->keyPressEvent(Qt::Key_Enter);
|
||||
QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
|
||||
}
|
||||
|
||||
void TestXdgShellWindow::testMaximizeAndChangeDecorationModeAfterInitialCommit()
|
||||
{
|
||||
// Ideally, the app would initialize the xdg-toplevel surface before the initial commit, but
|
||||
|
@ -1925,5 +2193,134 @@ void TestXdgShellWindow::testChangeDecorationModeAfterInitialCommit()
|
|||
QCOMPARE(decorationConfigureRequestedSpy.last().at(0).value<Test::XdgToplevelDecorationV1::mode>(), Test::XdgToplevelDecorationV1::mode_client_side);
|
||||
}
|
||||
|
||||
void TestXdgShellWindow::testModal()
|
||||
{
|
||||
auto parentSurface = Test::createSurface();
|
||||
auto parentToplevel = Test::createXdgToplevelSurface(parentSurface.get());
|
||||
auto parentWindow = Test::renderAndWaitForShown(parentSurface.get(), {200, 200}, Qt::cyan);
|
||||
QVERIFY(parentWindow);
|
||||
|
||||
auto childSurface = Test::createSurface();
|
||||
auto childToplevel = Test::createXdgToplevelSurface(childSurface.get(), [&parentToplevel](Test::XdgToplevel *toplevel) {
|
||||
toplevel->set_parent(parentToplevel->object());
|
||||
});
|
||||
auto childWindow = Test::renderAndWaitForShown(childSurface.get(), {200, 200}, Qt::yellow);
|
||||
QVERIFY(childWindow);
|
||||
QVERIFY(!childWindow->isModal());
|
||||
QCOMPARE(childWindow->transientFor(), parentWindow);
|
||||
|
||||
auto dialog = Test::createXdgDialogV1(childToplevel.get());
|
||||
QVERIFY(Test::waylandSync());
|
||||
QVERIFY(dialog);
|
||||
QVERIFY(!childWindow->isModal());
|
||||
|
||||
QSignalSpy modalChangedSpy(childWindow, &Window::modalChanged);
|
||||
|
||||
dialog->set_modal();
|
||||
Test::flushWaylandConnection();
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
QVERIFY(childWindow->isModal());
|
||||
|
||||
dialog->unset_modal();
|
||||
Test::flushWaylandConnection();
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
QVERIFY(!childWindow->isModal());
|
||||
|
||||
dialog->set_modal();
|
||||
Test::flushWaylandConnection();
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
Workspace::self()->activateWindow(parentWindow);
|
||||
QCOMPARE(Workspace::self()->activeWindow(), childWindow);
|
||||
|
||||
dialog.reset();
|
||||
Test::flushWaylandConnection();
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
QVERIFY(!childWindow->isModal());
|
||||
}
|
||||
|
||||
void TestXdgShellWindow::testCloseModal()
|
||||
{
|
||||
// This test verifies that the parent window will be activated when an active modal dialog is closed.
|
||||
|
||||
// Create a parent and a child windows.
|
||||
auto parentSurface = Test::createSurface();
|
||||
auto parentToplevel = Test::createXdgToplevelSurface(parentSurface.get());
|
||||
auto parent = Test::renderAndWaitForShown(parentSurface.get(), {200, 200}, Qt::cyan);
|
||||
QVERIFY(parent);
|
||||
|
||||
auto childSurface = Test::createSurface();
|
||||
auto childToplevel = Test::createXdgToplevelSurface(childSurface.get(), [&parentToplevel](Test::XdgToplevel *toplevel) {
|
||||
toplevel->set_parent(parentToplevel->object());
|
||||
});
|
||||
auto child = Test::renderAndWaitForShown(childSurface.get(), {200, 200}, Qt::yellow);
|
||||
QVERIFY(child);
|
||||
QVERIFY(!child->isModal());
|
||||
QCOMPARE(child->transientFor(), parent);
|
||||
|
||||
// Set modal state.
|
||||
auto dialog = Test::createXdgDialogV1(childToplevel.get());
|
||||
QSignalSpy modalChangedSpy(child, &Window::modalChanged);
|
||||
dialog->set_modal();
|
||||
Test::flushWaylandConnection();
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
QVERIFY(child->isModal());
|
||||
QCOMPARE(workspace()->activeWindow(), child);
|
||||
|
||||
// Close the child.
|
||||
QSignalSpy childClosedSpy(child, &Window::closed);
|
||||
childToplevel.reset();
|
||||
childSurface.reset();
|
||||
dialog.reset();
|
||||
Test::flushWaylandConnection();
|
||||
QVERIFY(childClosedSpy.wait());
|
||||
QCOMPARE(workspace()->activeWindow(), parent);
|
||||
}
|
||||
|
||||
void TestXdgShellWindow::testCloseInactiveModal()
|
||||
{
|
||||
// This test verifies that the parent window will not be activated when an inactive modal dialog is closed.
|
||||
|
||||
// Create a parent and a child windows.
|
||||
auto parentSurface = Test::createSurface();
|
||||
auto parentToplevel = Test::createXdgToplevelSurface(parentSurface.get());
|
||||
auto parent = Test::renderAndWaitForShown(parentSurface.get(), {200, 200}, Qt::cyan);
|
||||
QVERIFY(parent);
|
||||
|
||||
auto childSurface = Test::createSurface();
|
||||
auto childToplevel = Test::createXdgToplevelSurface(childSurface.get(), [&parentToplevel](Test::XdgToplevel *toplevel) {
|
||||
toplevel->set_parent(parentToplevel->object());
|
||||
});
|
||||
auto child = Test::renderAndWaitForShown(childSurface.get(), {200, 200}, Qt::yellow);
|
||||
QVERIFY(child);
|
||||
QVERIFY(!child->isModal());
|
||||
QCOMPARE(child->transientFor(), parent);
|
||||
|
||||
// Set modal state.
|
||||
auto dialog = Test::createXdgDialogV1(childToplevel.get());
|
||||
QSignalSpy modalChangedSpy(child, &Window::modalChanged);
|
||||
dialog->set_modal();
|
||||
Test::flushWaylandConnection();
|
||||
QVERIFY(modalChangedSpy.wait());
|
||||
QVERIFY(child->isModal());
|
||||
QCOMPARE(workspace()->activeWindow(), child);
|
||||
|
||||
// Show another window.
|
||||
auto otherSurface = Test::createSurface();
|
||||
auto otherToplevel = Test::createXdgToplevelSurface(otherSurface.get());
|
||||
auto otherWindow = Test::renderAndWaitForShown(otherSurface.get(), {200, 200}, Qt::magenta);
|
||||
QVERIFY(otherWindow);
|
||||
workspace()->setActiveWindow(otherWindow);
|
||||
QCOMPARE(workspace()->activeWindow(), otherWindow);
|
||||
|
||||
// Close the child.
|
||||
QSignalSpy childClosedSpy(child, &Window::closed);
|
||||
childToplevel.reset();
|
||||
childSurface.reset();
|
||||
dialog.reset();
|
||||
Test::flushWaylandConnection();
|
||||
QVERIFY(childClosedSpy.wait());
|
||||
QCOMPARE(workspace()->activeWindow(), otherWindow);
|
||||
}
|
||||
|
||||
WAYLANDTEST_MAIN(TestXdgShellWindow)
|
||||
#include "xdgshellwindow_test.moc"
|
||||
|
|
|
@ -19,6 +19,8 @@ class XkbTest : public QObject
|
|||
private Q_SLOTS:
|
||||
void testToQtKey_data();
|
||||
void testToQtKey();
|
||||
void testFromQtKey_data();
|
||||
void testFromQtKey();
|
||||
};
|
||||
|
||||
// from kwindowsystem/src/platforms/xcb/kkeyserver.cpp
|
||||
|
@ -498,5 +500,31 @@ void XkbTest::testToQtKey()
|
|||
QTEST(xkb.toQtKey(keySym), "qt");
|
||||
}
|
||||
|
||||
void XkbTest::testFromQtKey_data()
|
||||
{
|
||||
QTest::addColumn<xkb_keysym_t>("keySym");
|
||||
QTest::addColumn<int>("keyQt");
|
||||
for (std::size_t i = 0; i < sizeof(g_rgQtToSymX) / sizeof(TransKey); i++) {
|
||||
const QByteArray row = QByteArray::number(g_rgQtToSymX[i].keySymX, 16);
|
||||
QTest::newRow(row.constData()) << g_rgQtToSymX[i].keySymX << (g_rgQtToSymX[i].keySymQt | g_rgQtToSymX[i].modifiers).toCombined();
|
||||
}
|
||||
}
|
||||
|
||||
void XkbTest::testFromQtKey()
|
||||
{
|
||||
Xkb xkb;
|
||||
QFETCH(xkb_keysym_t, keySym);
|
||||
QFETCH(int, keyQt);
|
||||
QList<xkb_keysym_t> keys = xkb.keysymsFromQtKey(keyQt);
|
||||
|
||||
QEXPECT_FAIL(QByteArray::number(XKB_KEY_Hyper_L, 16), "keysymsFromQtKey doesn't map hyper to meta", Continue);
|
||||
QEXPECT_FAIL(QByteArray::number(XKB_KEY_Hyper_R, 16), "keysymsFromQtKey doesn't map hyper to meta", Continue);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 7, 1)
|
||||
QEXPECT_FAIL(QByteArray::number(XKB_KEY_KP_Equal, 16), "KP_Equal is not correctly identified as keypad key in Qt 6.7.0; fixed in 6.7.1: https://codereview.qt-project.org/c/qt/qtbase/+/546889", Continue);
|
||||
#endif
|
||||
|
||||
QVERIFY(keys.contains(keySym));
|
||||
}
|
||||
|
||||
QTEST_MAIN(XkbTest)
|
||||
#include "test_xkb.moc"
|
||||
|
|
|
@ -17,6 +17,11 @@ uint32_t Xcb::toXNative(qreal value)
|
|||
return value;
|
||||
}
|
||||
|
||||
QRect Xcb::toXNative(const QRectF &rect)
|
||||
{
|
||||
return rect.toRect();
|
||||
}
|
||||
|
||||
qreal Xcb::fromXNative(int value)
|
||||
{
|
||||
return value;
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# SPDX-FileCopyrightText: 2024 David Redondo <kde@david-redono.de>
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(PKG_Libeis QUIET libeis-1.0)
|
||||
|
||||
find_path(Libeis_INCLUDE_DIR
|
||||
NAMES libeis.h
|
||||
HINTS ${PKG_Libeis_INCLUDE_DIRS}
|
||||
)
|
||||
find_library(Libeis_LIBRARY
|
||||
NAMES eis
|
||||
PATHS ${PKG_Libeis_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
set(Libeis_VERSION ${PKG_Libeis_VERSION})
|
||||
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Libeis-1.0
|
||||
FOUND_VAR Libeis-1.0_FOUND
|
||||
REQUIRED_VARS
|
||||
Libeis_LIBRARY
|
||||
Libeis_INCLUDE_DIR
|
||||
VERSION_VAR Libeis_VERSION
|
||||
)
|
||||
|
||||
if(Libeis-1.0_FOUND AND NOT TARGET Libeis::Libeis)
|
||||
add_library(Libeis::Libeis UNKNOWN IMPORTED)
|
||||
set_target_properties(Libeis::Libeis PROPERTIES
|
||||
IMPORTED_LOCATION "${Libeis_LIBRARY}"
|
||||
INTERFACE_COMPILE_OPTIONS "${PKG_Libeis_CFLAGS_OTHER}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Libeis_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(Libeis_INCLUDE_DIR Libeis_LIBRARY)
|
|
@ -12,6 +12,8 @@
|
|||
# The version of Xwayland
|
||||
# ``Xwayland_HAVE_LISTENFD``
|
||||
# True if (the requested version of) Xwayland has -listenfd option
|
||||
# ``Xwayland_HAVE_ENABLE_EI_PORTAL``
|
||||
# True if (the requested version of) Xwayland has -enable-ei-portal option
|
||||
|
||||
#=============================================================================
|
||||
# SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
@ -25,6 +27,7 @@ pkg_check_modules(PKG_xwayland QUIET xwayland)
|
|||
|
||||
set(Xwayland_VERSION ${PKG_xwayland_VERSION})
|
||||
pkg_get_variable(Xwayland_HAVE_LISTENFD xwayland have_listenfd)
|
||||
pkg_get_variable(Xwayland_HAVE_ENABLE_EI_PORTAL xwayland have_enable_ei_portal)
|
||||
|
||||
find_program(Xwayland_EXECUTABLE NAMES Xwayland)
|
||||
find_package_handle_standard_args(Xwayland
|
||||
|
|
Before Width: | Height: | Size: 380 B After Width: | Height: | Size: 379 B |
Before Width: | Height: | Size: 611 B After Width: | Height: | Size: 609 B |
Before Width: | Height: | Size: 877 B After Width: | Height: | Size: 874 B |
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 412 B |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 178 KiB After Width: | Height: | Size: 152 KiB |
Before Width: | Height: | Size: 512 B After Width: | Height: | Size: 491 B |
Before Width: | Height: | Size: 375 B After Width: | Height: | Size: 354 B |
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 134 KiB |
|
@ -284,15 +284,6 @@ This controls the behavior of window focus with multiple screens. Note that thes
|
|||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry>
|
||||
<term><guilabel>Active screen follows mouse</guilabel></term>
|
||||
<listitem>
|
||||
<para>
|
||||
When this option is enabled, the active screen (where new windows appear, for example) is the screen containing the mouse pointer. When disabled, the active screen is the screen containing the focused window.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><guilabel>Separate screen focus</guilabel></term>
|
||||
<listitem>
|
||||
|
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 123 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 53 KiB |