mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-02-19 01:09:58 +00:00
Compare commits
920 Commits
B3-next
...
Release/B5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3df7d30cd5 | ||
|
|
74444f8eed | ||
|
|
6c43650cd3 | ||
|
|
181ee2b4f5 | ||
|
|
5bef733647 | ||
|
|
22cef47bc4 | ||
|
|
5b25e20a2e | ||
|
|
bb5c7a222c | ||
|
|
39ea6b0587 | ||
|
|
ddc6cb5277 | ||
|
|
b13a79880b | ||
|
|
53fdc2e148 | ||
|
|
9872d2e407 | ||
|
|
3ccf6de868 | ||
|
|
12461196c3 | ||
|
|
15ec80e80d | ||
|
|
d6eb72331c | ||
|
|
eea0ced627 | ||
|
|
94684324f4 | ||
|
|
194afa2d75 | ||
|
|
d96b2ef1fb | ||
|
|
ad40ea4195 | ||
|
|
65948034dd | ||
|
|
9d47ca4f12 | ||
|
|
27c7054505 | ||
|
|
02b59ba8f7 | ||
|
|
a5727262cd | ||
|
|
43545a4e17 | ||
|
|
adbe333414 | ||
|
|
5f80ce91e8 | ||
|
|
b6fa296d5a | ||
|
|
2e170ad06f | ||
|
|
a6720db749 | ||
|
|
aa6cf72718 | ||
|
|
15066c7345 | ||
|
|
88a95aeab0 | ||
|
|
abd6502c9d | ||
|
|
50feacad13 | ||
|
|
58f83da7bb | ||
|
|
3e9a21d3b9 | ||
|
|
9780f51558 | ||
|
|
bc022c77f4 | ||
|
|
8167ef2c4a | ||
|
|
a21eee26ab | ||
|
|
7075fe2c54 | ||
|
|
e82f8911a6 | ||
|
|
b515fa80d5 | ||
|
|
affc3f51f8 | ||
|
|
6078b11200 | ||
|
|
68a9504366 | ||
|
|
67ea8e06ba | ||
|
|
9d71655273 | ||
|
|
2f0b97a487 | ||
|
|
f69b869282 | ||
|
|
bc7cbf1173 | ||
|
|
ad6e3f96e6 | ||
|
|
fe712b7ec9 | ||
|
|
6a898c1e7c | ||
|
|
0990689df5 | ||
|
|
db0e03328c | ||
|
|
edf1e341da | ||
|
|
9969896876 | ||
|
|
9c5e34df0f | ||
|
|
dca5da02a0 | ||
|
|
c5f71d18c4 | ||
|
|
84a43fb651 | ||
|
|
2858ad3f7e | ||
|
|
9ddd260b22 | ||
|
|
bc34dc9e24 | ||
|
|
edc9825c04 | ||
|
|
70a751b58d | ||
|
|
fc037ccc95 | ||
|
|
74418106de | ||
|
|
c9e8de334a | ||
|
|
13fabc2e4a | ||
|
|
8dcf7af791 | ||
|
|
2d858da0f1 | ||
|
|
ee211803e4 | ||
|
|
c3d2ad92c5 | ||
|
|
7ce7dec272 | ||
|
|
12321a8880 | ||
|
|
148ab0278e | ||
|
|
d60dcb718b | ||
|
|
e914e56c48 | ||
|
|
24fa580519 | ||
|
|
35c57a862e | ||
|
|
e0c1394c33 | ||
|
|
6370350006 | ||
|
|
ad4b40fad6 | ||
|
|
3f72de78da | ||
|
|
4a76401c34 | ||
|
|
072c54977e | ||
|
|
5c7f168370 | ||
|
|
778d27f08a | ||
|
|
12840a8324 | ||
|
|
1f24ab0742 | ||
|
|
57d220a43b | ||
|
|
bc65de5987 | ||
|
|
df4a964496 | ||
|
|
72f3a9f3cf | ||
|
|
09d93abf1a | ||
|
|
2aa236e1f9 | ||
|
|
3cefe9f9b5 | ||
|
|
e249106ddf | ||
|
|
ab3738624f | ||
|
|
25ed3632f7 | ||
|
|
3adb7ca4b2 | ||
|
|
5fbb34f8f3 | ||
|
|
20acb9002a | ||
|
|
4ee83a2b31 | ||
|
|
909a9a903f | ||
|
|
ce091fd4e4 | ||
|
|
49725c9ea4 | ||
|
|
753b44e34f | ||
|
|
1b58f2592c | ||
|
|
e9bf225c75 | ||
|
|
f287b4625d | ||
|
|
43b0e80f93 | ||
|
|
92155de98d | ||
|
|
94d7ed300e | ||
|
|
924bd9e543 | ||
|
|
c4eadda389 | ||
|
|
f563e67e19 | ||
|
|
d7e4536e57 | ||
|
|
ab033d84b1 | ||
|
|
286da11172 | ||
|
|
ac926e4458 | ||
|
|
26d2feb4d8 | ||
|
|
ffabdd348c | ||
|
|
5fc561fa63 | ||
|
|
977a4f6323 | ||
|
|
8514f35474 | ||
|
|
e05bb196f0 | ||
|
|
c7666b065a | ||
|
|
0faafbff47 | ||
|
|
34fb2f9076 | ||
|
|
a6112feddb | ||
|
|
815aac28fb | ||
|
|
79a9127c04 | ||
|
|
31249da533 | ||
|
|
f4df690d9f | ||
|
|
239cb6a92b | ||
|
|
876a4125bf | ||
|
|
ffbf6cd8b4 | ||
|
|
39e42ba735 | ||
|
|
3345ff8448 | ||
|
|
44850f1699 | ||
|
|
8a2ae6860e | ||
|
|
1717555187 | ||
|
|
dc27638025 | ||
|
|
c85cc7d668 | ||
|
|
311f7241c6 | ||
|
|
b7b93f624c | ||
|
|
084837b936 | ||
|
|
4adb425337 | ||
|
|
e11246d46e | ||
|
|
f0beedb5ba | ||
|
|
bbd39b8185 | ||
|
|
f0624ccf89 | ||
|
|
e22a070dd3 | ||
|
|
0c27111260 | ||
|
|
5225d2e97f | ||
|
|
e5f2b3079e | ||
|
|
e6df0acad9 | ||
|
|
ba527761ef | ||
|
|
ceff9dca9b | ||
|
|
e040b88bf0 | ||
|
|
7c7eff8dba | ||
|
|
377757e743 | ||
|
|
53b4b4818b | ||
|
|
07d3d6cbe7 | ||
|
|
b71838a530 | ||
|
|
b118c3b681 | ||
|
|
e5e76d784e | ||
|
|
99761b195f | ||
|
|
24e0343156 | ||
|
|
0b70aa49d0 | ||
|
|
1e2caf4c9f | ||
|
|
28eae3bd86 | ||
|
|
4b3aaa7e0c | ||
|
|
164dd00490 | ||
|
|
9bd205a527 | ||
|
|
c0fa6c414c | ||
|
|
11a661ce3a | ||
|
|
c246b4a719 | ||
|
|
17617cc421 | ||
|
|
e1a4401ffa | ||
|
|
4b3a79c110 | ||
|
|
3c3c0f70be | ||
|
|
2d470b8deb | ||
|
|
9aa0d3ddab | ||
|
|
429620c48b | ||
|
|
5a906131eb | ||
|
|
1021c9ce92 | ||
|
|
ce3f11fd40 | ||
|
|
bb91b41c64 | ||
|
|
520460669c | ||
|
|
1c7d14169e | ||
|
|
ccda264648 | ||
|
|
2ff32b230e | ||
|
|
2dbd4f168e | ||
|
|
4ecf749f7e | ||
|
|
2de9e3e9be | ||
|
|
81c38e825c | ||
|
|
fd4a4114e6 | ||
|
|
cdda89cef7 | ||
|
|
104141eec1 | ||
|
|
4d907cecab | ||
|
|
b7d3bbbd82 | ||
|
|
8a5efef622 | ||
|
|
14ad83c6b8 | ||
|
|
1c5620ba25 | ||
|
|
982b4e6625 | ||
|
|
c3f7327187 | ||
|
|
8f5afe1848 | ||
|
|
36073586e7 | ||
|
|
c89518ead4 | ||
|
|
7cd0c55847 | ||
|
|
2dd1ad53f8 | ||
|
|
d35c448058 | ||
|
|
3a00277e93 | ||
|
|
0f6f89fa5b | ||
|
|
bbd173000f | ||
|
|
5b2fce0830 | ||
|
|
96738ab9d0 | ||
|
|
7045760490 | ||
|
|
9414449408 | ||
|
|
5f3bd778c0 | ||
|
|
f66486b0c7 | ||
|
|
2c02e6c4a0 | ||
|
|
94de061587 | ||
|
|
16adbab5d4 | ||
|
|
579f998519 | ||
|
|
85a96d1e06 | ||
|
|
b2630024a7 | ||
|
|
4f7ce91e7f | ||
|
|
a9241f6710 | ||
|
|
be1306f91a | ||
|
|
10ee6cd031 | ||
|
|
e5d252290d | ||
|
|
712dcee07f | ||
|
|
dda927da18 | ||
|
|
717b90366b | ||
|
|
a76b274e1a | ||
|
|
074341e421 | ||
|
|
acd5ce51db | ||
|
|
d3ea9662bf | ||
|
|
e945955d13 | ||
|
|
566c89e9d8 | ||
|
|
8c18817e2d | ||
|
|
35bd641d2a | ||
|
|
117e88c240 | ||
|
|
b3173bdddc | ||
|
|
61a4b0744d | ||
|
|
6387bf2d2e | ||
|
|
fe6339fc77 | ||
|
|
3f8c7c8d0d | ||
|
|
543c97987b | ||
|
|
06da52acfc | ||
|
|
5a2f34d71c | ||
|
|
8b2db071d8 | ||
|
|
3a1a9121eb | ||
|
|
f80b67bc50 | ||
|
|
fe823b6172 | ||
|
|
c4c60fd330 | ||
|
|
5a5b867c73 | ||
|
|
73f125dcc7 | ||
|
|
9bded74543 | ||
|
|
7e982a6658 | ||
|
|
604b44d6d8 | ||
|
|
22bbc2457e | ||
|
|
f0ea882165 | ||
|
|
f78154d282 | ||
|
|
cd5ecf3e5a | ||
|
|
a850a1b51b | ||
|
|
6e28d7a4a5 | ||
|
|
4a06f7cfd5 | ||
|
|
c1a362f8d3 | ||
|
|
9f4afcd944 | ||
|
|
4f1136d0cd | ||
|
|
127d3acd96 | ||
|
|
ccee347740 | ||
|
|
c3a143732c | ||
|
|
dc0b3a8d45 | ||
|
|
3b751a2017 | ||
|
|
230ce81eb8 | ||
|
|
e707f9d933 | ||
|
|
64ed383128 | ||
|
|
685499a0e0 | ||
|
|
705250f23d | ||
|
|
eb680086ef | ||
|
|
58964ce317 | ||
|
|
1128eb0e84 | ||
|
|
e41cbf5f32 | ||
|
|
f2b8ff9e8d | ||
|
|
cc3494437a | ||
|
|
88eada3494 | ||
|
|
37faccd014 | ||
|
|
30e6a258ad | ||
|
|
d24bc75519 | ||
|
|
92de467edc | ||
|
|
9b1d03fcfe | ||
|
|
4eda01949d | ||
|
|
062d18d5fa | ||
|
|
04a54598b3 | ||
|
|
79dcc6d4f1 | ||
|
|
57a74c156b | ||
|
|
6882e5c59f | ||
|
|
f7f8060447 | ||
|
|
53461d7515 | ||
|
|
9b87f4ba5e | ||
|
|
5e13549f74 | ||
|
|
87a21f5f5e | ||
|
|
9246e00163 | ||
|
|
1fd726eed7 | ||
|
|
bc7e59c9d7 | ||
|
|
179eaef29d | ||
|
|
f50ef4c23c | ||
|
|
86d6b67337 | ||
|
|
30ad28ffd1 | ||
|
|
69f6532b8d | ||
|
|
91d1b8d2cd | ||
|
|
baf9661530 | ||
|
|
2141046da9 | ||
|
|
266ad27998 | ||
|
|
f4a925a750 | ||
|
|
30ed563504 | ||
|
|
d347b28481 | ||
|
|
f8ae291090 | ||
|
|
45d1f27fb4 | ||
|
|
1a8267d55a | ||
|
|
b822e255d8 | ||
|
|
037b76750a | ||
|
|
e949f2f8d2 | ||
|
|
88c91d4752 | ||
|
|
3d7dbd6371 | ||
|
|
b3db1ba10b | ||
|
|
16f68d6b1b | ||
|
|
64da3465b8 | ||
|
|
12d256c7c8 | ||
|
|
3e32e01c30 | ||
|
|
ac3677d9ae | ||
|
|
0462cee9db | ||
|
|
cab95c5eed | ||
|
|
4205e49786 | ||
|
|
e755f0befa | ||
|
|
3e08e7aafa | ||
|
|
9f6ad864ed | ||
|
|
ec56b2760a | ||
|
|
e5a138d854 | ||
|
|
ad256e0b00 | ||
|
|
162b1b93db | ||
|
|
0ec66ba210 | ||
|
|
3d29967a8d | ||
|
|
5d3c00717a | ||
|
|
dc7fd74327 | ||
|
|
5b26017a8a | ||
|
|
3651852430 | ||
|
|
6bd454f77f | ||
|
|
08f3ad504c | ||
|
|
2f8ebc29e8 | ||
|
|
2856928b57 | ||
|
|
afbee641b1 | ||
|
|
95bbd67dea | ||
|
|
a3de0b2a59 | ||
|
|
719fec0a45 | ||
|
|
2fa09dbd95 | ||
|
|
3f3430de3f | ||
|
|
68eaf46d7c | ||
|
|
65e550a61c | ||
|
|
4da0c64583 | ||
|
|
0603a55492 | ||
|
|
a37b527bbd | ||
|
|
0af558345f | ||
|
|
44f815409d | ||
|
|
6e7f39edee | ||
|
|
6c84c0eca6 | ||
|
|
d90e658e3b | ||
|
|
85f3a71dd5 | ||
|
|
38ddfc0b61 | ||
|
|
ff01a197f3 | ||
|
|
6c44bbb53e | ||
|
|
f3f0157d3c | ||
|
|
4e81c7f724 | ||
|
|
51b9cd4e5a | ||
|
|
ecf59ac7d9 | ||
|
|
0941bd0fe5 | ||
|
|
8ebaf92006 | ||
|
|
62cd5e9c57 | ||
|
|
9192e2039a | ||
|
|
1885e2093b | ||
|
|
d2c36b8449 | ||
|
|
80c9f7223a | ||
|
|
c15d0dc672 | ||
|
|
0f7fa32d12 | ||
|
|
6933c278ce | ||
|
|
7fc717a839 | ||
|
|
4e435e6199 | ||
|
|
366ec16a63 | ||
|
|
04c9694ffa | ||
|
|
f7682c289a | ||
|
|
4b4a75475a | ||
|
|
55703b61b7 | ||
|
|
8545d15c85 | ||
|
|
87aac8cf03 | ||
|
|
f9977332a6 | ||
|
|
2dca056526 | ||
|
|
dd31a7ef93 | ||
|
|
a25c93b28e | ||
|
|
c0aec7d8f4 | ||
|
|
03ed8b7304 | ||
|
|
504bf02855 | ||
|
|
4d9ab81ef4 | ||
|
|
f3413815a9 | ||
|
|
bae19cb130 | ||
|
|
db501f689f | ||
|
|
b8561cab0a | ||
|
|
50f9baedba | ||
|
|
5d5e4ede1a | ||
|
|
8d78a5aa95 | ||
|
|
14839dc54e | ||
|
|
7912d268e9 | ||
|
|
8907a990a1 | ||
|
|
891ee3e789 | ||
|
|
037788f562 | ||
|
|
13d9c84dc9 | ||
|
|
e23144aecd | ||
|
|
23c77e8508 | ||
|
|
f08e2ece93 | ||
|
|
f64310320a | ||
|
|
695b7b793c | ||
|
|
986f92d0db | ||
|
|
2e4614cbc4 | ||
|
|
16aa04d539 | ||
|
|
9d95154b85 | ||
|
|
f8e1ab8f31 | ||
|
|
2d74c93232 | ||
|
|
17687fdea3 | ||
|
|
77b3d45e0e | ||
|
|
37196f1f0e | ||
|
|
b3b71d6f02 | ||
|
|
90b90e667a | ||
|
|
a094fb8104 | ||
|
|
96bcfff28b | ||
|
|
0ad469178a | ||
|
|
b3ca872cef | ||
|
|
3baed05728 | ||
|
|
aed370c7ce | ||
|
|
b9a7ce17fe | ||
|
|
2e0f765190 | ||
|
|
66df99f5fd | ||
|
|
7c3c68b84b | ||
|
|
6109067275 | ||
|
|
3369536cb8 | ||
|
|
fcbd255e99 | ||
|
|
f49948506b | ||
|
|
a11a20411b | ||
|
|
5e2f1b3fac | ||
|
|
75a14b8b45 | ||
|
|
b0c1714777 | ||
|
|
361ead59d3 | ||
|
|
41c5688fca | ||
|
|
c8dc037e94 | ||
|
|
a213ee960a | ||
|
|
3043296e52 | ||
|
|
be9a16e8a2 | ||
|
|
5e2dd589a1 | ||
|
|
9959578cbe | ||
|
|
971e91238a | ||
|
|
3143dc1e84 | ||
|
|
8898496eba | ||
|
|
7eb00bd24c | ||
|
|
a098bab114 | ||
|
|
7f6fd02d06 | ||
|
|
75e57baf6c | ||
|
|
68d8d95266 | ||
|
|
7d78cba38c | ||
|
|
7801575d99 | ||
|
|
1104bd821b | ||
|
|
b8b70e772e | ||
|
|
64c906b801 | ||
|
|
72ccd44681 | ||
|
|
7ca5e14938 | ||
|
|
6b6b3b724a | ||
|
|
86b50cc8ab | ||
|
|
10a27e7a27 | ||
|
|
edabd1bae7 | ||
|
|
f1b1da60ea | ||
|
|
0402dd521a | ||
|
|
11a5864969 | ||
|
|
da28db2ca4 | ||
|
|
c991de7ccd | ||
|
|
d9a3b6523c | ||
|
|
ea2651e39b | ||
|
|
a980cd9406 | ||
|
|
1c58b3a087 | ||
|
|
065d90c3f7 | ||
|
|
6c64965703 | ||
|
|
134829cbf2 | ||
|
|
aff3bff8b0 | ||
|
|
2ea84cd07e | ||
|
|
996b9e7e7b | ||
|
|
009ae02e32 | ||
|
|
552a37122a | ||
|
|
6ed1f4662d | ||
|
|
5f5f497cbd | ||
|
|
120fe63c0f | ||
|
|
181b165a4b | ||
|
|
dafd7e7b42 | ||
|
|
d9cdc8d26c | ||
|
|
d0722349e6 | ||
|
|
2ef80a5d34 | ||
|
|
e7761abf3c | ||
|
|
3905834807 | ||
|
|
60a58d4d8d | ||
|
|
8c2a77e84e | ||
|
|
e35facbb15 | ||
|
|
8528969efd | ||
|
|
9dffde74b2 | ||
|
|
b39f38350f | ||
|
|
f4daa9f527 | ||
|
|
2c745db544 | ||
|
|
3b37898eb2 | ||
|
|
aa2ea05af9 | ||
|
|
7316c1c46c | ||
|
|
38cb348201 | ||
|
|
be664c49c8 | ||
|
|
f09738678e | ||
|
|
0c35d9b057 | ||
|
|
cb9774bbd2 | ||
|
|
dd0edc1394 | ||
|
|
be44249c05 | ||
|
|
efb5019176 | ||
|
|
5153d35bb5 | ||
|
|
036f16b9ef | ||
|
|
436986d182 | ||
|
|
5d053128ac | ||
|
|
b5c5ecc074 | ||
|
|
56308fcbd1 | ||
|
|
628bdab21b | ||
|
|
df0397b10b | ||
|
|
334bfeecea | ||
|
|
3cf0257f34 | ||
|
|
6382fc11af | ||
|
|
b26067b0a0 | ||
|
|
947eac52f6 | ||
|
|
4c60409aaf | ||
|
|
eb5c588af9 | ||
|
|
3b6ad957e3 | ||
|
|
4acbf2e9a0 | ||
|
|
50f7a1a99c | ||
|
|
515f08d2da | ||
|
|
58ab77d237 | ||
|
|
fdbdf6f167 | ||
|
|
30c4a4786b | ||
|
|
a34d3bbab4 | ||
|
|
2310920e79 | ||
|
|
85f34602f4 | ||
|
|
4b016b441c | ||
|
|
27e3be3778 | ||
|
|
4954687a52 | ||
|
|
bb60107a3b | ||
|
|
ed18ead1ff | ||
|
|
e58506f1a5 | ||
|
|
08293c8721 | ||
|
|
6389a06903 | ||
|
|
8cf444ef31 | ||
|
|
1c8af28f26 | ||
|
|
9b472d62a9 | ||
|
|
f5dfc264ba | ||
|
|
8a70efafb5 | ||
|
|
b1c26aaa95 | ||
|
|
1a88996c47 | ||
|
|
3400c2c141 | ||
|
|
80bc9604ba | ||
|
|
669148bca0 | ||
|
|
26df3579a3 | ||
|
|
3c0ebd54ec | ||
|
|
b877bab48f | ||
|
|
e6e07e8f3f | ||
|
|
3f7261d7d9 | ||
|
|
26f16a3734 | ||
|
|
88fc1a6d24 | ||
|
|
2fc1d3cae6 | ||
|
|
28a67cad0d | ||
|
|
2bb0602ebb | ||
|
|
d82333519c | ||
|
|
5421bd8b1d | ||
|
|
f0c7e9bdfa | ||
|
|
0525515bee | ||
|
|
f5ad14b109 | ||
|
|
323d321a77 | ||
|
|
56833edae7 | ||
|
|
d57b5a320e | ||
|
|
563ad18f4e | ||
|
|
b4dc021381 | ||
|
|
ebda52b18b | ||
|
|
16ee1a825c | ||
|
|
a4f5ce08b9 | ||
|
|
aa41e4d2ce | ||
|
|
b8effaf42c | ||
|
|
0cbc529640 | ||
|
|
a8d4668c4d | ||
|
|
2de9912ac0 | ||
|
|
2038517861 | ||
|
|
33bf668697 | ||
|
|
2736e37e4a | ||
|
|
e4e1451eaa | ||
|
|
7c872d2d9e | ||
|
|
ab31040d5f | ||
|
|
45e1b5bce0 | ||
|
|
42d8f31eba | ||
|
|
c6a6230a56 | ||
|
|
a14de25661 | ||
|
|
09893fd728 | ||
|
|
e87bc3a83e | ||
|
|
6da9428d85 | ||
|
|
15bc6a1509 | ||
|
|
a4bf3c8088 | ||
|
|
6472c28473 | ||
|
|
f49f2af6cd | ||
|
|
061b9ba6c2 | ||
|
|
d4f8426ae4 | ||
|
|
ad974cfa0a | ||
|
|
e1fae8927f | ||
|
|
9ab85fd0b8 | ||
|
|
e0c9a71cd8 | ||
|
|
d44d87ee7f | ||
|
|
2b3f31700b | ||
|
|
12cb3e512f | ||
|
|
92706caddc | ||
|
|
893b2500c2 | ||
|
|
ef2da1902e | ||
|
|
9ce4990793 | ||
|
|
f274bec8fc | ||
|
|
00eb26a34f | ||
|
|
e42747f4e3 | ||
|
|
5ed3301cf5 | ||
|
|
442ab318fd | ||
|
|
6b16bb3ea1 | ||
|
|
d7f9afb3ba | ||
|
|
80ab4b5393 | ||
|
|
69b20aee05 | ||
|
|
38a018ebfa | ||
|
|
6695ca3f34 | ||
|
|
eb357fa58a | ||
|
|
e32494f684 | ||
|
|
39ec32b2ef | ||
|
|
d8b37a8d81 | ||
|
|
df2f623599 | ||
|
|
73357988e6 | ||
|
|
03c247a9ff | ||
|
|
092ce61908 | ||
|
|
b9d7674b20 | ||
|
|
8e3df5a38f | ||
|
|
23f9855768 | ||
|
|
2e76c874cc | ||
|
|
41403286d1 | ||
|
|
c3bc5fb0ff | ||
|
|
94ae9a95d7 | ||
|
|
bcffd70270 | ||
|
|
f08163fd72 | ||
|
|
c9d469fb91 | ||
|
|
25c88a1c6c | ||
|
|
7decb58bf7 | ||
|
|
d1ec19b30b | ||
|
|
74468cf799 | ||
|
|
411a6b1e49 | ||
|
|
e228165ff9 | ||
|
|
d615514799 | ||
|
|
ed717351cf | ||
|
|
4658244686 | ||
|
|
48ae5c69f4 | ||
|
|
4d065d577b | ||
|
|
789ee70674 | ||
|
|
3c0616bab7 | ||
|
|
3ce3b573a3 | ||
|
|
ce459c24ce | ||
|
|
7d0b9711bd | ||
|
|
e477663a7e | ||
|
|
eb01efe0cb | ||
|
|
8db4b65dee | ||
|
|
501b270890 | ||
|
|
fd8f8b2b28 | ||
|
|
7083b73720 | ||
|
|
511adbba68 | ||
|
|
78b8e2a73c | ||
|
|
041b95507d | ||
|
|
bfb47a0ae4 | ||
|
|
59efa6f0ad | ||
|
|
0acce0737f | ||
|
|
e96a80dd20 | ||
|
|
c626385845 | ||
|
|
54d0dc351b | ||
|
|
1effd5fddc | ||
|
|
ada6ada576 | ||
|
|
dacd0ab844 | ||
|
|
8c43972786 | ||
|
|
acac8b40cb | ||
|
|
6f5e5aa0c2 | ||
|
|
13e9488667 | ||
|
|
13e76e6db2 | ||
|
|
e2c3ffcbb4 | ||
|
|
733e634f4d | ||
|
|
1a7b7ce01e | ||
|
|
6c545806ab | ||
|
|
7c1e8a85cd | ||
|
|
82e10c1b7e | ||
|
|
cd10e02862 | ||
|
|
e9b009db9f | ||
|
|
d82f2e510d | ||
|
|
2386781d4f | ||
|
|
1345e92ec0 | ||
|
|
7b95cc72ed | ||
|
|
203ebc73eb | ||
|
|
d24b031fc5 | ||
|
|
55d185ef9c | ||
|
|
9cbc255448 | ||
|
|
212cc32097 | ||
|
|
d385b49f5f | ||
|
|
6419279c3c | ||
|
|
f3a1eaf557 | ||
|
|
facbb54776 | ||
|
|
2160dee23a | ||
|
|
9f25f7fced | ||
|
|
d36c4f0e83 | ||
|
|
4b99bba200 | ||
|
|
f07e4839b1 | ||
|
|
964403b4f8 | ||
|
|
20d20f5c8a | ||
|
|
d20e4d1de5 | ||
|
|
e87d2f1a20 | ||
|
|
4a75cc3bcf | ||
|
|
196050bd23 | ||
|
|
7300d00f66 | ||
|
|
21d86dd31d | ||
|
|
44bff58aa8 | ||
|
|
5c00f73d23 | ||
|
|
62c7fc2588 | ||
|
|
e6eeb2bb65 | ||
|
|
3ca2030c22 | ||
|
|
f02d61d665 | ||
|
|
7f5f46c448 | ||
|
|
24d0aa0c18 | ||
|
|
c0bbc88e9a | ||
|
|
fcf6abc7c6 | ||
|
|
1d7eb50608 | ||
|
|
0d9b0bd367 | ||
|
|
87568f97eb | ||
|
|
0b4e98881f | ||
|
|
fb1b30b728 | ||
|
|
75a9e38e3a | ||
|
|
c4468b1a0d | ||
|
|
b2974c38ef | ||
|
|
b657b57016 | ||
|
|
6058a0e243 | ||
|
|
1462ab0cb4 | ||
|
|
186b0f1efd | ||
|
|
000d91afca | ||
|
|
437bba1897 | ||
|
|
9ddfacc1a8 | ||
|
|
28640295f1 | ||
|
|
118d9a086e | ||
|
|
77c2b895c6 | ||
|
|
e6ed5574d5 | ||
|
|
09a7e177fa | ||
|
|
2e239ede3f | ||
|
|
3dfd8e8dfd | ||
|
|
1e4e668c81 | ||
|
|
3be645a357 | ||
|
|
95ec206b36 | ||
|
|
a7e4d9ec68 | ||
|
|
86c7cfe589 | ||
|
|
6da04655f0 | ||
|
|
fcc35331a3 | ||
|
|
b7765a22df | ||
|
|
4a2d6851b1 | ||
|
|
397af46909 | ||
|
|
8920d01dab | ||
|
|
00c15238e8 | ||
|
|
75d48de905 | ||
|
|
d5fb1bab9a | ||
|
|
94ffcfba23 | ||
|
|
5f20a026b2 | ||
|
|
b64fa56b26 | ||
|
|
df869a8e6b | ||
|
|
5fb6c73263 | ||
|
|
2bee8d91b0 | ||
|
|
77a4c114e9 | ||
|
|
b2e460d19d | ||
|
|
565b14f517 | ||
|
|
71cde7065b | ||
|
|
39c6b97582 | ||
|
|
211146b1a9 | ||
|
|
d44b55440f | ||
|
|
a868370a63 | ||
|
|
19cb37dc58 | ||
|
|
1e7b368329 | ||
|
|
c66a5c526a | ||
|
|
3972482922 | ||
|
|
ab5271a2f7 | ||
|
|
2875adc7c3 | ||
|
|
e24b2455d1 | ||
|
|
a89c7993f9 | ||
|
|
940269c220 | ||
|
|
5cb40875a2 | ||
|
|
83f060b895 | ||
|
|
02d6fd5133 | ||
|
|
0afaf040a1 | ||
|
|
9084e261a7 | ||
|
|
e5869942ca | ||
|
|
9d910ec37e | ||
|
|
fa871e9678 | ||
|
|
186038fd2e | ||
|
|
9669cd5ed7 | ||
|
|
91e55b9d5f | ||
|
|
5b0ca835f8 | ||
|
|
30d2035a3e | ||
|
|
1a530da139 | ||
|
|
9db3cd7b51 | ||
|
|
fd531bd39b | ||
|
|
e70cfd84fb | ||
|
|
f9a9953071 | ||
|
|
edbd6f6ade | ||
|
|
c6d7fb8dd0 | ||
|
|
903cc9815f | ||
|
|
f698e4589d | ||
|
|
bdfb18299d | ||
|
|
9dd11b5e04 | ||
|
|
0f2fd84724 | ||
|
|
71c9680245 | ||
|
|
00c2773e6b | ||
|
|
7acbc57bc7 | ||
|
|
62725bcb9d | ||
|
|
ee380451ca | ||
|
|
d0a12f6097 | ||
|
|
cd56321e65 | ||
|
|
a2d5c08460 | ||
|
|
dec4c8533c | ||
|
|
60feb3050c | ||
|
|
3c1282c92b | ||
|
|
9900b263d4 | ||
|
|
4c0c8f804f | ||
|
|
3912d3411c | ||
|
|
9015706fcb | ||
|
|
ec81a353c2 | ||
|
|
6a1ec9420e | ||
|
|
93d97424df | ||
|
|
2afad4e1be | ||
|
|
5d5eb47598 | ||
|
|
30c3b399f2 | ||
|
|
a380803d37 | ||
|
|
168d9890ae | ||
|
|
ae36abb1ca | ||
|
|
358515f4a8 | ||
|
|
d74307223f | ||
|
|
5aa7a391ac | ||
|
|
1bf9fb7d73 | ||
|
|
84d4c18c48 | ||
|
|
2f14d64289 | ||
|
|
ab4d7cb94b | ||
|
|
5bfb33c739 | ||
|
|
a089c4ea32 | ||
|
|
c12c6ea3c7 | ||
|
|
a4290b290b | ||
|
|
69dc598857 | ||
|
|
52a445e2f5 | ||
|
|
ba3243f88a | ||
|
|
d6a290a31d | ||
|
|
82e107af8a | ||
|
|
4031a862df | ||
|
|
854b53e28c | ||
|
|
fc7dd7dbb7 | ||
|
|
b00c25a822 | ||
|
|
f2c0b8c0b4 | ||
|
|
54da11a206 | ||
|
|
952f50eb8c | ||
|
|
484012a90c | ||
|
|
97009027d1 | ||
|
|
d6b8823dce | ||
|
|
5ad70ccc41 | ||
|
|
31c1452451 | ||
|
|
8c6cff012d | ||
|
|
c34fe10f23 | ||
|
|
f3b46e6d4f | ||
|
|
521ac706c1 | ||
|
|
9886c2bf40 | ||
|
|
c96ee0f786 | ||
|
|
5551420983 | ||
|
|
84e14edfe7 | ||
|
|
798a1aadb5 | ||
|
|
1111045546 | ||
|
|
5a075744bd | ||
|
|
2da81e0b5a | ||
|
|
389d8824e2 | ||
|
|
0089c1252f | ||
|
|
8771103abb | ||
|
|
9e2cfb9106 | ||
|
|
a8ab559de0 | ||
|
|
78f4249496 | ||
|
|
9ae53aca70 | ||
|
|
30b5287c38 | ||
|
|
0512c88ea8 | ||
|
|
46758efc8f | ||
|
|
6358f35cb7 | ||
|
|
e79661a924 | ||
|
|
99fc650550 | ||
|
|
28024de314 | ||
|
|
63ec2dc01b | ||
|
|
48be58378c | ||
|
|
fd50426dda | ||
|
|
89bdaec95a | ||
|
|
3ec73e2444 | ||
|
|
447aedc9a3 | ||
|
|
b35e19fc27 | ||
|
|
6b26089353 | ||
|
|
e46cadb211 | ||
|
|
ba6f26393f | ||
|
|
1fc31ba832 | ||
|
|
7e58278858 |
56
.github/workflows/build.yml
vendored
56
.github/workflows/build.yml
vendored
@@ -4,12 +4,20 @@ jobs:
|
||||
client:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
cc: [gcc, clang]
|
||||
compiler:
|
||||
- {cc: gcc, cxx: g++}
|
||||
- {cc: clang, cxx: clang++}
|
||||
wayland_shell: [xdg-shell, libdecor]
|
||||
build_type: [Release, Debug]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install libdecor PPA
|
||||
run: sudo add-apt-repository ppa:christianrauch/libdecoration
|
||||
if: ${{ matrix.wayland_shell == 'libdecor' }}
|
||||
- name: Update apt
|
||||
run: |
|
||||
sudo apt-get update
|
||||
@@ -17,19 +25,35 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get install \
|
||||
binutils-dev \
|
||||
libsdl2-dev libsdl2-ttf-dev \
|
||||
libspice-protocol-dev nettle-dev \
|
||||
libx11-dev libxss-dev libxi-dev \
|
||||
wayland-protocols
|
||||
libgl-dev libgles-dev \
|
||||
libx11-dev libxss-dev libxi-dev libxinerama-dev libxcursor-dev libxpresent-dev \
|
||||
libwayland-dev wayland-protocols libxkbcommon-dev \
|
||||
$([ '${{ matrix.wayland_shell }}' = libdecor ] && echo 'libdecor-dev libdbus-1-dev') \
|
||||
$([ '${{ matrix.compiler.cc }}' = clang ] && echo 'clang-tools')
|
||||
sudo pip3 install pyenchant
|
||||
- name: Configure client
|
||||
env:
|
||||
CC: /usr/bin/${{ matrix.compiler.cc }}
|
||||
CXX: /usr/bin/${{ matrix.compiler.cxx }}
|
||||
run: |
|
||||
mkdir client/build
|
||||
cd client/build
|
||||
CC=/usr/bin/${{ matrix.cc }} cmake ..
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
|
||||
-DCMAKE_LINKER:FILEPATH=/usr/bin/ld \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-DENABLE_LIBDECOR=${{ matrix.wayland_shell == 'libdecor' }} \
|
||||
..
|
||||
- name: Build client
|
||||
run: |
|
||||
cd client/build
|
||||
make -j$(nproc)
|
||||
- name: Checking help spelling
|
||||
run: ./client/build/looking-glass-client --help | ./doc/lgspell.py
|
||||
- name: Check GL function calls
|
||||
if: matrix.compiler.cc == 'clang'
|
||||
run: WAYLAND_SHELL='${{ matrix.wayland_shell }}' ./gl-check
|
||||
|
||||
module:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -53,7 +77,7 @@ jobs:
|
||||
sudo apt-get update
|
||||
- name: Install Linux host dependencies
|
||||
run: |
|
||||
sudo apt-get install binutils-dev libgl1-mesa-dev
|
||||
sudo apt-get install binutils-dev libgl1-mesa-dev libxcb-xfixes0-dev
|
||||
- name: Configure Linux host
|
||||
run: |
|
||||
mkdir host/build
|
||||
@@ -108,7 +132,7 @@ jobs:
|
||||
- name: Build Windows host installer
|
||||
run: |
|
||||
cd host\build
|
||||
makensis platform\Windows\installer.nsi
|
||||
makensis -DBUILD_32BIT platform\Windows\installer.nsi
|
||||
|
||||
obs:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -134,3 +158,21 @@ jobs:
|
||||
run: |
|
||||
cd obs/build
|
||||
make -j$(nproc)
|
||||
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Update apt
|
||||
run: |
|
||||
sudo apt-get update
|
||||
- name: Install docs dependencies
|
||||
run: |
|
||||
sudo apt-get install python3-sphinx
|
||||
sudo pip3 install sphinxcontrib-spelling
|
||||
- name: Build docs
|
||||
run: |
|
||||
cd doc
|
||||
make dirhtml SPHINXOPTS='-b spelling -W' -j$(nproc)
|
||||
|
||||
18
.github/workflows/pr-check.yml
vendored
Normal file
18
.github/workflows/pr-check.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
name: pr-check
|
||||
on: pull_request
|
||||
jobs:
|
||||
authors:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Check AUTHORS file
|
||||
run: |
|
||||
user="$(curl -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' -s https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }} | jq -r .user.login)"
|
||||
echo "Checking if GitHub user $user is in AUTHORS file..."
|
||||
if grep -q -E '> \('"$user"'\)' AUTHORS; then
|
||||
echo "$user found in AUTHORS file, all good!"
|
||||
else
|
||||
echo "$user not found in AUTHORS file."
|
||||
echo "Please add yourself to the AUTHORS file and try again."
|
||||
exit 1
|
||||
fi
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -8,3 +8,5 @@ module/modules.order
|
||||
*.o
|
||||
*.exe
|
||||
*/build
|
||||
__pycache__
|
||||
*.py[co]
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -4,3 +4,6 @@
|
||||
[submodule "repos/PureSpice"]
|
||||
path = repos/PureSpice
|
||||
url = https://github.com/gnif/PureSpice
|
||||
[submodule "repos/cimgui"]
|
||||
path = repos/cimgui
|
||||
url = https://github.com/cimgui/cimgui.git
|
||||
|
||||
56
AUTHORS
Normal file
56
AUTHORS
Normal file
@@ -0,0 +1,56 @@
|
||||
The following authors have licensed all their contributions to
|
||||
the Looking Glass project under the terms detailed in LICENSE:
|
||||
|
||||
Geoffrey McRae <geoff@hostfission.com> (gnif)
|
||||
Guanzhong Chen <quantum2048@gmail.com> (quantum5)
|
||||
Tudor Brindus <me@tbrindus.ca> (Xyene)
|
||||
Jonathan Rubenstein <jrubcop@gmail.com> (JJRcop)
|
||||
arcnmx <arcnmx@users.noreply.github.com> (arcnmx)
|
||||
TheCakeIsNaOH <TheCakeIsNaOH@gmail.com> (TheCakeIsNaOH)
|
||||
NamoDev <namodev@users.noreply.github.com> (NamoDev)
|
||||
feltcat <58396817+feltcat@users.noreply.github.com> (feltcat)
|
||||
Ali Abdel-Qader <abdelqaderali@protonmail.com>
|
||||
Jack Karamanian <karamanian.jack@gmail.com>
|
||||
Mikko Rasa <tdb@tdb.fi> (DataBeaver)
|
||||
Omar Pakker <Omar007@users.noreply.github.com> (Omar007)
|
||||
Yvan da Silva <yvan.m.silva@gmail.com> (YvanDaSilva)
|
||||
r4m0n <thiagoramon@gmail.com> (r4m0n)
|
||||
Łukasz Kostka <lukasz.kostka@netng.pl>
|
||||
A.J. Ruckman <aj@ruckman.dev> (ajruckman)
|
||||
Aaron <mcd1992@users.noreply.github.com> (mcd1992)
|
||||
Alam Arias <Alam.GBC@gmail.com> (alama)
|
||||
Alexander Olofsson <ace@haxalot.com> (ananace)
|
||||
Andrew Sheldon <asheldon55@gmail.com> (aqxa1)
|
||||
Andy Chun <andy@lolc.at> (noneucat)
|
||||
Arti Zirk <arti.zirk@gmail.com>
|
||||
Ash <5615358+ash-hat@users.noreply.github.com> (ash-hat)
|
||||
Dominik Csapak <d.csapak@proxmox.com> (flumm)
|
||||
Frediano Ziglio <fziglio@redhat.com>
|
||||
Luke Brown <axios350@gmail.com> (Dynamic-Gravity)
|
||||
Marius Barbu <msb@avengis.com> (mbarbu)
|
||||
Max Sistemich <maximilian.sistemich@rwth-aachen.de> (mafrasi2)
|
||||
Michael Golisch <mgolisch@googlemail.com> (mgolisch)
|
||||
Michał Zając <michal.zajac@gmail.com>
|
||||
Netboy3 <1472804+netboy3@users.noreply.github.com> (netboy3)
|
||||
Patrick Steinhardt <ps@pks.im>
|
||||
Paul Götzinger <paul70079@gmail.com> (pagdot)
|
||||
Rikard Falkeborn <rikard.falkeborn@gmail.com> (rikardfalkeborn)
|
||||
Rokas Kupstys <rokups@zoho.com> (rokups)
|
||||
Samuel Bowman <SamuelBowman@users.noreply.github.com> (SamuelBowman)
|
||||
Txanton <txb2@live.com> (tbejos)
|
||||
Tyler Watson <tyler@tw.id.au>
|
||||
Xiretza <xiretza@xiretza.xyz> (Xiretza)
|
||||
aspen <aspenuwu@protonmail.com> (aspenluxxxy)
|
||||
camr0 <CAMR0@protonmail.com> (camr0)
|
||||
chrsm <github.personal@c.chrsm.org> (chrsm)
|
||||
commander kotori <cmdrkotori@gmail.com> (cmdrkotori)
|
||||
eater <=@eater.me> (the-eater)
|
||||
fishery <i@ovo.so> (fisherwise)
|
||||
four0four <galen@shellspace.net> (four0four)
|
||||
jmossman <jmossman@users.noreply.github.com> (jmossman)
|
||||
jonpas <jonpas33@gmail.com> (jonpas)
|
||||
orcephrye <drakethebanditi@yahoo.com> (orcephrye)
|
||||
thejavascriptman <thejavascriptman@outlook.com> (thejavascriptman)
|
||||
vroad <396351+vroad@users.noreply.github.com> (vroad)
|
||||
williamvds <w.vigolodasilva@gmail.com> (williamvds)
|
||||
SytheZN <sythe.zn@gmail.com> (SytheZN)
|
||||
59
README.md
59
README.md
@@ -4,64 +4,19 @@ An extremely low latency KVMFR (KVM FrameRelay) implementation for guests with
|
||||
VGA PCI Passthrough.
|
||||
|
||||
* Project Website: https://looking-glass.io
|
||||
* Getting Started: https://looking-glass.io/wiki/Installation
|
||||
|
||||
## Donations
|
||||
|
||||
I (Geoffrey McRae) am the primary developer behind this project and I have
|
||||
invested thousands of hours of development time into it.
|
||||
|
||||
If you like this project and find it useful and would like to help out you can
|
||||
support me directly using the following platforms.
|
||||
|
||||
* [GitHub](https://github.com/sponsors/gnif)
|
||||
* [Ko-Fi](https://ko-fi.com/lookingglass)
|
||||
* [Patreon](https://www.patreon.com/gnif)
|
||||
* [Paypal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=ESQ72XUPGKXRY)
|
||||
* BTC - 14ZFcYjsKPiVreHqcaekvHGL846u3ZuT13
|
||||
* Documentation: https://looking-glass.io/docs
|
||||
|
||||
## Documentation
|
||||
|
||||
** IMPORTANT **
|
||||
❕❕❕ **IMPORTANT** ❕❕❕
|
||||
|
||||
This project contains submodules that must be checked out if building from the
|
||||
git repository! If you are not a developer and just want to compile Looking
|
||||
Glass please download the source archive from the website instead:
|
||||
Glass, please download the source archive from the website instead:
|
||||
|
||||
https://looking-glass.io/downloads
|
||||
|
||||
Please also be sure to see the following files for more information
|
||||
Note: The `README.md` files are slowly being deprecated from this project in
|
||||
favor of the wiki at https://looking-glass.io/wiki, and as such the
|
||||
information in these files may be dated.
|
||||
Source code for the documentation can be found in the `/doc` directory.
|
||||
|
||||
* [client/README.md](client/README.md)
|
||||
* [host/README.md](host/README.md)
|
||||
* [module/README.md](module/README.md)
|
||||
|
||||
## Latest Version
|
||||
|
||||
If you would like to use the latest bleeding edge version of Looking Glass please
|
||||
be aware there will be no support at this time.
|
||||
|
||||
Latest bleeding edge builds of the Windows host application can be obtained from:
|
||||
|
||||
https://looking-glass.io/downloads
|
||||
|
||||
# Help and support
|
||||
|
||||
## Web
|
||||
|
||||
https://forum.level1techs.com/c/software/lookingglass/142
|
||||
|
||||
## Discord
|
||||
|
||||
* Looking Glass: https://discord.gg/52SMupxkvt
|
||||
* VFIO: https://discord.gg/4ahCn4c
|
||||
|
||||
## IRC
|
||||
|
||||
Join us in the #LookingGlass channel on the FreeNode network
|
||||
|
||||
## Trello
|
||||
|
||||
* https://trello.com/b/tI1Xbwsg/looking-glass
|
||||
You may view this locally as HTML by running `make html` with `python3-sphinx`
|
||||
installed.
|
||||
|
||||
@@ -1,19 +1,31 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(looking-glass-client C)
|
||||
project(looking-glass-client C CXX)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
|
||||
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
|
||||
|
||||
if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
|
||||
message(FATAL_ERROR
|
||||
"\n"
|
||||
"In-source builds are not supported\n"
|
||||
"See build instructions provided in: "
|
||||
"${PROJECT_TOP}/doc/build.rst\n"
|
||||
"Refusing to continue"
|
||||
)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_TOP}/cmake/" "${PROJECT_SOURCE_DIR}/cmake/")
|
||||
|
||||
include(CheckSubmodule)
|
||||
include(GNUInstallDirs)
|
||||
include(CheckCCompilerFlag)
|
||||
include(FeatureSummary)
|
||||
|
||||
option(OPTIMIZE_FOR_NATIVE "Build with -march=native" ON)
|
||||
if(OPTIMIZE_FOR_NATIVE)
|
||||
CHECK_C_COMPILER_FLAG("-march=native" COMPILER_SUPPORTS_MARCH_NATIVE)
|
||||
if(COMPILER_SUPPORTS_MARCH_NATIVE)
|
||||
add_compile_options("-march=native")
|
||||
endif()
|
||||
endif()
|
||||
set(OPTIMIZE_FOR_NATIVE_DEFAULT ON)
|
||||
include(OptimizeForNative) # option(OPTIMIZE_FOR_NATIVE)
|
||||
include(UninstallTarget)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(FONTCONFIG REQUIRED IMPORTED_TARGET fontconfig)
|
||||
|
||||
option(ENABLE_OPENGL "Enable the OpenGL renderer" ON)
|
||||
add_feature_info(ENABLE_OPENGL ENABLE_OPENGL "Legacy OpenGL renderer.")
|
||||
@@ -36,6 +48,13 @@ add_feature_info(ENABLE_X11 ENABLE_X11 "X11 support.")
|
||||
option(ENABLE_WAYLAND "Build with Wayland support" ON)
|
||||
add_feature_info(ENABLE_WAYLAND ENABLE_WAYLAND "Wayland support.")
|
||||
|
||||
option(ENABLE_LIBDECOR "Build with libdecor support" OFF)
|
||||
add_feature_info(ENABLE_LIBDECOR ENABLE_LIBDECOR "libdecor support.")
|
||||
|
||||
if (NOT ENABLE_X11 AND NOT ENABLE_WAYLAND)
|
||||
message(FATAL_ERROR "Either ENABLE_X11 or ENABLE_WAYLAND must be on")
|
||||
endif()
|
||||
|
||||
add_compile_options(
|
||||
"-Wall"
|
||||
"-Wextra"
|
||||
@@ -71,16 +90,8 @@ if(ENABLE_UBSAN)
|
||||
set(EXE_FLAGS "${EXE_FLAGS} -fsanitize=undefined")
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(PKGCONFIG REQUIRED
|
||||
sdl2
|
||||
)
|
||||
|
||||
find_package(GMP)
|
||||
|
||||
add_definitions(-D ATOMIC_LOCKING)
|
||||
add_definitions(-D GL_GLEXT_PROTOTYPES)
|
||||
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/version.c
|
||||
@@ -92,13 +103,9 @@ add_custom_command(
|
||||
include_directories(
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
${CMAKE_BINARY_DIR}/include
|
||||
${PKGCONFIG_INCLUDE_DIRS} ${PKGCONFIG_OPT_INCLUDE_DIRS}
|
||||
${GMP_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
link_libraries(
|
||||
${PKGCONFIG_LIBRARIES} ${PKGCONFIG_OPT_LIBRARIES}
|
||||
${GMP_LIBRARIES}
|
||||
${CMAKE_DL_LIBS}
|
||||
rt
|
||||
m
|
||||
@@ -111,34 +118,50 @@ set(SOURCES
|
||||
src/app.c
|
||||
src/config.c
|
||||
src/keybind.c
|
||||
src/lg-renderer.c
|
||||
src/ll.c
|
||||
src/util.c
|
||||
src/clipboard.c
|
||||
src/kb.c
|
||||
src/gl_dynprocs.c
|
||||
src/egl_dynprocs.c
|
||||
src/eglutil.c
|
||||
src/overlay_utils.c
|
||||
|
||||
src/overlay/alert.c
|
||||
src/overlay/fps.c
|
||||
src/overlay/graphs.c
|
||||
src/overlay/help.c
|
||||
src/overlay/config.c
|
||||
)
|
||||
|
||||
# Force cimgui to build as a static library.
|
||||
set(IMGUI_STATIC "yes" CACHE STRING "Build as a static library")
|
||||
|
||||
add_subdirectory("${PROJECT_TOP}/common" "${CMAKE_BINARY_DIR}/common" )
|
||||
add_subdirectory("${PROJECT_TOP}/repos/LGMP/lgmp" "${CMAKE_BINARY_DIR}/LGMP" )
|
||||
add_subdirectory("${PROJECT_TOP}/repos/PureSpice" "${CMAKE_BINARY_DIR}/PureSpice")
|
||||
add_subdirectory("${PROJECT_TOP}/repos/cimgui" "${CMAKE_BINARY_DIR}/cimgui" EXCLUDE_FROM_ALL)
|
||||
|
||||
add_subdirectory(displayservers)
|
||||
add_subdirectory(renderers)
|
||||
add_subdirectory(fonts)
|
||||
|
||||
add_executable(looking-glass-client ${SOURCES})
|
||||
target_compile_options(looking-glass-client PUBLIC ${PKGCONFIG_CFLAGS_OTHER} ${PKGCONFIG_OPT_CFLAGS_OTHER})
|
||||
|
||||
target_compile_definitions(looking-glass-client PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1)
|
||||
|
||||
target_link_libraries(looking-glass-client
|
||||
${EXE_FLAGS}
|
||||
PkgConfig::FONTCONFIG
|
||||
lg_common
|
||||
displayservers
|
||||
lgmp
|
||||
purespice
|
||||
renderers
|
||||
fonts
|
||||
cimgui
|
||||
)
|
||||
|
||||
install(PROGRAMS ${CMAKE_BINARY_DIR}/looking-glass-client DESTINATION bin/ COMPONENT binary)
|
||||
install(TARGETS looking-glass-client
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
COMPONENT binary)
|
||||
|
||||
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)
|
||||
|
||||
205
client/README.md
205
client/README.md
@@ -1,205 +0,0 @@
|
||||
# Looking Glass Client
|
||||
|
||||
This is the Looking Glass client application that is designed to work in tandem with the Looking Glass Host application
|
||||
|
||||
---
|
||||
|
||||
## Building the Application
|
||||
|
||||
### Build Dependencies
|
||||
|
||||
* binutils-dev
|
||||
* cmake
|
||||
* fonts-freefont-ttf
|
||||
* libsdl2-dev
|
||||
* libsdl2-ttf-dev
|
||||
* libspice-protocol-dev
|
||||
* libfontconfig1-dev
|
||||
* libx11-dev
|
||||
* nettle-dev
|
||||
* libxss-dev
|
||||
* libxi-dev
|
||||
|
||||
#### Debian (and maybe Ubuntu)
|
||||
|
||||
apt-get install binutils-dev cmake fonts-freefont-ttf libsdl2-dev libsdl2-ttf-dev libspice-protocol-dev libfontconfig1-dev libx11-dev nettle-dev libxss-dev libxi-dev
|
||||
|
||||
### Building
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ../
|
||||
make
|
||||
|
||||
Should this all go well you should be left with the file `looking-glass-client`
|
||||
|
||||
### Removing Wayland or X11 support
|
||||
|
||||
Wayland and/or X11 support can be disabled with the compile options
|
||||
`ENABLE_WAYLAND` and `ENABLE_X11`, if both are specified only `SDL2` will remain
|
||||
and the client will fallback to using it.
|
||||
|
||||
cmake ../ -DENABLE_WAYLAND=OFF
|
||||
|
||||
At this time, X11 is the perferred and best supported interface. Wayland is not
|
||||
far behind, however it lacks some of the seamless interaction features that X11
|
||||
has due to the lack of cursor warp (programmatic movement of the local cusror) on
|
||||
Wayland.
|
||||
|
||||
---
|
||||
|
||||
## Usage Tips
|
||||
|
||||
### Key Bindings
|
||||
|
||||
By default Looking Glass uses the `Scroll Lock` key as the escape key for commands as well as the input capture mode toggle, this can be changed using the `-m` switch if you desire a different key.
|
||||
Below are a list of current key bindings:
|
||||
|
||||
| Command | Description |
|
||||
|-|-|
|
||||
| <kbd>ScrLk</kbd> | Toggle cursor screen capture |
|
||||
| <kbd>ScrLk</kbd>+<kbd>F</kbd> | Full Screen toggle |
|
||||
| <kbd>ScrLk</kbd>+<kbd>V</kbd> | Video stream toggle |
|
||||
| <kbd>ScrLk</kbd>+<kbd>I</kbd> | Spice keyboard & mouse enable toggle |
|
||||
| <kbd>ScrLk</kbd>+<kbd>N</kbd> | Toggle night vision mode (EGL renderer only!) |
|
||||
| <kbd>ScrLk</kbd>+<kbd>R</kbd> | Rotate the output clockwise by 90 degree increments |
|
||||
| <kbd>ScrLk</kbd>+<kbd>Q</kbd> | Quit |
|
||||
| <kbd>ScrLk</kbd>+<kbd>Insert</kbd> | Increase mouse sensitivity (in capture mode only) |
|
||||
| <kbd>ScrLk</kbd>+<kbd>Del</kbd> | Decrease mouse sensitivity (in capture mode only) |
|
||||
| <kbd>ScrLk</kbd>+<kbd>F1</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F1</kbd> to the guest |
|
||||
| <kbd>ScrLk</kbd>+<kbd>F2</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F2</kbd> to the guest |
|
||||
| <kbd>ScrLk</kbd>+<kbd>F3</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F3</kbd> to the guest |
|
||||
| <kbd>ScrLk</kbd>+<kbd>F4</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F4</kbd> to the guest |
|
||||
| <kbd>ScrLk</kbd>+<kbd>F5</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F5</kbd> to the guest |
|
||||
| <kbd>ScrLk</kbd>+<kbd>F6</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F6</kbd> to the guest |
|
||||
| <kbd>ScrLk</kbd>+<kbd>F7</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F7</kbd> to the guest |
|
||||
| <kbd>ScrLk</kbd>+<kbd>F8</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F8</kbd> to the guest |
|
||||
| <kbd>ScrLk</kbd>+<kbd>F9</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F9</kbd> to the guest |
|
||||
| <kbd>ScrLk</kbd>+<kbd>F10</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F10</kbd> to the guest |
|
||||
| <kbd>ScrLk</kbd>+<kbd>F11</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F11</kbd> to the guest |
|
||||
| <kbd>ScrLk</kbd>+<kbd>F12</kbd> | Send <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>F12</kbd> to the guest |
|
||||
| <kbd>ScrLk</kbd>+<kbd>LWin</kbd> | Send <kbd>LWin</kbd> to the guest |
|
||||
| <kbd>ScrLk</kbd>+<kbd>RWin</kbd> | Send <kbd>RWin</kbd> to the guest |
|
||||
|
||||
|
||||
|
||||
### Setting options via command line arguments
|
||||
|
||||
The syntax is simple: `module:name=value`, for example:
|
||||
|
||||
./looking-glass-client win:fullScreen=yes egl:nvGain=1
|
||||
|
||||
### Setting options via configuration files
|
||||
|
||||
By default the application will look for and load the config files in the following locations
|
||||
|
||||
* /etc/looking-glass-client.ini
|
||||
* ~/.looking-glass-client.ini
|
||||
|
||||
The format of this file is the commonly known INI format, for example:
|
||||
|
||||
[win]
|
||||
fullScreen=yes
|
||||
|
||||
[egl]
|
||||
nvGain=1
|
||||
|
||||
Command line arguments will override any options loaded from the config files.
|
||||
|
||||
### Supported options
|
||||
|
||||
```
|
||||
|--------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Long | Short | Value | Description |
|
||||
|--------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| app:configFile | -C | NULL | A file to read additional configuration from |
|
||||
| app:renderer | -g | auto | Specify the renderer to use |
|
||||
| app:license | -l | no | Show the license for this application and then terminate |
|
||||
| app:cursorPollInterval | | 1000 | How often to check for a cursor update in microseconds |
|
||||
| app:framePollInterval | | 1000 | How often to check for a frame update in microseconds |
|
||||
| app:allowDMA | | yes | Allow direct DMA transfers if possible (VM-VM only for now) |
|
||||
| app:shmFile | -f | /dev/shm/looking-glass | The path to the shared memory file, or the name of the kvmfr device to use, ie: kvmfr0 |
|
||||
|--------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|
||||
|---------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Long | Short | Value | Description |
|
||||
|---------------------------------------------------------------------------------------------------------------------------------|
|
||||
| win:title | | Looking Glass (client) | The window title |
|
||||
| win:position | | center | Initial window position at startup |
|
||||
| win:size | | 1024x768 | Initial window size at startup |
|
||||
| win:autoResize | -a | no | Auto resize the window to the guest |
|
||||
| win:allowResize | -n | yes | Allow the window to be manually resized |
|
||||
| win:keepAspect | -r | yes | Maintain the correct aspect ratio |
|
||||
| win:forceAspect | | yes | Force the window to maintain the aspect ratio |
|
||||
| win:dontUpscale | | no | Never try to upscale the window |
|
||||
| win:borderless | -d | no | Borderless mode |
|
||||
| win:fullScreen | -F | no | Launch in fullscreen borderless mode |
|
||||
| win:maximize | -T | no | Launch window maximized |
|
||||
| win:minimizeOnFocusLoss | | yes | Minimize window on focus loss |
|
||||
| win:fpsMin | -K | -1 | Frame rate minimum (0 = disable - not recommended, -1 = auto detect) |
|
||||
| win:showFPS | -k | no | Enable the FPS & UPS display |
|
||||
| win:ignoreQuit | -Q | no | Ignore requests to quit (ie: Alt+F4) |
|
||||
| win:noScreensaver | -S | no | Prevent the screensaver from starting |
|
||||
| win:alerts | -q | yes | Show on screen alert messages |
|
||||
| win:quickSplash | | no | Skip fading out the splash screen when a connection is established |
|
||||
| win:rotate | | 0 | Rotate the displayed image (0, 90, 180, 270) |
|
||||
|---------------------------------------------------------------------------------------------------------------------------------|
|
||||
|
||||
|-----------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Long | Short | Value | Description |
|
||||
|-----------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| input:grabKeyboard | -G | no | Grab the keyboard in capture mode |
|
||||
| input:grabKeyboardOnFocus | | no | Grab the keyboard when focused |
|
||||
| input:releaseKeysOnFocusLoss | | yes | On focus loss, send key up events to guest for all held keys |
|
||||
| input:escapeKey | -m | 70 = KEY_SCROLLLOCK | Specify the escape key, see <linux/input-event-codes.h> for valid values |
|
||||
| input:ignoreWindowsKeys | | no | Do not pass events for the windows keys to the guest |
|
||||
| input:hideCursor | -M | yes | Hide the local mouse cursor |
|
||||
| input:mouseSens | | 0 | Initial mouse sensitivity when in capture mode (-9 to 9) |
|
||||
| input:mouseSmoothing | | yes | Apply simple mouse smoothing when rawMouse is not in use (helps reduce aliasing) |
|
||||
| input:rawMouse | | no | Use RAW mouse input when in capture mode (good for gaming) |
|
||||
| input:mouseRedraw | | yes | Mouse movements trigger redraws (ignores FPS minimum) |
|
||||
| input:autoCapture | | no | Try to keep the mouse captured when needed |
|
||||
| input:captureOnly | | no | Only enable input via SPICE if in capture mode |
|
||||
|-----------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|
||||
|------------------------------------------------------------------------------------------------------------------|
|
||||
| Long | Short | Value | Description |
|
||||
|------------------------------------------------------------------------------------------------------------------|
|
||||
| spice:enable | -s | yes | Enable the built in SPICE client for input and/or clipboard support |
|
||||
| spice:host | -c | 127.0.0.1 | The SPICE server host or UNIX socket |
|
||||
| spice:port | -p | 5900 | The SPICE server port (0 = unix socket) |
|
||||
| spice:input | | yes | Use SPICE to send keyboard and mouse input events to the guest |
|
||||
| spice:clipboard | | yes | Use SPICE to syncronize the clipboard contents with the guest |
|
||||
| spice:clipboardToVM | | yes | Allow the clipboard to be syncronized TO the VM |
|
||||
| spice:clipboardToLocal | | yes | Allow the clipboard to be syncronized FROM the VM |
|
||||
| spice:scaleCursor | -j | yes | Scale cursor input position to screen size when up/down scaled |
|
||||
| spice:captureOnStart | | no | Capture mouse and keyboard on start |
|
||||
| spice:alwaysShowCursor | | no | Always show host cursor |
|
||||
|------------------------------------------------------------------------------------------------------------------|
|
||||
|
||||
|--------------------------------------------------------------------------------------------------------------|
|
||||
| Long | Short | Value | Description |
|
||||
|--------------------------------------------------------------------------------------------------------------|
|
||||
| egl:vsync | | no | Enable vsync |
|
||||
| egl:doubleBuffer | | no | Enable double buffering |
|
||||
| egl:multisample | | yes | Enable Multisampling |
|
||||
| egl:nvGainMax | | 1 | The maximum night vision gain |
|
||||
| egl:nvGain | | 0 | The initial night vision gain at startup |
|
||||
| egl:cbMode | | 0 | Color Blind Mode (0 = Off, 1 = Protanope, 2 = Deuteranope, 3 = Tritanope) |
|
||||
|--------------------------------------------------------------------------------------------------------------|
|
||||
|
||||
|------------------------------------------------------------------------------------|
|
||||
| Long | Short | Value | Description |
|
||||
|------------------------------------------------------------------------------------|
|
||||
| opengl:mipmap | | yes | Enable mipmapping |
|
||||
| opengl:vsync | | no | Enable vsync |
|
||||
| opengl:preventBuffer | | yes | Prevent the driver from buffering frames |
|
||||
| opengl:amdPinnedMem | | yes | Use GL_AMD_pinned_memory if it is available |
|
||||
|------------------------------------------------------------------------------------|
|
||||
|
||||
|-------------------------------------------------------------|
|
||||
| Long | Short | Value | Description |
|
||||
|-------------------------------------------------------------|
|
||||
| wayland:warpSupport | | yes | Enable cursor warping |
|
||||
|-------------------------------------------------------------|
|
||||
```
|
||||
@@ -1,19 +0,0 @@
|
||||
# Try to find the GMP librairies
|
||||
# GMP_FOUND - system has GMP lib
|
||||
# GMP_INCLUDE_DIR - the GMP include directory
|
||||
# GMP_LIBRARIES - Libraries needed to use GMP
|
||||
|
||||
if (GMP_INCLUDE_DIR AND GMP_LIBRARIES)
|
||||
# Already in cache, be silent
|
||||
set(GMP_FIND_QUIETLY TRUE)
|
||||
endif (GMP_INCLUDE_DIR AND GMP_LIBRARIES)
|
||||
|
||||
find_path(GMP_INCLUDE_DIR NAMES gmp.h )
|
||||
find_library(GMP_LIBRARIES NAMES gmp libgmp )
|
||||
find_library(GMPXX_LIBRARIES NAMES gmpxx libgmpxx )
|
||||
MESSAGE(STATUS "GMP libs: " ${GMP_LIBRARIES} " " ${GMPXX_LIBRARIES} )
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GMP DEFAULT_MSG GMP_INCLUDE_DIR GMP_LIBRARIES)
|
||||
|
||||
mark_as_advanced(GMP_INCLUDE_DIR GMP_LIBRARIES)
|
||||
@@ -5,15 +5,15 @@ function(make_object out_var)
|
||||
file(RELATIVE_PATH out_f ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/${in_f}")
|
||||
set(out_h "${CMAKE_CURRENT_BINARY_DIR}/${out_f}.h")
|
||||
set(out_f "${CMAKE_CURRENT_BINARY_DIR}/${out_f}.o")
|
||||
string(REGEX REPLACE "[/.]" "_" sym_in ${in_f})
|
||||
string(REGEX REPLACE "[/.-]" "_" sym_in "${in_f}")
|
||||
|
||||
add_custom_command(OUTPUT ${out_f}
|
||||
COMMAND ${CMAKE_LINKER} -r -b binary -o ${out_f} ${in_f}
|
||||
COMMAND ${CMAKE_LINKER} -r -b binary -o ${out_f} "${in_f}"
|
||||
COMMAND ${CMAKE_OBJCOPY} --rename-section .data=.rodata,CONTENTS,ALLOC,LOAD,READONLY,DATA ${out_f} ${out_f}
|
||||
COMMAND ${CMAKE_OBJCOPY} --redefine-sym _binary_${sym_in}_start=b_${sym_in} ${out_f} ${out_f}
|
||||
COMMAND ${CMAKE_OBJCOPY} --redefine-sym _binary_${sym_in}_end=b_${sym_in}_end ${out_f} ${out_f}
|
||||
COMMAND ${CMAKE_OBJCOPY} --strip-symbol _binary_${sym_in}_size ${out_f} ${out_f}
|
||||
DEPENDS ${in_f}
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${in_f}"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMENT "Creating object from ${in_f}"
|
||||
VERBATIM
|
||||
@@ -32,3 +32,11 @@ function(make_object out_var)
|
||||
set(${out_var}_OBJS "${result}" PARENT_SCOPE)
|
||||
set(${out_var}_INCS "${result_h}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
function(make_defines in_file out_file)
|
||||
add_custom_command(OUTPUT ${out_file}
|
||||
COMMAND grep "^#define" "${in_file}" > "${out_file}"
|
||||
DEPENDS ${in_file}
|
||||
COMMENT "Creating #defines from ${in_file}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
@@ -27,9 +27,6 @@ if (ENABLE_X11)
|
||||
add_displayserver(X11)
|
||||
endif()
|
||||
|
||||
# SDL must be last as it's the fallback implemntation
|
||||
add_displayserver(SDL)
|
||||
|
||||
list(REMOVE_AT DISPLAYSERVERS 0)
|
||||
list(REMOVE_AT DISPLAYSERVERS_LINK 0)
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(displayserver_SDL LANGUAGES C)
|
||||
|
||||
#find_package(PkgConfig)
|
||||
#pkg_check_modules(DISPLAYSERVER_SDL_PKGCONFIG REQUIRED
|
||||
# #sdl2
|
||||
#)
|
||||
|
||||
add_library(displayserver_SDL STATIC
|
||||
sdl.c
|
||||
)
|
||||
|
||||
target_link_libraries(displayserver_SDL
|
||||
${DISPLAYSERVER_SDL_PKGCONFIG_LIBRARIES}
|
||||
${DISPLAYSERVER_SDL_OPT_PKGCONFIG_LIBRARIES}
|
||||
lg_common
|
||||
)
|
||||
|
||||
target_include_directories(displayserver_SDL
|
||||
PRIVATE
|
||||
src
|
||||
${DISPLAYSERVER_SDL_PKGCONFIG_INCLUDE_DIRS}
|
||||
${DISPLAYSERVER_SDL_OPT_PKGCONFIG_INCLUDE_DIRS}
|
||||
)
|
||||
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
//FIXME: this should be made static once config.c is no longer using SDL
|
||||
//scancodes
|
||||
uint32_t sdl_to_xfree86[SDL_NUM_SCANCODES] =
|
||||
{
|
||||
[SDL_SCANCODE_UNKNOWN] /* = USB 0 */ = KEY_RESERVED,
|
||||
[SDL_SCANCODE_A] /* = USB 4 */ = KEY_A,
|
||||
[SDL_SCANCODE_B] /* = USB 5 */ = KEY_B,
|
||||
[SDL_SCANCODE_C] /* = USB 6 */ = KEY_C,
|
||||
[SDL_SCANCODE_D] /* = USB 7 */ = KEY_D,
|
||||
[SDL_SCANCODE_E] /* = USB 8 */ = KEY_E,
|
||||
[SDL_SCANCODE_F] /* = USB 9 */ = KEY_F,
|
||||
[SDL_SCANCODE_G] /* = USB 10 */ = KEY_G,
|
||||
[SDL_SCANCODE_H] /* = USB 11 */ = KEY_H,
|
||||
[SDL_SCANCODE_I] /* = USB 12 */ = KEY_I,
|
||||
[SDL_SCANCODE_J] /* = USB 13 */ = KEY_J,
|
||||
[SDL_SCANCODE_K] /* = USB 14 */ = KEY_K,
|
||||
[SDL_SCANCODE_L] /* = USB 15 */ = KEY_L,
|
||||
[SDL_SCANCODE_M] /* = USB 16 */ = KEY_M,
|
||||
[SDL_SCANCODE_N] /* = USB 17 */ = KEY_N,
|
||||
[SDL_SCANCODE_O] /* = USB 18 */ = KEY_O,
|
||||
[SDL_SCANCODE_P] /* = USB 19 */ = KEY_P,
|
||||
[SDL_SCANCODE_Q] /* = USB 20 */ = KEY_Q,
|
||||
[SDL_SCANCODE_R] /* = USB 21 */ = KEY_R,
|
||||
[SDL_SCANCODE_S] /* = USB 22 */ = KEY_S,
|
||||
[SDL_SCANCODE_T] /* = USB 23 */ = KEY_T,
|
||||
[SDL_SCANCODE_U] /* = USB 24 */ = KEY_U,
|
||||
[SDL_SCANCODE_V] /* = USB 25 */ = KEY_V,
|
||||
[SDL_SCANCODE_W] /* = USB 26 */ = KEY_W,
|
||||
[SDL_SCANCODE_X] /* = USB 27 */ = KEY_X,
|
||||
[SDL_SCANCODE_Y] /* = USB 28 */ = KEY_Y,
|
||||
[SDL_SCANCODE_Z] /* = USB 29 */ = KEY_Z,
|
||||
[SDL_SCANCODE_1] /* = USB 30 */ = KEY_1,
|
||||
[SDL_SCANCODE_2] /* = USB 31 */ = KEY_2,
|
||||
[SDL_SCANCODE_3] /* = USB 32 */ = KEY_3,
|
||||
[SDL_SCANCODE_4] /* = USB 33 */ = KEY_4,
|
||||
[SDL_SCANCODE_5] /* = USB 34 */ = KEY_5,
|
||||
[SDL_SCANCODE_6] /* = USB 35 */ = KEY_6,
|
||||
[SDL_SCANCODE_7] /* = USB 36 */ = KEY_7,
|
||||
[SDL_SCANCODE_8] /* = USB 37 */ = KEY_8,
|
||||
[SDL_SCANCODE_9] /* = USB 38 */ = KEY_9,
|
||||
[SDL_SCANCODE_0] /* = USB 39 */ = KEY_0,
|
||||
[SDL_SCANCODE_RETURN] /* = USB 40 */ = KEY_ENTER,
|
||||
[SDL_SCANCODE_ESCAPE] /* = USB 41 */ = KEY_ESC,
|
||||
[SDL_SCANCODE_BACKSPACE] /* = USB 42 */ = KEY_BACKSPACE,
|
||||
[SDL_SCANCODE_TAB] /* = USB 43 */ = KEY_TAB,
|
||||
[SDL_SCANCODE_SPACE] /* = USB 44 */ = KEY_SPACE,
|
||||
[SDL_SCANCODE_MINUS] /* = USB 45 */ = KEY_MINUS,
|
||||
[SDL_SCANCODE_EQUALS] /* = USB 46 */ = KEY_EQUAL,
|
||||
[SDL_SCANCODE_LEFTBRACKET] /* = USB 47 */ = KEY_LEFTBRACE,
|
||||
[SDL_SCANCODE_RIGHTBRACKET] /* = USB 48 */ = KEY_RIGHTBRACE,
|
||||
[SDL_SCANCODE_BACKSLASH] /* = USB 49 */ = KEY_BACKSLASH,
|
||||
[SDL_SCANCODE_SEMICOLON] /* = USB 51 */ = KEY_SEMICOLON,
|
||||
[SDL_SCANCODE_APOSTROPHE] /* = USB 52 */ = KEY_APOSTROPHE,
|
||||
[SDL_SCANCODE_GRAVE] /* = USB 53 */ = KEY_GRAVE,
|
||||
[SDL_SCANCODE_COMMA] /* = USB 54 */ = KEY_COMMA,
|
||||
[SDL_SCANCODE_PERIOD] /* = USB 55 */ = KEY_DOT,
|
||||
[SDL_SCANCODE_SLASH] /* = USB 56 */ = KEY_SLASH,
|
||||
[SDL_SCANCODE_CAPSLOCK] /* = USB 57 */ = KEY_CAPSLOCK,
|
||||
[SDL_SCANCODE_F1] /* = USB 58 */ = KEY_F1,
|
||||
[SDL_SCANCODE_F2] /* = USB 59 */ = KEY_F2,
|
||||
[SDL_SCANCODE_F3] /* = USB 60 */ = KEY_F3,
|
||||
[SDL_SCANCODE_F4] /* = USB 61 */ = KEY_F4,
|
||||
[SDL_SCANCODE_F5] /* = USB 62 */ = KEY_F5,
|
||||
[SDL_SCANCODE_F6] /* = USB 63 */ = KEY_F6,
|
||||
[SDL_SCANCODE_F7] /* = USB 64 */ = KEY_F7,
|
||||
[SDL_SCANCODE_F8] /* = USB 65 */ = KEY_F8,
|
||||
[SDL_SCANCODE_F9] /* = USB 66 */ = KEY_F9,
|
||||
[SDL_SCANCODE_F10] /* = USB 67 */ = KEY_F10,
|
||||
[SDL_SCANCODE_F11] /* = USB 68 */ = KEY_F11,
|
||||
[SDL_SCANCODE_F12] /* = USB 69 */ = KEY_F12,
|
||||
[SDL_SCANCODE_PRINTSCREEN] /* = USB 70 */ = KEY_SYSRQ,
|
||||
[SDL_SCANCODE_SCROLLLOCK] /* = USB 71 */ = KEY_SCROLLLOCK,
|
||||
[SDL_SCANCODE_PAUSE] /* = USB 72 */ = KEY_PAUSE,
|
||||
[SDL_SCANCODE_INSERT] /* = USB 73 */ = KEY_INSERT,
|
||||
[SDL_SCANCODE_HOME] /* = USB 74 */ = KEY_HOME,
|
||||
[SDL_SCANCODE_PAGEUP] /* = USB 75 */ = KEY_PAGEUP,
|
||||
[SDL_SCANCODE_DELETE] /* = USB 76 */ = KEY_DELETE,
|
||||
[SDL_SCANCODE_END] /* = USB 77 */ = KEY_END,
|
||||
[SDL_SCANCODE_PAGEDOWN] /* = USB 78 */ = KEY_PAGEDOWN,
|
||||
[SDL_SCANCODE_RIGHT] /* = USB 79 */ = KEY_RIGHT,
|
||||
[SDL_SCANCODE_LEFT] /* = USB 80 */ = KEY_LEFT,
|
||||
[SDL_SCANCODE_DOWN] /* = USB 81 */ = KEY_DOWN,
|
||||
[SDL_SCANCODE_UP] /* = USB 82 */ = KEY_UP,
|
||||
[SDL_SCANCODE_NUMLOCKCLEAR] /* = USB 83 */ = KEY_NUMLOCK,
|
||||
[SDL_SCANCODE_KP_DIVIDE] /* = USB 84 */ = KEY_KPSLASH,
|
||||
[SDL_SCANCODE_KP_MULTIPLY] /* = USB 85 */ = KEY_KPASTERISK,
|
||||
[SDL_SCANCODE_KP_MINUS] /* = USB 86 */ = KEY_KPMINUS,
|
||||
[SDL_SCANCODE_KP_PLUS] /* = USB 87 */ = KEY_KPPLUS,
|
||||
[SDL_SCANCODE_KP_ENTER] /* = USB 88 */ = KEY_KPENTER,
|
||||
[SDL_SCANCODE_KP_1] /* = USB 89 */ = KEY_KP1,
|
||||
[SDL_SCANCODE_KP_2] /* = USB 90 */ = KEY_KP2,
|
||||
[SDL_SCANCODE_KP_3] /* = USB 91 */ = KEY_KP3,
|
||||
[SDL_SCANCODE_KP_4] /* = USB 92 */ = KEY_KP4,
|
||||
[SDL_SCANCODE_KP_5] /* = USB 93 */ = KEY_KP5,
|
||||
[SDL_SCANCODE_KP_6] /* = USB 94 */ = KEY_KP6,
|
||||
[SDL_SCANCODE_KP_7] /* = USB 95 */ = KEY_KP7,
|
||||
[SDL_SCANCODE_KP_8] /* = USB 96 */ = KEY_KP8,
|
||||
[SDL_SCANCODE_KP_9] /* = USB 97 */ = KEY_KP9,
|
||||
[SDL_SCANCODE_KP_0] /* = USB 98 */ = KEY_KP0,
|
||||
[SDL_SCANCODE_KP_PERIOD] /* = USB 99 */ = KEY_KPDOT,
|
||||
[SDL_SCANCODE_NONUSBACKSLASH] /* = USB 100 */ = KEY_102ND,
|
||||
[SDL_SCANCODE_APPLICATION] /* = USB 101 */ = KEY_COMPOSE,
|
||||
[SDL_SCANCODE_POWER] /* = USB 102 */ = KEY_POWER,
|
||||
[SDL_SCANCODE_KP_EQUALS] /* = USB 103 */ = KEY_KPEQUAL,
|
||||
[SDL_SCANCODE_F13] /* = USB 104 */ = KEY_CONFIG,
|
||||
[SDL_SCANCODE_F14] /* = USB 105 */ = KEY_F14,
|
||||
[SDL_SCANCODE_F15] /* = USB 106 */ = KEY_F15,
|
||||
[SDL_SCANCODE_F16] /* = USB 107 */ = KEY_F16,
|
||||
[SDL_SCANCODE_F17] /* = USB 108 */ = KEY_F17,
|
||||
[SDL_SCANCODE_F18] /* = USB 109 */ = KEY_F18,
|
||||
[SDL_SCANCODE_F19] /* = USB 110 */ = KEY_F19,
|
||||
[SDL_SCANCODE_F20] /* = USB 111 */ = KEY_F20,
|
||||
[SDL_SCANCODE_HELP] /* = USB 117 */ = KEY_HELP,
|
||||
[SDL_SCANCODE_MENU] /* = USB 118 */ = KEY_MENU,
|
||||
[SDL_SCANCODE_STOP] /* = USB 120 */ = KEY_CANCEL,
|
||||
[SDL_SCANCODE_AGAIN] /* = USB 121 */ = KEY_AGAIN,
|
||||
[SDL_SCANCODE_UNDO] /* = USB 122 */ = KEY_UNDO,
|
||||
[SDL_SCANCODE_CUT] /* = USB 123 */ = KEY_CUT,
|
||||
[SDL_SCANCODE_COPY] /* = USB 124 */ = KEY_COPY,
|
||||
[SDL_SCANCODE_PASTE] /* = USB 125 */ = KEY_PASTE,
|
||||
[SDL_SCANCODE_FIND] /* = USB 126 */ = KEY_FIND,
|
||||
[SDL_SCANCODE_MUTE] /* = USB 127 */ = KEY_MUTE,
|
||||
[SDL_SCANCODE_VOLUMEUP] /* = USB 128 */ = KEY_VOLUMEUP,
|
||||
[SDL_SCANCODE_VOLUMEDOWN] /* = USB 129 */ = KEY_VOLUMEDOWN,
|
||||
[SDL_SCANCODE_KP_COMMA] /* = USB 133 */ = KEY_KPCOMMA,
|
||||
[SDL_SCANCODE_INTERNATIONAL1] /* = USB 135 */ = KEY_RO,
|
||||
[SDL_SCANCODE_INTERNATIONAL2] /* = USB 136 */ = KEY_KATAKANAHIRAGANA,
|
||||
[SDL_SCANCODE_INTERNATIONAL3] /* = USB 137 */ = KEY_YEN,
|
||||
[SDL_SCANCODE_INTERNATIONAL4] /* = USB 138 */ = KEY_HENKAN,
|
||||
[SDL_SCANCODE_INTERNATIONAL5] /* = USB 139 */ = KEY_MUHENKAN,
|
||||
[SDL_SCANCODE_LANG1] /* = USB 144 */ = KEY_HANGEUL,
|
||||
[SDL_SCANCODE_LANG2] /* = USB 145 */ = KEY_HANJA,
|
||||
[SDL_SCANCODE_SYSREQ] /* = USB 154 */ = KEY_RIGHTSHIFT,
|
||||
[SDL_SCANCODE_CANCEL] /* = USB 155 */ = KEY_STOP,
|
||||
[SDL_SCANCODE_KP_LEFTPAREN] /* = USB 182 */ = KEY_KPLEFTPAREN,
|
||||
[SDL_SCANCODE_KP_RIGHTPAREN] /* = USB 183 */ = KEY_KPRIGHTPAREN,
|
||||
[SDL_SCANCODE_KP_PLUSMINUS] /* = USB 215 */ = KEY_KPPLUSMINUS,
|
||||
[SDL_SCANCODE_LCTRL] /* = USB 224 */ = KEY_LEFTCTRL,
|
||||
[SDL_SCANCODE_LSHIFT] /* = USB 225 */ = KEY_LEFTSHIFT,
|
||||
[SDL_SCANCODE_LALT] /* = USB 226 */ = KEY_LEFTALT,
|
||||
[SDL_SCANCODE_LGUI] /* = USB 227 */ = KEY_LEFTMETA,
|
||||
[SDL_SCANCODE_RCTRL] /* = USB 228 */ = KEY_RIGHTCTRL,
|
||||
[SDL_SCANCODE_RSHIFT] /* = USB 229 */ = KEY_RIGHTSHIFT,
|
||||
[SDL_SCANCODE_RALT] /* = USB 230 */ = KEY_RIGHTALT,
|
||||
[SDL_SCANCODE_RGUI] /* = USB 231 */ = KEY_RIGHTMETA,
|
||||
[SDL_SCANCODE_MODE] /* = USB 257 */ = KEY_ZENKAKUHANKAKU,
|
||||
[SDL_SCANCODE_AUDIONEXT] /* = USB 258 */ = KEY_NEXTSONG,
|
||||
[SDL_SCANCODE_AUDIOPREV] /* = USB 259 */ = KEY_PREVIOUSSONG,
|
||||
[SDL_SCANCODE_AUDIOSTOP] /* = USB 260 */ = KEY_STOPCD,
|
||||
[SDL_SCANCODE_AUDIOPLAY] /* = USB 261 */ = KEY_PLAYPAUSE,
|
||||
[SDL_SCANCODE_MEDIASELECT] /* = USB 263 */ = KEY_MEDIA,
|
||||
[SDL_SCANCODE_WWW] /* = USB 264 */ = KEY_WWW,
|
||||
[SDL_SCANCODE_MAIL] /* = USB 265 */ = KEY_MAIL,
|
||||
[SDL_SCANCODE_CALCULATOR] /* = USB 266 */ = KEY_CALC,
|
||||
[SDL_SCANCODE_COMPUTER] /* = USB 267 */ = KEY_COMPUTER,
|
||||
[SDL_SCANCODE_AC_SEARCH] /* = USB 268 */ = KEY_SEARCH,
|
||||
[SDL_SCANCODE_AC_HOME] /* = USB 269 */ = KEY_HOMEPAGE,
|
||||
[SDL_SCANCODE_AC_BACK] /* = USB 270 */ = KEY_BACK,
|
||||
[SDL_SCANCODE_AC_FORWARD] /* = USB 271 */ = KEY_FORWARD,
|
||||
[SDL_SCANCODE_AC_REFRESH] /* = USB 273 */ = KEY_REFRESH,
|
||||
[SDL_SCANCODE_AC_BOOKMARKS] /* = USB 274 */ = KEY_BOOKMARKS,
|
||||
[SDL_SCANCODE_BRIGHTNESSDOWN] /* = USB 275 */ = KEY_BRIGHTNESSDOWN,
|
||||
[SDL_SCANCODE_BRIGHTNESSUP] /* = USB 276 */ = KEY_BRIGHTNESSUP,
|
||||
[SDL_SCANCODE_DISPLAYSWITCH] /* = USB 277 */ = KEY_SWITCHVIDEOMODE,
|
||||
[SDL_SCANCODE_KBDILLUMTOGGLE] /* = USB 278 */ = KEY_KBDILLUMTOGGLE,
|
||||
[SDL_SCANCODE_KBDILLUMDOWN] /* = USB 279 */ = KEY_KBDILLUMDOWN,
|
||||
[SDL_SCANCODE_KBDILLUMUP] /* = USB 280 */ = KEY_KBDILLUMUP,
|
||||
[SDL_SCANCODE_EJECT] /* = USB 281 */ = KEY_EJECTCD,
|
||||
[SDL_SCANCODE_SLEEP] /* = USB 282 */ = KEY_SLEEP,
|
||||
[SDL_SCANCODE_APP1] /* = USB 283 */ = KEY_PROG1,
|
||||
[SDL_SCANCODE_APP2] /* = USB 284 */ = KEY_PROG2,
|
||||
[SDL_SCANCODE_AUDIOREWIND] /* = USB 285 */ = KEY_REWIND,
|
||||
[SDL_SCANCODE_AUDIOFASTFORWARD] /* = USB 286 */ = KEY_FASTFORWARD,
|
||||
};
|
||||
@@ -1,551 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "interface/displayserver.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_syswm.h>
|
||||
|
||||
#ifdef ENABLE_EGL
|
||||
#include <EGL/eglext.h>
|
||||
#endif
|
||||
|
||||
#if defined(SDL_VIDEO_DRIVER_WAYLAND)
|
||||
#include <wayland-egl.h>
|
||||
#endif
|
||||
|
||||
#include "app.h"
|
||||
#include "kb.h"
|
||||
#include "egl_dynprocs.h"
|
||||
#include "common/types.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
struct SDLDSState
|
||||
{
|
||||
SDL_Window * window;
|
||||
SDL_Cursor * cursor;
|
||||
|
||||
EGLNativeWindowType wlDisplay;
|
||||
|
||||
bool keyboardGrabbed;
|
||||
bool pointerGrabbed;
|
||||
bool exiting;
|
||||
};
|
||||
|
||||
static struct SDLDSState sdl;
|
||||
|
||||
/* forwards */
|
||||
static int sdlEventFilter(void * userdata, SDL_Event * event);
|
||||
|
||||
static void sdlSetup(void)
|
||||
{
|
||||
}
|
||||
|
||||
static bool sdlProbe(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sdlEarlyInit(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sdlInit(const LG_DSInitParams params)
|
||||
{
|
||||
memset(&sdl, 0, sizeof(sdl));
|
||||
|
||||
// Allow screensavers for now: we will enable and disable as needed.
|
||||
SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1");
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
||||
{
|
||||
DEBUG_ERROR("SDL_Init Failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
if (params.opengl)
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER , 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE , 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE , 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE , 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
sdl.window = SDL_CreateWindow(
|
||||
params.title,
|
||||
params.center ? SDL_WINDOWPOS_CENTERED : params.x,
|
||||
params.center ? SDL_WINDOWPOS_CENTERED : params.y,
|
||||
params.w,
|
||||
params.h,
|
||||
(
|
||||
SDL_WINDOW_HIDDEN |
|
||||
(params.resizable ? SDL_WINDOW_RESIZABLE : 0) |
|
||||
(params.borderless ? SDL_WINDOW_BORDERLESS : 0) |
|
||||
(params.maximize ? SDL_WINDOW_MAXIMIZED : 0) |
|
||||
(params.opengl ? SDL_WINDOW_OPENGL : 0)
|
||||
)
|
||||
);
|
||||
|
||||
if (sdl.window == NULL)
|
||||
{
|
||||
DEBUG_ERROR("Could not create an SDL window: %s\n", SDL_GetError());
|
||||
goto fail_init;
|
||||
}
|
||||
|
||||
const uint8_t data[4] = {0xf, 0x9, 0x9, 0xf};
|
||||
const uint8_t mask[4] = {0xf, 0xf, 0xf, 0xf};
|
||||
sdl.cursor = SDL_CreateCursor(data, mask, 8, 4, 4, 0);
|
||||
SDL_SetCursor(sdl.cursor);
|
||||
|
||||
SDL_ShowWindow(sdl.window);
|
||||
|
||||
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS,
|
||||
params.minimizeOnFocusLoss ? "1" : "0");
|
||||
|
||||
if (params.fullscreen)
|
||||
SDL_SetWindowFullscreen(sdl.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
|
||||
if (!params.center)
|
||||
SDL_SetWindowPosition(sdl.window, params.x, params.y);
|
||||
|
||||
// ensure mouse acceleration is identical in server mode
|
||||
SDL_SetHintWithPriority(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1", SDL_HINT_OVERRIDE);
|
||||
|
||||
SDL_SetEventFilter(sdlEventFilter, NULL);
|
||||
return true;
|
||||
|
||||
fail_init:
|
||||
SDL_Quit();
|
||||
return false;
|
||||
}
|
||||
|
||||
static void sdlStartup(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void sdlShutdown(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void sdlFree(void)
|
||||
{
|
||||
SDL_DestroyWindow(sdl.window);
|
||||
|
||||
if (sdl.cursor)
|
||||
SDL_FreeCursor(sdl.cursor);
|
||||
|
||||
if (sdl.window)
|
||||
SDL_DestroyWindow(sdl.window);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
static bool sdlGetProp(LG_DSProperty prop, void * ret)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EGL
|
||||
static EGLDisplay sdlGetEGLDisplay(void)
|
||||
{
|
||||
SDL_SysWMinfo wminfo;
|
||||
SDL_VERSION(&wminfo.version);
|
||||
if (!SDL_GetWindowWMInfo(sdl.window, &wminfo))
|
||||
{
|
||||
DEBUG_ERROR("SDL_GetWindowWMInfo failed");
|
||||
return EGL_NO_DISPLAY;
|
||||
}
|
||||
|
||||
EGLNativeDisplayType native;
|
||||
EGLenum platform;
|
||||
|
||||
switch(wminfo.subsystem)
|
||||
{
|
||||
case SDL_SYSWM_X11:
|
||||
native = (EGLNativeDisplayType)wminfo.info.x11.display;
|
||||
platform = EGL_PLATFORM_X11_KHR;
|
||||
break;
|
||||
|
||||
#if defined(SDL_VIDEO_DRIVER_WAYLAND)
|
||||
case SDL_SYSWM_WAYLAND:
|
||||
native = (EGLNativeDisplayType)wminfo.info.wl.display;
|
||||
platform = EGL_PLATFORM_WAYLAND_KHR;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Unsupported subsystem");
|
||||
return EGL_NO_DISPLAY;
|
||||
}
|
||||
|
||||
const char *early_exts = eglQueryString(NULL, EGL_EXTENSIONS);
|
||||
|
||||
if (strstr(early_exts, "EGL_KHR_platform_base") != NULL &&
|
||||
g_egl_dynProcs.eglGetPlatformDisplay)
|
||||
{
|
||||
DEBUG_INFO("Using eglGetPlatformDisplay");
|
||||
return g_egl_dynProcs.eglGetPlatformDisplay(platform, native, NULL);
|
||||
}
|
||||
|
||||
if (strstr(early_exts, "EGL_EXT_platform_base") != NULL &&
|
||||
g_egl_dynProcs.eglGetPlatformDisplayEXT)
|
||||
{
|
||||
DEBUG_INFO("Using eglGetPlatformDisplayEXT");
|
||||
return g_egl_dynProcs.eglGetPlatformDisplayEXT(platform, native, NULL);
|
||||
}
|
||||
|
||||
DEBUG_INFO("Using eglGetDisplay");
|
||||
return eglGetDisplay(native);
|
||||
}
|
||||
|
||||
static EGLNativeWindowType sdlGetEGLNativeWindow(void)
|
||||
{
|
||||
SDL_SysWMinfo wminfo;
|
||||
SDL_VERSION(&wminfo.version);
|
||||
if (!SDL_GetWindowWMInfo(sdl.window, &wminfo))
|
||||
{
|
||||
DEBUG_ERROR("SDL_GetWindowWMInfo failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch(wminfo.subsystem)
|
||||
{
|
||||
case SDL_SYSWM_X11:
|
||||
return (EGLNativeWindowType)wminfo.info.x11.window;
|
||||
|
||||
#if defined(SDL_VIDEO_DRIVER_WAYLAND)
|
||||
case SDL_SYSWM_WAYLAND:
|
||||
{
|
||||
if (sdl.wlDisplay)
|
||||
return sdl.wlDisplay;
|
||||
|
||||
int width, height;
|
||||
SDL_GetWindowSize(sdl.window, &width, &height);
|
||||
sdl.wlDisplay = (EGLNativeWindowType)wl_egl_window_create(
|
||||
wminfo.info.wl.surface, width, height);
|
||||
|
||||
return sdl.wlDisplay;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Unsupported subsystem");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void sdlEGLSwapBuffers(EGLDisplay display, EGLSurface surface)
|
||||
{
|
||||
eglSwapBuffers(display, surface);
|
||||
}
|
||||
#endif //ENABLE_EGL
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
static LG_DSGLContext sdlGLCreateContext(void)
|
||||
{
|
||||
return (LG_DSGLContext)SDL_GL_CreateContext(sdl.window);
|
||||
}
|
||||
|
||||
static void sdlGLDeleteContext(LG_DSGLContext context)
|
||||
{
|
||||
SDL_GL_DeleteContext((SDL_GLContext)context);
|
||||
}
|
||||
|
||||
static void sdlGLMakeCurrent(LG_DSGLContext context)
|
||||
{
|
||||
SDL_GL_MakeCurrent(sdl.window, (SDL_GLContext)context);
|
||||
}
|
||||
|
||||
static void sdlGLSetSwapInterval(int interval)
|
||||
{
|
||||
SDL_GL_SetSwapInterval(interval);
|
||||
}
|
||||
|
||||
static void sdlGLSwapBuffers(void)
|
||||
{
|
||||
SDL_GL_SwapWindow(sdl.window);
|
||||
}
|
||||
#endif //ENABLE_OPENGL
|
||||
|
||||
static int sdlEventFilter(void * userdata, SDL_Event * event)
|
||||
{
|
||||
switch(event->type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
app_handleCloseEvent();
|
||||
break;
|
||||
|
||||
case SDL_MOUSEMOTION:
|
||||
// stop motion events during the warp out of the window
|
||||
if (sdl.exiting)
|
||||
break;
|
||||
|
||||
app_updateCursorPos(event->motion.x, event->motion.y);
|
||||
app_handleMouseRelative(event->motion.xrel, event->motion.yrel,
|
||||
event->motion.xrel, event->motion.yrel);
|
||||
break;
|
||||
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
{
|
||||
int button = event->button.button;
|
||||
if (button > 3)
|
||||
button += 2;
|
||||
|
||||
app_handleButtonPress(button);
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
{
|
||||
int button = event->button.button;
|
||||
if (button > 3)
|
||||
button += 2;
|
||||
|
||||
app_handleButtonRelease(button);
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_MOUSEWHEEL:
|
||||
{
|
||||
int button = event->wheel.y > 0 ? 4 : 5;
|
||||
app_handleButtonPress(button);
|
||||
app_handleButtonRelease(button);
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_KEYDOWN:
|
||||
{
|
||||
SDL_Scancode sc = event->key.keysym.scancode;
|
||||
app_handleKeyPress(sdl_to_xfree86[sc]);
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_KEYUP:
|
||||
{
|
||||
SDL_Scancode sc = event->key.keysym.scancode;
|
||||
app_handleKeyRelease(sdl_to_xfree86[sc]);
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
switch(event->window.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_ENTER:
|
||||
app_handleEnterEvent(true);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_LEAVE:
|
||||
sdl.exiting = false;
|
||||
app_handleEnterEvent(false);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
app_handleFocusEvent(true);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
app_handleFocusEvent(false);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
{
|
||||
struct Border border;
|
||||
SDL_GetWindowBordersSize(
|
||||
sdl.window,
|
||||
&border.top,
|
||||
&border.left,
|
||||
&border.bottom,
|
||||
&border.right
|
||||
);
|
||||
app_handleResizeEvent(
|
||||
event->window.data1,
|
||||
event->window.data2,
|
||||
border);
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_WINDOWEVENT_MOVED:
|
||||
app_updateWindowPos(event->window.data1, event->window.data2);
|
||||
break;
|
||||
|
||||
case SDL_WINDOWEVENT_CLOSE:
|
||||
app_handleCloseEvent();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sdlShowPointer(bool show)
|
||||
{
|
||||
SDL_ShowCursor(show ? SDL_ENABLE : SDL_DISABLE);
|
||||
}
|
||||
|
||||
static void sdlGrabPointer(void)
|
||||
{
|
||||
SDL_SetWindowGrab(sdl.window, SDL_TRUE);
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
sdl.pointerGrabbed = true;
|
||||
}
|
||||
|
||||
static void sdlUngrabPointer(void)
|
||||
{
|
||||
SDL_SetWindowGrab(sdl.window, SDL_FALSE);
|
||||
SDL_SetRelativeMouseMode(SDL_FALSE);
|
||||
sdl.pointerGrabbed = false;
|
||||
}
|
||||
|
||||
static void sdlGrabKeyboard(void)
|
||||
{
|
||||
if (sdl.pointerGrabbed)
|
||||
SDL_SetWindowGrab(sdl.window, SDL_FALSE);
|
||||
else
|
||||
{
|
||||
DEBUG_WARN("SDL does not support grabbing only the keyboard, grabbing all");
|
||||
sdl.pointerGrabbed = true;
|
||||
}
|
||||
|
||||
SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1");
|
||||
SDL_SetWindowGrab(sdl.window, SDL_TRUE);
|
||||
sdl.keyboardGrabbed = true;
|
||||
}
|
||||
|
||||
static void sdlUngrabKeyboard(void)
|
||||
{
|
||||
SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "0");
|
||||
SDL_SetWindowGrab(sdl.window, SDL_FALSE);
|
||||
if (sdl.pointerGrabbed)
|
||||
SDL_SetWindowGrab(sdl.window, SDL_TRUE);
|
||||
sdl.keyboardGrabbed = false;
|
||||
}
|
||||
|
||||
static void sdlWarpPointer(int x, int y, bool exiting)
|
||||
{
|
||||
if (sdl.exiting)
|
||||
return;
|
||||
|
||||
sdl.exiting = exiting;
|
||||
|
||||
// if exiting turn off relative mode
|
||||
if (exiting)
|
||||
SDL_SetRelativeMouseMode(SDL_FALSE);
|
||||
|
||||
// issue the warp
|
||||
SDL_WarpMouseInWindow(sdl.window, x, y);
|
||||
}
|
||||
|
||||
static void sdlRealignPointer(void)
|
||||
{
|
||||
app_handleMouseRelative(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
static bool sdlIsValidPointerPos(int x, int y)
|
||||
{
|
||||
const int displays = SDL_GetNumVideoDisplays();
|
||||
for(int i = 0; i < displays; ++i)
|
||||
{
|
||||
SDL_Rect r;
|
||||
SDL_GetDisplayBounds(i, &r);
|
||||
if ((x >= r.x && x < r.x + r.w) &&
|
||||
(y >= r.y && y < r.y + r.h))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void sdlInhibitIdle(void)
|
||||
{
|
||||
SDL_DisableScreenSaver();
|
||||
}
|
||||
|
||||
static void sdlUninhibitIdle(void)
|
||||
{
|
||||
SDL_EnableScreenSaver();
|
||||
}
|
||||
|
||||
static void sdlWait(unsigned int time)
|
||||
{
|
||||
SDL_WaitEventTimeout(NULL, time);
|
||||
}
|
||||
|
||||
static void sdlSetWindowSize(int x, int y)
|
||||
{
|
||||
SDL_SetWindowSize(sdl.window, x, y);
|
||||
}
|
||||
|
||||
static void sdlSetFullscreen(bool fs)
|
||||
{
|
||||
SDL_SetWindowFullscreen(sdl.window, fs ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
||||
}
|
||||
|
||||
static bool sdlGetFullscreen(void)
|
||||
{
|
||||
return (SDL_GetWindowFlags(sdl.window) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0;
|
||||
}
|
||||
|
||||
struct LG_DisplayServerOps LGDS_SDL =
|
||||
{
|
||||
.setup = sdlSetup,
|
||||
.probe = sdlProbe,
|
||||
.earlyInit = sdlEarlyInit,
|
||||
.init = sdlInit,
|
||||
.startup = sdlStartup,
|
||||
.shutdown = sdlShutdown,
|
||||
.free = sdlFree,
|
||||
.getProp = sdlGetProp,
|
||||
|
||||
#ifdef ENABLE_EGL
|
||||
.getEGLDisplay = sdlGetEGLDisplay,
|
||||
.getEGLNativeWindow = sdlGetEGLNativeWindow,
|
||||
.eglSwapBuffers = sdlEGLSwapBuffers,
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
.glCreateContext = sdlGLCreateContext,
|
||||
.glDeleteContext = sdlGLDeleteContext,
|
||||
.glMakeCurrent = sdlGLMakeCurrent,
|
||||
.glSetSwapInterval = sdlGLSetSwapInterval,
|
||||
.glSwapBuffers = sdlGLSwapBuffers,
|
||||
#endif
|
||||
|
||||
.showPointer = sdlShowPointer,
|
||||
.grabPointer = sdlGrabPointer,
|
||||
.ungrabPointer = sdlUngrabPointer,
|
||||
.grabKeyboard = sdlGrabKeyboard,
|
||||
.ungrabKeyboard = sdlUngrabKeyboard,
|
||||
.warpPointer = sdlWarpPointer,
|
||||
.realignPointer = sdlRealignPointer,
|
||||
.isValidPointerPos = sdlIsValidPointerPos,
|
||||
.inhibitIdle = sdlInhibitIdle,
|
||||
.uninhibitIdle = sdlUninhibitIdle,
|
||||
.wait = sdlWait,
|
||||
.setWindowSize = sdlSetWindowSize,
|
||||
.setFullscreen = sdlSetFullscreen,
|
||||
.getFullscreen = sdlGetFullscreen,
|
||||
|
||||
/* SDL does not have clipboard support */
|
||||
.cbInit = NULL,
|
||||
};
|
||||
@@ -2,12 +2,25 @@ cmake_minimum_required(VERSION 3.0)
|
||||
project(displayserver_Wayland LANGUAGES C)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(DISPLAYSERVER_Wayland_PKGCONFIG REQUIRED
|
||||
pkg_check_modules(DISPLAYSERVER_Wayland REQUIRED IMPORTED_TARGET
|
||||
wayland-client
|
||||
wayland-cursor
|
||||
xkbcommon
|
||||
)
|
||||
|
||||
#pkg_check_modules(DISPLAYSERVER_Wayland_OPT_PKGCONFIG
|
||||
#)
|
||||
set(DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES "")
|
||||
set(displayserver_Wayland_SHELL_SRC "")
|
||||
|
||||
if (ENABLE_LIBDECOR)
|
||||
pkg_check_modules(DISPLAYSERVER_Wayland_LIBDECOR REQUIRED IMPORTED_TARGET
|
||||
libdecor-0
|
||||
)
|
||||
list(APPEND DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES PkgConfig::DISPLAYSERVER_Wayland_LIBDECOR)
|
||||
list(APPEND displayserver_Wayland_SHELL_SRC shell_libdecor.c)
|
||||
add_compile_definitions(ENABLE_LIBDECOR)
|
||||
else()
|
||||
list(APPEND displayserver_Wayland_SHELL_SRC shell_xdg.c)
|
||||
endif()
|
||||
|
||||
add_library(displayserver_Wayland STATIC
|
||||
clipboard.c
|
||||
@@ -15,15 +28,18 @@ add_library(displayserver_Wayland STATIC
|
||||
gl.c
|
||||
idle.c
|
||||
input.c
|
||||
output.c
|
||||
poll.c
|
||||
presentation.c
|
||||
state.c
|
||||
registry.c
|
||||
wayland.c
|
||||
window.c
|
||||
${displayserver_Wayland_SHELL_SRC}
|
||||
)
|
||||
|
||||
target_link_libraries(displayserver_Wayland
|
||||
${DISPLAYSERVER_Wayland_PKGCONFIG_LIBRARIES}
|
||||
PkgConfig::DISPLAYSERVER_Wayland
|
||||
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES}
|
||||
lg_common
|
||||
)
|
||||
@@ -31,8 +47,6 @@ target_link_libraries(displayserver_Wayland
|
||||
target_include_directories(displayserver_Wayland
|
||||
PRIVATE
|
||||
src
|
||||
${DISPLAYSERVER_Wayland_PKGCONFIG_INCLUDE_DIRS}
|
||||
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
|
||||
@@ -58,6 +72,12 @@ include_directories("${CMAKE_BINARY_DIR}/wayland")
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-shell-client-protocol")
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/stable/presentation-time/presentation-time.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-presentation-time-client-protocol")
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/stable/viewporter/viewporter.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-viewporter-client-protocol")
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-decoration-unstable-v1-client-protocol")
|
||||
@@ -73,4 +93,6 @@ wayland_generate(
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-idle-inhibit-unstable-v1-client-protocol")
|
||||
|
||||
wayland_generate(
|
||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-output/xdg-output-unstable-v1.xml"
|
||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-output-unstable-v1-client-protocol")
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
|
||||
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
|
||||
https://looking-glass.io
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "wayland.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -33,6 +32,11 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "app.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
struct DataOffer {
|
||||
bool isSelfCopy;
|
||||
char * mimetypes[LG_CLIPBOARD_DATA_NONE];
|
||||
};
|
||||
|
||||
static const char * textMimetypes[] =
|
||||
{
|
||||
"text/plain",
|
||||
@@ -116,6 +120,9 @@ static bool isTextMimetype(const char * mimetype)
|
||||
if (containsMimetype(textMimetypes, mimetype))
|
||||
return true;
|
||||
|
||||
if (!strcmp(mimetype, "text/ico"))
|
||||
return false;
|
||||
|
||||
char * text = "text/";
|
||||
if (!strncmp(mimetype, text, strlen(text)))
|
||||
return true;
|
||||
@@ -151,27 +158,74 @@ static enum LG_ClipboardData mimetypeToCbType(const char * mimetype)
|
||||
return LG_CLIPBOARD_DATA_NONE;
|
||||
}
|
||||
|
||||
static bool isImageCbtype(enum LG_ClipboardData type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case LG_CLIPBOARD_DATA_TEXT:
|
||||
return false;
|
||||
case LG_CLIPBOARD_DATA_PNG:
|
||||
case LG_CLIPBOARD_DATA_BMP:
|
||||
case LG_CLIPBOARD_DATA_TIFF:
|
||||
case LG_CLIPBOARD_DATA_JPEG:
|
||||
return true;
|
||||
default:
|
||||
DEBUG_ERROR("invalid clipboard type");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static bool hasAnyMimetype(char ** mimetypes)
|
||||
{
|
||||
for (enum LG_ClipboardData i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
||||
if (mimetypes[i])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool hasImageMimetype(char ** mimetypes)
|
||||
{
|
||||
for (enum LG_ClipboardData i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
||||
if (isImageCbtype(i) && mimetypes[i])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Destination client handlers.
|
||||
|
||||
static void dataOfferHandleOffer(void * data, struct wl_data_offer * offer,
|
||||
static void dataOfferHandleOffer(void * opaque, struct wl_data_offer * offer,
|
||||
const char * mimetype)
|
||||
{
|
||||
enum LG_ClipboardData type = mimetypeToCbType(mimetype);
|
||||
// We almost never prefer text/html, as that's used to represent rich text.
|
||||
// Since we can't copy or paste rich text, we should instead prefer actual
|
||||
// images or plain text.
|
||||
if (type != LG_CLIPBOARD_DATA_NONE &&
|
||||
(wlCb.pendingType == LG_CLIPBOARD_DATA_NONE ||
|
||||
strstr(wlCb.pendingMimetype, "html")))
|
||||
{
|
||||
wlCb.pendingType = type;
|
||||
if (wlCb.pendingMimetype)
|
||||
free(wlCb.pendingMimetype);
|
||||
wlCb.pendingMimetype = strdup(mimetype);
|
||||
}
|
||||
struct DataOffer * data = opaque;
|
||||
|
||||
if (!strcmp(mimetype, wlCb.lgMimetype))
|
||||
wlCb.isSelfCopy = true;
|
||||
{
|
||||
data->isSelfCopy = true;
|
||||
return;
|
||||
}
|
||||
|
||||
enum LG_ClipboardData type = mimetypeToCbType(mimetype);
|
||||
|
||||
if (type == LG_CLIPBOARD_DATA_NONE)
|
||||
return;
|
||||
|
||||
// text/html represents rich text format, which is almost never desirable when
|
||||
// and should not be used when a plain text or image format is available.
|
||||
if ((isImageCbtype(type) || containsMimetype(textMimetypes, mimetype)) &&
|
||||
data->mimetypes[LG_CLIPBOARD_DATA_TEXT] &&
|
||||
strstr(data->mimetypes[LG_CLIPBOARD_DATA_TEXT], "html"))
|
||||
{
|
||||
free(data->mimetypes[LG_CLIPBOARD_DATA_TEXT]);
|
||||
data->mimetypes[LG_CLIPBOARD_DATA_TEXT] = NULL;
|
||||
}
|
||||
|
||||
if (strstr(mimetype, "html") && hasImageMimetype(data->mimetypes))
|
||||
return;
|
||||
|
||||
if (data->mimetypes[type])
|
||||
return;
|
||||
|
||||
data->mimetypes[type] = strdup(mimetype);
|
||||
}
|
||||
|
||||
static void dataOfferHandleSourceActions(void * data,
|
||||
@@ -195,132 +249,93 @@ static const struct wl_data_offer_listener dataOfferListener = {
|
||||
static void dataDeviceHandleDataOffer(void * data,
|
||||
struct wl_data_device * dataDevice, struct wl_data_offer * offer)
|
||||
{
|
||||
wlCb.pendingType = LG_CLIPBOARD_DATA_NONE;
|
||||
wlCb.isSelfCopy = false;
|
||||
wl_data_offer_add_listener(offer, &dataOfferListener, NULL);
|
||||
}
|
||||
|
||||
static void clipboardReadCancel(struct ClipboardRead * data, bool freeBuf)
|
||||
{
|
||||
waylandEpollUnregister(data->fd);
|
||||
close(data->fd);
|
||||
wl_data_offer_destroy(data->offer);
|
||||
if (freeBuf)
|
||||
free(data->buf);
|
||||
free(data);
|
||||
wlCb.currentRead = NULL;
|
||||
}
|
||||
|
||||
static void clipboardReadCallback(uint32_t events, void * opaque)
|
||||
{
|
||||
struct ClipboardRead * data = opaque;
|
||||
if (events & EPOLLERR)
|
||||
struct DataOffer * extra = calloc(1, sizeof(struct DataOffer));
|
||||
if (!extra)
|
||||
{
|
||||
clipboardReadCancel(data, true);
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t result = read(data->fd, data->buf + data->numRead, data->size - data->numRead);
|
||||
if (result < 0)
|
||||
{
|
||||
DEBUG_ERROR("Failed to read from clipboard: %s", strerror(errno));
|
||||
clipboardReadCancel(data, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
data->buf[data->numRead] = 0;
|
||||
wlCb.stashedType = data->type;
|
||||
wlCb.stashedSize = data->numRead;
|
||||
wlCb.stashedContents = data->buf;
|
||||
|
||||
clipboardReadCancel(data, false);
|
||||
app_clipboardNotify(wlCb.stashedType, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
data->numRead += result;
|
||||
if (data->numRead >= data->size)
|
||||
{
|
||||
data->size *= 2;
|
||||
void * nbuf = realloc(data->buf, data->size);
|
||||
if (!nbuf) {
|
||||
DEBUG_ERROR("Failed to realloc clipboard buffer: %s", strerror(errno));
|
||||
clipboardReadCancel(data, true);
|
||||
return;
|
||||
}
|
||||
|
||||
data->buf = nbuf;
|
||||
DEBUG_ERROR("Out of memory while handling clipboard");
|
||||
abort();
|
||||
}
|
||||
wl_data_offer_set_user_data(offer, extra);
|
||||
wl_data_offer_add_listener(offer, &dataOfferListener, extra);
|
||||
}
|
||||
|
||||
static void dataDeviceHandleSelection(void * opaque,
|
||||
struct wl_data_device * dataDevice, struct wl_data_offer * offer)
|
||||
{
|
||||
if (wlCb.pendingType == LG_CLIPBOARD_DATA_NONE || wlCb.isSelfCopy || !offer)
|
||||
return;
|
||||
|
||||
if (wlCb.currentRead)
|
||||
clipboardReadCancel(wlCb.currentRead, true);
|
||||
|
||||
int fds[2];
|
||||
if (pipe(fds) < 0)
|
||||
if (!offer)
|
||||
{
|
||||
DEBUG_ERROR("Failed to get a clipboard pipe: %s", strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
wl_data_offer_receive(offer, wlCb.pendingMimetype, fds[1]);
|
||||
close(fds[1]);
|
||||
free(wlCb.pendingMimetype);
|
||||
wlCb.pendingMimetype = NULL;
|
||||
|
||||
wl_display_roundtrip(wlWm.display);
|
||||
|
||||
if (wlCb.stashedContents)
|
||||
{
|
||||
free(wlCb.stashedContents);
|
||||
wlCb.stashedContents = NULL;
|
||||
}
|
||||
|
||||
struct ClipboardRead * data = malloc(sizeof(struct ClipboardRead));
|
||||
if (!data)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory to read clipboard");
|
||||
close(fds[0]);
|
||||
waylandCBInvalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
data->fd = fds[0];
|
||||
data->size = 4096;
|
||||
data->numRead = 0;
|
||||
data->buf = malloc(data->size);
|
||||
data->offer = offer;
|
||||
data->type = wlCb.pendingType;
|
||||
|
||||
if (!data->buf)
|
||||
struct DataOffer * extra = wl_data_offer_get_user_data(offer);
|
||||
if (!hasAnyMimetype(extra->mimetypes) || extra->isSelfCopy)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory to receive clipboard data");
|
||||
close(data->fd);
|
||||
free(data);
|
||||
waylandCBInvalidate();
|
||||
wl_data_offer_destroy(offer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!waylandEpollRegister(data->fd, clipboardReadCallback, data, EPOLLIN))
|
||||
{
|
||||
DEBUG_ERROR("Failed to register clipboard read into epoll: %s", strerror(errno));
|
||||
close(data->fd);
|
||||
free(data->buf);
|
||||
free(data);
|
||||
}
|
||||
wlCb.offer = offer;
|
||||
|
||||
wlCb.currentRead = data;
|
||||
for (enum LG_ClipboardData i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
||||
free(wlCb.mimetypes[i]);
|
||||
memcpy(wlCb.mimetypes, extra->mimetypes, sizeof(wlCb.mimetypes));
|
||||
|
||||
wl_data_offer_set_user_data(offer, NULL);
|
||||
free(extra);
|
||||
|
||||
int idx = 0;
|
||||
enum LG_ClipboardData types[LG_CLIPBOARD_DATA_NONE];
|
||||
for (enum LG_ClipboardData i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
||||
if (wlCb.mimetypes[i])
|
||||
types[idx++] = i;
|
||||
|
||||
app_clipboardNotifyTypes(types, idx);
|
||||
}
|
||||
|
||||
static void dataDeviceHandleEnter(void * data, struct wl_data_device * device,
|
||||
uint32_t serial, struct wl_surface * surface, wl_fixed_t sxW, wl_fixed_t syW,
|
||||
struct wl_data_offer * offer)
|
||||
{
|
||||
DEBUG_ASSERT(wlCb.dndOffer == NULL);
|
||||
wlCb.dndOffer = offer;
|
||||
|
||||
struct DataOffer * extra = wl_data_offer_get_user_data(offer);
|
||||
for (enum LG_ClipboardData i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
||||
free(extra->mimetypes[i]);
|
||||
free(extra);
|
||||
|
||||
wl_data_offer_set_user_data(offer, NULL);
|
||||
wl_data_offer_set_actions(offer, WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE,
|
||||
WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE);
|
||||
}
|
||||
|
||||
static void dataDeviceHandleMotion(void * data, struct wl_data_device * device,
|
||||
uint32_t time, wl_fixed_t sxW, wl_fixed_t syW)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
static void dataDeviceHandleLeave(void * data, struct wl_data_device * device)
|
||||
{
|
||||
wl_data_offer_destroy(wlCb.dndOffer);
|
||||
wlCb.dndOffer = NULL;
|
||||
}
|
||||
|
||||
static void dataDeviceHandleDrop(void * data, struct wl_data_device * device)
|
||||
{
|
||||
wl_data_offer_destroy(wlCb.dndOffer);
|
||||
wlCb.dndOffer = NULL;
|
||||
}
|
||||
|
||||
static const struct wl_data_device_listener dataDeviceListener = {
|
||||
.data_offer = dataDeviceHandleDataOffer,
|
||||
.selection = dataDeviceHandleSelection,
|
||||
.enter = dataDeviceHandleEnter,
|
||||
.motion = dataDeviceHandleMotion,
|
||||
.leave = dataDeviceHandleLeave,
|
||||
.drop = dataDeviceHandleDrop,
|
||||
};
|
||||
|
||||
bool waylandCBInit(void)
|
||||
@@ -329,7 +344,7 @@ bool waylandCBInit(void)
|
||||
|
||||
if (!wlWm.dataDeviceManager)
|
||||
{
|
||||
DEBUG_ERROR("Missing wl_data_device_manager interface");
|
||||
DEBUG_ERROR("Missing wl_data_device_manager interface (version 3+)");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -341,7 +356,6 @@ bool waylandCBInit(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
wlCb.stashedType = LG_CLIPBOARD_DATA_NONE;
|
||||
wl_data_device_add_listener(wlCb.dataDevice, &dataDeviceListener, NULL);
|
||||
|
||||
snprintf(wlCb.lgMimetype, sizeof(wlCb.lgMimetype),
|
||||
@@ -350,11 +364,120 @@ bool waylandCBInit(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void clipboardReadCancel(struct ClipboardRead * data)
|
||||
{
|
||||
waylandPollUnregister(data->fd);
|
||||
close(data->fd);
|
||||
free(data->buf);
|
||||
free(data);
|
||||
wlCb.currentRead = NULL;
|
||||
}
|
||||
|
||||
static void clipboardReadCallback(uint32_t events, void * opaque)
|
||||
{
|
||||
struct ClipboardRead * data = opaque;
|
||||
if (events & EPOLLERR)
|
||||
{
|
||||
clipboardReadCancel(data);
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t result = read(data->fd, data->buf + data->numRead, data->size - data->numRead);
|
||||
if (result < 0)
|
||||
{
|
||||
DEBUG_ERROR("Failed to read from clipboard: %s", strerror(errno));
|
||||
clipboardReadCancel(data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
app_clipboardNotifySize(data->type, data->numRead);
|
||||
app_clipboardData(data->type, data->buf, data->numRead);
|
||||
clipboardReadCancel(data);
|
||||
return;
|
||||
}
|
||||
|
||||
data->numRead += result;
|
||||
if (data->numRead >= data->size)
|
||||
{
|
||||
data->size *= 2;
|
||||
void * nbuf = realloc(data->buf, data->size);
|
||||
if (!nbuf) {
|
||||
DEBUG_ERROR("Failed to realloc clipboard buffer: %s", strerror(errno));
|
||||
clipboardReadCancel(data);
|
||||
return;
|
||||
}
|
||||
|
||||
data->buf = nbuf;
|
||||
}
|
||||
}
|
||||
|
||||
void waylandCBInvalidate(void)
|
||||
{
|
||||
if (wlCb.currentRead)
|
||||
clipboardReadCancel(wlCb.currentRead);
|
||||
|
||||
app_clipboardRelease();
|
||||
|
||||
if (wlCb.offer)
|
||||
wl_data_offer_destroy(wlCb.offer);
|
||||
wlCb.offer = NULL;
|
||||
}
|
||||
|
||||
void waylandCBRequest(LG_ClipboardData type)
|
||||
{
|
||||
// We only notified once, so it must be this.
|
||||
assert(type == wlCb.stashedType);
|
||||
app_clipboardData(wlCb.stashedType, wlCb.stashedContents, wlCb.stashedSize);
|
||||
if (!wlCb.offer || !wlCb.mimetypes[type])
|
||||
{
|
||||
app_clipboardRelease();
|
||||
return;
|
||||
}
|
||||
|
||||
if (wlCb.currentRead)
|
||||
clipboardReadCancel(wlCb.currentRead);
|
||||
|
||||
int fds[2];
|
||||
if (pipe(fds) < 0)
|
||||
{
|
||||
DEBUG_ERROR("Failed to get a clipboard pipe: %s", strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
wl_data_offer_receive(wlCb.offer, wlCb.mimetypes[type], fds[1]);
|
||||
close(fds[1]);
|
||||
|
||||
struct ClipboardRead * data = malloc(sizeof(*data));
|
||||
if (!data)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory to read clipboard");
|
||||
close(fds[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
data->fd = fds[0];
|
||||
data->size = 4096;
|
||||
data->numRead = 0;
|
||||
data->buf = malloc(data->size);
|
||||
data->offer = wlCb.offer;
|
||||
data->type = type;
|
||||
|
||||
if (!data->buf)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory to receive clipboard data");
|
||||
close(data->fd);
|
||||
free(data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!waylandPollRegister(data->fd, clipboardReadCallback, data, EPOLLIN))
|
||||
{
|
||||
DEBUG_ERROR("Failed to register clipboard read into epoll: %s", strerror(errno));
|
||||
close(data->fd);
|
||||
free(data->buf);
|
||||
free(data);
|
||||
}
|
||||
|
||||
wlCb.currentRead = data;
|
||||
}
|
||||
|
||||
struct ClipboardWrite
|
||||
@@ -383,7 +506,7 @@ static void clipboardWriteCallback(uint32_t events, void * opaque)
|
||||
return;
|
||||
|
||||
error:
|
||||
waylandEpollUnregister(data->fd);
|
||||
waylandPollUnregister(data->fd);
|
||||
close(data->fd);
|
||||
countedBufferRelease(&data->buffer);
|
||||
free(data);
|
||||
@@ -395,12 +518,7 @@ static void dataSourceHandleSend(void * data, struct wl_data_source * source,
|
||||
struct WCBTransfer * transfer = (struct WCBTransfer *) data;
|
||||
if (containsMimetype(transfer->mimetypes, mimetype))
|
||||
{
|
||||
// Consider making this do non-blocking sends to not stall the Wayland
|
||||
// event loop if it becomes a problem. This is "fine" in the sense that
|
||||
// wl-copy also stalls like this, but it's not necessary.
|
||||
fcntl(fd, F_SETFL, 0);
|
||||
|
||||
struct ClipboardWrite * data = malloc(sizeof(struct ClipboardWrite));
|
||||
struct ClipboardWrite * data = malloc(sizeof(*data));
|
||||
if (!data)
|
||||
{
|
||||
DEBUG_ERROR("Out of memory trying to allocate ClipboardWrite");
|
||||
@@ -411,7 +529,7 @@ static void dataSourceHandleSend(void * data, struct wl_data_source * source,
|
||||
data->pos = 0;
|
||||
data->buffer = transfer->data;
|
||||
countedBufferAddRef(transfer->data);
|
||||
waylandEpollRegister(fd, clipboardWriteCallback, data, EPOLLOUT);
|
||||
waylandPollRegister(fd, clipboardWriteCallback, data, EPOLLOUT);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -436,7 +554,7 @@ static const struct wl_data_source_listener dataSourceListener = {
|
||||
static void waylandCBReplyFn(void * opaque, LG_ClipboardData type,
|
||||
uint8_t * data, uint32_t size)
|
||||
{
|
||||
struct WCBTransfer * transfer = malloc(sizeof(struct WCBTransfer));
|
||||
struct WCBTransfer * transfer = malloc(sizeof(*transfer));
|
||||
if (!transfer)
|
||||
{
|
||||
DEBUG_ERROR("Out of memory when allocating WCBTransfer");
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
|
||||
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
|
||||
https://looking-glass.io
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include "wayland.h"
|
||||
@@ -38,7 +38,7 @@ static const uint32_t cursorBitmap[] = {
|
||||
0x000000, 0x000000, 0x000000, 0x000000,
|
||||
};
|
||||
|
||||
static struct wl_buffer * createCursorBuffer(void)
|
||||
static struct wl_buffer * createSquareCursorBuffer(void)
|
||||
{
|
||||
int fd = memfd_create("lg-cursor", 0);
|
||||
if (fd < 0)
|
||||
@@ -74,6 +74,68 @@ fail:
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool loadThemedCursor(const char * name, struct wl_surface ** surface,
|
||||
struct Point * hotspot)
|
||||
{
|
||||
struct wl_cursor * cursor = wl_cursor_theme_get_cursor(wlWm.cursorTheme, name);
|
||||
if (!cursor)
|
||||
return false;
|
||||
|
||||
struct wl_buffer * buffer = wl_cursor_image_get_buffer(cursor->images[0]);
|
||||
if (!buffer)
|
||||
return false;
|
||||
|
||||
*surface = wl_compositor_create_surface(wlWm.compositor);
|
||||
if (!*surface)
|
||||
return NULL;
|
||||
|
||||
wl_surface_attach(*surface, buffer, 0, 0);
|
||||
wl_surface_set_buffer_scale(*surface, wlWm.cursorScale);
|
||||
wl_surface_commit(*surface);
|
||||
|
||||
*hotspot = (struct Point) {
|
||||
.x = cursor->images[0]->hotspot_x,
|
||||
.y = cursor->images[0]->hotspot_y,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char ** nameLists[LG_POINTER_COUNT] = {
|
||||
[LG_POINTER_ARROW ] = (const char *[]) { "left_ptr", "arrow", NULL },
|
||||
[LG_POINTER_INPUT ] = (const char *[]) { "text", "xterm", "ibeam", NULL },
|
||||
[LG_POINTER_MOVE ] = (const char *[]) {
|
||||
"move", "4498f0e0c1937ffe01fd06f973665830", "9081237383d90e509aa00f00170e968f", NULL
|
||||
},
|
||||
[LG_POINTER_RESIZE_NS ] = (const char *[]) {
|
||||
"sb_v_double_arrow", "size_ver", "v_double_arrow",
|
||||
"2870a09082c103050810ffdffffe0204", "00008160000006810000408080010102", NULL
|
||||
},
|
||||
[LG_POINTER_RESIZE_EW ] = (const char *[]) {
|
||||
"sb_h_double_arrow", "size_hor", "h_double_arrow",
|
||||
"14fef782d02440884392942c11205230", "028006030e0e7ebffc7f7070c0600140", NULL
|
||||
},
|
||||
[LG_POINTER_RESIZE_NESW] = (const char *[]) {
|
||||
"fd_double_arrow", "size_bdiag", "fcf1c3c7cd4491d801f1e1c78f100000", NULL
|
||||
},
|
||||
[LG_POINTER_RESIZE_NWSE] = (const char *[]) {
|
||||
"bd_double_arrow", "size_fdiag", "c7088f0f3e6c8088236ef8e1e3e70000", NULL
|
||||
},
|
||||
[LG_POINTER_HAND ] = (const char *[]) {
|
||||
"hand", "pointing_hand", "hand1", "hand2", "pointer",
|
||||
"e29285e634086352946a0e7090d73106", "9d800788f1b08800ae810202380a0822", NULL
|
||||
},
|
||||
[LG_POINTER_NOT_ALLOWED] = (const char *[]) { "crossed_circle", "not-allowed", NULL },
|
||||
};
|
||||
|
||||
static void reloadCursors(void)
|
||||
{
|
||||
if (wlWm.cursorTheme)
|
||||
for (LG_DSPointer pointer = LG_POINTER_ARROW; pointer < LG_POINTER_COUNT; ++pointer)
|
||||
for (const char ** names = nameLists[pointer]; *names; ++names)
|
||||
if (loadThemedCursor(*names, wlWm.cursors + pointer, wlWm.cursorHot + pointer))
|
||||
break;
|
||||
}
|
||||
|
||||
bool waylandCursorInit(void)
|
||||
{
|
||||
if (!wlWm.compositor)
|
||||
@@ -82,19 +144,79 @@ bool waylandCursorInit(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
struct wl_buffer * cursorBuffer = createCursorBuffer();
|
||||
if (cursorBuffer)
|
||||
wlWm.cursorSquareBuffer = createSquareCursorBuffer();
|
||||
if (wlWm.cursorSquareBuffer)
|
||||
{
|
||||
wlWm.cursor = wl_compositor_create_surface(wlWm.compositor);
|
||||
wl_surface_attach(wlWm.cursor, cursorBuffer, 0, 0);
|
||||
wl_surface_commit(wlWm.cursor);
|
||||
wlWm.cursors[LG_POINTER_SQUARE] = wl_compositor_create_surface(wlWm.compositor);
|
||||
wl_surface_attach(wlWm.cursors[LG_POINTER_SQUARE], wlWm.cursorSquareBuffer, 0, 0);
|
||||
wl_surface_commit(wlWm.cursors[LG_POINTER_SQUARE]);
|
||||
}
|
||||
|
||||
wlWm.cursorThemeName = getenv("XCURSOR_THEME");
|
||||
wlWm.cursorSize = 24;
|
||||
|
||||
const char * cursorSizeEnv = getenv("XCURSOR_SIZE");
|
||||
if (cursorSizeEnv)
|
||||
{
|
||||
int size = atoi(cursorSizeEnv);
|
||||
if (size)
|
||||
wlWm.cursorSize = size;
|
||||
}
|
||||
|
||||
wlWm.cursorTheme = wl_cursor_theme_load(wlWm.cursorThemeName, wlWm.cursorSize, wlWm.shm);
|
||||
wlWm.cursorScale = 1;
|
||||
reloadCursors();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void waylandShowPointer(bool show)
|
||||
void waylandCursorFree(void)
|
||||
{
|
||||
wlWm.showPointer = show;
|
||||
wl_pointer_set_cursor(wlWm.pointer, wlWm.pointerEnterSerial, show ? wlWm.cursor : NULL, 0, 0);
|
||||
for (int i = 0; i < LG_POINTER_COUNT; ++i)
|
||||
if (wlWm.cursors[i])
|
||||
wl_surface_destroy(wlWm.cursors[i]);
|
||||
if (wlWm.cursorTheme)
|
||||
wl_cursor_theme_destroy(wlWm.cursorTheme);
|
||||
if (wlWm.cursorSquareBuffer)
|
||||
wl_buffer_destroy(wlWm.cursorSquareBuffer);
|
||||
}
|
||||
|
||||
void waylandCursorScaleChange(void)
|
||||
{
|
||||
int newScale = ceil(wl_fixed_to_double(wlWm.scale));
|
||||
if (newScale == wlWm.cursorScale)
|
||||
return;
|
||||
|
||||
struct wl_cursor_theme * new = wl_cursor_theme_load(wlWm.cursorThemeName,
|
||||
wlWm.cursorSize * newScale, wlWm.shm);
|
||||
|
||||
if (!new)
|
||||
return;
|
||||
|
||||
struct wl_surface * old[LG_POINTER_COUNT];
|
||||
memcpy(old, wlWm.cursors, sizeof(old));
|
||||
memset(wlWm.cursors, 0, sizeof(wlWm.cursors));
|
||||
|
||||
if (wlWm.cursorTheme)
|
||||
wl_cursor_theme_destroy(wlWm.cursorTheme);
|
||||
|
||||
wlWm.cursorTheme = new;
|
||||
wlWm.cursorScale = newScale;
|
||||
reloadCursors();
|
||||
|
||||
waylandSetPointer(wlWm.cursorId);
|
||||
|
||||
for (int i = 0; i < LG_POINTER_COUNT; ++i)
|
||||
if (old[i])
|
||||
wl_surface_destroy(old[i]);
|
||||
}
|
||||
|
||||
void waylandSetPointer(LG_DSPointer pointer)
|
||||
{
|
||||
wlWm.cursorId = pointer;
|
||||
wlWm.cursor = wlWm.cursors[pointer];
|
||||
wlWm.cursorHotX = wlWm.cursorHot[pointer].x;
|
||||
wlWm.cursorHotY = wlWm.cursorHot[pointer].y;
|
||||
if (wlWm.pointer)
|
||||
wl_pointer_set_cursor(wlWm.pointer, wlWm.pointerEnterSerial, wlWm.cursor, wlWm.cursorHotX, wlWm.cursorHotY);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
|
||||
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
|
||||
https://looking-glass.io
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "wayland.h"
|
||||
|
||||
@@ -28,9 +28,11 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include "app.h"
|
||||
#include "common/debug.h"
|
||||
#include "util.h"
|
||||
|
||||
#if defined(ENABLE_EGL) || defined(ENABLE_OPENGL)
|
||||
#include "egl_dynprocs.h"
|
||||
#include "eglutil.h"
|
||||
|
||||
bool waylandEGLInit(int w, int h)
|
||||
{
|
||||
@@ -40,6 +42,7 @@ bool waylandEGLInit(int w, int h)
|
||||
DEBUG_ERROR("Failed to create EGL window");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -49,14 +52,14 @@ EGLDisplay waylandGetEGLDisplay(void)
|
||||
|
||||
const char *early_exts = eglQueryString(NULL, EGL_EXTENSIONS);
|
||||
|
||||
if (strstr(early_exts, "EGL_KHR_platform_wayland") != NULL &&
|
||||
if (util_hasGLExt(early_exts, "EGL_KHR_platform_wayland") &&
|
||||
g_egl_dynProcs.eglGetPlatformDisplay)
|
||||
{
|
||||
DEBUG_INFO("Using eglGetPlatformDisplay");
|
||||
return g_egl_dynProcs.eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, native, NULL);
|
||||
}
|
||||
|
||||
if (strstr(early_exts, "EGL_EXT_platform_wayland") != NULL &&
|
||||
if (util_hasGLExt(early_exts, "EGL_EXT_platform_wayland") &&
|
||||
g_egl_dynProcs.eglGetPlatformDisplayEXT)
|
||||
{
|
||||
DEBUG_INFO("Using eglGetPlatformDisplayEXT");
|
||||
@@ -67,23 +70,65 @@ EGLDisplay waylandGetEGLDisplay(void)
|
||||
return eglGetDisplay(native);
|
||||
}
|
||||
|
||||
void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface)
|
||||
void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count)
|
||||
{
|
||||
eglSwapBuffers(display, surface);
|
||||
|
||||
if (wlWm.resizeSerial)
|
||||
if (!wlWm.swapWithDamage.init)
|
||||
{
|
||||
wl_egl_window_resize(wlWm.eglWindow, wlWm.width, wlWm.height, 0, 0);
|
||||
if (wl_proxy_get_version((struct wl_proxy *) wlWm.surface) < 4)
|
||||
{
|
||||
DEBUG_INFO("Swapping buffers with damage: not supported, need wl_compositor v4");
|
||||
swapWithDamageDisable(&wlWm.swapWithDamage);
|
||||
}
|
||||
else
|
||||
swapWithDamageInit(&wlWm.swapWithDamage, display);
|
||||
}
|
||||
|
||||
waylandPresentationFrame();
|
||||
swapWithDamage(&wlWm.swapWithDamage, display, surface, damage, count);
|
||||
|
||||
if (wlWm.needsResize)
|
||||
{
|
||||
wl_egl_window_resize(wlWm.eglWindow, wl_fixed_to_int(wlWm.width * wlWm.scale),
|
||||
wl_fixed_to_int(wlWm.height * wlWm.scale), 0, 0);
|
||||
|
||||
if (wlWm.fractionalScale)
|
||||
{
|
||||
wl_surface_set_buffer_scale(wlWm.surface, 1);
|
||||
if (!wlWm.viewport)
|
||||
wlWm.viewport = wp_viewporter_get_viewport(wlWm.viewporter, wlWm.surface);
|
||||
wp_viewport_set_source(wlWm.viewport, 0, 0, wlWm.width * wlWm.scale, wlWm.height * wlWm.scale);
|
||||
wp_viewport_set_destination(wlWm.viewport, wlWm.width, wlWm.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (wlWm.viewport)
|
||||
{
|
||||
// Clearing the source and destination rectangles should happen in wp_viewport_destroy.
|
||||
// However, wlroots does not clear the rectangle until fixed in 456c6e22 (2021-08-02).
|
||||
// This should be kept to work around old versions of wlroots.
|
||||
wl_fixed_t clear = wl_fixed_from_int(-1);
|
||||
wp_viewport_set_source(wlWm.viewport, clear, clear, clear, clear);
|
||||
wp_viewport_set_destination(wlWm.viewport, -1, -1);
|
||||
|
||||
wp_viewport_destroy(wlWm.viewport);
|
||||
wlWm.viewport = NULL;
|
||||
}
|
||||
wl_surface_set_buffer_scale(wlWm.surface, wl_fixed_to_int(wlWm.scale));
|
||||
}
|
||||
|
||||
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
|
||||
wl_region_add(region, 0, 0, wlWm.width, wlWm.height);
|
||||
wl_surface_set_opaque_region(wlWm.surface, region);
|
||||
wl_region_destroy(region);
|
||||
|
||||
app_handleResizeEvent(wlWm.width, wlWm.height, (struct Border) {0, 0, 0, 0});
|
||||
xdg_surface_ack_configure(wlWm.xdgSurface, wlWm.resizeSerial);
|
||||
wlWm.resizeSerial = 0;
|
||||
app_handleResizeEvent(wlWm.width, wlWm.height, wl_fixed_to_double(wlWm.scale),
|
||||
(struct Border) {0, 0, 0, 0});
|
||||
app_invalidateWindow(true);
|
||||
waylandStopWaitFrame();
|
||||
wlWm.needsResize = false;
|
||||
}
|
||||
|
||||
waylandShellAckConfigureIfNeeded();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -166,6 +211,6 @@ void waylandGLSetSwapInterval(int interval)
|
||||
|
||||
void waylandGLSwapBuffers(void)
|
||||
{
|
||||
waylandEGLSwapBuffers(wlWm.glDisplay, wlWm.glSurface);
|
||||
waylandEGLSwapBuffers(wlWm.glDisplay, wlWm.glSurface, NULL, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
|
||||
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
|
||||
https://looking-glass.io
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "wayland.h"
|
||||
|
||||
|
||||
@@ -1,30 +1,33 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
|
||||
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
|
||||
https://looking-glass.io
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "wayland.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
#include "app.h"
|
||||
#include "common/debug.h"
|
||||
@@ -46,9 +49,13 @@ static void pointerEnterHandler(void * data, struct wl_pointer * pointer,
|
||||
uint32_t serial, struct wl_surface * surface, wl_fixed_t sxW,
|
||||
wl_fixed_t syW)
|
||||
{
|
||||
if (surface != wlWm.surface)
|
||||
return;
|
||||
|
||||
wlWm.pointerInSurface = true;
|
||||
app_handleEnterEvent(true);
|
||||
|
||||
wl_pointer_set_cursor(pointer, serial, wlWm.showPointer ? wlWm.cursor : NULL, 0, 0);
|
||||
wl_pointer_set_cursor(pointer, serial, wlWm.cursor, wlWm.cursorHotX, wlWm.cursorHotY);
|
||||
wlWm.pointerEnterSerial = serial;
|
||||
|
||||
wlWm.cursorX = wl_fixed_to_double(sxW);
|
||||
@@ -71,17 +78,25 @@ static void pointerEnterHandler(void * data, struct wl_pointer * pointer,
|
||||
static void pointerLeaveHandler(void * data, struct wl_pointer * pointer,
|
||||
uint32_t serial, struct wl_surface * surface)
|
||||
{
|
||||
if (surface != wlWm.surface)
|
||||
return;
|
||||
|
||||
wlWm.pointerInSurface = false;
|
||||
app_handleEnterEvent(false);
|
||||
}
|
||||
|
||||
static void pointerAxisHandler(void * data, struct wl_pointer * pointer,
|
||||
uint32_t serial, uint32_t axis, wl_fixed_t value)
|
||||
{
|
||||
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
|
||||
return;
|
||||
|
||||
int button = value > 0 ?
|
||||
5 /* SPICE_MOUSE_BUTTON_DOWN */ :
|
||||
4 /* SPICE_MOUSE_BUTTON_UP */;
|
||||
app_handleButtonPress(button);
|
||||
app_handleButtonRelease(button);
|
||||
app_handleWheelMotion(wl_fixed_to_double(value) / 15.0);
|
||||
}
|
||||
|
||||
static int mapWaylandToSpiceButton(uint32_t button)
|
||||
@@ -147,12 +162,60 @@ static const struct zwp_relative_pointer_v1_listener relativePointerListener = {
|
||||
static void keyboardKeymapHandler(void * data, struct wl_keyboard * keyboard,
|
||||
uint32_t format, int fd, uint32_t size)
|
||||
{
|
||||
if (!wlWm.xkb)
|
||||
goto done;
|
||||
|
||||
if (wlWm.keymap)
|
||||
{
|
||||
xkb_keymap_unref(wlWm.keymap);
|
||||
wlWm.keymap = NULL;
|
||||
}
|
||||
|
||||
if (wlWm.xkbState)
|
||||
{
|
||||
xkb_state_unref(wlWm.xkbState);
|
||||
wlWm.xkbState = NULL;
|
||||
}
|
||||
|
||||
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
|
||||
{
|
||||
DEBUG_WARN("Unsupported keymap format, keyboard input will not work: %d", format);
|
||||
goto done;
|
||||
}
|
||||
|
||||
char * map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (map == MAP_FAILED)
|
||||
{
|
||||
DEBUG_ERROR("Failed to mmap keymap: %s", strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
|
||||
wlWm.keymap = xkb_keymap_new_from_string(wlWm.xkb, map,
|
||||
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||
|
||||
if (!wlWm.keymap)
|
||||
DEBUG_WARN("Failed to load keymap, keyboard input will not work");
|
||||
|
||||
munmap(map, size);
|
||||
|
||||
if (wlWm.keymap)
|
||||
{
|
||||
wlWm.xkbState = xkb_state_new(wlWm.keymap);
|
||||
if (!wlWm.xkbState)
|
||||
DEBUG_WARN("Failed to create xkb_state");
|
||||
}
|
||||
|
||||
done:
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
|
||||
uint32_t serial, struct wl_surface * surface, struct wl_array * keys)
|
||||
{
|
||||
if (surface != wlWm.surface)
|
||||
return;
|
||||
|
||||
wlWm.focusedOnSurface = true;
|
||||
app_handleFocusEvent(true);
|
||||
wlWm.keyboardEnterSerial = serial;
|
||||
|
||||
@@ -164,23 +227,59 @@ static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
|
||||
static void keyboardLeaveHandler(void * data, struct wl_keyboard * keyboard,
|
||||
uint32_t serial, struct wl_surface * surface)
|
||||
{
|
||||
if (surface != wlWm.surface)
|
||||
return;
|
||||
|
||||
wlWm.focusedOnSurface = false;
|
||||
waylandCBInvalidate();
|
||||
app_handleFocusEvent(false);
|
||||
}
|
||||
|
||||
static void keyboardKeyHandler(void * data, struct wl_keyboard * keyboard,
|
||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
|
||||
{
|
||||
if (!wlWm.focusedOnSurface)
|
||||
return;
|
||||
|
||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||
app_handleKeyPress(key);
|
||||
else
|
||||
app_handleKeyRelease(key);
|
||||
|
||||
if (!wlWm.xkbState || !app_isOverlayMode() || state != WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||
return;
|
||||
|
||||
key += 8; // xkb scancode is evdev scancode + 8
|
||||
int size = xkb_state_key_get_utf8(wlWm.xkbState, key, NULL, 0);
|
||||
|
||||
if (size <= 0)
|
||||
return;
|
||||
|
||||
char buffer[size + 1];
|
||||
xkb_state_key_get_utf8(wlWm.xkbState, key, buffer, size + 1);
|
||||
app_handleKeyboardTyped(buffer);
|
||||
}
|
||||
|
||||
static void keyboardModifiersHandler(void * data,
|
||||
struct wl_keyboard * keyboard, uint32_t serial, uint32_t modsDepressed,
|
||||
uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
|
||||
{
|
||||
// Do nothing.
|
||||
if (!wlWm.xkbState)
|
||||
return;
|
||||
|
||||
xkb_state_update_mask(wlWm.xkbState, modsDepressed, modsLatched, modsLocked, 0, 0, group);
|
||||
app_handleKeyboardModifiers(
|
||||
xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0,
|
||||
xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) > 0,
|
||||
xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) > 0,
|
||||
xkb_state_mod_name_is_active(wlWm.xkbState, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) > 0
|
||||
);
|
||||
|
||||
app_handleKeyboardLEDs(
|
||||
xkb_state_led_name_is_active(wlWm.xkbState, XKB_LED_NAME_NUM) > 0,
|
||||
xkb_state_led_name_is_active(wlWm.xkbState, XKB_LED_NAME_CAPS) > 0,
|
||||
xkb_state_led_name_is_active(wlWm.xkbState, XKB_LED_NAME_SCROLL) > 0
|
||||
);
|
||||
}
|
||||
|
||||
static const struct wl_keyboard_listener keyboardListener = {
|
||||
@@ -191,20 +290,53 @@ static const struct wl_keyboard_listener keyboardListener = {
|
||||
.modifiers = keyboardModifiersHandler,
|
||||
};
|
||||
|
||||
static void waylandCleanUpPointer(void)
|
||||
{
|
||||
INTERLOCKED_SECTION(wlWm.confineLock, {
|
||||
if (wlWm.lockedPointer)
|
||||
{
|
||||
zwp_locked_pointer_v1_destroy(wlWm.lockedPointer);
|
||||
wlWm.lockedPointer = NULL;
|
||||
}
|
||||
|
||||
if (wlWm.confinedPointer)
|
||||
{
|
||||
zwp_confined_pointer_v1_destroy(wlWm.confinedPointer);
|
||||
wlWm.confinedPointer = NULL;
|
||||
}
|
||||
});
|
||||
|
||||
if (wlWm.relativePointer)
|
||||
{
|
||||
zwp_relative_pointer_v1_destroy(wlWm.relativePointer);
|
||||
wlWm.relativePointer = NULL;
|
||||
}
|
||||
|
||||
wl_pointer_destroy(wlWm.pointer);
|
||||
wlWm.pointer = NULL;
|
||||
}
|
||||
|
||||
// Seat-handling listeners.
|
||||
|
||||
static void handlePointerCapability(uint32_t capabilities)
|
||||
{
|
||||
bool hasPointer = capabilities & WL_SEAT_CAPABILITY_POINTER;
|
||||
if (!hasPointer && wlWm.pointer)
|
||||
{
|
||||
wl_pointer_destroy(wlWm.pointer);
|
||||
wlWm.pointer = NULL;
|
||||
}
|
||||
waylandCleanUpPointer();
|
||||
else if (hasPointer && !wlWm.pointer)
|
||||
{
|
||||
wlWm.pointer = wl_seat_get_pointer(wlWm.seat);
|
||||
wl_pointer_add_listener(wlWm.pointer, &pointerListener, NULL);
|
||||
waylandSetPointer(wlWm.cursorId);
|
||||
|
||||
if (wlWm.warpSupport)
|
||||
{
|
||||
wlWm.relativePointer =
|
||||
zwp_relative_pointer_manager_v1_get_relative_pointer(
|
||||
wlWm.relativePointerManager, wlWm.pointer);
|
||||
zwp_relative_pointer_v1_add_listener(wlWm.relativePointer,
|
||||
&relativePointerListener, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,17 +401,14 @@ bool waylandInputInit(void)
|
||||
DEBUG_WARN("zwp_keyboard_shortcuts_inhibit_manager_v1 not exported by "
|
||||
"compositor, keyboard will not be grabbed");
|
||||
|
||||
wlWm.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!wlWm.xkb)
|
||||
DEBUG_WARN("Failed to initialize xkb, keyboard input will not work");
|
||||
|
||||
wl_seat_add_listener(wlWm.seat, &seatListener, NULL);
|
||||
wl_display_roundtrip(wlWm.display);
|
||||
|
||||
if (wlWm.warpSupport)
|
||||
{
|
||||
wlWm.relativePointer =
|
||||
zwp_relative_pointer_manager_v1_get_relative_pointer(
|
||||
wlWm.relativePointerManager, wlWm.pointer);
|
||||
zwp_relative_pointer_v1_add_listener(wlWm.relativePointer,
|
||||
&relativePointerListener, NULL);
|
||||
}
|
||||
LG_LOCK_INIT(wlWm.confineLock);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -287,9 +416,26 @@ bool waylandInputInit(void)
|
||||
void waylandInputFree(void)
|
||||
{
|
||||
waylandUngrabPointer();
|
||||
wl_pointer_destroy(wlWm.pointer);
|
||||
wl_keyboard_destroy(wlWm.keyboard);
|
||||
LG_LOCK_FREE(wlWm.confineLock);
|
||||
|
||||
if (wlWm.pointer)
|
||||
waylandCleanUpPointer();
|
||||
|
||||
// The only legal way the keyboard can be null is if it never existed.
|
||||
// When unplugged, the compositor must have an inert object.
|
||||
if (wlWm.keyboard)
|
||||
wl_keyboard_destroy(wlWm.keyboard);
|
||||
|
||||
wl_seat_destroy(wlWm.seat);
|
||||
|
||||
if (wlWm.xkbState)
|
||||
xkb_state_unref(wlWm.xkbState);
|
||||
|
||||
if (wlWm.keymap)
|
||||
xkb_keymap_unref(wlWm.keymap);
|
||||
|
||||
if (wlWm.xkb)
|
||||
xkb_context_unref(wlWm.xkb);
|
||||
}
|
||||
|
||||
void waylandGrabPointer(void)
|
||||
@@ -306,22 +452,31 @@ void waylandGrabPointer(void)
|
||||
&relativePointerListener, NULL);
|
||||
}
|
||||
|
||||
if (!wlWm.confinedPointer)
|
||||
INTERLOCKED_SECTION(wlWm.confineLock,
|
||||
{
|
||||
wlWm.confinedPointer = zwp_pointer_constraints_v1_confine_pointer(
|
||||
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,
|
||||
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
|
||||
}
|
||||
if (!wlWm.confinedPointer)
|
||||
{
|
||||
wlWm.confinedPointer = zwp_pointer_constraints_v1_confine_pointer(
|
||||
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,
|
||||
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void waylandUngrabPointer(void)
|
||||
inline static void internalUngrabPointer(bool lock)
|
||||
{
|
||||
if (lock)
|
||||
LG_LOCK(wlWm.confineLock);
|
||||
|
||||
if (wlWm.confinedPointer)
|
||||
{
|
||||
zwp_confined_pointer_v1_destroy(wlWm.confinedPointer);
|
||||
wlWm.confinedPointer = NULL;
|
||||
}
|
||||
|
||||
if (lock)
|
||||
LG_UNLOCK(wlWm.confineLock);
|
||||
|
||||
if (!wlWm.warpSupport)
|
||||
{
|
||||
if (!wlWm.relativePointer)
|
||||
@@ -338,6 +493,61 @@ void waylandUngrabPointer(void)
|
||||
}
|
||||
}
|
||||
|
||||
void waylandUngrabPointer(void)
|
||||
{
|
||||
internalUngrabPointer(true);
|
||||
}
|
||||
|
||||
void waylandCapturePointer(void)
|
||||
{
|
||||
if (!wlWm.warpSupport)
|
||||
{
|
||||
waylandGrabPointer();
|
||||
return;
|
||||
}
|
||||
|
||||
INTERLOCKED_SECTION(wlWm.confineLock,
|
||||
{
|
||||
if (wlWm.confinedPointer)
|
||||
{
|
||||
zwp_confined_pointer_v1_destroy(wlWm.confinedPointer);
|
||||
wlWm.confinedPointer = NULL;
|
||||
}
|
||||
|
||||
wlWm.lockedPointer = zwp_pointer_constraints_v1_lock_pointer(
|
||||
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,
|
||||
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
|
||||
});
|
||||
}
|
||||
|
||||
void waylandUncapturePointer(void)
|
||||
{
|
||||
INTERLOCKED_SECTION(wlWm.confineLock,
|
||||
{
|
||||
if (wlWm.lockedPointer)
|
||||
{
|
||||
zwp_locked_pointer_v1_destroy(wlWm.lockedPointer);
|
||||
wlWm.lockedPointer = NULL;
|
||||
}
|
||||
|
||||
/* we need to ungrab the pointer on the following conditions when exiting capture mode:
|
||||
* - if warp is not supported, exit via window edge detection will never work
|
||||
* as the cursor can not be warped out of the window when we release it.
|
||||
* - if the format is invalid as we do not know where the guest cursor is,
|
||||
* which also breaks edge detection.
|
||||
* - if the user has opted to use captureInputOnly mode.
|
||||
*/
|
||||
if (!wlWm.warpSupport || !app_isFormatValid() || app_isCaptureOnlyMode())
|
||||
internalUngrabPointer(false);
|
||||
else if (wlWm.pointer)
|
||||
{
|
||||
wlWm.confinedPointer = zwp_pointer_constraints_v1_confine_pointer(
|
||||
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, NULL,
|
||||
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void waylandGrabKeyboard(void)
|
||||
{
|
||||
if (wlWm.keyboardInhibitManager && !wlWm.keyboardInhibitor)
|
||||
@@ -358,32 +568,44 @@ void waylandUngrabKeyboard(void)
|
||||
|
||||
void waylandWarpPointer(int x, int y, bool exiting)
|
||||
{
|
||||
if (x < 0) x = 0;
|
||||
else if (x >= wlWm.width) x = wlWm.width - 1;
|
||||
if (y < 0) y = 0;
|
||||
else if (y >= wlWm.height) y = wlWm.height - 1;
|
||||
if (!wlWm.pointerInSurface || wlWm.lockedPointer)
|
||||
return;
|
||||
|
||||
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
|
||||
wl_region_add(region, x, y, 1, 1);
|
||||
|
||||
if (wlWm.confinedPointer)
|
||||
INTERLOCKED_SECTION(wlWm.confineLock,
|
||||
{
|
||||
zwp_confined_pointer_v1_set_region(wlWm.confinedPointer, region);
|
||||
wl_surface_commit(wlWm.surface);
|
||||
zwp_confined_pointer_v1_set_region(wlWm.confinedPointer, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct zwp_confined_pointer_v1 * confine;
|
||||
confine = zwp_pointer_constraints_v1_confine_pointer(
|
||||
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, region,
|
||||
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
|
||||
wl_surface_commit(wlWm.surface);
|
||||
zwp_confined_pointer_v1_destroy(confine);
|
||||
}
|
||||
if (wlWm.lockedPointer)
|
||||
{
|
||||
LG_UNLOCK(wlWm.confineLock);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_surface_commit(wlWm.surface);
|
||||
wl_region_destroy(region);
|
||||
if (x < 0) x = 0;
|
||||
else if (x >= wlWm.width) x = wlWm.width - 1;
|
||||
if (y < 0) y = 0;
|
||||
else if (y >= wlWm.height) y = wlWm.height - 1;
|
||||
|
||||
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
|
||||
wl_region_add(region, x, y, 1, 1);
|
||||
|
||||
if (wlWm.confinedPointer)
|
||||
{
|
||||
zwp_confined_pointer_v1_set_region(wlWm.confinedPointer, region);
|
||||
wl_surface_commit(wlWm.surface);
|
||||
zwp_confined_pointer_v1_set_region(wlWm.confinedPointer, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct zwp_confined_pointer_v1 * confine;
|
||||
confine = zwp_pointer_constraints_v1_confine_pointer(
|
||||
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, region,
|
||||
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
|
||||
wl_surface_commit(wlWm.surface);
|
||||
zwp_confined_pointer_v1_destroy(confine);
|
||||
}
|
||||
|
||||
wl_surface_commit(wlWm.surface);
|
||||
wl_region_destroy(region);
|
||||
});
|
||||
}
|
||||
|
||||
void waylandRealignPointer(void)
|
||||
@@ -391,3 +613,11 @@ void waylandRealignPointer(void)
|
||||
if (!wlWm.warpSupport)
|
||||
app_resyncMouseBasic();
|
||||
}
|
||||
|
||||
void waylandGuestPointerUpdated(double x, double y, double localX, double localY)
|
||||
{
|
||||
if (!wlWm.warpSupport || !wlWm.pointerInSurface || wlWm.lockedPointer)
|
||||
return;
|
||||
|
||||
waylandWarpPointer((int) localX, (int) localY, false);
|
||||
}
|
||||
|
||||
220
client/displayservers/Wayland/output.c
Normal file
220
client/displayservers/Wayland/output.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "wayland.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
static void outputUpdateScale(struct WaylandOutput * node)
|
||||
{
|
||||
wl_fixed_t original = node->scale;
|
||||
|
||||
if (!wlWm.useFractionalScale || !wlWm.viewporter || !node->logicalWidth)
|
||||
node->scale = wl_fixed_from_int(node->scaleInt);
|
||||
else
|
||||
{
|
||||
int32_t modeWidth = node->modeRotate ? node->modeHeight : node->modeWidth;
|
||||
node->scale = wl_fixed_from_double(1.0 * modeWidth / node->logicalWidth);
|
||||
}
|
||||
|
||||
if (original != node->scale)
|
||||
waylandWindowUpdateScale();
|
||||
}
|
||||
|
||||
static void outputGeometryHandler(void * opaque, struct wl_output * output, int32_t x, int32_t y,
|
||||
int32_t physical_width, int32_t physical_height, int32_t subpixel, const char * make,
|
||||
const char * model, int32_t output_transform)
|
||||
{
|
||||
struct WaylandOutput * node = opaque;
|
||||
|
||||
switch (output_transform)
|
||||
{
|
||||
case WL_OUTPUT_TRANSFORM_90:
|
||||
case WL_OUTPUT_TRANSFORM_270:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
|
||||
node->modeRotate = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
node->modeRotate = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void outputModeHandler(void * opaque, struct wl_output * wl_output, uint32_t flags,
|
||||
int32_t width, int32_t height, int32_t refresh)
|
||||
{
|
||||
if (!(flags & WL_OUTPUT_MODE_CURRENT))
|
||||
return;
|
||||
|
||||
struct WaylandOutput * node = opaque;
|
||||
node->modeWidth = width;
|
||||
node->modeHeight = height;
|
||||
}
|
||||
|
||||
static void outputDoneHandler(void * opaque, struct wl_output * output)
|
||||
{
|
||||
struct WaylandOutput * node = opaque;
|
||||
outputUpdateScale(node);
|
||||
}
|
||||
|
||||
static void outputScaleHandler(void * opaque, struct wl_output * output, int32_t scale)
|
||||
{
|
||||
struct WaylandOutput * node = opaque;
|
||||
node->scaleInt = scale;
|
||||
}
|
||||
|
||||
static const struct wl_output_listener outputListener = {
|
||||
.geometry = outputGeometryHandler,
|
||||
.mode = outputModeHandler,
|
||||
.done = outputDoneHandler,
|
||||
.scale = outputScaleHandler,
|
||||
};
|
||||
|
||||
static void xdgOutputLogicalPositionHandler(void * opaque, struct zxdg_output_v1 * xdgOutput,
|
||||
int32_t x, int32_t y)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
static void xdgOutputLogicalSizeHandler(void * opaque, struct zxdg_output_v1 * xdgOutput,
|
||||
int32_t width, int32_t height)
|
||||
{
|
||||
struct WaylandOutput * node = opaque;
|
||||
node->logicalWidth = width;
|
||||
node->logicalHeight = height;
|
||||
}
|
||||
|
||||
static void xdgOutputDoneHandler(void * opaque, struct zxdg_output_v1 * xdgOutput)
|
||||
{
|
||||
struct WaylandOutput * node = opaque;
|
||||
outputUpdateScale(node);
|
||||
}
|
||||
|
||||
static void xdgOutputNameHandler(void * opaque, struct zxdg_output_v1 * xdgOutput, const char * name)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
static void xdgOutputDescriptionHandler(void * opaque, struct zxdg_output_v1 * xdgOutput,
|
||||
const char * description)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
static const struct zxdg_output_v1_listener xdgOutputListener = {
|
||||
.logical_position = xdgOutputLogicalPositionHandler,
|
||||
.logical_size = xdgOutputLogicalSizeHandler,
|
||||
.done = xdgOutputDoneHandler,
|
||||
.name = xdgOutputNameHandler,
|
||||
.description = xdgOutputDescriptionHandler,
|
||||
};
|
||||
|
||||
bool waylandOutputInit(void)
|
||||
{
|
||||
wl_list_init(&wlWm.outputs);
|
||||
return true;
|
||||
}
|
||||
|
||||
void waylandOutputFree(void)
|
||||
{
|
||||
struct WaylandOutput * node;
|
||||
struct WaylandOutput * temp;
|
||||
wl_list_for_each_safe(node, temp, &wlWm.outputs, link)
|
||||
{
|
||||
if (node->version >= 3)
|
||||
wl_output_release(node->output);
|
||||
if (node->xdgOutput)
|
||||
zxdg_output_v1_destroy(node->xdgOutput);
|
||||
wl_list_remove(&node->link);
|
||||
free(node);
|
||||
}
|
||||
}
|
||||
|
||||
void waylandOutputBind(uint32_t name, uint32_t version)
|
||||
{
|
||||
struct WaylandOutput * node = calloc(1, sizeof(struct WaylandOutput));
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
if (version < 2)
|
||||
{
|
||||
DEBUG_WARN("wl_output version too old: expected >= 2, got %d", version);
|
||||
free(node);
|
||||
return;
|
||||
}
|
||||
|
||||
node->name = name;
|
||||
node->scale = 0;
|
||||
node->version = version;
|
||||
node->output = wl_registry_bind(wlWm.registry, name,
|
||||
&wl_output_interface, version >= 3 ? 3 : 2);
|
||||
|
||||
if (!node->output)
|
||||
{
|
||||
DEBUG_ERROR("Failed to bind to wl_output %u\n", name);
|
||||
free(node);
|
||||
return;
|
||||
}
|
||||
|
||||
if (wlWm.xdgOutputManager)
|
||||
{
|
||||
node->xdgOutput = zxdg_output_manager_v1_get_xdg_output(wlWm.xdgOutputManager, node->output);
|
||||
if (node->xdgOutput)
|
||||
zxdg_output_v1_add_listener(node->xdgOutput, &xdgOutputListener, node);
|
||||
}
|
||||
|
||||
wl_output_add_listener(node->output, &outputListener, node);
|
||||
wl_list_insert(&wlWm.outputs, &node->link);
|
||||
}
|
||||
|
||||
void waylandOutputTryUnbind(uint32_t name)
|
||||
{
|
||||
struct WaylandOutput * node;
|
||||
|
||||
wl_list_for_each(node, &wlWm.outputs, link)
|
||||
{
|
||||
if (node->name == name)
|
||||
{
|
||||
if (node->version >= 3)
|
||||
wl_output_release(node->output);
|
||||
if (node->xdgOutput)
|
||||
zxdg_output_v1_destroy(node->xdgOutput);
|
||||
wl_list_remove(&node->link);
|
||||
free(node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wl_fixed_t waylandOutputGetScale(struct wl_output * output)
|
||||
{
|
||||
struct WaylandOutput * node;
|
||||
|
||||
wl_list_for_each(node, &wlWm.outputs, link)
|
||||
if (node->output == output)
|
||||
return node->scale;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
|
||||
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
|
||||
https://looking-glass.io
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "wayland.h"
|
||||
|
||||
@@ -30,8 +30,13 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "common/debug.h"
|
||||
#include "common/locking.h"
|
||||
|
||||
#ifdef ENABLE_LIBDECOR
|
||||
#include <libdecor.h>
|
||||
#endif
|
||||
|
||||
#define EPOLL_EVENTS 10 // Maximum number of fds we can process at once in waylandWait
|
||||
|
||||
#ifndef ENABLE_LIBDECOR
|
||||
static void waylandDisplayCallback(uint32_t events, void * opaque)
|
||||
{
|
||||
if (events & EPOLLERR)
|
||||
@@ -40,6 +45,7 @@ static void waylandDisplayCallback(uint32_t events, void * opaque)
|
||||
wl_display_read_events(wlWm.display);
|
||||
wl_display_dispatch_pending(wlWm.display);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool waylandPollInit(void)
|
||||
{
|
||||
@@ -55,21 +61,27 @@ bool waylandPollInit(void)
|
||||
LG_LOCK_INIT(wlWm.pollLock);
|
||||
LG_LOCK_INIT(wlWm.pollFreeLock);
|
||||
|
||||
#ifndef ENABLE_LIBDECOR
|
||||
wlWm.displayFd = wl_display_get_fd(wlWm.display);
|
||||
if (!waylandEpollRegister(wlWm.displayFd, waylandDisplayCallback, NULL, EPOLLIN))
|
||||
if (!waylandPollRegister(wlWm.displayFd, waylandDisplayCallback, NULL, EPOLLIN))
|
||||
{
|
||||
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void waylandWait(unsigned int time)
|
||||
{
|
||||
#ifdef ENABLE_LIBDECOR
|
||||
libdecor_dispatch(wlWm.libdecor, 0);
|
||||
#else
|
||||
while (wl_display_prepare_read(wlWm.display))
|
||||
wl_display_dispatch_pending(wlWm.display);
|
||||
wl_display_flush(wlWm.display);
|
||||
#endif
|
||||
|
||||
struct epoll_event events[EPOLL_EVENTS];
|
||||
int count;
|
||||
@@ -77,21 +89,29 @@ void waylandWait(unsigned int time)
|
||||
{
|
||||
if (errno != EINTR)
|
||||
DEBUG_INFO("epoll failed: %s", strerror(errno));
|
||||
#ifndef ENABLE_LIBDECOR
|
||||
wl_display_cancel_read(wlWm.display);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef ENABLE_LIBDECOR
|
||||
bool sawDisplay = false;
|
||||
#endif
|
||||
for (int i = 0; i < count; ++i) {
|
||||
struct WaylandPoll * poll = events[i].data.ptr;
|
||||
if (!poll->removed)
|
||||
poll->callback(events[i].events, poll->opaque);
|
||||
#ifndef ENABLE_LIBDECOR
|
||||
if (poll->fd == wlWm.displayFd)
|
||||
sawDisplay = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef ENABLE_LIBDECOR
|
||||
if (!sawDisplay)
|
||||
wl_display_cancel_read(wlWm.display);
|
||||
#endif
|
||||
|
||||
INTERLOCKED_SECTION(wlWm.pollFreeLock,
|
||||
{
|
||||
@@ -105,7 +125,7 @@ void waylandWait(unsigned int time)
|
||||
});
|
||||
}
|
||||
|
||||
static void waylandEpollRemoveNode(struct WaylandPoll * node)
|
||||
static void waylandPollRemoveNode(struct WaylandPoll * node)
|
||||
{
|
||||
INTERLOCKED_SECTION(wlWm.pollLock,
|
||||
{
|
||||
@@ -113,9 +133,9 @@ static void waylandEpollRemoveNode(struct WaylandPoll * node)
|
||||
});
|
||||
}
|
||||
|
||||
bool waylandEpollRegister(int fd, WaylandPollCallback callback, void * opaque, uint32_t events)
|
||||
bool waylandPollRegister(int fd, WaylandPollCallback callback, void * opaque, uint32_t events)
|
||||
{
|
||||
struct WaylandPoll * node = malloc(sizeof(struct WaylandPoll));
|
||||
struct WaylandPoll * node = malloc(sizeof(*node));
|
||||
if (!node)
|
||||
return false;
|
||||
|
||||
@@ -134,7 +154,7 @@ bool waylandEpollRegister(int fd, WaylandPollCallback callback, void * opaque, u
|
||||
.data = (epoll_data_t) { .ptr = node },
|
||||
}) < 0)
|
||||
{
|
||||
waylandEpollRemoveNode(node);
|
||||
waylandPollRemoveNode(node);
|
||||
free(node);
|
||||
return false;
|
||||
}
|
||||
@@ -142,7 +162,7 @@ bool waylandEpollRegister(int fd, WaylandPollCallback callback, void * opaque, u
|
||||
return true;
|
||||
}
|
||||
|
||||
bool waylandEpollUnregister(int fd)
|
||||
bool waylandPollUnregister(int fd)
|
||||
{
|
||||
struct WaylandPoll * node = NULL;
|
||||
INTERLOCKED_SECTION(wlWm.pollLock,
|
||||
@@ -167,7 +187,7 @@ bool waylandEpollUnregister(int fd)
|
||||
return false;
|
||||
}
|
||||
|
||||
waylandEpollRemoveNode(node);
|
||||
waylandPollRemoveNode(node);
|
||||
|
||||
INTERLOCKED_SECTION(wlWm.pollFreeLock,
|
||||
{
|
||||
|
||||
119
client/displayservers/Wayland/presentation.c
Normal file
119
client/displayservers/Wayland/presentation.c
Normal file
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include "wayland.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/time.h"
|
||||
|
||||
struct FrameData
|
||||
{
|
||||
struct timespec sent;
|
||||
};
|
||||
|
||||
static void presentationClockId(void * data,
|
||||
struct wp_presentation * presentation, uint32_t clkId)
|
||||
{
|
||||
wlWm.clkId = clkId;
|
||||
}
|
||||
|
||||
static const struct wp_presentation_listener presentationListener = {
|
||||
.clock_id = presentationClockId,
|
||||
};
|
||||
|
||||
static void presentationFeedbackSyncOutput(void * data,
|
||||
struct wp_presentation_feedback * feedback, struct wl_output * output)
|
||||
{
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
static void presentationFeedbackPresented(void * opaque,
|
||||
struct wp_presentation_feedback * feedback, uint32_t tvSecHi, uint32_t tvSecLo,
|
||||
uint32_t tvNsec, uint32_t refresh, uint32_t seqHi, uint32_t seqLo, uint32_t flags)
|
||||
{
|
||||
struct FrameData * data = opaque;
|
||||
struct timespec present = {
|
||||
.tv_sec = (uint64_t) tvSecHi << 32 | tvSecLo,
|
||||
.tv_nsec = tvNsec,
|
||||
};
|
||||
struct timespec delta;
|
||||
|
||||
tsDiff(&delta, &present, &data->sent);
|
||||
ringbuffer_push(wlWm.photonTimings, &(float){ delta.tv_sec + delta.tv_nsec * 1e-6f });
|
||||
free(data);
|
||||
wp_presentation_feedback_destroy(feedback);
|
||||
}
|
||||
|
||||
static void presentationFeedbackDiscarded(void * data,
|
||||
struct wp_presentation_feedback * feedback)
|
||||
{
|
||||
free(data);
|
||||
wp_presentation_feedback_destroy(feedback);
|
||||
}
|
||||
|
||||
static const struct wp_presentation_feedback_listener presentationFeedbackListener = {
|
||||
.sync_output = presentationFeedbackSyncOutput,
|
||||
.presented = presentationFeedbackPresented,
|
||||
.discarded = presentationFeedbackDiscarded,
|
||||
};
|
||||
|
||||
bool waylandPresentationInit(void)
|
||||
{
|
||||
if (wlWm.presentation)
|
||||
{
|
||||
wlWm.photonTimings = ringbuffer_new(256, sizeof(float));
|
||||
wlWm.photonGraph = app_registerGraph("PHOTON", wlWm.photonTimings, 0.0f, 30.0f);
|
||||
wp_presentation_add_listener(wlWm.presentation, &presentationListener, NULL);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void waylandPresentationFree(void)
|
||||
{
|
||||
if (!wlWm.presentation)
|
||||
return;
|
||||
|
||||
wp_presentation_destroy(wlWm.presentation);
|
||||
app_unregisterGraph(wlWm.photonGraph);
|
||||
ringbuffer_free(&wlWm.photonTimings);
|
||||
}
|
||||
|
||||
void waylandPresentationFrame(void)
|
||||
{
|
||||
if (!wlWm.presentation)
|
||||
return;
|
||||
|
||||
struct FrameData * data = malloc(sizeof(*data));
|
||||
if (clock_gettime(wlWm.clkId, &data->sent))
|
||||
{
|
||||
DEBUG_ERROR("clock_gettime failed: %s\n", strerror(errno));
|
||||
free(data);
|
||||
}
|
||||
|
||||
struct wp_presentation_feedback * feedback = wp_presentation_feedback(wlWm.presentation, wlWm.surface);
|
||||
wp_presentation_feedback_add_listener(feedback, &presentationFeedbackListener, data);
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
|
||||
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
|
||||
https://looking-glass.io
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "wayland.h"
|
||||
|
||||
@@ -30,17 +30,29 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
static void registryGlobalHandler(void * data, struct wl_registry * registry,
|
||||
uint32_t name, const char * interface, uint32_t version)
|
||||
{
|
||||
if (!strcmp(interface, wl_seat_interface.name) && !wlWm.seat)
|
||||
if (!strcmp(interface, wl_output_interface.name))
|
||||
waylandOutputBind(name, version);
|
||||
else if (!strcmp(interface, wl_seat_interface.name) && !wlWm.seat)
|
||||
wlWm.seat = wl_registry_bind(wlWm.registry, name, &wl_seat_interface, 1);
|
||||
else if (!strcmp(interface, wl_shm_interface.name))
|
||||
wlWm.shm = wl_registry_bind(wlWm.registry, name, &wl_shm_interface, 1);
|
||||
else if (!strcmp(interface, wl_compositor_interface.name))
|
||||
wlWm.compositor = wl_registry_bind(wlWm.registry, name, &wl_compositor_interface, 4);
|
||||
else if (!strcmp(interface, wl_compositor_interface.name) && version >= 3)
|
||||
wlWm.compositor = wl_registry_bind(wlWm.registry, name,
|
||||
// we only need v3 to run, but v4 can use eglSwapBuffersWithDamageKHR
|
||||
&wl_compositor_interface, version > 4 ? 4 : version);
|
||||
#ifndef ENABLE_LIBDECOR
|
||||
else if (!strcmp(interface, xdg_wm_base_interface.name))
|
||||
wlWm.xdgWmBase = wl_registry_bind(wlWm.registry, name, &xdg_wm_base_interface, 1);
|
||||
else if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name))
|
||||
wlWm.xdgDecorationManager = wl_registry_bind(wlWm.registry, name,
|
||||
&zxdg_decoration_manager_v1_interface, 1);
|
||||
#endif
|
||||
else if (!strcmp(interface, wp_presentation_interface.name))
|
||||
wlWm.presentation = wl_registry_bind(wlWm.registry, name,
|
||||
&wp_presentation_interface, 1);
|
||||
else if (!strcmp(interface, wp_viewporter_interface.name))
|
||||
wlWm.viewporter = wl_registry_bind(wlWm.registry, name,
|
||||
&wp_viewporter_interface, 1);
|
||||
else if (!strcmp(interface, zwp_relative_pointer_manager_v1_interface.name))
|
||||
wlWm.relativePointerManager = wl_registry_bind(wlWm.registry, name,
|
||||
&zwp_relative_pointer_manager_v1_interface, 1);
|
||||
@@ -50,18 +62,22 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
|
||||
else if (!strcmp(interface, zwp_keyboard_shortcuts_inhibit_manager_v1_interface.name))
|
||||
wlWm.keyboardInhibitManager = wl_registry_bind(wlWm.registry, name,
|
||||
&zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
|
||||
else if (!strcmp(interface, wl_data_device_manager_interface.name))
|
||||
else if (!strcmp(interface, wl_data_device_manager_interface.name) && version >= 3)
|
||||
wlWm.dataDeviceManager = wl_registry_bind(wlWm.registry, name,
|
||||
&wl_data_device_manager_interface, 3);
|
||||
else if (!strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name))
|
||||
wlWm.idleInhibitManager = wl_registry_bind(wlWm.registry, name,
|
||||
&zwp_idle_inhibit_manager_v1_interface, 1);
|
||||
else if (!strcmp(interface, zxdg_output_manager_v1_interface.name) && version >= 2)
|
||||
wlWm.xdgOutputManager = wl_registry_bind(wlWm.registry, name,
|
||||
// we only need v2 to run, but v3 saves a callback
|
||||
&zxdg_output_manager_v1_interface, version > 3 ? 3 : version);
|
||||
}
|
||||
|
||||
static void registryGlobalRemoveHandler(void * data,
|
||||
struct wl_registry * registry, uint32_t name)
|
||||
{
|
||||
// Do nothing.
|
||||
waylandOutputTryUnbind(name);
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registryListener = {
|
||||
|
||||
178
client/displayservers/Wayland/shell_libdecor.c
Normal file
178
client/displayservers/Wayland/shell_libdecor.c
Normal file
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "wayland.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include <libdecor.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "app.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
struct libdecor_configuration {
|
||||
uint32_t serial;
|
||||
|
||||
bool has_window_state;
|
||||
enum libdecor_window_state window_state;
|
||||
|
||||
bool has_size;
|
||||
int window_width;
|
||||
int window_height;
|
||||
};
|
||||
|
||||
static void libdecorHandleError(struct libdecor * context, enum libdecor_error error,
|
||||
const char *message)
|
||||
{
|
||||
DEBUG_ERROR("Got libdecor error (%d): %s", error, message);
|
||||
}
|
||||
|
||||
static void libdecorFrameConfigure(struct libdecor_frame * frame,
|
||||
struct libdecor_configuration * configuration, void * opaque)
|
||||
{
|
||||
if (!wlWm.configured)
|
||||
{
|
||||
xdg_surface_ack_configure(libdecor_frame_get_xdg_surface(frame), configuration->serial);
|
||||
wlWm.configured = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int width, height;
|
||||
if (libdecor_configuration_get_content_size(configuration, frame, &width, &height))
|
||||
{
|
||||
wlWm.width = width;
|
||||
wlWm.height = height;
|
||||
|
||||
struct libdecor_state * state = libdecor_state_new(wlWm.width, wlWm.height);
|
||||
libdecor_frame_commit(wlWm.libdecorFrame, state, NULL);
|
||||
libdecor_state_free(state);
|
||||
}
|
||||
|
||||
enum libdecor_window_state windowState;
|
||||
if (libdecor_configuration_get_window_state(configuration, &windowState))
|
||||
wlWm.fullscreen = windowState & LIBDECOR_WINDOW_STATE_FULLSCREEN;
|
||||
|
||||
wlWm.needsResize = true;
|
||||
wlWm.resizeSerial = configuration->serial;
|
||||
app_invalidateWindow(true);
|
||||
waylandStopWaitFrame();
|
||||
}
|
||||
|
||||
static void libdecorFrameClose(struct libdecor_frame * frame, void * opaque)
|
||||
{
|
||||
app_handleCloseEvent();
|
||||
}
|
||||
|
||||
static void libdecorFrameCommit(struct libdecor_frame * frame, void * opaque)
|
||||
{
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
static struct libdecor_interface libdecorListener = {
|
||||
libdecorHandleError,
|
||||
};
|
||||
|
||||
static struct libdecor_frame_interface libdecorFrameListener = {
|
||||
libdecorFrameConfigure,
|
||||
libdecorFrameClose,
|
||||
libdecorFrameCommit,
|
||||
};
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
static void libdecorCallback(uint32_t events, void * opaque)
|
||||
{
|
||||
libdecor_dispatch(wlWm.libdecor, 0);
|
||||
}
|
||||
|
||||
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
|
||||
{
|
||||
wlWm.libdecor = libdecor_new(wlWm.display, &libdecorListener);
|
||||
wlWm.libdecorFrame = libdecor_decorate(wlWm.libdecor, wlWm.surface, &libdecorFrameListener, NULL);
|
||||
|
||||
libdecor_frame_set_app_id(wlWm.libdecorFrame, "looking-glass-client");
|
||||
libdecor_frame_set_title(wlWm.libdecorFrame, title);
|
||||
libdecor_frame_map(wlWm.libdecorFrame);
|
||||
|
||||
if (resizable)
|
||||
libdecor_frame_set_capabilities(wlWm.libdecorFrame, LIBDECOR_ACTION_RESIZE);
|
||||
else
|
||||
libdecor_frame_unset_capabilities(wlWm.libdecorFrame, LIBDECOR_ACTION_RESIZE);
|
||||
|
||||
while (!wlWm.configured)
|
||||
libdecor_dispatch(wlWm.libdecor, 0);
|
||||
|
||||
if (!waylandPollRegister(libdecor_get_fd(wlWm.libdecor), libdecorCallback, NULL, EPOLLIN))
|
||||
{
|
||||
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void waylandShellAckConfigureIfNeeded(void)
|
||||
{
|
||||
if (wlWm.resizeSerial)
|
||||
{
|
||||
xdg_surface_ack_configure(libdecor_frame_get_xdg_surface(wlWm.libdecorFrame), wlWm.resizeSerial);
|
||||
wlWm.resizeSerial = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void waylandSetFullscreen(bool fs)
|
||||
{
|
||||
if (fs)
|
||||
libdecor_frame_set_fullscreen(wlWm.libdecorFrame, NULL);
|
||||
else
|
||||
libdecor_frame_unset_fullscreen(wlWm.libdecorFrame);
|
||||
|
||||
libdecor_frame_set_visibility(wlWm.libdecorFrame, !fs);
|
||||
}
|
||||
|
||||
bool waylandGetFullscreen(void)
|
||||
{
|
||||
return wlWm.fullscreen;
|
||||
}
|
||||
|
||||
void waylandMinimize(void)
|
||||
{
|
||||
libdecor_frame_set_minimized(wlWm.libdecorFrame);
|
||||
}
|
||||
|
||||
void waylandShellResize(int w, int h)
|
||||
{
|
||||
if (!libdecor_frame_is_floating(wlWm.libdecorFrame))
|
||||
return;
|
||||
|
||||
wlWm.width = w;
|
||||
wlWm.height = h;
|
||||
|
||||
struct libdecor_state * state = libdecor_state_new(w, h);
|
||||
libdecor_frame_commit(wlWm.libdecorFrame, state, NULL);
|
||||
libdecor_state_free(state);
|
||||
|
||||
wlWm.needsResize = true;
|
||||
app_invalidateWindow(true);
|
||||
waylandStopWaitFrame();
|
||||
}
|
||||
160
client/displayservers/Wayland/shell_xdg.c
Normal file
160
client/displayservers/Wayland/shell_xdg.c
Normal file
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "wayland.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "app.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
// XDG WM base listeners.
|
||||
|
||||
static void xdgWmBasePing(void * data, struct xdg_wm_base * xdgWmBase, uint32_t serial)
|
||||
{
|
||||
xdg_wm_base_pong(xdgWmBase, serial);
|
||||
}
|
||||
|
||||
static const struct xdg_wm_base_listener xdgWmBaseListener = {
|
||||
.ping = xdgWmBasePing,
|
||||
};
|
||||
|
||||
// XDG Surface listeners.
|
||||
|
||||
static void xdgSurfaceConfigure(void * data, struct xdg_surface * xdgSurface,
|
||||
uint32_t serial)
|
||||
{
|
||||
if (wlWm.configured)
|
||||
{
|
||||
wlWm.needsResize = true;
|
||||
wlWm.resizeSerial = serial;
|
||||
app_invalidateWindow(true);
|
||||
waylandStopWaitFrame();
|
||||
}
|
||||
else
|
||||
{
|
||||
xdg_surface_ack_configure(xdgSurface, serial);
|
||||
wlWm.configured = true;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct xdg_surface_listener xdgSurfaceListener = {
|
||||
.configure = xdgSurfaceConfigure,
|
||||
};
|
||||
|
||||
// XDG Toplevel listeners.
|
||||
|
||||
static void xdgToplevelConfigure(void * data, struct xdg_toplevel * xdgToplevel,
|
||||
int32_t width, int32_t height, struct wl_array * states)
|
||||
{
|
||||
wlWm.width = width;
|
||||
wlWm.height = height;
|
||||
wlWm.fullscreen = false;
|
||||
|
||||
enum xdg_toplevel_state * state;
|
||||
wl_array_for_each(state, states)
|
||||
{
|
||||
if (*state == XDG_TOPLEVEL_STATE_FULLSCREEN)
|
||||
wlWm.fullscreen = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void xdgToplevelClose(void * data, struct xdg_toplevel * xdgToplevel)
|
||||
{
|
||||
app_handleCloseEvent();
|
||||
}
|
||||
|
||||
static const struct xdg_toplevel_listener xdgToplevelListener = {
|
||||
.configure = xdgToplevelConfigure,
|
||||
.close = xdgToplevelClose,
|
||||
};
|
||||
|
||||
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
|
||||
{
|
||||
if (!wlWm.xdgWmBase)
|
||||
{
|
||||
DEBUG_ERROR("Compositor missing xdg_wm_base, will not proceed");
|
||||
return false;
|
||||
}
|
||||
|
||||
xdg_wm_base_add_listener(wlWm.xdgWmBase, &xdgWmBaseListener, NULL);
|
||||
|
||||
wlWm.xdgSurface = xdg_wm_base_get_xdg_surface(wlWm.xdgWmBase, wlWm.surface);
|
||||
xdg_surface_add_listener(wlWm.xdgSurface, &xdgSurfaceListener, NULL);
|
||||
|
||||
wlWm.xdgToplevel = xdg_surface_get_toplevel(wlWm.xdgSurface);
|
||||
xdg_toplevel_add_listener(wlWm.xdgToplevel, &xdgToplevelListener, NULL);
|
||||
xdg_toplevel_set_title(wlWm.xdgToplevel, title);
|
||||
xdg_toplevel_set_app_id(wlWm.xdgToplevel, "looking-glass-client");
|
||||
|
||||
if (fullscreen)
|
||||
xdg_toplevel_set_fullscreen(wlWm.xdgToplevel, NULL);
|
||||
|
||||
if (maximize)
|
||||
xdg_toplevel_set_maximized(wlWm.xdgToplevel);
|
||||
|
||||
if (wlWm.xdgDecorationManager)
|
||||
{
|
||||
wlWm.xdgToplevelDecoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
|
||||
wlWm.xdgDecorationManager, wlWm.xdgToplevel);
|
||||
if (wlWm.xdgToplevelDecoration)
|
||||
{
|
||||
zxdg_toplevel_decoration_v1_set_mode(wlWm.xdgToplevelDecoration,
|
||||
borderless ?
|
||||
ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE :
|
||||
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void waylandShellAckConfigureIfNeeded(void)
|
||||
{
|
||||
if (wlWm.resizeSerial)
|
||||
{
|
||||
xdg_surface_ack_configure(wlWm.xdgSurface, wlWm.resizeSerial);
|
||||
wlWm.resizeSerial = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void waylandSetFullscreen(bool fs)
|
||||
{
|
||||
if (fs)
|
||||
xdg_toplevel_set_fullscreen(wlWm.xdgToplevel, NULL);
|
||||
else
|
||||
xdg_toplevel_unset_fullscreen(wlWm.xdgToplevel);
|
||||
}
|
||||
|
||||
bool waylandGetFullscreen(void)
|
||||
{
|
||||
return wlWm.fullscreen;
|
||||
}
|
||||
|
||||
void waylandMinimize(void)
|
||||
{
|
||||
xdg_toplevel_set_minimized(wlWm.xdgToplevel);
|
||||
}
|
||||
|
||||
void waylandShellResize(int w, int h)
|
||||
{
|
||||
//TODO: Implement resize for XDG.
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
|
||||
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
|
||||
https://looking-glass.io
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "wayland.h"
|
||||
|
||||
|
||||
@@ -1,58 +1,33 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include "wayland.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/input.h>
|
||||
#include <poll.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <string.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#if defined(ENABLE_EGL) || defined(ENABLE_OPENGL)
|
||||
# include <wayland-egl.h>
|
||||
# include "egl_dynprocs.h"
|
||||
# include <EGL/eglext.h>
|
||||
#endif
|
||||
|
||||
#include "app.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/locking.h"
|
||||
#include "common/countedbuffer.h"
|
||||
#include "common/option.h"
|
||||
|
||||
#include "wayland-xdg-shell-client-protocol.h"
|
||||
#include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
||||
#include "wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
|
||||
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
|
||||
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
|
||||
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
|
||||
|
||||
static struct Option waylandOptions[] =
|
||||
{
|
||||
{
|
||||
@@ -62,6 +37,13 @@ static struct Option waylandOptions[] =
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true,
|
||||
},
|
||||
{
|
||||
.module = "wayland",
|
||||
.name = "fractionScale",
|
||||
.description = "Enable fractional scale",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true,
|
||||
},
|
||||
{0}
|
||||
};
|
||||
|
||||
@@ -89,32 +71,42 @@ static bool waylandProbe(void)
|
||||
static bool waylandInit(const LG_DSInitParams params)
|
||||
{
|
||||
memset(&wlWm, 0, sizeof(wlWm));
|
||||
wl_list_init(&wlWm.surfaceOutputs);
|
||||
|
||||
wlWm.warpSupport = option_get_bool("wayland", "warpSupport");
|
||||
wlWm.warpSupport = option_get_bool("wayland", "warpSupport");
|
||||
wlWm.useFractionalScale = option_get_bool("wayland", "fractionScale");
|
||||
|
||||
wlWm.display = wl_display_connect(NULL);
|
||||
wlWm.width = params.w;
|
||||
wlWm.height = params.h;
|
||||
|
||||
if (!waylandPollInit())
|
||||
return false;
|
||||
|
||||
if (!waylandOutputInit())
|
||||
return false;
|
||||
|
||||
if (!waylandRegistryInit())
|
||||
return false;
|
||||
|
||||
if (!waylandIdleInit())
|
||||
return false;
|
||||
|
||||
if (!waylandInputInit())
|
||||
return false;
|
||||
|
||||
if (!waylandWindowInit(params.title, params.fullscreen, params.maximize, params.borderless))
|
||||
return false;
|
||||
|
||||
if (!waylandEGLInit(params.w, params.h))
|
||||
if (!waylandPresentationInit())
|
||||
return false;
|
||||
|
||||
if (!waylandCursorInit())
|
||||
return false;
|
||||
|
||||
if (!waylandInputInit())
|
||||
return false;
|
||||
|
||||
if (!waylandWindowInit(params.title, params.fullscreen, params.maximize, params.borderless, params.resizable))
|
||||
return false;
|
||||
|
||||
if (!waylandEGLInit(params.w, params.h))
|
||||
return false;
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
if (params.opengl && !waylandOpenGLInit())
|
||||
return false;
|
||||
@@ -138,8 +130,11 @@ static void waylandFree(void)
|
||||
{
|
||||
waylandIdleFree();
|
||||
waylandWindowFree();
|
||||
waylandPresentationFree();
|
||||
waylandInputFree();
|
||||
waylandOutputFree();
|
||||
waylandRegistryFree();
|
||||
waylandCursorFree();
|
||||
wl_display_disconnect(wlWm.display);
|
||||
}
|
||||
|
||||
@@ -156,43 +151,49 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret)
|
||||
|
||||
struct LG_DisplayServerOps LGDS_Wayland =
|
||||
{
|
||||
.setup = waylandSetup,
|
||||
.probe = waylandProbe,
|
||||
.earlyInit = waylandEarlyInit,
|
||||
.init = waylandInit,
|
||||
.startup = waylandStartup,
|
||||
.shutdown = waylandShutdown,
|
||||
.free = waylandFree,
|
||||
.getProp = waylandGetProp,
|
||||
.setup = waylandSetup,
|
||||
.probe = waylandProbe,
|
||||
.earlyInit = waylandEarlyInit,
|
||||
.init = waylandInit,
|
||||
.startup = waylandStartup,
|
||||
.shutdown = waylandShutdown,
|
||||
.free = waylandFree,
|
||||
.getProp = waylandGetProp,
|
||||
|
||||
#ifdef ENABLE_EGL
|
||||
.getEGLDisplay = waylandGetEGLDisplay,
|
||||
.getEGLNativeWindow = waylandGetEGLNativeWindow,
|
||||
.eglSwapBuffers = waylandEGLSwapBuffers,
|
||||
.getEGLDisplay = waylandGetEGLDisplay,
|
||||
.getEGLNativeWindow = waylandGetEGLNativeWindow,
|
||||
.eglSwapBuffers = waylandEGLSwapBuffers,
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
.glCreateContext = waylandGLCreateContext,
|
||||
.glDeleteContext = waylandGLDeleteContext,
|
||||
.glMakeCurrent = waylandGLMakeCurrent,
|
||||
.glSetSwapInterval = waylandGLSetSwapInterval,
|
||||
.glSwapBuffers = waylandGLSwapBuffers,
|
||||
.glCreateContext = waylandGLCreateContext,
|
||||
.glDeleteContext = waylandGLDeleteContext,
|
||||
.glMakeCurrent = waylandGLMakeCurrent,
|
||||
.glSetSwapInterval = waylandGLSetSwapInterval,
|
||||
.glSwapBuffers = waylandGLSwapBuffers,
|
||||
#endif
|
||||
|
||||
.showPointer = waylandShowPointer,
|
||||
.grabPointer = waylandGrabPointer,
|
||||
.ungrabPointer = waylandUngrabPointer,
|
||||
.grabKeyboard = waylandGrabKeyboard,
|
||||
.ungrabKeyboard = waylandUngrabKeyboard,
|
||||
.warpPointer = waylandWarpPointer,
|
||||
.realignPointer = waylandRealignPointer,
|
||||
.isValidPointerPos = waylandIsValidPointerPos,
|
||||
.inhibitIdle = waylandInhibitIdle,
|
||||
.uninhibitIdle = waylandUninhibitIdle,
|
||||
.wait = waylandWait,
|
||||
.setWindowSize = waylandSetWindowSize,
|
||||
.setFullscreen = waylandSetFullscreen,
|
||||
.getFullscreen = waylandGetFullscreen,
|
||||
.waitFrame = waylandWaitFrame,
|
||||
.skipFrame = waylandSkipFrame,
|
||||
.stopWaitFrame = waylandStopWaitFrame,
|
||||
.guestPointerUpdated = waylandGuestPointerUpdated,
|
||||
.setPointer = waylandSetPointer,
|
||||
.grabPointer = waylandGrabPointer,
|
||||
.ungrabPointer = waylandUngrabPointer,
|
||||
.capturePointer = waylandCapturePointer,
|
||||
.uncapturePointer = waylandUncapturePointer,
|
||||
.grabKeyboard = waylandGrabKeyboard,
|
||||
.ungrabKeyboard = waylandUngrabKeyboard,
|
||||
.warpPointer = waylandWarpPointer,
|
||||
.realignPointer = waylandRealignPointer,
|
||||
.isValidPointerPos = waylandIsValidPointerPos,
|
||||
.inhibitIdle = waylandInhibitIdle,
|
||||
.uninhibitIdle = waylandUninhibitIdle,
|
||||
.wait = waylandWait,
|
||||
.setWindowSize = waylandSetWindowSize,
|
||||
.setFullscreen = waylandSetFullscreen,
|
||||
.getFullscreen = waylandGetFullscreen,
|
||||
.minimize = waylandMinimize,
|
||||
|
||||
.cbInit = waylandCBInit,
|
||||
.cbNotice = waylandCBNotice,
|
||||
|
||||
@@ -1,44 +1,52 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
|
||||
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
|
||||
https://looking-glass.io
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-cursor.h>
|
||||
|
||||
#if defined(ENABLE_EGL) || defined(ENABLE_OPENGL)
|
||||
# include <wayland-egl.h>
|
||||
# include <EGL/egl.h>
|
||||
# include <EGL/eglext.h>
|
||||
# include "eglutil.h"
|
||||
#endif
|
||||
|
||||
#include "app.h"
|
||||
#include "egl_dynprocs.h"
|
||||
#include "common/locking.h"
|
||||
#include "common/countedbuffer.h"
|
||||
#include "common/ringbuffer.h"
|
||||
#include "interface/displayserver.h"
|
||||
|
||||
#include "wayland-xdg-shell-client-protocol.h"
|
||||
#include "wayland-presentation-time-client-protocol.h"
|
||||
#include "wayland-viewporter-client-protocol.h"
|
||||
#include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
||||
#include "wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
|
||||
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
|
||||
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
|
||||
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
|
||||
#include "wayland-xdg-output-unstable-v1-client-protocol.h"
|
||||
|
||||
typedef void (*WaylandPollCallback)(uint32_t events, void * opaque);
|
||||
|
||||
@@ -51,10 +59,45 @@ struct WaylandPoll
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
struct WaylandOutput
|
||||
{
|
||||
uint32_t name;
|
||||
wl_fixed_t scale;
|
||||
int32_t scaleInt;
|
||||
int32_t logicalWidth;
|
||||
int32_t logicalHeight;
|
||||
int32_t modeWidth;
|
||||
int32_t modeHeight;
|
||||
bool modeRotate;
|
||||
struct wl_output * output;
|
||||
struct zxdg_output_v1 * xdgOutput;
|
||||
uint32_t version;
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
struct SurfaceOutput
|
||||
{
|
||||
struct wl_output * output;
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
enum EGLSwapWithDamageState {
|
||||
SWAP_WITH_DAMAGE_UNKNOWN,
|
||||
SWAP_WITH_DAMAGE_UNSUPPORTED,
|
||||
SWAP_WITH_DAMAGE_KHR,
|
||||
SWAP_WITH_DAMAGE_EXT,
|
||||
};
|
||||
|
||||
struct xkb_context;
|
||||
struct xkb_keymap;
|
||||
struct xkb_state;
|
||||
|
||||
struct WaylandDSState
|
||||
{
|
||||
bool pointerGrabbed;
|
||||
bool keyboardGrabbed;
|
||||
bool pointerInSurface;
|
||||
bool focusedOnSurface;
|
||||
|
||||
struct wl_display * display;
|
||||
struct wl_surface * surface;
|
||||
@@ -64,14 +107,18 @@ struct WaylandDSState
|
||||
struct wl_compositor * compositor;
|
||||
|
||||
int32_t width, height;
|
||||
wl_fixed_t scale;
|
||||
bool fractionalScale;
|
||||
bool needsResize;
|
||||
bool fullscreen;
|
||||
uint32_t resizeSerial;
|
||||
bool configured;
|
||||
bool warpSupport;
|
||||
double cursorX, cursorY;
|
||||
|
||||
#ifdef ENABLE_EGL
|
||||
#if defined(ENABLE_EGL) || defined(ENABLE_OPENGL)
|
||||
struct wl_egl_window * eglWindow;
|
||||
struct SwapWithDamageData swapWithDamage;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
@@ -80,13 +127,33 @@ struct WaylandDSState
|
||||
EGLSurface glSurface;
|
||||
#endif
|
||||
|
||||
struct wp_presentation * presentation;
|
||||
clockid_t clkId;
|
||||
RingBuffer photonTimings;
|
||||
GraphHandle photonGraph;
|
||||
|
||||
#ifdef ENABLE_LIBDECOR
|
||||
struct libdecor * libdecor;
|
||||
struct libdecor_frame * libdecorFrame;
|
||||
#else
|
||||
struct xdg_wm_base * xdgWmBase;
|
||||
struct xdg_surface * xdgSurface;
|
||||
struct xdg_toplevel * xdgToplevel;
|
||||
struct zxdg_decoration_manager_v1 * xdgDecorationManager;
|
||||
struct zxdg_toplevel_decoration_v1 * xdgToplevelDecoration;
|
||||
#endif
|
||||
|
||||
struct wl_surface * cursor;
|
||||
const char * cursorThemeName;
|
||||
int cursorSize;
|
||||
int cursorScale;
|
||||
struct wl_cursor_theme * cursorTheme;
|
||||
struct wl_buffer * cursorSquareBuffer;
|
||||
struct wl_surface * cursors[LG_POINTER_COUNT];
|
||||
struct Point cursorHot[LG_POINTER_COUNT];
|
||||
LG_DSPointer cursorId;
|
||||
struct wl_surface * cursor;
|
||||
int cursorHotX;
|
||||
int cursorHotY;
|
||||
|
||||
struct wl_data_device_manager * dataDeviceManager;
|
||||
|
||||
@@ -96,18 +163,32 @@ struct WaylandDSState
|
||||
struct zwp_keyboard_shortcuts_inhibit_manager_v1 * keyboardInhibitManager;
|
||||
struct zwp_keyboard_shortcuts_inhibitor_v1 * keyboardInhibitor;
|
||||
uint32_t keyboardEnterSerial;
|
||||
struct xkb_context * xkb;
|
||||
struct xkb_state * xkbState;
|
||||
struct xkb_keymap * keymap;
|
||||
|
||||
struct wl_pointer * pointer;
|
||||
struct zwp_relative_pointer_manager_v1 * relativePointerManager;
|
||||
struct zwp_pointer_constraints_v1 * pointerConstraints;
|
||||
struct zwp_relative_pointer_v1 * relativePointer;
|
||||
struct zwp_confined_pointer_v1 * confinedPointer;
|
||||
struct zwp_locked_pointer_v1 * lockedPointer;
|
||||
bool showPointer;
|
||||
uint32_t pointerEnterSerial;
|
||||
LG_Lock confineLock;
|
||||
|
||||
struct zwp_idle_inhibit_manager_v1 * idleInhibitManager;
|
||||
struct zwp_idle_inhibitor_v1 * idleInhibitor;
|
||||
|
||||
struct wp_viewporter * viewporter;
|
||||
struct wp_viewport * viewport;
|
||||
struct zxdg_output_manager_v1 * xdgOutputManager;
|
||||
struct wl_list outputs; // WaylandOutput::link
|
||||
struct wl_list surfaceOutputs; // SurfaceOutput::link
|
||||
bool useFractionalScale;
|
||||
|
||||
LGEvent * frameEvent;
|
||||
|
||||
struct wl_list poll; // WaylandPoll::link
|
||||
struct wl_list pollFree; // WaylandPoll::link
|
||||
LG_Lock pollLock;
|
||||
@@ -137,13 +218,9 @@ struct WCBState
|
||||
struct wl_data_device * dataDevice;
|
||||
char lgMimetype[64];
|
||||
|
||||
enum LG_ClipboardData pendingType;
|
||||
char * pendingMimetype;
|
||||
bool isSelfCopy;
|
||||
|
||||
enum LG_ClipboardData stashedType;
|
||||
uint8_t * stashedContents;
|
||||
ssize_t stashedSize;
|
||||
char * mimetypes[LG_CLIPBOARD_DATA_NONE];
|
||||
struct wl_data_offer * offer;
|
||||
struct wl_data_offer * dndOffer;
|
||||
|
||||
bool haveRequest;
|
||||
LG_ClipboardData type;
|
||||
@@ -159,16 +236,19 @@ bool waylandCBInit(void);
|
||||
void waylandCBRequest(LG_ClipboardData type);
|
||||
void waylandCBNotice(LG_ClipboardData type);
|
||||
void waylandCBRelease(void);
|
||||
void waylandCBInvalidate(void);
|
||||
|
||||
// cursor module
|
||||
bool waylandCursorInit(void);
|
||||
void waylandShowPointer(bool show);
|
||||
void waylandCursorFree(void);
|
||||
void waylandSetPointer(LG_DSPointer pointer);
|
||||
void waylandCursorScaleChange(void);
|
||||
|
||||
// gl module
|
||||
#if defined(ENABLE_EGL) || defined(ENABLE_OPENGL)
|
||||
bool waylandEGLInit(int w, int h);
|
||||
EGLDisplay waylandGetEGLDisplay(void);
|
||||
void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface);
|
||||
void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_EGL
|
||||
@@ -197,23 +277,48 @@ void waylandGrabKeyboard(void);
|
||||
void waylandGrabPointer(void);
|
||||
void waylandUngrabKeyboard(void);
|
||||
void waylandUngrabPointer(void);
|
||||
void waylandCapturePointer(void);
|
||||
void waylandUncapturePointer(void);
|
||||
void waylandRealignPointer(void);
|
||||
void waylandWarpPointer(int x, int y, bool exiting);
|
||||
void waylandGuestPointerUpdated(double x, double y, double localX, double localY);
|
||||
|
||||
// output module
|
||||
bool waylandOutputInit(void);
|
||||
void waylandOutputFree(void);
|
||||
void waylandOutputBind(uint32_t name, uint32_t version);
|
||||
void waylandOutputTryUnbind(uint32_t name);
|
||||
wl_fixed_t waylandOutputGetScale(struct wl_output * output);
|
||||
|
||||
// poll module
|
||||
bool waylandPollInit(void);
|
||||
void waylandWait(unsigned int time);
|
||||
bool waylandEpollRegister(int fd, WaylandPollCallback callback, void * opaque, uint32_t events);
|
||||
bool waylandEpollUnregister(int fd);
|
||||
bool waylandPollRegister(int fd, WaylandPollCallback callback, void * opaque, uint32_t events);
|
||||
bool waylandPollUnregister(int fd);
|
||||
|
||||
// presentation module
|
||||
bool waylandPresentationInit(void);
|
||||
void waylandPresentationFrame(void);
|
||||
void waylandPresentationFree(void);
|
||||
|
||||
// registry module
|
||||
bool waylandRegistryInit(void);
|
||||
void waylandRegistryFree(void);
|
||||
|
||||
// window module
|
||||
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless);
|
||||
void waylandWindowFree(void);
|
||||
void waylandSetWindowSize(int x, int y);
|
||||
// shell module
|
||||
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable);
|
||||
void waylandShellAckConfigureIfNeeded(void);
|
||||
void waylandSetFullscreen(bool fs);
|
||||
bool waylandGetFullscreen(void);
|
||||
void waylandMinimize(void);
|
||||
void waylandShellResize(int w, int h);
|
||||
|
||||
// window module
|
||||
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable);
|
||||
void waylandWindowFree(void);
|
||||
void waylandWindowUpdateScale(void);
|
||||
void waylandSetWindowSize(int x, int y);
|
||||
bool waylandIsValidPointerPos(int x, int y);
|
||||
bool waylandWaitFrame(void);
|
||||
void waylandSkipFrame(void);
|
||||
void waylandStopWaitFrame(void);
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2021 Guanzhong Chen (quantum2048@gmail.com)
|
||||
Copyright (C) 2021 Tudor Brindus (contact@tbrindus.ca)
|
||||
https://looking-glass.io
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "wayland.h"
|
||||
|
||||
@@ -27,80 +27,76 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include "app.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
// XDG WM base listeners.
|
||||
|
||||
static void xdgWmBasePing(void * data, struct xdg_wm_base * xdgWmBase, uint32_t serial)
|
||||
{
|
||||
xdg_wm_base_pong(xdgWmBase, serial);
|
||||
}
|
||||
|
||||
static const struct xdg_wm_base_listener xdgWmBaseListener = {
|
||||
.ping = xdgWmBasePing,
|
||||
};
|
||||
#include "common/event.h"
|
||||
|
||||
// Surface-handling listeners.
|
||||
|
||||
static void xdgSurfaceConfigure(void * data, struct xdg_surface * xdgSurface,
|
||||
uint32_t serial)
|
||||
void waylandWindowUpdateScale(void)
|
||||
{
|
||||
if (wlWm.configured)
|
||||
wlWm.resizeSerial = serial;
|
||||
else
|
||||
wl_fixed_t maxScale = 0;
|
||||
struct SurfaceOutput * node;
|
||||
|
||||
wl_list_for_each(node, &wlWm.surfaceOutputs, link)
|
||||
{
|
||||
xdg_surface_ack_configure(xdgSurface, serial);
|
||||
wlWm.configured = true;
|
||||
wl_fixed_t scale = waylandOutputGetScale(node->output);
|
||||
if (scale > maxScale)
|
||||
maxScale = scale;
|
||||
}
|
||||
|
||||
if (maxScale)
|
||||
{
|
||||
wlWm.scale = maxScale;
|
||||
wlWm.fractionalScale = wl_fixed_from_int(wl_fixed_to_int(maxScale)) != maxScale;
|
||||
wlWm.needsResize = true;
|
||||
waylandCursorScaleChange();
|
||||
app_invalidateWindow(true);
|
||||
waylandStopWaitFrame();
|
||||
}
|
||||
}
|
||||
|
||||
static const struct xdg_surface_listener xdgSurfaceListener = {
|
||||
.configure = xdgSurfaceConfigure,
|
||||
static void wlSurfaceEnterHandler(void * data, struct wl_surface * surface, struct wl_output * output)
|
||||
{
|
||||
struct SurfaceOutput * node = malloc(sizeof(*node));
|
||||
node->output = output;
|
||||
wl_list_insert(&wlWm.surfaceOutputs, &node->link);
|
||||
waylandWindowUpdateScale();
|
||||
}
|
||||
|
||||
static void wlSurfaceLeaveHandler(void * data, struct wl_surface * surface, struct wl_output * output)
|
||||
{
|
||||
struct SurfaceOutput * node;
|
||||
wl_list_for_each(node, &wlWm.surfaceOutputs, link)
|
||||
if (node->output == output)
|
||||
{
|
||||
wl_list_remove(&node->link);
|
||||
break;
|
||||
}
|
||||
waylandWindowUpdateScale();
|
||||
}
|
||||
|
||||
static const struct wl_surface_listener wlSurfaceListener = {
|
||||
.enter = wlSurfaceEnterHandler,
|
||||
.leave = wlSurfaceLeaveHandler,
|
||||
};
|
||||
|
||||
// XDG Surface listeners.
|
||||
|
||||
static void xdgToplevelConfigure(void * data, struct xdg_toplevel * xdgToplevel,
|
||||
int32_t width, int32_t height, struct wl_array * states)
|
||||
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
|
||||
{
|
||||
wlWm.width = width;
|
||||
wlWm.height = height;
|
||||
wlWm.fullscreen = false;
|
||||
wlWm.scale = wl_fixed_from_int(1);
|
||||
|
||||
enum xdg_toplevel_state * state;
|
||||
wl_array_for_each(state, states)
|
||||
wlWm.frameEvent = lgCreateEvent(true, 0);
|
||||
if (!wlWm.frameEvent)
|
||||
{
|
||||
if (*state == XDG_TOPLEVEL_STATE_FULLSCREEN)
|
||||
wlWm.fullscreen = true;
|
||||
DEBUG_ERROR("Failed to initialize event for waitFrame");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
lgSignalEvent(wlWm.frameEvent);
|
||||
|
||||
static void xdgToplevelClose(void * data, struct xdg_toplevel * xdgToplevel)
|
||||
{
|
||||
app_handleCloseEvent();
|
||||
}
|
||||
|
||||
static const struct xdg_toplevel_listener xdgToplevelListener = {
|
||||
.configure = xdgToplevelConfigure,
|
||||
.close = xdgToplevelClose,
|
||||
};
|
||||
|
||||
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless)
|
||||
{
|
||||
if (!wlWm.compositor)
|
||||
{
|
||||
DEBUG_ERROR("Compositor missing wl_compositor, will not proceed");
|
||||
DEBUG_ERROR("Compositor missing wl_compositor (version 3+), will not proceed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!wlWm.xdgWmBase)
|
||||
{
|
||||
DEBUG_ERROR("Compositor missing xdg_wm_base, will not proceed");
|
||||
return false;
|
||||
}
|
||||
|
||||
xdg_wm_base_add_listener(wlWm.xdgWmBase, &xdgWmBaseListener, NULL);
|
||||
//wl_display_roundtrip(wlWm.display);
|
||||
|
||||
wlWm.surface = wl_compositor_create_surface(wlWm.compositor);
|
||||
if (!wlWm.surface)
|
||||
{
|
||||
@@ -108,59 +104,24 @@ bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool
|
||||
return false;
|
||||
}
|
||||
|
||||
wlWm.xdgSurface = xdg_wm_base_get_xdg_surface(wlWm.xdgWmBase, wlWm.surface);
|
||||
xdg_surface_add_listener(wlWm.xdgSurface, &xdgSurfaceListener, NULL);
|
||||
wl_surface_add_listener(wlWm.surface, &wlSurfaceListener, NULL);
|
||||
|
||||
wlWm.xdgToplevel = xdg_surface_get_toplevel(wlWm.xdgSurface);
|
||||
xdg_toplevel_add_listener(wlWm.xdgToplevel, &xdgToplevelListener, NULL);
|
||||
xdg_toplevel_set_title(wlWm.xdgToplevel, title);
|
||||
xdg_toplevel_set_app_id(wlWm.xdgToplevel, "looking-glass-client");
|
||||
|
||||
if (fullscreen)
|
||||
xdg_toplevel_set_fullscreen(wlWm.xdgToplevel, NULL);
|
||||
|
||||
if (maximize)
|
||||
xdg_toplevel_set_maximized(wlWm.xdgToplevel);
|
||||
if (!waylandShellInit(title, fullscreen, maximize, borderless, resizable))
|
||||
return false;
|
||||
|
||||
wl_surface_commit(wlWm.surface);
|
||||
|
||||
if (wlWm.xdgDecorationManager)
|
||||
{
|
||||
wlWm.xdgToplevelDecoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
|
||||
wlWm.xdgDecorationManager, wlWm.xdgToplevel);
|
||||
if (wlWm.xdgToplevelDecoration)
|
||||
{
|
||||
zxdg_toplevel_decoration_v1_set_mode(wlWm.xdgToplevelDecoration,
|
||||
borderless ?
|
||||
ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE :
|
||||
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void waylandWindowFree(void)
|
||||
{
|
||||
wl_surface_destroy(wlWm.surface);
|
||||
lgFreeEvent(wlWm.frameEvent);
|
||||
}
|
||||
|
||||
void waylandSetWindowSize(int x, int y)
|
||||
{
|
||||
// FIXME: implement.
|
||||
}
|
||||
|
||||
void waylandSetFullscreen(bool fs)
|
||||
{
|
||||
if (fs)
|
||||
xdg_toplevel_set_fullscreen(wlWm.xdgToplevel, NULL);
|
||||
else
|
||||
xdg_toplevel_unset_fullscreen(wlWm.xdgToplevel);
|
||||
}
|
||||
|
||||
bool waylandGetFullscreen(void)
|
||||
{
|
||||
return wlWm.fullscreen;
|
||||
waylandShellResize(x, y);
|
||||
}
|
||||
|
||||
bool waylandIsValidPointerPos(int x, int y)
|
||||
@@ -168,4 +129,34 @@ bool waylandIsValidPointerPos(int x, int y)
|
||||
return x >= 0 && x < wlWm.width && y >= 0 && y < wlWm.height;
|
||||
}
|
||||
|
||||
static void frameHandler(void * opaque, struct wl_callback * callback, unsigned int data)
|
||||
{
|
||||
lgSignalEvent(wlWm.frameEvent);
|
||||
wl_callback_destroy(callback);
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener frame_listener = {
|
||||
.done = frameHandler,
|
||||
};
|
||||
|
||||
bool waylandWaitFrame(void)
|
||||
{
|
||||
lgWaitEvent(wlWm.frameEvent, TIMEOUT_INFINITE);
|
||||
|
||||
struct wl_callback * callback = wl_surface_frame(wlWm.surface);
|
||||
if (callback)
|
||||
wl_callback_add_listener(callback, &frame_listener, NULL);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void waylandSkipFrame(void)
|
||||
{
|
||||
// If we decided to not render, we must commit the surface so that the callback is registered.
|
||||
wl_surface_commit(wlWm.surface);
|
||||
}
|
||||
|
||||
void waylandStopWaitFrame(void)
|
||||
{
|
||||
lgSignalEvent(wlWm.frameEvent);
|
||||
}
|
||||
|
||||
@@ -2,27 +2,30 @@ cmake_minimum_required(VERSION 3.0)
|
||||
project(displayserver_X11 LANGUAGES C)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(DISPLAYSERVER_X11_PKGCONFIG REQUIRED
|
||||
pkg_check_modules(DISPLAYSERVER_X11 REQUIRED IMPORTED_TARGET
|
||||
x11
|
||||
xi
|
||||
xfixes
|
||||
xscrnsaver
|
||||
xinerama
|
||||
xcursor
|
||||
xpresent
|
||||
)
|
||||
|
||||
add_library(displayserver_X11 STATIC
|
||||
x11.c
|
||||
atoms.c
|
||||
clipboard.c
|
||||
)
|
||||
|
||||
add_definitions(-D GLX_GLXEXT_PROTOTYPES)
|
||||
|
||||
target_link_libraries(displayserver_X11
|
||||
${DISPLAYSERVER_X11_PKGCONFIG_LIBRARIES}
|
||||
PkgConfig::DISPLAYSERVER_X11
|
||||
lg_common
|
||||
)
|
||||
|
||||
target_include_directories(displayserver_X11
|
||||
PRIVATE
|
||||
src
|
||||
${DISPLAYSERVER_X11_PKGCONFIG_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
32
client/displayservers/X11/atoms.c
Normal file
32
client/displayservers/X11/atoms.c
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "atoms.h"
|
||||
#include "x11.h"
|
||||
|
||||
struct X11DSAtoms x11atoms = { 0 };
|
||||
|
||||
void X11AtomsInit(void)
|
||||
{
|
||||
#define DEF_ATOM(x, onlyIfExists) \
|
||||
x11atoms.x = XInternAtom(x11.display, #x, onlyIfExists);
|
||||
DEF_ATOMS()
|
||||
#undef DEF_ATOM
|
||||
}
|
||||
60
client/displayservers/X11/atoms.h
Normal file
60
client/displayservers/X11/atoms.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_X11DS_ATOMS_
|
||||
#define _H_X11DS_ATOMS_
|
||||
|
||||
#define DEF_ATOMS() \
|
||||
DEF_ATOM(_NET_SUPPORTING_WM_CHECK, True) \
|
||||
DEF_ATOM(_NET_SUPPORTED, True) \
|
||||
DEF_ATOM(_NET_WM_NAME, True) \
|
||||
DEF_ATOM(_NET_REQUEST_FRAME_EXTENTS, True) \
|
||||
DEF_ATOM(_NET_FRAME_EXTENTS, True) \
|
||||
DEF_ATOM(_NET_WM_BYPASS_COMPOSITOR, False) \
|
||||
DEF_ATOM(_NET_WM_STATE, True) \
|
||||
DEF_ATOM(_NET_WM_STATE_FULLSCREEN, True) \
|
||||
DEF_ATOM(_NET_WM_STATE_FOCUSED, True) \
|
||||
DEF_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ, True) \
|
||||
DEF_ATOM(_NET_WM_STATE_MAXIMIZED_VERT, True) \
|
||||
DEF_ATOM(_NET_WM_WINDOW_TYPE, True) \
|
||||
DEF_ATOM(_NET_WM_WINDOW_TYPE_NORMAL, True) \
|
||||
DEF_ATOM(_NET_WM_WINDOW_TYPE_UTILITY, True) \
|
||||
DEF_ATOM(WM_DELETE_WINDOW, True) \
|
||||
DEF_ATOM(_MOTIF_WM_HINTS, True) \
|
||||
\
|
||||
DEF_ATOM(CLIPBOARD, False) \
|
||||
DEF_ATOM(TARGETS, False) \
|
||||
DEF_ATOM(SEL_DATA, False) \
|
||||
DEF_ATOM(INCR, False)
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#define DEF_ATOM(x, onlyIfExists) Atom x;
|
||||
struct X11DSAtoms
|
||||
{
|
||||
DEF_ATOMS()
|
||||
};
|
||||
#undef DEF_ATOM
|
||||
|
||||
extern struct X11DSAtoms x11atoms;
|
||||
|
||||
void X11AtomsInit(void);
|
||||
|
||||
#endif
|
||||
424
client/displayservers/X11/clipboard.c
Normal file
424
client/displayservers/X11/clipboard.c
Normal file
@@ -0,0 +1,424 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "clipboard.h"
|
||||
#include "x11.h"
|
||||
#include "atoms.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
#include "app.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
struct X11ClipboardState
|
||||
{
|
||||
Atom aCurSelection;
|
||||
Atom aTypes[LG_CLIPBOARD_DATA_NONE];
|
||||
LG_ClipboardData type;
|
||||
bool haveRequest;
|
||||
|
||||
bool incrStart;
|
||||
unsigned int lowerBound;
|
||||
};
|
||||
|
||||
static const char * atomTypes[] =
|
||||
{
|
||||
"UTF8_STRING",
|
||||
"image/png",
|
||||
"image/bmp",
|
||||
"image/tiff",
|
||||
"image/jpeg"
|
||||
};
|
||||
|
||||
static struct X11ClipboardState x11cb;
|
||||
|
||||
// forwards
|
||||
static void x11CBSelectionRequest(const XSelectionRequestEvent e);
|
||||
static void x11CBSelectionClear(const XSelectionClearEvent e);
|
||||
static void x11CBSelectionIncr(const XPropertyEvent e);
|
||||
static void x11CBSelectionNotify(const XSelectionEvent e);
|
||||
static void x11CBXFixesSelectionNotify(const XFixesSelectionNotifyEvent e);
|
||||
|
||||
bool x11CBEventThread(const XEvent xe)
|
||||
{
|
||||
switch(xe.type)
|
||||
{
|
||||
case SelectionRequest:
|
||||
x11CBSelectionRequest(xe.xselectionrequest);
|
||||
return true;
|
||||
|
||||
case SelectionClear:
|
||||
x11CBSelectionClear(xe.xselectionclear);
|
||||
return true;
|
||||
|
||||
case SelectionNotify:
|
||||
x11CBSelectionNotify(xe.xselection);
|
||||
return true;
|
||||
|
||||
case PropertyNotify:
|
||||
if (xe.xproperty.state != PropertyNewValue)
|
||||
break;
|
||||
|
||||
if (xe.xproperty.atom == x11atoms.SEL_DATA)
|
||||
{
|
||||
if (x11cb.lowerBound == 0)
|
||||
return true;
|
||||
|
||||
x11CBSelectionIncr(xe.xproperty);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (xe.type == x11.eventBase + XFixesSelectionNotify)
|
||||
{
|
||||
XFixesSelectionNotifyEvent * sne = (XFixesSelectionNotifyEvent *)&xe;
|
||||
x11CBXFixesSelectionNotify(*sne);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool x11CBInit()
|
||||
{
|
||||
x11cb.aCurSelection = BadValue;
|
||||
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
||||
{
|
||||
x11cb.aTypes[i] = XInternAtom(x11.display, atomTypes[i], False);
|
||||
if (x11cb.aTypes[i] == BadAlloc || x11cb.aTypes[i] == BadValue)
|
||||
{
|
||||
DEBUG_ERROR("failed to get atom for type: %s", atomTypes[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// use xfixes to get clipboard change notifications
|
||||
if (!XFixesQueryExtension(x11.display, &x11.eventBase, &x11.errorBase))
|
||||
{
|
||||
DEBUG_ERROR("failed to initialize xfixes");
|
||||
return false;
|
||||
}
|
||||
|
||||
XFixesSelectSelectionInput(x11.display, x11.window,
|
||||
XA_PRIMARY, XFixesSetSelectionOwnerNotifyMask);
|
||||
XFixesSelectSelectionInput(x11.display, x11.window,
|
||||
x11atoms.CLIPBOARD, XFixesSetSelectionOwnerNotifyMask);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void x11CBReplyFn(void * opaque, LG_ClipboardData type,
|
||||
uint8_t * data, uint32_t size)
|
||||
{
|
||||
XEvent *s = (XEvent *)opaque;
|
||||
|
||||
XChangeProperty(
|
||||
x11.display ,
|
||||
s->xselection.requestor,
|
||||
s->xselection.property ,
|
||||
s->xselection.target ,
|
||||
8,
|
||||
PropModeReplace,
|
||||
data,
|
||||
size);
|
||||
|
||||
XSendEvent(x11.display, s->xselection.requestor, 0, 0, s);
|
||||
XFlush(x11.display);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void x11CBSelectionRequest(const XSelectionRequestEvent e)
|
||||
{
|
||||
XEvent * s = malloc(sizeof(*s));
|
||||
s->xselection.type = SelectionNotify;
|
||||
s->xselection.requestor = e.requestor;
|
||||
s->xselection.selection = e.selection;
|
||||
s->xselection.target = e.target;
|
||||
s->xselection.property = e.property;
|
||||
s->xselection.time = e.time;
|
||||
|
||||
if (!x11cb.haveRequest)
|
||||
goto nodata;
|
||||
|
||||
// target list requested
|
||||
if (e.target == x11atoms.TARGETS)
|
||||
{
|
||||
Atom targets[2];
|
||||
targets[0] = x11atoms.TARGETS;
|
||||
targets[1] = x11cb.aTypes[x11cb.type];
|
||||
|
||||
XChangeProperty(
|
||||
e.display,
|
||||
e.requestor,
|
||||
e.property,
|
||||
XA_ATOM,
|
||||
32,
|
||||
PropModeReplace,
|
||||
(unsigned char*)targets,
|
||||
sizeof(targets) / sizeof(Atom));
|
||||
|
||||
goto send;
|
||||
}
|
||||
|
||||
// look to see if we can satisfy the data type
|
||||
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
||||
if (x11cb.aTypes[i] == e.target && x11cb.type == i)
|
||||
{
|
||||
// request the data
|
||||
app_clipboardRequest(x11CBReplyFn, s);
|
||||
return;
|
||||
}
|
||||
|
||||
nodata:
|
||||
// report no data
|
||||
s->xselection.property = None;
|
||||
|
||||
send:
|
||||
XSendEvent(x11.display, e.requestor, 0, 0, s);
|
||||
XFlush(x11.display);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void x11CBSelectionClear(const XSelectionClearEvent e)
|
||||
{
|
||||
if (e.selection != XA_PRIMARY && e.selection != x11atoms.CLIPBOARD)
|
||||
return;
|
||||
|
||||
x11cb.aCurSelection = BadValue;
|
||||
app_clipboardRelease();
|
||||
return;
|
||||
}
|
||||
|
||||
static void x11CBSelectionIncr(const XPropertyEvent e)
|
||||
{
|
||||
Atom type;
|
||||
int format;
|
||||
unsigned long itemCount, after;
|
||||
unsigned char *data;
|
||||
|
||||
if (XGetWindowProperty(
|
||||
e.display,
|
||||
e.window,
|
||||
e.atom,
|
||||
0, ~0L, // start and length
|
||||
True, // delete the property
|
||||
x11atoms.INCR,
|
||||
&type,
|
||||
&format,
|
||||
&itemCount,
|
||||
&after,
|
||||
&data) != Success)
|
||||
{
|
||||
DEBUG_INFO("GetProp Failed");
|
||||
app_clipboardNotifySize(LG_CLIPBOARD_DATA_NONE, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
LG_ClipboardData dataType;
|
||||
for(dataType = 0; dataType < LG_CLIPBOARD_DATA_NONE; ++dataType)
|
||||
if (x11cb.aTypes[dataType] == type)
|
||||
break;
|
||||
|
||||
if (dataType == LG_CLIPBOARD_DATA_NONE)
|
||||
{
|
||||
DEBUG_WARN("clipboard data (%s) not in a supported format",
|
||||
XGetAtomName(x11.display, type));
|
||||
|
||||
x11cb.lowerBound = 0;
|
||||
app_clipboardNotifySize(LG_CLIPBOARD_DATA_NONE, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (x11cb.incrStart)
|
||||
{
|
||||
app_clipboardNotifySize(dataType, x11cb.lowerBound);
|
||||
x11cb.incrStart = false;
|
||||
}
|
||||
|
||||
XFree(data);
|
||||
data = NULL;
|
||||
|
||||
if (XGetWindowProperty(
|
||||
e.display,
|
||||
e.window,
|
||||
e.atom,
|
||||
0, ~0L, // start and length
|
||||
True, // delete the property
|
||||
type,
|
||||
&type,
|
||||
&format,
|
||||
&itemCount,
|
||||
&after,
|
||||
&data) != Success)
|
||||
{
|
||||
DEBUG_ERROR("XGetWindowProperty Failed");
|
||||
app_clipboardNotifySize(LG_CLIPBOARD_DATA_NONE, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
app_clipboardData(dataType, data, itemCount);
|
||||
x11cb.lowerBound -= itemCount;
|
||||
|
||||
out:
|
||||
if (data)
|
||||
XFree(data);
|
||||
}
|
||||
|
||||
static void x11CBXFixesSelectionNotify(const XFixesSelectionNotifyEvent e)
|
||||
{
|
||||
// check if the selection is valid and it isn't ourself
|
||||
if ((e.selection != XA_PRIMARY && e.selection != x11atoms.CLIPBOARD) ||
|
||||
e.owner == x11.window || e.owner == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// remember which selection we are working with
|
||||
x11cb.aCurSelection = e.selection;
|
||||
XConvertSelection(
|
||||
x11.display,
|
||||
e.selection,
|
||||
x11atoms.TARGETS,
|
||||
x11atoms.TARGETS,
|
||||
x11.window,
|
||||
CurrentTime);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void x11CBSelectionNotify(const XSelectionEvent e)
|
||||
{
|
||||
if (e.property == None)
|
||||
return;
|
||||
|
||||
Atom type;
|
||||
int format;
|
||||
unsigned long itemCount, after;
|
||||
unsigned char *data;
|
||||
|
||||
if (XGetWindowProperty(
|
||||
e.display,
|
||||
e.requestor,
|
||||
e.property,
|
||||
0, ~0L, // start and length
|
||||
True , // delete the property
|
||||
AnyPropertyType,
|
||||
&type,
|
||||
&format,
|
||||
&itemCount,
|
||||
&after,
|
||||
&data) != Success)
|
||||
{
|
||||
app_clipboardNotifySize(LG_CLIPBOARD_DATA_NONE, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (type == x11atoms.INCR)
|
||||
{
|
||||
x11cb.incrStart = true;
|
||||
x11cb.lowerBound = *(unsigned int *)data;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// the target list
|
||||
if (e.property == x11atoms.TARGETS)
|
||||
{
|
||||
// the format is 32-bit and we must have data
|
||||
// this is technically incorrect however as it's
|
||||
// an array of padded 64-bit values
|
||||
if (!data || format != 32)
|
||||
goto out;
|
||||
|
||||
int typeCount = 0;
|
||||
LG_ClipboardData types[itemCount];
|
||||
|
||||
// see if we support any of the targets listed
|
||||
const uint64_t * targets = (const uint64_t *)data;
|
||||
for(unsigned long i = 0; i < itemCount; ++i)
|
||||
{
|
||||
for(int n = 0; n < LG_CLIPBOARD_DATA_NONE; ++n)
|
||||
if (x11cb.aTypes[n] == targets[i])
|
||||
types[typeCount++] = n;
|
||||
}
|
||||
|
||||
app_clipboardNotifyTypes(types, typeCount);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (e.property == x11atoms.SEL_DATA)
|
||||
{
|
||||
LG_ClipboardData dataType;
|
||||
for(dataType = 0; dataType < LG_CLIPBOARD_DATA_NONE; ++dataType)
|
||||
if (x11cb.aTypes[dataType] == type)
|
||||
break;
|
||||
|
||||
if (dataType == LG_CLIPBOARD_DATA_NONE)
|
||||
{
|
||||
DEBUG_WARN("clipboard data (%s) not in a supported format",
|
||||
XGetAtomName(x11.display, type));
|
||||
goto out;
|
||||
}
|
||||
|
||||
app_clipboardData(dataType, data, itemCount);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (data)
|
||||
XFree(data);
|
||||
}
|
||||
|
||||
void x11CBNotice(LG_ClipboardData type)
|
||||
{
|
||||
x11cb.haveRequest = true;
|
||||
x11cb.type = type;
|
||||
XSetSelectionOwner(x11.display, XA_PRIMARY , x11.window, CurrentTime);
|
||||
XSetSelectionOwner(x11.display, x11atoms.CLIPBOARD, x11.window, CurrentTime);
|
||||
XFlush(x11.display);
|
||||
}
|
||||
|
||||
void x11CBRelease(void)
|
||||
{
|
||||
x11cb.haveRequest = false;
|
||||
XSetSelectionOwner(x11.display, XA_PRIMARY , None, CurrentTime);
|
||||
XSetSelectionOwner(x11.display, x11atoms.CLIPBOARD, None, CurrentTime);
|
||||
XFlush(x11.display);
|
||||
}
|
||||
|
||||
void x11CBRequest(LG_ClipboardData type)
|
||||
{
|
||||
if (x11cb.aCurSelection == BadValue)
|
||||
return;
|
||||
|
||||
XConvertSelection(
|
||||
x11.display,
|
||||
x11cb.aCurSelection,
|
||||
x11cb.aTypes[type],
|
||||
x11atoms.SEL_DATA,
|
||||
x11.window,
|
||||
CurrentTime);
|
||||
}
|
||||
36
client/displayservers/X11/clipboard.h
Normal file
36
client/displayservers/X11/clipboard.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_X11DS_CLIPBOARD_
|
||||
#define _H_X11DS_CLIPBOARD_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
|
||||
#include "interface/displayserver.h"
|
||||
|
||||
bool x11CBEventThread(const XEvent xe);
|
||||
|
||||
bool x11CBInit();
|
||||
void x11CBNotice(LG_ClipboardData type);
|
||||
void x11CBRelease(void);
|
||||
void x11CBRequest(LG_ClipboardData type);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
103
client/displayservers/X11/x11.h
Normal file
103
client/displayservers/X11/x11.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_X11DS_X11_
|
||||
#define _H_X11DS_X11_
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
|
||||
#include <GL/glx.h>
|
||||
|
||||
#include "interface/displayserver.h"
|
||||
#include "common/thread.h"
|
||||
#include "common/types.h"
|
||||
|
||||
enum Modifiers
|
||||
{
|
||||
MOD_CTRL_LEFT = 0,
|
||||
MOD_CTRL_RIGHT,
|
||||
MOD_SHIFT_LEFT,
|
||||
MOD_SHIFT_RIGHT,
|
||||
MOD_ALT_LEFT,
|
||||
MOD_ALT_RIGHT,
|
||||
MOD_SUPER_LEFT,
|
||||
MOD_SUPER_RIGHT,
|
||||
};
|
||||
|
||||
#define MOD_COUNT (MOD_SUPER_RIGHT + 1)
|
||||
|
||||
struct X11DSState
|
||||
{
|
||||
Display * display;
|
||||
Window window;
|
||||
XVisualInfo * visual;
|
||||
|
||||
//Extended Window Manager Hints
|
||||
//ref: https://specifications.freedesktop.org/wm-spec/latest/
|
||||
bool ewmhSupport;
|
||||
bool ewmhHasFocusEvent;
|
||||
|
||||
_Atomic(uint64_t) lastWMEvent;
|
||||
bool invalidateAll;
|
||||
|
||||
int xpresentOp;
|
||||
bool jitRender;
|
||||
_Atomic(uint64_t) presentMsc, presentUst;
|
||||
uint32_t presentSerial;
|
||||
Pixmap presentPixmap;
|
||||
XserverRegion presentRegion;
|
||||
LGEvent * frameEvent;
|
||||
|
||||
LGThread * eventThread;
|
||||
|
||||
int xinputOp;
|
||||
int pointerDev;
|
||||
int keyboardDev;
|
||||
int xValuator;
|
||||
int yValuator;
|
||||
|
||||
bool pointerGrabbed;
|
||||
bool keyboardGrabbed;
|
||||
bool entered;
|
||||
bool focused;
|
||||
bool fullscreen;
|
||||
|
||||
struct Rect rect;
|
||||
struct Border border;
|
||||
|
||||
Cursor cursors[LG_POINTER_COUNT];
|
||||
|
||||
XIM xim;
|
||||
XIC xic;
|
||||
bool modifiers[MOD_COUNT];
|
||||
|
||||
// XFixes vars
|
||||
int eventBase;
|
||||
int errorBase;
|
||||
};
|
||||
|
||||
extern struct X11DSState x11;
|
||||
|
||||
#endif
|
||||
@@ -1,41 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(fonts LANGUAGES C)
|
||||
|
||||
set(FONT_H "${CMAKE_BINARY_DIR}/include/dynamic/fonts.h")
|
||||
set(FONT_C "${CMAKE_BINARY_DIR}/src/fonts.c")
|
||||
|
||||
file(WRITE ${FONT_H} "#include \"interface/font.h\"\n\n")
|
||||
file(APPEND ${FONT_H} "extern LG_Font * LG_Fonts[];\n\n")
|
||||
|
||||
file(WRITE ${FONT_C} "#include \"interface/font.h\"\n\n")
|
||||
file(APPEND ${FONT_C} "#include <stddef.h>\n\n")
|
||||
|
||||
set(FONTS "_")
|
||||
set(FONTS_LINK "_")
|
||||
function(add_font name)
|
||||
set(FONTS "${FONTS};${name}" PARENT_SCOPE)
|
||||
set(FONTS_LINK "${FONTS_LINK};font_${name}" PARENT_SCOPE)
|
||||
add_subdirectory(${name})
|
||||
endfunction()
|
||||
|
||||
# Add/remove fonts here!
|
||||
add_font(SDL)
|
||||
|
||||
list(REMOVE_AT FONTS 0)
|
||||
list(REMOVE_AT FONTS_LINK 0)
|
||||
|
||||
list(LENGTH FONTS FONT_COUNT)
|
||||
file(APPEND ${FONT_H} "#define LG_FONT_COUNT ${FONT_COUNT}\n")
|
||||
|
||||
foreach(font ${FONTS})
|
||||
file(APPEND ${FONT_C} "extern LG_Font LGF_${font};\n")
|
||||
endforeach()
|
||||
|
||||
file(APPEND ${FONT_C} "\nconst LG_Font * LG_Fonts[] =\n{\n")
|
||||
foreach(font ${FONTS})
|
||||
file(APPEND ${FONT_C} " &LGF_${font},\n")
|
||||
endforeach()
|
||||
file(APPEND ${FONT_C} " NULL\n};\n\n")
|
||||
|
||||
add_library(fonts STATIC ${FONT_C})
|
||||
target_link_libraries(fonts ${FONTS_LINK})
|
||||
@@ -1,26 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(font_SDL LANGUAGES C)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(FONT_SDL_PKGCONFIG REQUIRED
|
||||
SDL2_ttf
|
||||
fontconfig
|
||||
)
|
||||
|
||||
add_library(font_SDL STATIC
|
||||
src/sdl.c
|
||||
)
|
||||
|
||||
target_link_libraries(font_SDL
|
||||
${FONT_SDL_PKGCONFIG_LIBRARIES}
|
||||
lg_common
|
||||
)
|
||||
|
||||
target_include_directories(font_SDL
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
PRIVATE
|
||||
src
|
||||
${FONT_SDL_PKGCONFIG_INCLUDE_DIRS}
|
||||
)
|
||||
@@ -1,251 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "interface/font.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
#include <fontconfig/fontconfig.h>
|
||||
|
||||
static int g_initCount = 0;
|
||||
static FcConfig * g_fontConfig = NULL;
|
||||
|
||||
struct Inst
|
||||
{
|
||||
TTF_Font * font;
|
||||
};
|
||||
|
||||
static bool lgf_sdl_create(LG_FontObj * opaque, const char * font_name, unsigned int size)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if (g_initCount == 0)
|
||||
{
|
||||
if (TTF_Init() < 0)
|
||||
{
|
||||
DEBUG_ERROR("TTF_Init Failed");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
g_fontConfig = FcInitLoadConfigAndFonts();
|
||||
if (!g_fontConfig)
|
||||
{
|
||||
DEBUG_ERROR("FcInitLoadConfigAndFonts Failed");
|
||||
goto fail_init;
|
||||
}
|
||||
}
|
||||
|
||||
*opaque = malloc(sizeof(struct Inst));
|
||||
if (!*opaque)
|
||||
{
|
||||
DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct Inst));
|
||||
goto fail_config;
|
||||
}
|
||||
|
||||
memset(*opaque, 0, sizeof(struct Inst));
|
||||
struct Inst * this = (struct Inst *)*opaque;
|
||||
|
||||
if (!font_name)
|
||||
font_name = "FreeMono";
|
||||
|
||||
FcPattern * pat = FcNameParse((const FcChar8*)font_name);
|
||||
if (!pat)
|
||||
{
|
||||
DEBUG_ERROR("FCNameParse failed");
|
||||
goto fail_opaque;
|
||||
}
|
||||
|
||||
FcConfigSubstitute(g_fontConfig, pat, FcMatchPattern);
|
||||
FcDefaultSubstitute(pat);
|
||||
FcResult result;
|
||||
FcChar8 * file = NULL;
|
||||
FcPattern * match = FcFontMatch(g_fontConfig, pat, &result);
|
||||
|
||||
if (!match)
|
||||
{
|
||||
DEBUG_ERROR("FcFontMatch Failed");
|
||||
goto fail_parse;
|
||||
}
|
||||
|
||||
if (FcPatternGetString(match, FC_FILE, 0, &file) == FcResultMatch)
|
||||
{
|
||||
this->font = TTF_OpenFont((char *)file, size);
|
||||
if (!this->font)
|
||||
{
|
||||
DEBUG_ERROR("TTL_OpenFont Failed");
|
||||
goto fail_match;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_ERROR("Failed to locate the requested font: %s", font_name);
|
||||
goto fail_match;
|
||||
}
|
||||
|
||||
++g_initCount;
|
||||
ret = true;
|
||||
|
||||
fail_match:
|
||||
FcPatternDestroy(match);
|
||||
|
||||
fail_parse:
|
||||
FcPatternDestroy(pat);
|
||||
|
||||
if (ret)
|
||||
return true;
|
||||
|
||||
fail_opaque:
|
||||
free(this);
|
||||
*opaque = NULL;
|
||||
|
||||
fail_config:
|
||||
if (g_initCount == 0)
|
||||
{
|
||||
FcConfigDestroy(g_fontConfig);
|
||||
g_fontConfig = NULL;
|
||||
}
|
||||
|
||||
fail_init:
|
||||
if (g_initCount == 0)
|
||||
TTF_Quit();
|
||||
|
||||
fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
static void lgf_sdl_destroy(LG_FontObj opaque)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
|
||||
if (this->font)
|
||||
TTF_CloseFont(this->font);
|
||||
free(this);
|
||||
|
||||
if (--g_initCount == 0)
|
||||
{
|
||||
FcConfigDestroy(g_fontConfig);
|
||||
g_fontConfig = NULL;
|
||||
|
||||
TTF_Quit();
|
||||
}
|
||||
}
|
||||
|
||||
static int lgf_sdl_multiline_width(TTF_Font * font, const char * text)
|
||||
{
|
||||
int w, maxW = 0;
|
||||
const char * ptr = text;
|
||||
char * buf = NULL;
|
||||
size_t size = 0;
|
||||
|
||||
while (*ptr)
|
||||
{
|
||||
const char * newLine = strchr(ptr, '\n');
|
||||
size_t line = newLine ? newLine - ptr : strlen(ptr);
|
||||
if (line > size)
|
||||
{
|
||||
size = line * 2 + 1;
|
||||
void * new = realloc(buf, size);
|
||||
if (!new)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory");
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
buf = new;
|
||||
}
|
||||
memcpy(buf, ptr, line);
|
||||
buf[line] = '\0';
|
||||
|
||||
if (TTF_SizeUTF8(font, buf, &w, NULL) < 0)
|
||||
{
|
||||
free(buf);
|
||||
DEBUG_ERROR("Failed to measure text: %s", TTF_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (w > maxW)
|
||||
maxW = w;
|
||||
ptr += line + 1;
|
||||
}
|
||||
free(buf);
|
||||
return maxW;
|
||||
}
|
||||
|
||||
static LG_FontBitmap * lgf_sdl_render(LG_FontObj opaque, unsigned int fg_color, const char * text)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
|
||||
SDL_Surface * surface;
|
||||
SDL_Color color;
|
||||
color.r = (fg_color & 0xff000000) >> 24;
|
||||
color.g = (fg_color & 0x00ff0000) >> 16;
|
||||
color.b = (fg_color & 0x0000ff00) >> 8;
|
||||
color.a = (fg_color & 0x000000ff) >> 0;
|
||||
|
||||
if (strchr(text, '\n'))
|
||||
{
|
||||
int width = lgf_sdl_multiline_width(this->font, text);
|
||||
if (width < 1)
|
||||
return NULL;
|
||||
surface = TTF_RenderUTF8_Blended_Wrapped(this->font, text, color, width);
|
||||
}
|
||||
else
|
||||
surface = TTF_RenderUTF8_Blended(this->font, text, color);
|
||||
|
||||
if (!surface)
|
||||
{
|
||||
DEBUG_ERROR("Failed to render text: %s", TTF_GetError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LG_FontBitmap * out = malloc(sizeof(LG_FontBitmap));
|
||||
if (!out)
|
||||
{
|
||||
SDL_FreeSurface(surface);
|
||||
DEBUG_ERROR("Failed to allocate memory for font bitmap");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
out->reserved = surface;
|
||||
out->width = surface->w;
|
||||
out->height = surface->h;
|
||||
out->bpp = surface->format->BytesPerPixel;
|
||||
out->pixels = surface->pixels;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static void lgf_sdl_release(LG_FontObj opaque, LG_FontBitmap * font)
|
||||
{
|
||||
LG_FontBitmap * bitmap = (LG_FontBitmap *)font;
|
||||
SDL_FreeSurface(bitmap->reserved);
|
||||
free(bitmap);
|
||||
}
|
||||
|
||||
struct LG_Font LGF_SDL =
|
||||
{
|
||||
.name = "SDL",
|
||||
.create = lgf_sdl_create,
|
||||
.destroy = lgf_sdl_destroy,
|
||||
.render = lgf_sdl_render,
|
||||
.release = lgf_sdl_release
|
||||
};
|
||||
@@ -1,21 +1,22 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_LG_APP_
|
||||
#define _H_LG_APP_
|
||||
@@ -23,8 +24,10 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include <stdbool.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include "common/ringbuffer.h"
|
||||
#include "common/types.h"
|
||||
#include "interface/displayserver.h"
|
||||
#include "interface/overlay.h"
|
||||
|
||||
typedef enum LG_MsgAlert
|
||||
{
|
||||
@@ -37,9 +40,14 @@ LG_MsgAlert;
|
||||
|
||||
bool app_isRunning(void);
|
||||
bool app_inputEnabled(void);
|
||||
bool app_isCaptureMode(void);
|
||||
bool app_isCaptureOnlyMode(void);
|
||||
bool app_isFormatValid(void);
|
||||
bool app_isOverlayMode(void);
|
||||
void app_updateCursorPos(double x, double y);
|
||||
void app_updateWindowPos(int x, int y);
|
||||
void app_handleResizeEvent(int w, int h, const struct Border border);
|
||||
void app_handleResizeEvent(int w, int h, double scale, const struct Border border);
|
||||
void app_invalidateWindow(bool full);
|
||||
|
||||
void app_handleMouseRelative(double normx, double normy,
|
||||
double rawx, double rawy);
|
||||
@@ -49,11 +57,16 @@ void app_resyncMouseBasic(void);
|
||||
|
||||
void app_handleButtonPress(int button);
|
||||
void app_handleButtonRelease(int button);
|
||||
void app_handleWheelMotion(double motion);
|
||||
void app_handleKeyPress(int scancode);
|
||||
void app_handleKeyRelease(int scancode);
|
||||
void app_handleKeyboardTyped(const char * typed);
|
||||
void app_handleKeyboardModifiers(bool ctrl, bool shift, bool alt, bool super);
|
||||
void app_handleKeyboardLEDs(bool numLock, bool capsLock, bool scrollLock);
|
||||
void app_handleEnterEvent(bool entered);
|
||||
void app_handleFocusEvent(bool focused);
|
||||
void app_handleCloseEvent(void);
|
||||
void app_handleRenderEvent(const uint64_t timeUs);
|
||||
|
||||
void app_setFullscreen(bool fs);
|
||||
bool app_getFullscreen(void);
|
||||
@@ -62,7 +75,7 @@ bool app_getProp(LG_DSProperty prop, void * ret);
|
||||
#ifdef ENABLE_EGL
|
||||
EGLDisplay app_getEGLDisplay(void);
|
||||
EGLNativeWindowType app_getEGLNativeWindow(void);
|
||||
void app_eglSwapBuffers(EGLDisplay display, EGLSurface surface);
|
||||
void app_eglSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
@@ -73,8 +86,37 @@ void app_glSetSwapInterval(int interval);
|
||||
void app_glSwapBuffers(void);
|
||||
#endif
|
||||
|
||||
#define MAX_OVERLAY_RECTS 10
|
||||
void app_registerOverlay(const struct LG_OverlayOps * ops, const void * params);
|
||||
void app_initOverlays(void);
|
||||
void app_setOverlay(bool enable);
|
||||
bool app_overlayNeedsRender(void);
|
||||
/**
|
||||
* render the overlay
|
||||
* returns:
|
||||
* -1 for full output damage
|
||||
* 0 for no overlay
|
||||
* >0 number of rects written into rects
|
||||
*/
|
||||
int app_renderOverlay(struct Rect * rects, int maxRects);
|
||||
|
||||
void app_freeOverlays(void);
|
||||
|
||||
struct OverlayGraph;
|
||||
typedef struct OverlayGraph * GraphHandle;
|
||||
|
||||
GraphHandle app_registerGraph(const char * name, RingBuffer buffer, float min, float max);
|
||||
void app_unregisterGraph(GraphHandle handle);
|
||||
|
||||
void app_overlayConfigRegister(const char * title,
|
||||
void (*callback)(void * udata, int * id), void * udata);
|
||||
|
||||
void app_overlayConfigRegisterTab(const char * title,
|
||||
void (*callback)(void * udata, int * id), void * udata);
|
||||
|
||||
void app_clipboardRelease(void);
|
||||
void app_clipboardNotify(const LG_ClipboardData type, size_t size);
|
||||
void app_clipboardNotifyTypes(const LG_ClipboardData types[], int count);
|
||||
void app_clipboardNotifySize(const LG_ClipboardData type, size_t size);
|
||||
void app_clipboardData(const LG_ClipboardData type, uint8_t * data, size_t size);
|
||||
void app_clipboardRequest(const LG_ClipboardReplyFn replyFn, void * opaque);
|
||||
|
||||
@@ -110,14 +152,4 @@ void app_releaseKeybind(KeybindHandle * handle);
|
||||
*/
|
||||
void app_releaseAllKeybinds(void);
|
||||
|
||||
/**
|
||||
* Changes whether the help message is displayed or not.
|
||||
*/
|
||||
void app_showHelp(bool show);
|
||||
|
||||
/**
|
||||
* Changes whether the FPS is displayed or not.
|
||||
*/
|
||||
void app_showFPS(bool showFPS);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,37 +1,45 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_LG_EGL_DYNPROCS_
|
||||
#define _H_LG_EGL_DYNPROCS_
|
||||
#ifdef ENABLE_EGL
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <GL/gl.h>
|
||||
|
||||
typedef EGLDisplay (*eglGetPlatformDisplayEXT_t)(EGLenum platform,
|
||||
void *native_display, const EGLint *attrib_list);
|
||||
typedef void (*glEGLImageTargetTexture2DOES_t)(GLenum target,
|
||||
GLeglImageOES image);
|
||||
#include <EGL/eglext.h>
|
||||
#undef GL_KHR_debug
|
||||
#include <GLES3/gl3.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
|
||||
struct EGLDynProcs
|
||||
{
|
||||
eglGetPlatformDisplayEXT_t eglGetPlatformDisplay;
|
||||
eglGetPlatformDisplayEXT_t eglGetPlatformDisplayEXT;
|
||||
glEGLImageTargetTexture2DOES_t glEGLImageTargetTexture2DOES;
|
||||
PFNEGLGETPLATFORMDISPLAYPROC eglGetPlatformDisplay;
|
||||
PFNEGLGETPLATFORMDISPLAYPROC eglGetPlatformDisplayEXT;
|
||||
PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC eglSwapBuffersWithDamageKHR;
|
||||
PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC eglSwapBuffersWithDamageEXT;
|
||||
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
|
||||
PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallback;
|
||||
PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallbackKHR;
|
||||
PFNGLBUFFERSTORAGEEXTPROC glBufferStorageEXT;
|
||||
PFNEGLCREATEIMAGEPROC eglCreateImage;
|
||||
PFNEGLDESTROYIMAGEPROC eglDestroyImage;
|
||||
};
|
||||
|
||||
extern struct EGLDynProcs g_egl_dynProcs;
|
||||
@@ -41,3 +49,5 @@ void egl_dynProcsInit(void);
|
||||
#else
|
||||
#define egl_dynProcsInit(...)
|
||||
#endif
|
||||
|
||||
#endif // _H_LG_EGL_DYNPROCS_
|
||||
|
||||
41
client/include/eglutil.h
Normal file
41
client/include/eglutil.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_LG_GLUTIL_
|
||||
#define _H_LG_GLUTIL_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
struct SwapWithDamageData
|
||||
{
|
||||
bool init;
|
||||
PFNEGLSWAPBUFFERSWITHDAMAGEKHRPROC func;
|
||||
};
|
||||
|
||||
void swapWithDamageInit(struct SwapWithDamageData * data, EGLDisplay display);
|
||||
void swapWithDamageDisable(struct SwapWithDamageData * data);
|
||||
void swapWithDamage(struct SwapWithDamageData * data, EGLDisplay display, EGLSurface surface,
|
||||
const struct Rect * damage, int count);
|
||||
|
||||
#endif
|
||||
50
client/include/gl_dynprocs.h
Normal file
50
client/include/gl_dynprocs.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_LG_GL_DYNPROCS_
|
||||
#define _H_LG_GL_DYNPROCS_
|
||||
#ifdef ENABLE_OPENGL
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
|
||||
struct GLDynProcs
|
||||
{
|
||||
PFNGLGENBUFFERSPROC glGenBuffers;
|
||||
PFNGLBINDBUFFERPROC glBindBuffer;
|
||||
PFNGLBUFFERDATAPROC glBufferData;
|
||||
PFNGLBUFFERSUBDATAPROC glBufferSubData;
|
||||
PFNGLDELETEBUFFERSPROC glDeleteBuffers;
|
||||
PFNGLISSYNCPROC glIsSync;
|
||||
PFNGLFENCESYNCPROC glFenceSync;
|
||||
PFNGLCLIENTWAITSYNCPROC glClientWaitSync;
|
||||
PFNGLDELETESYNCPROC glDeleteSync;
|
||||
PFNGLGENERATEMIPMAPPROC glGenerateMipmap;
|
||||
};
|
||||
|
||||
extern struct GLDynProcs g_gl_dynProcs;
|
||||
|
||||
void gl_dynProcsInit(void);
|
||||
|
||||
#else
|
||||
#define gl_dynProcsInit(...)
|
||||
#endif
|
||||
|
||||
#endif // _H_LG_GL_DYNPROCS_
|
||||
@@ -1,27 +1,30 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_I_DISPLAYSERVER_
|
||||
#define _H_I_DISPLAYSERVER_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <EGL/egl.h>
|
||||
#include "common/types.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
typedef enum LG_ClipboardData
|
||||
{
|
||||
@@ -60,6 +63,24 @@ enum LG_DSWarpSupport
|
||||
LG_DS_WARP_SCREEN,
|
||||
};
|
||||
|
||||
typedef enum LG_DSPointer
|
||||
{
|
||||
LG_POINTER_NONE = 0,
|
||||
LG_POINTER_SQUARE,
|
||||
LG_POINTER_ARROW,
|
||||
LG_POINTER_INPUT,
|
||||
LG_POINTER_MOVE,
|
||||
LG_POINTER_RESIZE_NS,
|
||||
LG_POINTER_RESIZE_EW,
|
||||
LG_POINTER_RESIZE_NESW,
|
||||
LG_POINTER_RESIZE_NWSE,
|
||||
LG_POINTER_HAND,
|
||||
LG_POINTER_NOT_ALLOWED,
|
||||
}
|
||||
LG_DSPointer;
|
||||
|
||||
#define LG_POINTER_COUNT (LG_POINTER_NOT_ALLOWED + 1)
|
||||
|
||||
typedef struct LG_DSInitParams
|
||||
{
|
||||
const char * title;
|
||||
@@ -69,10 +90,13 @@ typedef struct LG_DSInitParams
|
||||
bool resizable;
|
||||
bool borderless;
|
||||
bool maximize;
|
||||
bool minimizeOnFocusLoss;
|
||||
|
||||
// if true the renderer requires an OpenGL context
|
||||
bool opengl;
|
||||
|
||||
// x11 needs to know if this is in use so we can decide to setup for
|
||||
// presentation times
|
||||
bool jitRender;
|
||||
}
|
||||
LG_DSInitParams;
|
||||
|
||||
@@ -82,6 +106,8 @@ typedef void (* LG_ClipboardReplyFn)(void * opaque, const LG_ClipboardData type,
|
||||
typedef struct LG_DSGLContext
|
||||
* LG_DSGLContext;
|
||||
|
||||
typedef struct LGEvent LGEvent;
|
||||
|
||||
struct LG_DisplayServerOps
|
||||
{
|
||||
/* called before options are parsed, useful for registering options */
|
||||
@@ -116,7 +142,7 @@ struct LG_DisplayServerOps
|
||||
/* EGL support */
|
||||
EGLDisplay (*getEGLDisplay)(void);
|
||||
EGLNativeWindowType (*getEGLNativeWindow)(void);
|
||||
void (*eglSwapBuffers)(EGLDisplay display, EGLSurface surface);
|
||||
void (*eglSwapBuffers)(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
@@ -128,12 +154,30 @@ struct LG_DisplayServerOps
|
||||
void (*glSwapBuffers)(void);
|
||||
#endif
|
||||
|
||||
/* Waits for a good time to render the next frame in time for the next vblank.
|
||||
* This is optional and a display server may choose to not implement it.
|
||||
*
|
||||
* return true to force the frame to be rendered, this is used by X11 for
|
||||
* calibration */
|
||||
bool (*waitFrame)(void);
|
||||
|
||||
/* This must be called when waitFrame returns, but no frame is actually rendered. */
|
||||
void (*skipFrame)(void);
|
||||
|
||||
/* This is used to interrupt waitFrame. */
|
||||
void (*stopWaitFrame)(void);
|
||||
|
||||
/* dm specific cursor implementations */
|
||||
void (*showPointer)(bool show);
|
||||
void (*grabPointer)();
|
||||
void (*ungrabPointer)();
|
||||
void (*guestPointerUpdated)(double x, double y, double localX, double localY);
|
||||
void (*setPointer)(LG_DSPointer pointer);
|
||||
void (*grabKeyboard)();
|
||||
void (*ungrabKeyboard)();
|
||||
/* (un)grabPointer is used to toggle cursor tracking/confine in normal mode */
|
||||
void (*grabPointer)();
|
||||
void (*ungrabPointer)();
|
||||
/* (un)capturePointer is used do toggle special cursor tracking in capture mode */
|
||||
void (*capturePointer)();
|
||||
void (*uncapturePointer)();
|
||||
|
||||
/* exiting = true if the warp is to leave the window */
|
||||
void (*warpPointer)(int x, int y, bool exiting);
|
||||
@@ -153,10 +197,11 @@ struct LG_DisplayServerOps
|
||||
/* wait for the specified time without blocking UI processing/event loops */
|
||||
void (*wait)(unsigned int time);
|
||||
|
||||
/* get/set the window dimensions */
|
||||
/* get/set the window dimensions & state */
|
||||
void (*setWindowSize)(int x, int y);
|
||||
bool (*getFullscreen)(void);
|
||||
void (*setFullscreen)(bool fs);
|
||||
void (*minimize)(void);
|
||||
|
||||
/* clipboard support, optional, if not supported set to NULL */
|
||||
bool (*cbInit)(void);
|
||||
@@ -166,45 +211,50 @@ struct LG_DisplayServerOps
|
||||
};
|
||||
|
||||
#ifdef ENABLE_EGL
|
||||
#define ASSERT_EGL_FN(x) assert(x);
|
||||
#define ASSERT_EGL_FN(x) DEBUG_ASSERT(x)
|
||||
#else
|
||||
#define ASSERT_EGL_FN(x)
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
#define ASSERT_OPENGL_FN(x) assert(x)
|
||||
#define ASSERT_OPENGL_FN(x) DEBUG_ASSERT(x)
|
||||
#else
|
||||
#define ASSERT_OPENGL_FN(x)
|
||||
#endif
|
||||
|
||||
#define ASSERT_LG_DS_VALID(x) \
|
||||
assert((x)->setup ); \
|
||||
assert((x)->probe ); \
|
||||
assert((x)->earlyInit ); \
|
||||
assert((x)->init ); \
|
||||
assert((x)->startup ); \
|
||||
assert((x)->shutdown ); \
|
||||
assert((x)->free ); \
|
||||
assert((x)->getProp ); \
|
||||
ASSERT_EGL_FN((x)->getEGLDisplay ); \
|
||||
ASSERT_EGL_FN((x)->getEGLNativeWindow ); \
|
||||
ASSERT_EGL_FN((x)->eglSwapBuffers ); \
|
||||
DEBUG_ASSERT((x)->setup ); \
|
||||
DEBUG_ASSERT((x)->probe ); \
|
||||
DEBUG_ASSERT((x)->earlyInit); \
|
||||
DEBUG_ASSERT((x)->init ); \
|
||||
DEBUG_ASSERT((x)->startup ); \
|
||||
DEBUG_ASSERT((x)->shutdown ); \
|
||||
DEBUG_ASSERT((x)->free ); \
|
||||
DEBUG_ASSERT((x)->getProp ); \
|
||||
ASSERT_EGL_FN((x)->getEGLDisplay ); \
|
||||
ASSERT_EGL_FN((x)->getEGLNativeWindow); \
|
||||
ASSERT_EGL_FN((x)->eglSwapBuffers ); \
|
||||
ASSERT_OPENGL_FN((x)->glCreateContext ); \
|
||||
ASSERT_OPENGL_FN((x)->glDeleteContext ); \
|
||||
ASSERT_OPENGL_FN((x)->glMakeCurrent ); \
|
||||
ASSERT_OPENGL_FN((x)->glSetSwapInterval); \
|
||||
ASSERT_OPENGL_FN((x)->glSwapBuffers ); \
|
||||
assert((x)->showPointer ); \
|
||||
assert((x)->grabPointer ); \
|
||||
assert((x)->ungrabPointer ); \
|
||||
assert((x)->warpPointer ); \
|
||||
assert((x)->realignPointer ); \
|
||||
assert((x)->isValidPointerPos ); \
|
||||
assert((x)->inhibitIdle ); \
|
||||
assert((x)->uninhibitIdle ); \
|
||||
assert((x)->wait ); \
|
||||
assert((x)->setWindowSize ); \
|
||||
assert((x)->setFullscreen ); \
|
||||
assert((x)->getFullscreen );
|
||||
DEBUG_ASSERT(!(x)->waitFrame == !(x)->stopWaitFrame); \
|
||||
DEBUG_ASSERT((x)->guestPointerUpdated); \
|
||||
DEBUG_ASSERT((x)->setPointer ); \
|
||||
DEBUG_ASSERT((x)->grabPointer ); \
|
||||
DEBUG_ASSERT((x)->ungrabPointer ); \
|
||||
DEBUG_ASSERT((x)->capturePointer ); \
|
||||
DEBUG_ASSERT((x)->uncapturePointer ); \
|
||||
DEBUG_ASSERT((x)->warpPointer ); \
|
||||
DEBUG_ASSERT((x)->realignPointer ); \
|
||||
DEBUG_ASSERT((x)->isValidPointerPos ); \
|
||||
DEBUG_ASSERT((x)->inhibitIdle ); \
|
||||
DEBUG_ASSERT((x)->uninhibitIdle ); \
|
||||
DEBUG_ASSERT((x)->wait ); \
|
||||
DEBUG_ASSERT((x)->setWindowSize ); \
|
||||
DEBUG_ASSERT((x)->setFullscreen ); \
|
||||
DEBUG_ASSERT((x)->getFullscreen ); \
|
||||
DEBUG_ASSERT((x)->minimize );
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
71
client/include/interface/overlay.h
Normal file
71
client/include/interface/overlay.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_I_OVERLAY_
|
||||
#define _H_I_OVERLAY_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
struct LG_OverlayOps
|
||||
{
|
||||
/* internal name of the overlay for debugging */
|
||||
const char * name;
|
||||
|
||||
/* called very early to allow for option registration, optional */
|
||||
void (*earlyInit)(void);
|
||||
|
||||
/* called when the overlay is registered */
|
||||
bool (*init)(void ** udata, const void * params);
|
||||
|
||||
/* final free */
|
||||
void (*free)(void * udata);
|
||||
|
||||
/* return true if realtime rendering is required when in jitRender mode
|
||||
* optional, if omitted assumes false */
|
||||
bool (*needs_render)(void * udata, bool interactive);
|
||||
|
||||
/* perform the actual drawing/rendering
|
||||
*
|
||||
* `interactive` is true if the application is currently in overlay interaction
|
||||
* mode.
|
||||
*
|
||||
* `windowRects` is an array of window rects that were rendered using screen
|
||||
* coordinates. Will be `NULL` if the information is not required.
|
||||
*
|
||||
* `maxRects` is the length of `windowRects`, or 0 if `windowRects` is `NULL`
|
||||
*
|
||||
* returns the number of rects written to `windowRects`, or -1 if there is not
|
||||
* enough room left.
|
||||
*/
|
||||
int (*render)(void * udata, bool interactive, struct Rect * windowRects,
|
||||
int maxRects);
|
||||
|
||||
/* TODO: add load/save settings capabillity */
|
||||
};
|
||||
|
||||
#define ASSERT_LG_OVERLAY_VALID(x) \
|
||||
DEBUG_ASSERT((x)->name ); \
|
||||
DEBUG_ASSERT((x)->init ); \
|
||||
DEBUG_ASSERT((x)->free ); \
|
||||
DEBUG_ASSERT((x)->render);
|
||||
|
||||
#endif
|
||||
@@ -1,21 +1,22 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
@@ -26,26 +27,21 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "common/framebuffer.h"
|
||||
|
||||
#define IS_LG_RENDERER_VALID(x) \
|
||||
((x)->get_name && \
|
||||
((x)->getName && \
|
||||
(x)->create && \
|
||||
(x)->initialize && \
|
||||
(x)->deinitialize && \
|
||||
(x)->on_restart && \
|
||||
(x)->on_resize && \
|
||||
(x)->on_mouse_shape && \
|
||||
(x)->on_mouse_event && \
|
||||
(x)->on_alert && \
|
||||
(x)->on_help && \
|
||||
(x)->on_show_fps && \
|
||||
(x)->render_startup && \
|
||||
(x)->render && \
|
||||
(x)->update_fps)
|
||||
(x)->onRestart && \
|
||||
(x)->onResize && \
|
||||
(x)->onMouseShape && \
|
||||
(x)->onMouseEvent && \
|
||||
(x)->renderStartup && \
|
||||
(x)->needsRender && \
|
||||
(x)->render)
|
||||
|
||||
typedef struct LG_RendererParams
|
||||
{
|
||||
// TTF_Font * font;
|
||||
// TTF_Font * alertFont;
|
||||
bool quickSplash;
|
||||
bool quickSplash;
|
||||
}
|
||||
LG_RendererParams;
|
||||
|
||||
@@ -98,49 +94,82 @@ typedef enum LG_RendererCursor
|
||||
}
|
||||
LG_RendererCursor;
|
||||
|
||||
// returns the friendly name of the renderer
|
||||
typedef const char * (* LG_RendererGetName)();
|
||||
typedef struct LG_Renderer LG_Renderer;
|
||||
|
||||
// called pre-creation to allow the renderer to register any options it might have
|
||||
typedef void (* LG_RendererSetup)();
|
||||
typedef struct LG_RendererOps
|
||||
{
|
||||
/* returns the friendly name of the renderer */
|
||||
const char * (*getName)(void);
|
||||
|
||||
typedef bool (* LG_RendererCreate )(void ** opaque, const LG_RendererParams params, bool * needsOpenGL);
|
||||
typedef bool (* LG_RendererInitialize )(void * opaque);
|
||||
typedef void (* LG_RendererDeInitialize )(void * opaque);
|
||||
typedef bool (* LG_RendererSupports )(void * opaque, LG_RendererSupport support);
|
||||
typedef void (* LG_RendererOnRestart )(void * opaque);
|
||||
typedef void (* LG_RendererOnResize )(void * opaque, const int width, const int height, const LG_RendererRect destRect, LG_RendererRotate rotate);
|
||||
typedef bool (* LG_RendererOnMouseShape )(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data);
|
||||
typedef bool (* LG_RendererOnMouseEvent )(void * opaque, const bool visible , const int x, const int y);
|
||||
typedef bool (* LG_RendererOnFrameFormat)(void * opaque, const LG_RendererFormat format, bool useDMA);
|
||||
typedef bool (* LG_RendererOnFrame )(void * opaque, const FrameBuffer * frame, int dmaFD);
|
||||
typedef void (* LG_RendererOnAlert )(void * opaque, const LG_MsgAlert alert, const char * message, bool ** closeFlag);
|
||||
typedef void (* LG_RendererOnHelp )(void * opaque, const char * message);
|
||||
typedef void (* LG_RendererOnShowFPS )(void * opaque, bool showFPS);
|
||||
typedef bool (* LG_RendererRenderStartup)(void * opaque);
|
||||
typedef bool (* LG_RendererRender )(void * opaque, LG_RendererRotate rotate);
|
||||
typedef void (* LG_RendererUpdateFPS )(void * opaque, const float avgUPS, const float avgFPS);
|
||||
/* called pre-creation to allow the renderer to register any options it may
|
||||
* have */
|
||||
void (*setup)(void);
|
||||
|
||||
/* creates an instance of the renderer
|
||||
* Context: lg_run */
|
||||
bool (*create)(LG_Renderer ** renderer, const LG_RendererParams params,
|
||||
bool * needsOpenGL);
|
||||
|
||||
/* initializes the renderer for use
|
||||
* Context: lg_run */
|
||||
bool (*initialize)(LG_Renderer * renderer);
|
||||
|
||||
/* deinitializes & frees the renderer
|
||||
* Context: lg_run & renderThread */
|
||||
void (*deinitialize)(LG_Renderer * renderer);
|
||||
|
||||
/* returns true if the specified feature is supported
|
||||
* Context: renderThread */
|
||||
bool (*supports)(LG_Renderer * renderer, LG_RendererSupport support);
|
||||
|
||||
/* called when the renderer is to reset it's state
|
||||
* Context: lg_run & frameThread */
|
||||
void (*onRestart)(LG_Renderer * renderer);
|
||||
|
||||
/* called when the viewport has been resized
|
||||
* Context: renderThrtead */
|
||||
void (*onResize)(LG_Renderer * renderer, const int width, const int height,
|
||||
const double scale, const LG_RendererRect destRect,
|
||||
LG_RendererRotate rotate);
|
||||
|
||||
/* called when the mouse shape has changed
|
||||
* Context: cursorThread */
|
||||
bool (*onMouseShape)(LG_Renderer * renderer, const LG_RendererCursor cursor,
|
||||
const int width, const int height, const int pitch, const uint8_t * data);
|
||||
|
||||
/* called when the mouse has moved or changed visibillity
|
||||
* Context: cursorThread */
|
||||
bool (*onMouseEvent)(LG_Renderer * renderer, const bool visible, const int x,
|
||||
const int y);
|
||||
|
||||
/* called when the frame format has changed
|
||||
* Context: frameThread */
|
||||
bool (*onFrameFormat)(LG_Renderer * renderer,
|
||||
const LG_RendererFormat format);
|
||||
|
||||
/* called when there is a new frame
|
||||
* Context: frameThread */
|
||||
bool (*onFrame)(LG_Renderer * renderer, const FrameBuffer * frame, int dmaFD,
|
||||
const FrameDamageRect * damage, int damageCount);
|
||||
|
||||
/* called when the rederer is to startup
|
||||
* Context: renderThread */
|
||||
bool (*renderStartup)(LG_Renderer * renderer, bool useDMA);
|
||||
|
||||
/* returns if the render method must be called even if nothing has changed.
|
||||
* Context: renderThread */
|
||||
bool (*needsRender)(LG_Renderer * renderer);
|
||||
|
||||
/* called to render the scene
|
||||
* Context: renderThread */
|
||||
bool (*render)(LG_Renderer * renderer, LG_RendererRotate rotate,
|
||||
const bool newFrame, const bool invalidateWindow,
|
||||
void (*preSwap)(void * udata), void * udata);
|
||||
}
|
||||
LG_RendererOps;
|
||||
|
||||
typedef struct LG_Renderer
|
||||
{
|
||||
LG_RendererGetName get_name;
|
||||
LG_RendererSetup setup;
|
||||
|
||||
LG_RendererCreate create;
|
||||
LG_RendererInitialize initialize;
|
||||
LG_RendererDeInitialize deinitialize;
|
||||
LG_RendererSupports supports;
|
||||
LG_RendererOnRestart on_restart;
|
||||
LG_RendererOnResize on_resize;
|
||||
LG_RendererOnMouseShape on_mouse_shape;
|
||||
LG_RendererOnMouseEvent on_mouse_event;
|
||||
LG_RendererOnFrameFormat on_frame_format;
|
||||
LG_RendererOnFrame on_frame;
|
||||
LG_RendererOnAlert on_alert;
|
||||
LG_RendererOnHelp on_help;
|
||||
LG_RendererOnShowFPS on_show_fps;
|
||||
LG_RendererRenderStartup render_startup;
|
||||
LG_RendererRender render;
|
||||
LG_RendererUpdateFPS update_fps;
|
||||
LG_RendererOps ops;
|
||||
}
|
||||
LG_Renderer;
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
/*
|
||||
KVMGFX Client - A KVM Client for VGA Passthrough
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
struct ll;
|
||||
|
||||
35
client/include/overlay_utils.h
Normal file
35
client/include/overlay_utils.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_LG_OVERLAY_UTILS_
|
||||
#define _H_LG_OVERLAY_UTILS_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
typedef struct ImVec2 ImVec2;
|
||||
|
||||
void overlayGetImGuiRect(struct Rect * rect);
|
||||
ImVec2 * overlayGetScreenSize(void);
|
||||
void overlayTextURL(const char * url, const char * text);
|
||||
void overlayTextMaybeURL(const char * text, bool wrapped);
|
||||
|
||||
#endif
|
||||
@@ -1,21 +1,22 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _H_LG_UTIL_
|
||||
#define _H_LG_UTIL_
|
||||
@@ -24,6 +25,12 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include <stdbool.h>
|
||||
#include "common/types.h"
|
||||
|
||||
#define min(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; })
|
||||
#define max(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a > _b ? _a : _b; })
|
||||
|
||||
#define UPCAST(type, x) \
|
||||
(type *)((uintptr_t)(x) - offsetof(type, base))
|
||||
|
||||
// reads the specified file into a new buffer
|
||||
// the callee must free the buffer
|
||||
bool util_fileGetContents(const char * filename, char ** buffer, size_t * length);
|
||||
@@ -32,5 +39,17 @@ void util_cursorToInt(double ex, double ey, int *x, int *y);
|
||||
bool util_guestCurToLocal(struct DoublePoint *local);
|
||||
void util_localCurToGuest(struct DoublePoint *guest);
|
||||
void util_rotatePoint(struct DoublePoint *point);
|
||||
bool util_hasGLExt(const char * exts, const char * ext);
|
||||
|
||||
static inline double util_clamp(double x, double min, double max)
|
||||
{
|
||||
if (x < min) return min;
|
||||
if (x > max) return max;
|
||||
return x;
|
||||
}
|
||||
|
||||
bool util_initUIFonts(void);
|
||||
void util_freeUIFonts(void);
|
||||
char * util_getUIFont(const char * fontName);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,7 @@ set(RENDERER_H "${CMAKE_BINARY_DIR}/include/dynamic/renderers.h")
|
||||
set(RENDERER_C "${CMAKE_BINARY_DIR}/src/renderers.c")
|
||||
|
||||
file(WRITE ${RENDERER_H} "#include \"interface/renderer.h\"\n\n")
|
||||
file(APPEND ${RENDERER_H} "extern LG_Renderer * LG_Renderers[];\n\n")
|
||||
file(APPEND ${RENDERER_H} "extern LG_RendererOps * LG_Renderers[];\n\n")
|
||||
|
||||
file(WRITE ${RENDERER_C} "#include \"interface/renderer.h\"\n\n")
|
||||
file(APPEND ${RENDERER_C} "#include <stddef.h>\n\n")
|
||||
@@ -33,10 +33,10 @@ list(LENGTH RENDERERS RENDERER_COUNT)
|
||||
file(APPEND ${RENDERER_H} "#define LG_RENDERER_COUNT ${RENDERER_COUNT}\n")
|
||||
|
||||
foreach(renderer ${RENDERERS})
|
||||
file(APPEND ${RENDERER_C} "extern LG_Renderer LGR_${renderer};\n")
|
||||
file(APPEND ${RENDERER_C} "extern LG_RendererOps LGR_${renderer};\n")
|
||||
endforeach()
|
||||
|
||||
file(APPEND ${RENDERER_C} "\nconst LG_Renderer * LG_Renderers[] =\n{\n")
|
||||
file(APPEND ${RENDERER_C} "\nconst LG_RendererOps * LG_Renderers[] =\n{\n")
|
||||
foreach(renderer ${RENDERERS})
|
||||
file(APPEND ${RENDERER_C} " &LGR_${renderer},\n")
|
||||
endforeach()
|
||||
|
||||
@@ -1,66 +1,122 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(renderer_EGL LANGUAGES C)
|
||||
project(renderer_EGL LANGUAGES C CXX)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(RENDERER_EGL_PKGCONFIG REQUIRED
|
||||
pkg_check_modules(RENDERER_EGL REQUIRED IMPORTED_TARGET
|
||||
egl
|
||||
gl
|
||||
)
|
||||
|
||||
pkg_check_modules(RENDERER_EGL_OPT_PKGCONFIG
|
||||
pkg_check_modules(RENDERER_EGL_OPT IMPORTED_TARGET
|
||||
wayland-egl
|
||||
)
|
||||
|
||||
find_program(AWK NAMES gawk mawk original-awk awk)
|
||||
|
||||
if(AWK MATCHES ".+-NOTFOUND")
|
||||
message(FATAL_ERROR "FATAL: some known version of awk couldn't be found (${AWK}).")
|
||||
else()
|
||||
message(STATUS "Using awk: ${AWK}")
|
||||
endif()
|
||||
|
||||
include(MakeObject)
|
||||
make_object(
|
||||
EGL_SHADER
|
||||
function(build_shaders header_dir)
|
||||
file(GLOB headers "${header_dir}/*.h")
|
||||
set(EGL_SHADER_PROCESSED)
|
||||
foreach(shader ${ARGN})
|
||||
set(out_f "${CMAKE_CURRENT_BINARY_DIR}/${shader}")
|
||||
add_custom_command(OUTPUT "${out_f}"
|
||||
COMMAND "${AWK}" -f "${CMAKE_CURRENT_SOURCE_DIR}/glsl.include.awk"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${shader}" > "${out_f}"
|
||||
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/${shader}"
|
||||
DEPENDS ${headers}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/shader"
|
||||
COMMENT "Preprocessing shader ${shader}"
|
||||
VERBATIM
|
||||
)
|
||||
endforeach()
|
||||
|
||||
set(CMAKE_CURRENT_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
make_object(
|
||||
EGL_SHADER
|
||||
${ARGN}
|
||||
)
|
||||
|
||||
set(EGL_SHADER_OBJS "${EGL_SHADER_OBJS}" PARENT_SCOPE)
|
||||
set(EGL_SHADER_INCS "${EGL_SHADER_INCS}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
build_shaders(
|
||||
shader
|
||||
shader/desktop.vert
|
||||
shader/desktop_rgb.frag
|
||||
shader/cursor.vert
|
||||
shader/cursor_rgb.frag
|
||||
shader/cursor_mono.frag
|
||||
shader/fps.vert
|
||||
shader/fps.frag
|
||||
shader/fps_bg.frag
|
||||
shader/help.vert
|
||||
shader/help.frag
|
||||
shader/help_bg.frag
|
||||
shader/alert.vert
|
||||
shader/alert.frag
|
||||
shader/alert_bg.frag
|
||||
shader/damage.vert
|
||||
shader/damage.frag
|
||||
shader/splash_bg.vert
|
||||
shader/splash_bg.frag
|
||||
shader/splash_logo.vert
|
||||
shader/splash_logo.frag
|
||||
shader/basic.vert
|
||||
shader/ffx_cas.frag
|
||||
shader/ffx_fsr1_easu.frag
|
||||
shader/ffx_fsr1_rcas.frag
|
||||
shader/downscale.frag
|
||||
shader/downscale_lanczos2.frag
|
||||
shader/downscale_linear.frag
|
||||
)
|
||||
|
||||
make_defines(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/shader/desktop_rgb.frag"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
|
||||
)
|
||||
|
||||
add_library(renderer_EGL STATIC
|
||||
egl.c
|
||||
egldebug.c
|
||||
shader.c
|
||||
texture_util.c
|
||||
texture.c
|
||||
texture_buffer.c
|
||||
texture_framebuffer.c
|
||||
texture_dmabuf.c
|
||||
model.c
|
||||
desktop.c
|
||||
desktop_rects.c
|
||||
cursor.c
|
||||
fps.c
|
||||
help.c
|
||||
draw.c
|
||||
splash.c
|
||||
alert.c
|
||||
damage.c
|
||||
framebuffer.c
|
||||
postprocess.c
|
||||
ffx.c
|
||||
filter.c
|
||||
filter_ffx_cas.c
|
||||
filter_ffx_fsr1.c
|
||||
filter_downscale.c
|
||||
${EGL_SHADER_OBJS}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
|
||||
${PROJECT_TOP}/repos/cimgui/imgui/backends/imgui_impl_opengl3.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(renderer_EGL PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1 IMGUI_IMPL_OPENGL_ES3)
|
||||
|
||||
target_link_libraries(renderer_EGL
|
||||
${RENDERER_EGL_PKGCONFIG_LIBRARIES}
|
||||
${RENDERER_EGL_OPT_PKGCONFIG_LIBRARIES}
|
||||
PkgConfig::RENDERER_EGL
|
||||
lg_common
|
||||
fonts
|
||||
|
||||
cimgui
|
||||
)
|
||||
if(RENDERER_EGL_OPT_FOUND)
|
||||
target_link_libraries(renderer_EGL
|
||||
PkgConfig::RENDERER_EGL_OPT
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(renderer_EGL
|
||||
PRIVATE
|
||||
src
|
||||
${EGL_SHADER_INCS}
|
||||
${RENDERER_EGL_PKGCONFIG_INCLUDE_DIRS}
|
||||
${RENDERER_EGL_OPT_PKGCONFIG_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
cahe terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "alert.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/locking.h"
|
||||
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
#include "model.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// these headers are auto generated by cmake
|
||||
#include "alert.vert.h"
|
||||
#include "alert.frag.h"
|
||||
#include "alert_bg.frag.h"
|
||||
|
||||
struct EGL_Alert
|
||||
{
|
||||
const LG_Font * font;
|
||||
LG_FontObj fontObj;
|
||||
|
||||
EGL_Texture * texture;
|
||||
EGL_Shader * shader;
|
||||
EGL_Shader * shaderBG;
|
||||
EGL_Model * model;
|
||||
|
||||
LG_Lock lock;
|
||||
bool update;
|
||||
LG_FontBitmap * bmp;
|
||||
|
||||
bool ready;
|
||||
float width , height ;
|
||||
float bgWidth, bgHeight;
|
||||
float r, g, b, a;
|
||||
|
||||
// uniforms
|
||||
GLint uScreen , uSize;
|
||||
GLint uScreenBG, uSizeBG, uColorBG;
|
||||
};
|
||||
|
||||
bool egl_alert_init(EGL_Alert ** alert, const LG_Font * font, LG_FontObj fontObj)
|
||||
{
|
||||
*alert = (EGL_Alert *)malloc(sizeof(EGL_Alert));
|
||||
if (!*alert)
|
||||
{
|
||||
DEBUG_ERROR("Failed to malloc EGL_Alert");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(*alert, 0, sizeof(EGL_Alert));
|
||||
|
||||
(*alert)->font = font;
|
||||
(*alert)->fontObj = fontObj;
|
||||
LG_LOCK_INIT((*alert)->lock);
|
||||
|
||||
if (!egl_texture_init(&(*alert)->texture, NULL))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the alert texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shader_init(&(*alert)->shader))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the alert shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shader_init(&(*alert)->shaderBG))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the alert bg shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!egl_shader_compile((*alert)->shader,
|
||||
b_shader_alert_vert, b_shader_alert_vert_size,
|
||||
b_shader_alert_frag, b_shader_alert_frag_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the alert shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shader_compile((*alert)->shaderBG,
|
||||
b_shader_alert_vert , b_shader_alert_vert_size,
|
||||
b_shader_alert_bg_frag, b_shader_alert_bg_frag_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the alert shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
(*alert)->uSize = egl_shader_get_uniform_location((*alert)->shader , "size" );
|
||||
(*alert)->uScreen = egl_shader_get_uniform_location((*alert)->shader , "screen");
|
||||
(*alert)->uSizeBG = egl_shader_get_uniform_location((*alert)->shaderBG, "size" );
|
||||
(*alert)->uScreenBG = egl_shader_get_uniform_location((*alert)->shaderBG, "screen");
|
||||
(*alert)->uColorBG = egl_shader_get_uniform_location((*alert)->shaderBG, "color" );
|
||||
|
||||
if (!egl_model_init(&(*alert)->model))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the alert model");
|
||||
return false;
|
||||
}
|
||||
|
||||
egl_model_set_default((*alert)->model);
|
||||
egl_model_set_texture((*alert)->model, (*alert)->texture);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_alert_free(EGL_Alert ** alert)
|
||||
{
|
||||
if (!*alert)
|
||||
return;
|
||||
|
||||
egl_texture_free(&(*alert)->texture );
|
||||
egl_shader_free (&(*alert)->shader );
|
||||
egl_shader_free (&(*alert)->shaderBG);
|
||||
egl_model_free (&(*alert)->model );
|
||||
|
||||
free(*alert);
|
||||
*alert = NULL;
|
||||
}
|
||||
|
||||
void egl_alert_set_color(EGL_Alert * alert, const uint32_t color)
|
||||
{
|
||||
alert->r = (1.0f / 0xff) * ((color >> 24) & 0xFF);
|
||||
alert->g = (1.0f / 0xff) * ((color >> 16) & 0xFF);
|
||||
alert->b = (1.0f / 0xff) * ((color >> 8) & 0xFF);
|
||||
alert->a = (1.0f / 0xff) * ((color >> 0) & 0xFF);
|
||||
}
|
||||
|
||||
void egl_alert_set_text (EGL_Alert * alert, const char * str)
|
||||
{
|
||||
LG_LOCK(alert->lock);
|
||||
alert->bmp = alert->font->render(alert->fontObj, 0xffffff00, str);
|
||||
if (!alert->bmp)
|
||||
{
|
||||
alert->update = false;
|
||||
LG_UNLOCK(alert->lock);
|
||||
DEBUG_ERROR("Failed to render alert text");
|
||||
return;
|
||||
}
|
||||
|
||||
alert->update = true;
|
||||
LG_UNLOCK(alert->lock);
|
||||
}
|
||||
|
||||
void egl_alert_render(EGL_Alert * alert, const float scaleX, const float scaleY)
|
||||
{
|
||||
if (alert->update)
|
||||
{
|
||||
LG_LOCK(alert->lock);
|
||||
egl_texture_setup(
|
||||
alert->texture,
|
||||
EGL_PF_BGRA,
|
||||
alert->bmp->width ,
|
||||
alert->bmp->height,
|
||||
alert->bmp->width * alert->bmp->bpp,
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
egl_texture_update(alert->texture, alert->bmp->pixels);
|
||||
|
||||
alert->width = alert->bgWidth = alert->bmp->width;
|
||||
alert->height = alert->bgHeight = alert->bmp->height;
|
||||
|
||||
if (alert->bgWidth < 200)
|
||||
alert->bgWidth = 200;
|
||||
alert->bgHeight += 4;
|
||||
|
||||
alert->ready = true;
|
||||
|
||||
alert->font->release(alert->fontObj, alert->bmp);
|
||||
alert->update = false;
|
||||
alert->bmp = NULL;
|
||||
LG_UNLOCK(alert->lock);
|
||||
}
|
||||
|
||||
if (!alert->ready)
|
||||
return;
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// render the background first
|
||||
egl_shader_use(alert->shaderBG);
|
||||
glUniform2f(alert->uScreenBG, scaleX , scaleY );
|
||||
glUniform2i(alert->uSizeBG , alert->bgWidth, alert->bgHeight);
|
||||
glUniform4f(alert->uColorBG , alert->r, alert->g, alert->b, alert->a);
|
||||
egl_model_render(alert->model);
|
||||
|
||||
// render the texture over the background
|
||||
egl_shader_use(alert->shader);
|
||||
glUniform2f(alert->uScreen, scaleX , scaleY );
|
||||
glUniform2i(alert->uSize , alert->width, alert->height);
|
||||
egl_model_render(alert->model);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "interface/font.h"
|
||||
|
||||
typedef struct EGL_Alert EGL_Alert;
|
||||
|
||||
bool egl_alert_init(EGL_Alert ** alert, const LG_Font * font, LG_FontObj fontObj);
|
||||
void egl_alert_free(EGL_Alert ** alert);
|
||||
|
||||
void egl_alert_set_color(EGL_Alert * alert, const uint32_t color);
|
||||
void egl_alert_set_text (EGL_Alert * alert, const char * str);
|
||||
void egl_alert_render (EGL_Alert * alert, const float scaleX, const float scaleY);
|
||||
@@ -1,21 +1,22 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
cahe terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cursor.h"
|
||||
#include "common/debug.h"
|
||||
@@ -25,7 +26,9 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
#include "model.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -43,6 +46,16 @@ struct CursorTex
|
||||
GLuint uCBMode;
|
||||
};
|
||||
|
||||
struct CursorPos
|
||||
{
|
||||
float x, y;
|
||||
};
|
||||
|
||||
struct CursorSize
|
||||
{
|
||||
float w, h;
|
||||
};
|
||||
|
||||
struct EGL_Cursor
|
||||
{
|
||||
LG_Lock lock;
|
||||
@@ -56,103 +69,111 @@ struct EGL_Cursor
|
||||
|
||||
// cursor state
|
||||
bool visible;
|
||||
float x, y, w, h;
|
||||
LG_RendererRotate rotate;
|
||||
int cbMode;
|
||||
|
||||
_Atomic(struct CursorPos) pos;
|
||||
_Atomic(struct CursorSize) size;
|
||||
|
||||
struct CursorTex norm;
|
||||
struct CursorTex mono;
|
||||
struct EGL_Model * model;
|
||||
};
|
||||
|
||||
static bool egl_cursor_tex_init(struct CursorTex * t,
|
||||
static bool cursorTexInit(struct CursorTex * t,
|
||||
const char * vertex_code , size_t vertex_size,
|
||||
const char * fragment_code, size_t fragment_size)
|
||||
{
|
||||
if (!egl_texture_init(&t->texture, NULL))
|
||||
if (!egl_textureInit(&t->texture, NULL, EGL_TEXTYPE_BUFFER, false))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the cursor texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shader_init(&t->shader))
|
||||
if (!egl_shaderInit(&t->shader))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the cursor shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shader_compile(t->shader,
|
||||
if (!egl_shaderCompile(t->shader,
|
||||
vertex_code, vertex_size, fragment_code, fragment_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the cursor shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
t->uMousePos = egl_shader_get_uniform_location(t->shader, "mouse" );
|
||||
t->uRotate = egl_shader_get_uniform_location(t->shader, "rotate");
|
||||
t->uCBMode = egl_shader_get_uniform_location(t->shader, "cbMode");
|
||||
t->uMousePos = egl_shaderGetUniform(t->shader, "mouse" );
|
||||
t->uRotate = egl_shaderGetUniform(t->shader, "rotate");
|
||||
t->uCBMode = egl_shaderGetUniform(t->shader, "cbMode");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void egl_cursor_tex_uniforms(EGL_Cursor * cursor, struct CursorTex * t, bool mono)
|
||||
static inline void setCursorTexUniforms(EGL_Cursor * cursor,
|
||||
struct CursorTex * t, bool mono, float x, float y, float w, float h)
|
||||
{
|
||||
if (mono)
|
||||
{
|
||||
glUniform4f(t->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h / 2);
|
||||
glUniform4f(t->uMousePos, x, y, w, h / 2);
|
||||
glUniform1i(t->uRotate , cursor->rotate);
|
||||
glUniform1i(t->uCBMode , cursor->cbMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
glUniform4f(t->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h);
|
||||
glUniform4f(t->uMousePos, x, y, w, h);
|
||||
glUniform1i(t->uRotate , cursor->rotate);
|
||||
glUniform1i(t->uCBMode , cursor->cbMode);
|
||||
}
|
||||
}
|
||||
|
||||
static void egl_cursor_tex_free(struct CursorTex * t)
|
||||
static void cursorTexFree(struct CursorTex * t)
|
||||
{
|
||||
egl_texture_free(&t->texture);
|
||||
egl_shader_free (&t->shader );
|
||||
egl_textureFree(&t->texture);
|
||||
egl_shaderFree (&t->shader );
|
||||
};
|
||||
|
||||
bool egl_cursor_init(EGL_Cursor ** cursor)
|
||||
bool egl_cursorInit(EGL_Cursor ** cursor)
|
||||
{
|
||||
*cursor = (EGL_Cursor *)malloc(sizeof(EGL_Cursor));
|
||||
*cursor = malloc(sizeof(**cursor));
|
||||
if (!*cursor)
|
||||
{
|
||||
DEBUG_ERROR("Failed to malloc EGL_Cursor");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(*cursor, 0, sizeof(EGL_Cursor));
|
||||
memset(*cursor, 0, sizeof(**cursor));
|
||||
LG_LOCK_INIT((*cursor)->lock);
|
||||
|
||||
if (!egl_cursor_tex_init(&(*cursor)->norm,
|
||||
if (!cursorTexInit(&(*cursor)->norm,
|
||||
b_shader_cursor_vert , b_shader_cursor_vert_size,
|
||||
b_shader_cursor_rgb_frag, b_shader_cursor_rgb_frag_size))
|
||||
return false;
|
||||
|
||||
if (!egl_cursor_tex_init(&(*cursor)->mono,
|
||||
if (!cursorTexInit(&(*cursor)->mono,
|
||||
b_shader_cursor_vert , b_shader_cursor_vert_size,
|
||||
b_shader_cursor_mono_frag, b_shader_cursor_mono_frag_size))
|
||||
return false;
|
||||
|
||||
if (!egl_model_init(&(*cursor)->model))
|
||||
if (!egl_modelInit(&(*cursor)->model))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the cursor model");
|
||||
return false;
|
||||
}
|
||||
|
||||
egl_model_set_default((*cursor)->model);
|
||||
egl_modelSetDefault((*cursor)->model, true);
|
||||
|
||||
(*cursor)->cbMode = option_get_int("egl", "cbMode");
|
||||
|
||||
struct CursorPos pos = { .x = 0, .y = 0 };
|
||||
struct CursorSize size = { .w = 0, .h = 0 };
|
||||
atomic_init(&(*cursor)->pos, pos);
|
||||
atomic_init(&(*cursor)->size, size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_cursor_free(EGL_Cursor ** cursor)
|
||||
void egl_cursorFree(EGL_Cursor ** cursor)
|
||||
{
|
||||
if (!*cursor)
|
||||
return;
|
||||
@@ -161,15 +182,15 @@ void egl_cursor_free(EGL_Cursor ** cursor)
|
||||
if ((*cursor)->data)
|
||||
free((*cursor)->data);
|
||||
|
||||
egl_cursor_tex_free(&(*cursor)->norm);
|
||||
egl_cursor_tex_free(&(*cursor)->mono);
|
||||
egl_model_free(&(*cursor)->model);
|
||||
cursorTexFree(&(*cursor)->norm);
|
||||
cursorTexFree(&(*cursor)->mono);
|
||||
egl_modelFree(&(*cursor)->model);
|
||||
|
||||
free(*cursor);
|
||||
*cursor = NULL;
|
||||
}
|
||||
|
||||
bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type,
|
||||
bool egl_cursorSetShape(EGL_Cursor * cursor, const LG_RendererCursor type,
|
||||
const int width, const int height, const int stride, const uint8_t * data)
|
||||
{
|
||||
LG_LOCK(cursor->lock);
|
||||
@@ -185,7 +206,7 @@ bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type,
|
||||
if (cursor->data)
|
||||
free(cursor->data);
|
||||
|
||||
cursor->data = (uint8_t *)malloc(size);
|
||||
cursor->data = malloc(size);
|
||||
if (!cursor->data)
|
||||
{
|
||||
DEBUG_ERROR("Failed to malloc buffer for cursor shape");
|
||||
@@ -202,23 +223,24 @@ bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type,
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_cursor_set_size(EGL_Cursor * cursor, const float w, const float h)
|
||||
void egl_cursorSetSize(EGL_Cursor * cursor, const float w, const float h)
|
||||
{
|
||||
cursor->w = w;
|
||||
cursor->h = h;
|
||||
struct CursorSize size = { .w = w, .h = h };
|
||||
atomic_store(&cursor->size, size);
|
||||
}
|
||||
|
||||
void egl_cursor_set_state(EGL_Cursor * cursor, const bool visible, const float x, const float y)
|
||||
void egl_cursorSetState(EGL_Cursor * cursor, const bool visible, const float x, const float y)
|
||||
{
|
||||
cursor->visible = visible;
|
||||
cursor->x = x;
|
||||
cursor->y = y;
|
||||
struct CursorPos pos = { .x = x, .y = y};
|
||||
atomic_store(&cursor->pos, pos);
|
||||
}
|
||||
|
||||
void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate)
|
||||
struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
LG_RendererRotate rotate, int width, int height)
|
||||
{
|
||||
if (!cursor->visible)
|
||||
return;
|
||||
return (struct CursorState) { .visible = false };
|
||||
|
||||
if (cursor->update)
|
||||
{
|
||||
@@ -234,9 +256,9 @@ void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate)
|
||||
|
||||
case LG_CURSOR_COLOR:
|
||||
{
|
||||
egl_texture_setup(cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->stride, false, false);
|
||||
egl_texture_update(cursor->norm.texture, data);
|
||||
egl_model_set_texture(cursor->model, cursor->norm.texture);
|
||||
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->stride);
|
||||
egl_textureUpdate(cursor->norm.texture, data);
|
||||
egl_modelSetTexture(cursor->model, cursor->norm.texture);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -258,10 +280,10 @@ void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate)
|
||||
xor[y * cursor->width + x] = xorMask;
|
||||
}
|
||||
|
||||
egl_texture_setup (cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false, false);
|
||||
egl_texture_setup (cursor->mono.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false, false);
|
||||
egl_texture_update(cursor->norm.texture, (uint8_t *)and);
|
||||
egl_texture_update(cursor->mono.texture, (uint8_t *)xor);
|
||||
egl_textureSetup (cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4);
|
||||
egl_textureSetup (cursor->mono.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4);
|
||||
egl_textureUpdate(cursor->norm.texture, (uint8_t *)and);
|
||||
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -270,42 +292,88 @@ void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate)
|
||||
|
||||
cursor->rotate = rotate;
|
||||
|
||||
struct CursorPos pos = atomic_load(&cursor->pos);
|
||||
struct CursorSize size = atomic_load(&cursor->size);
|
||||
|
||||
struct CursorState state = {
|
||||
.visible = true,
|
||||
};
|
||||
|
||||
switch (rotate)
|
||||
{
|
||||
case LG_ROTATE_0:
|
||||
state.rect.x = (pos.x * width + width) / 2;
|
||||
state.rect.y = (-pos.y * height + height) / 2 - size.h * height;
|
||||
state.rect.w = size.w * width + 3;
|
||||
state.rect.h = size.h * height + 3;
|
||||
break;
|
||||
|
||||
case LG_ROTATE_90:
|
||||
state.rect.x = (-pos.y * width + width) / 2 - size.h * width;
|
||||
state.rect.y = (-pos.x * height + height) / 2 - size.w * height;
|
||||
state.rect.w = size.h * width + 3;
|
||||
state.rect.h = size.w * height + 3;
|
||||
break;
|
||||
|
||||
case LG_ROTATE_180:
|
||||
state.rect.x = (-pos.x * width + width) / 2 - size.w * width;
|
||||
state.rect.y = (pos.y * height + height) / 2;
|
||||
state.rect.w = size.w * width + 3;
|
||||
state.rect.h = size.h * height + 3;
|
||||
break;
|
||||
|
||||
case LG_ROTATE_270:
|
||||
state.rect.x = (pos.y * width + width) / 2;
|
||||
state.rect.y = (pos.x * height + height) / 2;
|
||||
state.rect.w = size.h * width + 3;
|
||||
state.rect.h = size.w * height + 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_UNREACHABLE();
|
||||
}
|
||||
|
||||
state.rect.x = max(0, state.rect.x - 1);
|
||||
state.rect.y = max(0, state.rect.y - 1);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
switch(cursor->type)
|
||||
{
|
||||
case LG_CURSOR_MONOCHROME:
|
||||
{
|
||||
egl_shader_use(cursor->norm.shader);
|
||||
egl_cursor_tex_uniforms(cursor, &cursor->norm, true);;
|
||||
egl_shaderUse(cursor->norm.shader);
|
||||
setCursorTexUniforms(cursor, &cursor->norm, true, pos.x, pos.y, size.w, size.h);
|
||||
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
|
||||
egl_model_set_texture(cursor->model, cursor->norm.texture);
|
||||
egl_model_render(cursor->model);
|
||||
egl_modelSetTexture(cursor->model, cursor->norm.texture);
|
||||
egl_modelRender(cursor->model);
|
||||
|
||||
egl_shader_use(cursor->mono.shader);
|
||||
egl_cursor_tex_uniforms(cursor, &cursor->mono, true);;
|
||||
egl_shaderUse(cursor->mono.shader);
|
||||
setCursorTexUniforms(cursor, &cursor->mono, true, pos.x, pos.y, size.w, size.h);
|
||||
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
|
||||
egl_model_set_texture(cursor->model, cursor->mono.texture);
|
||||
egl_model_render(cursor->model);
|
||||
egl_modelSetTexture(cursor->model, cursor->mono.texture);
|
||||
egl_modelRender(cursor->model);
|
||||
break;
|
||||
}
|
||||
|
||||
case LG_CURSOR_COLOR:
|
||||
{
|
||||
egl_shader_use(cursor->norm.shader);
|
||||
egl_cursor_tex_uniforms(cursor, &cursor->norm, false);
|
||||
egl_shaderUse(cursor->norm.shader);
|
||||
setCursorTexUniforms(cursor, &cursor->norm, false, pos.x, pos.y, size.w, size.h);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
egl_model_render(cursor->model);
|
||||
egl_modelRender(cursor->model);
|
||||
break;
|
||||
}
|
||||
|
||||
case LG_CURSOR_MASKED_COLOR:
|
||||
{
|
||||
egl_shader_use(cursor->mono.shader);
|
||||
egl_cursor_tex_uniforms(cursor, &cursor->mono, false);
|
||||
egl_shaderUse(cursor->mono.shader);
|
||||
setCursorTexUniforms(cursor, &cursor->mono, false, pos.x, pos.y, size.w, size.h);
|
||||
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
|
||||
egl_model_render(cursor->model);
|
||||
egl_modelRender(cursor->model);
|
||||
break;
|
||||
}
|
||||
}
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -1,34 +1,41 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "egl.h"
|
||||
#include "interface/renderer.h"
|
||||
|
||||
typedef struct EGL_Cursor EGL_Cursor;
|
||||
|
||||
bool egl_cursor_init(EGL_Cursor ** cursor);
|
||||
void egl_cursor_free(EGL_Cursor ** cursor);
|
||||
struct CursorState {
|
||||
bool visible;
|
||||
struct Rect rect;
|
||||
};
|
||||
|
||||
bool egl_cursor_set_shape(
|
||||
bool egl_cursorInit(EGL_Cursor ** cursor);
|
||||
void egl_cursorFree(EGL_Cursor ** cursor);
|
||||
|
||||
bool egl_cursorSetShape(
|
||||
EGL_Cursor * cursor,
|
||||
const LG_RendererCursor type,
|
||||
const int width,
|
||||
@@ -36,9 +43,10 @@ bool egl_cursor_set_shape(
|
||||
const int stride,
|
||||
const uint8_t * data);
|
||||
|
||||
void egl_cursor_set_size(EGL_Cursor * cursor, const float x, const float y);
|
||||
void egl_cursorSetSize(EGL_Cursor * cursor, const float x, const float y);
|
||||
|
||||
void egl_cursor_set_state(EGL_Cursor * cursor, const bool visible,
|
||||
void egl_cursorSetState(EGL_Cursor * cursor, const bool visible,
|
||||
const float x, const float y);
|
||||
|
||||
void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate);
|
||||
struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
||||
LG_RendererRotate rotate, int width, int height);
|
||||
|
||||
156
client/renderers/EGL/damage.c
Normal file
156
client/renderers/EGL/damage.c
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "damage.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/KVMFR.h"
|
||||
#include "common/locking.h"
|
||||
|
||||
#include "app.h"
|
||||
#include "desktop_rects.h"
|
||||
#include "shader.h"
|
||||
#include "cimgui.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// these headers are auto generated by cmake
|
||||
#include "damage.vert.h"
|
||||
#include "damage.frag.h"
|
||||
|
||||
struct EGL_Damage
|
||||
{
|
||||
EGL_Shader * shader;
|
||||
EGL_DesktopRects * mesh;
|
||||
GLfloat transform[6];
|
||||
|
||||
bool show;
|
||||
|
||||
int width , height;
|
||||
float translateX, translateY;
|
||||
float scaleX , scaleY;
|
||||
LG_RendererRotate rotate;
|
||||
|
||||
// uniforms
|
||||
GLint uTransform;
|
||||
};
|
||||
|
||||
void egl_damageConfigUI(EGL_Damage * damage)
|
||||
{
|
||||
igCheckbox("Show damage overlay", &damage->show);
|
||||
}
|
||||
|
||||
bool egl_damageInit(EGL_Damage ** damage)
|
||||
{
|
||||
*damage = malloc(sizeof(**damage));
|
||||
if (!*damage)
|
||||
{
|
||||
DEBUG_ERROR("Failed to malloc EGL_Damage");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(*damage, 0, sizeof(EGL_Damage));
|
||||
|
||||
if (!egl_shaderInit(&(*damage)->shader))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the damage shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shaderCompile((*damage)->shader,
|
||||
b_shader_damage_vert, b_shader_damage_vert_size,
|
||||
b_shader_damage_frag, b_shader_damage_frag_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the damage shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_desktopRectsInit(&(*damage)->mesh, KVMFR_MAX_DAMAGE_RECTS))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
(*damage)->uTransform = egl_shaderGetUniform((*damage)->shader, "transform");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_damageFree(EGL_Damage ** damage)
|
||||
{
|
||||
if (!*damage)
|
||||
return;
|
||||
|
||||
egl_desktopRectsFree(&(*damage)->mesh);
|
||||
egl_shaderFree(&(*damage)->shader);
|
||||
|
||||
free(*damage);
|
||||
*damage = NULL;
|
||||
}
|
||||
|
||||
static void update_matrix(EGL_Damage * damage)
|
||||
{
|
||||
egl_desktopRectsMatrix(damage->transform, damage->width, damage->height,
|
||||
damage->translateX, damage->translateY, damage->scaleX, damage->scaleY, damage->rotate);
|
||||
}
|
||||
|
||||
void egl_damageSetup(EGL_Damage * damage, int width, int height)
|
||||
{
|
||||
damage->width = width;
|
||||
damage->height = height;
|
||||
update_matrix(damage);
|
||||
}
|
||||
|
||||
void egl_damageResize(EGL_Damage * damage, float translateX, float translateY,
|
||||
float scaleX, float scaleY)
|
||||
{
|
||||
damage->translateX = translateX;
|
||||
damage->translateY = translateY;
|
||||
damage->scaleX = scaleX;
|
||||
damage->scaleY = scaleY;
|
||||
update_matrix(damage);
|
||||
}
|
||||
|
||||
bool egl_damageRender(EGL_Damage * damage, LG_RendererRotate rotate, const struct DesktopDamage * data)
|
||||
{
|
||||
if (!damage->show)
|
||||
return false;
|
||||
|
||||
if (rotate != damage->rotate)
|
||||
{
|
||||
damage->rotate = rotate;
|
||||
update_matrix(damage);
|
||||
}
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
egl_shaderUse(damage->shader);
|
||||
glUniformMatrix3x2fv(damage->uTransform, 1, GL_FALSE, damage->transform);
|
||||
|
||||
if (data && data->count != 0)
|
||||
egl_desktopRectsUpdate(damage->mesh, (const struct DamageRects *) data,
|
||||
damage->width, damage->height);
|
||||
|
||||
egl_desktopRectsRender(damage->mesh);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
return true;
|
||||
}
|
||||
44
client/renderers/EGL/damage.h
Normal file
44
client/renderers/EGL/damage.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "common/KVMFR.h"
|
||||
#include "interface/renderer.h"
|
||||
#include "desktop_rects.h"
|
||||
|
||||
struct DesktopDamage
|
||||
{
|
||||
int count;
|
||||
FrameDamageRect rects[KVMFR_MAX_DAMAGE_RECTS];
|
||||
};
|
||||
|
||||
typedef struct EGL_Damage EGL_Damage;
|
||||
|
||||
bool egl_damageInit(EGL_Damage ** damage);
|
||||
void egl_damageFree(EGL_Damage ** damage);
|
||||
|
||||
void egl_damageConfigUI(EGL_Damage * damage);
|
||||
void egl_damageSetup(EGL_Damage * damage, int width, int height);
|
||||
void egl_damageResize(EGL_Damage * damage, float translateX, float translateY,
|
||||
float scaleX, float scaleY);
|
||||
bool egl_damageRender(EGL_Damage * damage, LG_RendererRotate rotate,
|
||||
const struct DesktopDamage * data);
|
||||
@@ -1,31 +1,34 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
cahe terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "desktop.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/option.h"
|
||||
#include "common/locking.h"
|
||||
#include "common/array.h"
|
||||
|
||||
#include "app.h"
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
#include "model.h"
|
||||
#include "desktop_rects.h"
|
||||
#include "cimgui.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -33,32 +36,38 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
// these headers are auto generated by cmake
|
||||
#include "desktop.vert.h"
|
||||
#include "desktop_rgb.frag.h"
|
||||
#include "desktop_rgb.def.h"
|
||||
|
||||
#include "postprocess.h"
|
||||
#include "filters.h"
|
||||
|
||||
struct DesktopShader
|
||||
{
|
||||
EGL_Shader * shader;
|
||||
GLint uDesktopPos;
|
||||
GLint uTransform;
|
||||
GLint uDesktopSize;
|
||||
GLint uRotate;
|
||||
GLint uNearest;
|
||||
GLint uNV, uNVGain;
|
||||
GLint uScaleAlgo;
|
||||
GLint uNVGain;
|
||||
GLint uCBMode;
|
||||
};
|
||||
|
||||
struct EGL_Desktop
|
||||
{
|
||||
EGL * egl;
|
||||
EGLDisplay * display;
|
||||
|
||||
EGL_Texture * texture;
|
||||
struct DesktopShader * shader; // the active shader
|
||||
EGL_Model * model;
|
||||
GLuint sampler;
|
||||
struct DesktopShader shader;
|
||||
EGL_DesktopRects * mesh;
|
||||
CountedBuffer * matrix;
|
||||
|
||||
// internals
|
||||
int width, height;
|
||||
LG_RendererRotate rotate;
|
||||
|
||||
// shader instances
|
||||
struct DesktopShader shader_generic;
|
||||
// scale algorithm
|
||||
int scaleAlgo;
|
||||
|
||||
// night vision
|
||||
int nvMax;
|
||||
@@ -66,58 +75,65 @@ struct EGL_Desktop
|
||||
|
||||
// colorblind mode
|
||||
int cbMode;
|
||||
|
||||
bool useDMA;
|
||||
LG_RendererFormat format;
|
||||
|
||||
EGL_PostProcess * pp;
|
||||
_Atomic(bool) processFrame;
|
||||
};
|
||||
|
||||
// forwards
|
||||
void egl_desktop_toggle_nv(int key, void * opaque);
|
||||
void toggleNV(int key, void * opaque);
|
||||
|
||||
static bool egl_init_desktop_shader(
|
||||
static bool egl_initDesktopShader(
|
||||
struct DesktopShader * shader,
|
||||
const char * vertex_code , size_t vertex_size,
|
||||
const char * fragment_code, size_t fragment_size
|
||||
)
|
||||
{
|
||||
if (!egl_shader_init(&shader->shader))
|
||||
if (!egl_shaderInit(&shader->shader))
|
||||
return false;
|
||||
|
||||
if (!egl_shader_compile(shader->shader,
|
||||
if (!egl_shaderCompile(shader->shader,
|
||||
vertex_code , vertex_size,
|
||||
fragment_code, fragment_size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
shader->uDesktopPos = egl_shader_get_uniform_location(shader->shader, "position");
|
||||
shader->uDesktopSize = egl_shader_get_uniform_location(shader->shader, "size" );
|
||||
shader->uRotate = egl_shader_get_uniform_location(shader->shader, "rotate" );
|
||||
shader->uNearest = egl_shader_get_uniform_location(shader->shader, "nearest" );
|
||||
shader->uNV = egl_shader_get_uniform_location(shader->shader, "nv" );
|
||||
shader->uNVGain = egl_shader_get_uniform_location(shader->shader, "nvGain" );
|
||||
shader->uCBMode = egl_shader_get_uniform_location(shader->shader, "cbMode" );
|
||||
shader->uTransform = egl_shaderGetUniform(shader->shader, "transform" );
|
||||
shader->uDesktopSize = egl_shaderGetUniform(shader->shader, "desktopSize");
|
||||
shader->uScaleAlgo = egl_shaderGetUniform(shader->shader, "scaleAlgo" );
|
||||
shader->uNVGain = egl_shaderGetUniform(shader->shader, "nvGain" );
|
||||
shader->uCBMode = egl_shaderGetUniform(shader->shader, "cbMode" );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_desktop_init(EGL_Desktop ** desktop, EGLDisplay * display)
|
||||
bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
|
||||
bool useDMA, int maxRects)
|
||||
{
|
||||
*desktop = (EGL_Desktop *)malloc(sizeof(EGL_Desktop));
|
||||
if (!*desktop)
|
||||
EGL_Desktop * desktop = calloc(1, sizeof(EGL_Desktop));
|
||||
if (!desktop)
|
||||
{
|
||||
DEBUG_ERROR("Failed to malloc EGL_Desktop");
|
||||
return false;
|
||||
}
|
||||
*desktop_ = desktop;
|
||||
|
||||
memset(*desktop, 0, sizeof(EGL_Desktop));
|
||||
(*desktop)->display = display;
|
||||
desktop->egl = egl;
|
||||
desktop->display = display;
|
||||
|
||||
if (!egl_texture_init(&(*desktop)->texture, display))
|
||||
if (!egl_textureInit(&desktop->texture, display,
|
||||
useDMA ? EGL_TEXTYPE_DMABUF : EGL_TEXTYPE_FRAMEBUFFER, true))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the desktop texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_init_desktop_shader(
|
||||
&(*desktop)->shader_generic,
|
||||
if (!egl_initDesktopShader(
|
||||
&desktop->shader,
|
||||
b_shader_desktop_vert , b_shader_desktop_vert_size,
|
||||
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size))
|
||||
{
|
||||
@@ -125,25 +141,41 @@ bool egl_desktop_init(EGL_Desktop ** desktop, EGLDisplay * display)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_model_init(&(*desktop)->model))
|
||||
if (!egl_desktopRectsInit(&desktop->mesh, maxRects))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the desktop model");
|
||||
DEBUG_ERROR("Failed to initialize the desktop mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
egl_model_set_default((*desktop)->model);
|
||||
egl_model_set_texture((*desktop)->model, (*desktop)->texture);
|
||||
desktop->matrix = countedBufferNew(6 * sizeof(GLfloat));
|
||||
if (!desktop->matrix)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate the desktop matrix buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
app_registerKeybind(KEY_N, egl_desktop_toggle_nv, *desktop, "Toggle night vision mode");
|
||||
app_registerKeybind(KEY_N, toggleNV, desktop,
|
||||
"Toggle night vision mode");
|
||||
|
||||
(*desktop)->nvMax = option_get_int("egl", "nvGainMax");
|
||||
(*desktop)->nvGain = option_get_int("egl", "nvGain" );
|
||||
(*desktop)->cbMode = option_get_int("egl", "cbMode" );
|
||||
desktop->nvMax = option_get_int("egl", "nvGainMax");
|
||||
desktop->nvGain = option_get_int("egl", "nvGain" );
|
||||
desktop->cbMode = option_get_int("egl", "cbMode" );
|
||||
desktop->scaleAlgo = option_get_int("egl", "scale" );
|
||||
desktop->useDMA = useDMA;
|
||||
|
||||
if (!egl_postProcessInit(&desktop->pp))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the post process manager");
|
||||
return false;
|
||||
}
|
||||
|
||||
egl_postProcessAdd(desktop->pp, &egl_filterDownscaleOps);
|
||||
egl_postProcessAdd(desktop->pp, &egl_filterFFXCASOps );
|
||||
egl_postProcessAdd(desktop->pp, &egl_filterFFXFSR1Ops );
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_desktop_toggle_nv(int key, void * opaque)
|
||||
void toggleNV(int key, void * opaque)
|
||||
{
|
||||
EGL_Desktop * desktop = (EGL_Desktop *)opaque;
|
||||
if (desktop->nvGain++ == desktop->nvMax)
|
||||
@@ -152,44 +184,95 @@ void egl_desktop_toggle_nv(int key, void * opaque)
|
||||
if (desktop->nvGain == 0) app_alert(LG_ALERT_INFO, "NV Disabled");
|
||||
else if (desktop->nvGain == 1) app_alert(LG_ALERT_INFO, "NV Enabled");
|
||||
else app_alert(LG_ALERT_INFO, "NV Gain + %d", desktop->nvGain - 1);
|
||||
|
||||
app_invalidateWindow(true);
|
||||
}
|
||||
|
||||
void egl_desktop_free(EGL_Desktop ** desktop)
|
||||
bool egl_desktopScaleValidate(struct Option * opt, const char ** error)
|
||||
{
|
||||
if (opt->value.x_int >= 0 && opt->value.x_int < EGL_SCALE_MAX)
|
||||
return true;
|
||||
|
||||
*error = "Invalid scale algorithm number";
|
||||
return false;
|
||||
}
|
||||
|
||||
void egl_desktopFree(EGL_Desktop ** desktop)
|
||||
{
|
||||
if (!*desktop)
|
||||
return;
|
||||
|
||||
egl_texture_free(&(*desktop)->texture );
|
||||
egl_shader_free (&(*desktop)->shader_generic.shader);
|
||||
egl_model_free (&(*desktop)->model );
|
||||
egl_textureFree (&(*desktop)->texture );
|
||||
egl_shaderFree (&(*desktop)->shader.shader);
|
||||
egl_desktopRectsFree(&(*desktop)->mesh );
|
||||
countedBufferRelease(&(*desktop)->matrix );
|
||||
|
||||
egl_postProcessFree(&(*desktop)->pp);
|
||||
|
||||
free(*desktop);
|
||||
*desktop = NULL;
|
||||
}
|
||||
|
||||
bool egl_desktop_setup(EGL_Desktop * desktop, const LG_RendererFormat format, bool useDMA)
|
||||
static const char * algorithmNames[EGL_SCALE_MAX] = {
|
||||
[EGL_SCALE_AUTO] = "Automatic (downscale: linear, upscale: nearest)",
|
||||
[EGL_SCALE_NEAREST] = "Nearest",
|
||||
[EGL_SCALE_LINEAR] = "Linear",
|
||||
};
|
||||
|
||||
void egl_desktopConfigUI(EGL_Desktop * desktop)
|
||||
{
|
||||
igText("Scale algorithm:");
|
||||
igPushItemWidth(igGetWindowWidth() - igGetStyle()->WindowPadding.x * 2);
|
||||
if (igBeginCombo("##scale", algorithmNames[desktop->scaleAlgo], 0))
|
||||
{
|
||||
for (int i = 0; i < EGL_SCALE_MAX; ++i)
|
||||
{
|
||||
bool selected = i == desktop->scaleAlgo;
|
||||
if (igSelectableBool(algorithmNames[i], selected, 0, (ImVec2) { 0.0f, 0.0f }))
|
||||
desktop->scaleAlgo = i;
|
||||
if (selected)
|
||||
igSetItemDefaultFocus();
|
||||
}
|
||||
igEndCombo();
|
||||
}
|
||||
igPopItemWidth();
|
||||
|
||||
igText("Night vision mode:");
|
||||
igSameLine(0.0f, -1.0f);
|
||||
igPushItemWidth(igGetWindowWidth() - igGetCursorPosX() - igGetStyle()->WindowPadding.x);
|
||||
|
||||
const char * format;
|
||||
switch (desktop->nvGain)
|
||||
{
|
||||
case 0: format = "off"; break;
|
||||
case 1: format = "on"; break;
|
||||
default: format = "gain: %d";
|
||||
}
|
||||
igSliderInt("##nvgain", &desktop->nvGain, 0, desktop->nvMax, format, 0);
|
||||
igPopItemWidth();
|
||||
}
|
||||
|
||||
bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
|
||||
{
|
||||
memcpy(&desktop->format, &format, sizeof(LG_RendererFormat));
|
||||
|
||||
enum EGL_PixelFormat pixFmt;
|
||||
switch(format.type)
|
||||
{
|
||||
case FRAME_TYPE_BGRA:
|
||||
pixFmt = EGL_PF_BGRA;
|
||||
desktop->shader = &desktop->shader_generic;
|
||||
break;
|
||||
|
||||
case FRAME_TYPE_RGBA:
|
||||
pixFmt = EGL_PF_RGBA;
|
||||
desktop->shader = &desktop->shader_generic;
|
||||
break;
|
||||
|
||||
case FRAME_TYPE_RGBA10:
|
||||
pixFmt = EGL_PF_RGBA10;
|
||||
desktop->shader = &desktop->shader_generic;
|
||||
break;
|
||||
|
||||
case FRAME_TYPE_RGBA16F:
|
||||
pixFmt = EGL_PF_RGBA16F;
|
||||
desktop->shader = &desktop->shader_generic;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -200,85 +283,177 @@ bool egl_desktop_setup(EGL_Desktop * desktop, const LG_RendererFormat format, bo
|
||||
desktop->width = format.width;
|
||||
desktop->height = format.height;
|
||||
|
||||
if (!egl_texture_setup(
|
||||
if (!egl_textureSetup(
|
||||
desktop->texture,
|
||||
pixFmt,
|
||||
format.width,
|
||||
format.height,
|
||||
format.pitch,
|
||||
true, // streaming texture
|
||||
useDMA
|
||||
format.pitch
|
||||
))
|
||||
{
|
||||
DEBUG_ERROR("Failed to setup the desktop texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
glGenSamplers(1, &desktop->sampler);
|
||||
glSamplerParameteri(desktop->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(desktop->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(desktop->sampler, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(desktop->sampler, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_desktop_update(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd)
|
||||
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd,
|
||||
const FrameDamageRect * damageRects, int damageRectsCount)
|
||||
{
|
||||
if (dmaFd >= 0)
|
||||
if (desktop->useDMA && dmaFd >= 0)
|
||||
{
|
||||
if (!egl_texture_update_from_dma(desktop->texture, frame, dmaFd))
|
||||
if (egl_textureUpdateFromDMA(desktop->texture, frame, dmaFd))
|
||||
{
|
||||
atomic_store(&desktop->processFrame, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
DEBUG_WARN("DMA update failed, disabling DMABUF imports");
|
||||
|
||||
const char * vendor = (const char *)glGetString(GL_VENDOR);
|
||||
if (strstr(vendor, "NVIDIA"))
|
||||
{
|
||||
DEBUG_WARN("NVIDIA's DMABUF support is incomplete, please direct your complaints to NVIDIA");
|
||||
DEBUG_WARN("This is not a bug in Looking Glass");
|
||||
}
|
||||
|
||||
desktop->useDMA = false;
|
||||
|
||||
const char * gl_exts = (const char *)glGetString(GL_EXTENSIONS);
|
||||
if (!util_hasGLExt(gl_exts, "GL_EXT_buffer_storage"))
|
||||
{
|
||||
DEBUG_ERROR("GL_EXT_buffer_storage is needed to use EGL backend");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!egl_texture_update_from_frame(desktop->texture, frame))
|
||||
}
|
||||
|
||||
egl_textureFree(&desktop->texture);
|
||||
if (!egl_textureInit(&desktop->texture, desktop->display,
|
||||
EGL_TEXTYPE_FRAMEBUFFER, true))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the desktop texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_desktopSetup(desktop, desktop->format))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (egl_textureUpdateFromFrame(desktop->texture, frame,
|
||||
damageRects, damageRectsCount))
|
||||
{
|
||||
atomic_store(&desktop->processFrame, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void egl_desktopResize(EGL_Desktop * desktop, int width, int height)
|
||||
{
|
||||
atomic_store(&desktop->processFrame, true);
|
||||
}
|
||||
|
||||
bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
unsigned int outputHeight, const float x, const float y,
|
||||
const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType,
|
||||
LG_RendererRotate rotate, const struct DamageRects * rects)
|
||||
{
|
||||
if (outputWidth == 0 && outputHeight == 0)
|
||||
DEBUG_FATAL("outputWidth || outputHeight == 0");
|
||||
|
||||
enum EGL_TexStatus status;
|
||||
if ((status = egl_texture_process(desktop->texture)) != EGL_TEX_STATUS_OK)
|
||||
if ((status = egl_textureProcess(desktop->texture)) != EGL_TEX_STATUS_OK)
|
||||
{
|
||||
if (status != EGL_TEX_STATUS_NOTREADY)
|
||||
DEBUG_ERROR("Failed to process the desktop texture");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
int scaleAlgo = EGL_SCALE_NEAREST;
|
||||
|
||||
bool egl_desktop_render(EGL_Desktop * desktop, const float x, const float y,
|
||||
const float scaleX, const float scaleY, const bool nearest,
|
||||
LG_RendererRotate rotate)
|
||||
{
|
||||
if (!desktop->shader)
|
||||
return false;
|
||||
egl_desktopRectsMatrix((float *)desktop->matrix->data,
|
||||
desktop->width, desktop->height, x, y, scaleX, scaleY, rotate);
|
||||
egl_desktopRectsUpdate(desktop->mesh, rects, desktop->width, desktop->height);
|
||||
|
||||
bool useNearest = nearest;
|
||||
if (!nearest)
|
||||
if (atomic_exchange(&desktop->processFrame, false) ||
|
||||
egl_postProcessConfigModified(desktop->pp))
|
||||
egl_postProcessRun(desktop->pp, desktop->texture, desktop->mesh,
|
||||
desktop->width, desktop->height, outputWidth, outputHeight);
|
||||
|
||||
unsigned int finalSizeX, finalSizeY;
|
||||
GLuint texture = egl_postProcessGetOutput(desktop->pp,
|
||||
&finalSizeX, &finalSizeY);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
egl_resetViewport(desktop->egl);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glBindSampler(0, desktop->sampler);
|
||||
|
||||
if (finalSizeX > desktop->width || finalSizeY > desktop->height)
|
||||
scaleType = EGL_DESKTOP_DOWNSCALE;
|
||||
|
||||
switch (desktop->scaleAlgo)
|
||||
{
|
||||
case EGL_SCALE_AUTO:
|
||||
switch (scaleType)
|
||||
{
|
||||
case EGL_DESKTOP_UPSCALE:
|
||||
scaleAlgo = EGL_SCALE_NEAREST;
|
||||
break;
|
||||
|
||||
case EGL_DESKTOP_NOSCALE:
|
||||
case EGL_DESKTOP_DOWNSCALE:
|
||||
scaleAlgo = EGL_SCALE_LINEAR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
scaleAlgo = desktop->scaleAlgo;
|
||||
}
|
||||
|
||||
const struct DesktopShader * shader = &desktop->shader;
|
||||
EGL_Uniform uniforms[] =
|
||||
{
|
||||
switch(rotate)
|
||||
{
|
||||
case LG_ROTATE_90:
|
||||
case LG_ROTATE_270:
|
||||
if (scaleX < 1.0f || scaleY < 1.0f)
|
||||
useNearest = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
.type = EGL_UNIFORM_TYPE_1I,
|
||||
.location = shader->uScaleAlgo,
|
||||
.i = { scaleAlgo },
|
||||
},
|
||||
{
|
||||
.type = EGL_UNIFORM_TYPE_2F,
|
||||
.location = shader->uDesktopSize,
|
||||
.f = { desktop->width, desktop->height },
|
||||
},
|
||||
{
|
||||
.type = EGL_UNIFORM_TYPE_M3x2FV,
|
||||
.location = shader->uTransform,
|
||||
.m.transpose = GL_FALSE,
|
||||
.m.v = desktop->matrix
|
||||
},
|
||||
{
|
||||
.type = EGL_UNIFORM_TYPE_1F,
|
||||
.location = shader->uNVGain,
|
||||
.f = { (float)desktop->nvGain }
|
||||
},
|
||||
{
|
||||
.type = EGL_UNIFORM_TYPE_1I,
|
||||
.location = shader->uCBMode,
|
||||
.f = { desktop->cbMode }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const struct DesktopShader * shader = desktop->shader;
|
||||
egl_shader_use(shader->shader);
|
||||
glUniform4f(shader->uDesktopPos , x, y, scaleX, scaleY);
|
||||
glUniform1i(shader->uRotate , rotate);
|
||||
glUniform1i(shader->uNearest , useNearest ? 1 : 0);
|
||||
glUniform2f(shader->uDesktopSize, desktop->width, desktop->height);
|
||||
|
||||
if (desktop->nvGain)
|
||||
{
|
||||
glUniform1i(shader->uNV, 1);
|
||||
glUniform1f(shader->uNVGain, (float)desktop->nvGain);
|
||||
}
|
||||
else
|
||||
glUniform1i(shader->uNV, 0);
|
||||
|
||||
glUniform1i(shader->uCBMode, desktop->cbMode);
|
||||
egl_model_render(desktop->model);
|
||||
egl_shaderSetUniforms(shader->shader, uniforms, ARRAY_LENGTH(uniforms));
|
||||
egl_shaderUse(shader->shader);
|
||||
egl_desktopRectsRender(desktop->mesh);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,35 +1,52 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "interface/renderer.h"
|
||||
#include "egl.h"
|
||||
#include "desktop_rects.h"
|
||||
|
||||
typedef struct EGL_Desktop EGL_Desktop;
|
||||
|
||||
bool egl_desktop_init(EGL_Desktop ** desktop, EGLDisplay * display);
|
||||
void egl_desktop_free(EGL_Desktop ** desktop);
|
||||
enum EGL_DesktopScaleType
|
||||
{
|
||||
EGL_DESKTOP_NOSCALE,
|
||||
EGL_DESKTOP_UPSCALE,
|
||||
EGL_DESKTOP_DOWNSCALE,
|
||||
};
|
||||
|
||||
bool egl_desktop_setup (EGL_Desktop * desktop, const LG_RendererFormat format, bool useDMA);
|
||||
bool egl_desktop_update(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd);
|
||||
bool egl_desktop_render(EGL_Desktop * desktop, const float x, const float y,
|
||||
const float scaleX, const float scaleY, const bool nearest,
|
||||
LG_RendererRotate rotate);
|
||||
struct Option;
|
||||
bool egl_desktopScaleValidate(struct Option * opt, const char ** error);
|
||||
|
||||
bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop, EGLDisplay * display,
|
||||
bool useDMA, int maxRects);
|
||||
void egl_desktopFree(EGL_Desktop ** desktop);
|
||||
|
||||
void egl_desktopConfigUI(EGL_Desktop * desktop);
|
||||
bool egl_desktopSetup (EGL_Desktop * desktop, const LG_RendererFormat format);
|
||||
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd,
|
||||
const FrameDamageRect * damageRects, int damageRectsCount);
|
||||
void egl_desktopResize(EGL_Desktop * desktop, int width, int height);
|
||||
bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
||||
unsigned int outputHeight, const float x, const float y,
|
||||
const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType,
|
||||
LG_RendererRotate rotate, const struct DamageRects * rects);
|
||||
|
||||
275
client/renderers/EGL/desktop_rects.c
Normal file
275
client/renderers/EGL/desktop_rects.c
Normal file
@@ -0,0 +1,275 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "desktop_rects.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/KVMFR.h"
|
||||
#include "common/locking.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <GLES3/gl3.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
struct EGL_DesktopRects
|
||||
{
|
||||
GLuint buffers[2];
|
||||
GLuint vao;
|
||||
int count;
|
||||
int maxCount;
|
||||
};
|
||||
|
||||
bool egl_desktopRectsInit(EGL_DesktopRects ** rects_, int maxCount)
|
||||
{
|
||||
EGL_DesktopRects * rects = malloc(sizeof(*rects));
|
||||
if (!rects)
|
||||
{
|
||||
DEBUG_ERROR("Failed to malloc EGL_DesktopRects");
|
||||
return false;
|
||||
}
|
||||
*rects_ = rects;
|
||||
memset(rects, 0, sizeof(*rects));
|
||||
|
||||
glGenVertexArrays(1, &rects->vao);
|
||||
glBindVertexArray(rects->vao);
|
||||
|
||||
glGenBuffers(2, rects->buffers);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, rects->buffers[0]);
|
||||
glBufferData(GL_ARRAY_BUFFER, maxCount * 8 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
GLushort indices[maxCount * 6];
|
||||
for (int i = 0; i < maxCount; ++i)
|
||||
{
|
||||
indices[6 * i + 0] = 4 * i + 0;
|
||||
indices[6 * i + 1] = 4 * i + 1;
|
||||
indices[6 * i + 2] = 4 * i + 2;
|
||||
indices[6 * i + 3] = 4 * i + 0;
|
||||
indices[6 * i + 4] = 4 * i + 2;
|
||||
indices[6 * i + 5] = 4 * i + 3;
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, rects->buffers[1]);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof indices, indices, GL_STATIC_DRAW);
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
||||
rects->count = 0;
|
||||
rects->maxCount = maxCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_desktopRectsFree(EGL_DesktopRects ** rects_)
|
||||
{
|
||||
EGL_DesktopRects * rects = *rects_;
|
||||
if (!rects)
|
||||
return;
|
||||
|
||||
glDeleteVertexArrays(1, &rects->vao);
|
||||
glDeleteBuffers(2, rects->buffers);
|
||||
free(rects);
|
||||
*rects_ = NULL;
|
||||
}
|
||||
|
||||
inline static void rectToVertices(GLfloat * vertex, const FrameDamageRect * rect)
|
||||
{
|
||||
vertex[0] = rect->x;
|
||||
vertex[1] = rect->y;
|
||||
vertex[2] = rect->x + rect->width;
|
||||
vertex[3] = rect->y;
|
||||
vertex[4] = rect->x + rect->width;
|
||||
vertex[5] = rect->y + rect->height;
|
||||
vertex[6] = rect->x;
|
||||
vertex[7] = rect->y + rect->height;
|
||||
}
|
||||
|
||||
void egl_desktopRectsUpdate(EGL_DesktopRects * rects, const struct DamageRects * data,
|
||||
int width, int height)
|
||||
{
|
||||
if (data && data->count == 0)
|
||||
{
|
||||
rects->count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
GLfloat vertices[(!data || data->count < 0 ? 1 : data->count) * 8];
|
||||
if (!data || data->count < 0)
|
||||
{
|
||||
FrameDamageRect full = {
|
||||
.x = 0, .y = 0, .width = width, .height = height,
|
||||
};
|
||||
rects->count = 1;
|
||||
rectToVertices(vertices, &full);
|
||||
}
|
||||
else
|
||||
{
|
||||
rects->count = data->count;
|
||||
DEBUG_ASSERT(rects->count <= rects->maxCount);
|
||||
|
||||
for (int i = 0; i < rects->count; ++i)
|
||||
rectToVertices(vertices + i * 8, data->rects + i);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, rects->buffers[0]);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, rects->count * 8 * sizeof(GLfloat), vertices);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
static void desktopToGLSpace(double matrix[6], int width, int height, double translateX,
|
||||
double translateY, double scaleX, double scaleY, LG_RendererRotate rotate)
|
||||
{
|
||||
switch (rotate)
|
||||
{
|
||||
case LG_ROTATE_0:
|
||||
matrix[0] = 2.0 * scaleX / width;
|
||||
matrix[1] = 0.0;
|
||||
matrix[2] = 0.0;
|
||||
matrix[3] = -2.0 * scaleY / height;
|
||||
matrix[4] = translateX - scaleX;
|
||||
matrix[5] = translateY + scaleY;
|
||||
return;
|
||||
|
||||
case LG_ROTATE_90:
|
||||
matrix[0] = 0.0;
|
||||
matrix[1] = -2.0 * scaleY / width;
|
||||
matrix[2] = -2.0 * scaleX / height;
|
||||
matrix[3] = 0.0;
|
||||
matrix[4] = translateX + scaleX;
|
||||
matrix[5] = translateY + scaleY;
|
||||
return;
|
||||
|
||||
case LG_ROTATE_180:
|
||||
matrix[0] = -2.0 * scaleX / width;
|
||||
matrix[1] = 0.0;
|
||||
matrix[2] = 0.0;
|
||||
matrix[3] = 2.0 * scaleY / height;
|
||||
matrix[4] = translateX + scaleX;
|
||||
matrix[5] = translateY - scaleY;
|
||||
return;
|
||||
|
||||
case LG_ROTATE_270:
|
||||
matrix[0] = 0.0;
|
||||
matrix[1] = 2.0 * scaleY / width;
|
||||
matrix[2] = 2.0 * scaleX / height;
|
||||
matrix[3] = 0.0;
|
||||
matrix[4] = translateX - scaleX;
|
||||
matrix[5] = translateY - scaleY;
|
||||
}
|
||||
}
|
||||
|
||||
void egl_desktopRectsMatrix(float matrix[6], int width, int height, float translateX,
|
||||
float translateY, float scaleX, float scaleY, LG_RendererRotate rotate)
|
||||
{
|
||||
double temp[6];
|
||||
desktopToGLSpace(temp, width, height, translateX, translateY, scaleX, scaleY, rotate);
|
||||
for (int i = 0; i < 6; ++i)
|
||||
matrix[i] = temp[i];
|
||||
}
|
||||
|
||||
void egl_desktopToScreenMatrix(double matrix[6], int frameWidth, int frameHeight,
|
||||
double translateX, double translateY, double scaleX, double scaleY, LG_RendererRotate rotate,
|
||||
double windowWidth, double windowHeight)
|
||||
{
|
||||
desktopToGLSpace(matrix, frameWidth, frameHeight, translateX, translateY, scaleX, scaleY, rotate);
|
||||
|
||||
double hw = windowWidth / 2;
|
||||
double hh = windowHeight / 2;
|
||||
matrix[0] *= hw;
|
||||
matrix[1] *= hh;
|
||||
matrix[2] *= hw;
|
||||
matrix[3] *= hh;
|
||||
matrix[4] = matrix[4] * hw + hw;
|
||||
matrix[5] = matrix[5] * hh + hh;
|
||||
}
|
||||
|
||||
inline static void matrixMultiply(const double matrix[6], double * nx, double * ny, double x, double y)
|
||||
{
|
||||
*nx = matrix[0] * x + matrix[2] * y + matrix[4];
|
||||
*ny = matrix[1] * x + matrix[3] * y + matrix[5];
|
||||
}
|
||||
|
||||
struct Rect egl_desktopToScreen(const double matrix[6], const struct FrameDamageRect * rect)
|
||||
{
|
||||
double x1, y1, x2, y2;
|
||||
matrixMultiply(matrix, &x1, &y1, rect->x, rect->y);
|
||||
matrixMultiply(matrix, &x2, &y2, rect->x + rect->width, rect->y + rect->height);
|
||||
|
||||
int x3 = min(x1, x2);
|
||||
int y3 = min(y1, y2);
|
||||
return (struct Rect) {
|
||||
.x = x3,
|
||||
.y = y3,
|
||||
.w = ceil(max(x1, x2)) - x3,
|
||||
.h = ceil(max(y1, y2)) - y3,
|
||||
};
|
||||
}
|
||||
|
||||
void egl_screenToDesktopMatrix(double matrix[6], int frameWidth, int frameHeight,
|
||||
double translateX, double translateY, double scaleX, double scaleY, LG_RendererRotate rotate,
|
||||
double windowWidth, double windowHeight)
|
||||
{
|
||||
double inverted[6] = {0};
|
||||
egl_desktopToScreenMatrix(inverted, frameWidth, frameHeight, translateX, translateY,
|
||||
scaleX, scaleY, rotate, windowWidth, windowHeight);
|
||||
|
||||
double det = inverted[0] * inverted[3] - inverted[1] * inverted[2];
|
||||
matrix[0] = inverted[3] / det;
|
||||
matrix[1] = -inverted[1] / det;
|
||||
matrix[2] = -inverted[2] / det;
|
||||
matrix[3] = inverted[0] / det;
|
||||
matrix[4] = (inverted[2] * inverted[5] - inverted[3] * inverted[4]) / det;
|
||||
matrix[5] = (inverted[1] * inverted[4] - inverted[0] * inverted[5]) / det;
|
||||
}
|
||||
|
||||
bool egl_screenToDesktop(struct FrameDamageRect * output, const double matrix[6],
|
||||
const struct Rect * rect, int width, int height)
|
||||
{
|
||||
double x1, y1, x2, y2;
|
||||
matrixMultiply(matrix, &x1, &y1, rect->x - 1, rect->y - 1);
|
||||
matrixMultiply(matrix, &x2, &y2, rect->x + rect->w + 1, rect->y + rect->h + 1);
|
||||
|
||||
int x3 = min(x1, x2);
|
||||
int y3 = min(y1, y2);
|
||||
int x4 = ceil(max(x1, x2));
|
||||
int y4 = ceil(max(y1, y2));
|
||||
|
||||
if (x4 < 0 || y4 < 0 || x3 >= width || y3 >= height)
|
||||
return false;
|
||||
|
||||
output->x = max(x3, 0);
|
||||
output->y = max(y3, 0);
|
||||
output->width = min(width, x4) - output->x;
|
||||
output->height = min(height, y4) - output->y;
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_desktopRectsRender(EGL_DesktopRects * rects)
|
||||
{
|
||||
if (!rects->count)
|
||||
return;
|
||||
|
||||
glBindVertexArray(rects->vao);
|
||||
glDrawElements(GL_TRIANGLES, 6 * rects->count, GL_UNSIGNED_SHORT, NULL);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
53
client/renderers/EGL/desktop_rects.h
Normal file
53
client/renderers/EGL/desktop_rects.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "common/types.h"
|
||||
#include "interface/renderer.h"
|
||||
|
||||
struct DamageRects
|
||||
{
|
||||
int count;
|
||||
FrameDamageRect rects[];
|
||||
};
|
||||
|
||||
typedef struct EGL_DesktopRects EGL_DesktopRects;
|
||||
|
||||
bool egl_desktopRectsInit(EGL_DesktopRects ** rects, int maxCount);
|
||||
void egl_desktopRectsFree(EGL_DesktopRects ** rects);
|
||||
|
||||
void egl_desktopRectsMatrix(float matrix[6], int width, int height, float translateX,
|
||||
float translateY, float scaleX, float scaleY, LG_RendererRotate rotate);
|
||||
void egl_desktopToScreenMatrix(double matrix[6], int frameWidth, int frameHeight,
|
||||
double translateX, double translateY, double scaleX, double scaleY, LG_RendererRotate rotate,
|
||||
double windowWidth, double windowHeight);
|
||||
struct Rect egl_desktopToScreen(const double matrix[6], const struct FrameDamageRect * rect);
|
||||
|
||||
void egl_screenToDesktopMatrix(double matrix[6], int frameWidth, int frameHeight,
|
||||
double translateX, double translateY, double scaleX, double scaleY, LG_RendererRotate rotate,
|
||||
double windowWidth, double windowHeight);
|
||||
bool egl_screenToDesktop(struct FrameDamageRect * output, const double matrix[6],
|
||||
const struct Rect * rect, int width, int height);
|
||||
|
||||
void egl_desktopRectsUpdate(EGL_DesktopRects * rects, const struct DamageRects * data,
|
||||
int width, int height);
|
||||
void egl_desktopRectsRender(EGL_DesktopRects * rects);
|
||||
@@ -1,29 +1,31 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "draw.h"
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
void egl_draw_torus(EGL_Model * model, unsigned int pts, float x, float y, float inner, float outer)
|
||||
void egl_drawTorus(EGL_Model * model, unsigned int pts, float x, float y,
|
||||
float inner, float outer)
|
||||
{
|
||||
GLfloat * v = (GLfloat *)malloc(sizeof(GLfloat) * (pts + 1) * 6);
|
||||
GLfloat * v = malloc(sizeof(*v) * (pts + 1) * 6);
|
||||
GLfloat * dst = v;
|
||||
|
||||
for(unsigned int i = 0; i <= pts; ++i)
|
||||
@@ -39,13 +41,14 @@ void egl_draw_torus(EGL_Model * model, unsigned int pts, float x, float y, float
|
||||
*dst = 0.0f; ++dst;
|
||||
}
|
||||
|
||||
egl_model_add_verticies(model, v, NULL, (pts + 1) * 2);
|
||||
egl_modelAddVerts(model, v, NULL, (pts + 1) * 2);
|
||||
free(v);
|
||||
}
|
||||
|
||||
void egl_draw_torus_arc(EGL_Model * model, unsigned int pts, float x, float y, float inner, float outer, float s, float e)
|
||||
void egl_drawTorusArc(EGL_Model * model, unsigned int pts, float x, float y,
|
||||
float inner, float outer, float s, float e)
|
||||
{
|
||||
GLfloat * v = (GLfloat *)malloc(sizeof(GLfloat) * (pts + 1) * 6);
|
||||
GLfloat * v = malloc(sizeof(*v) * (pts + 1) * 6);
|
||||
GLfloat * dst = v;
|
||||
|
||||
for(unsigned int i = 0; i <= pts; ++i)
|
||||
@@ -61,6 +64,6 @@ void egl_draw_torus_arc(EGL_Model * model, unsigned int pts, float x, float y, f
|
||||
*dst = 0.0f; ++dst;
|
||||
}
|
||||
|
||||
egl_model_add_verticies(model, v, NULL, (pts + 1) * 2);
|
||||
egl_modelAddVerts(model, v, NULL, (pts + 1) * 2);
|
||||
free(v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "model.h"
|
||||
|
||||
void egl_draw_torus (EGL_Model * model, unsigned int pts, float x, float y, float inner, float outer);
|
||||
void egl_draw_torus_arc(EGL_Model * model, unsigned int pts, float x, float y, float inner, float outer, float s, float e);
|
||||
void egl_drawTorus(EGL_Model * model, unsigned int pts, float x, float y,
|
||||
float inner, float outer);
|
||||
|
||||
void egl_drawTorusArc(EGL_Model * model, unsigned int pts, float x, float y,
|
||||
float inner, float outer, float s, float e);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
24
client/renderers/EGL/egl.h
Normal file
24
client/renderers/EGL/egl.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef struct Inst EGL;
|
||||
void egl_resetViewport(EGL * egl);
|
||||
@@ -1,24 +1,25 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "egldebug.h"
|
||||
#include <GL/gl.h>
|
||||
#include <GLES3/gl3.h>
|
||||
#include <EGL/egl.h>
|
||||
|
||||
const char * egl_getErrorStr(void)
|
||||
@@ -43,3 +44,17 @@ const char * egl_getErrorStr(void)
|
||||
default : return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char * gl_getErrorStr(void)
|
||||
{
|
||||
switch (glGetError())
|
||||
{
|
||||
case GL_NO_ERROR : return "GL_NO_ERROR";
|
||||
case GL_INVALID_ENUM : return "GL_INVALID_ENUM";
|
||||
case GL_INVALID_VALUE : return "GL_INVALID_VALUE";
|
||||
case GL_INVALID_OPERATION : return "GL_INVALID_OPERATION";
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION";
|
||||
case GL_OUT_OF_MEMORY : return "GL_OUT_OF_MEMORY";
|
||||
default : return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,36 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
const char * egl_getErrorStr(void);
|
||||
const char * gl_getErrorStr(void);
|
||||
|
||||
#define DEBUG_EGL_WARN(fmt, ...) \
|
||||
DEBUG_WARN(fmt " (%s)", ##__VA_ARGS__, egl_getErrorStr())
|
||||
|
||||
#define DEBUG_EGL_ERROR(fmt, ...) \
|
||||
DEBUG_ERROR(fmt " (%s)", ##__VA_ARGS__, egl_getErrorStr())
|
||||
|
||||
#define DEBUG_GL_WARN(fmt, ...) \
|
||||
DEBUG_WARN(fmt " (%s)", ##__VA_ARGS__, gl_getErrorStr())
|
||||
|
||||
#define DEBUG_GL_ERROR(fmt, ...) \
|
||||
DEBUG_ERROR(fmt " (%s)", ##__VA_ARGS__, gl_getErrorStr())
|
||||
|
||||
72
client/renderers/EGL/egltypes.h
Normal file
72
client/renderers/EGL/egltypes.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
typedef enum EGL_TexType
|
||||
{
|
||||
EGL_TEXTYPE_BUFFER,
|
||||
EGL_TEXTYPE_FRAMEBUFFER,
|
||||
EGL_TEXTYPE_DMABUF
|
||||
}
|
||||
EGL_TexType;
|
||||
|
||||
typedef enum EGL_PixelFormat
|
||||
{
|
||||
EGL_PF_RGBA,
|
||||
EGL_PF_BGRA,
|
||||
EGL_PF_RGBA10,
|
||||
EGL_PF_RGBA16F
|
||||
}
|
||||
EGL_PixelFormat;
|
||||
|
||||
typedef enum EGL_TexStatus
|
||||
{
|
||||
EGL_TEX_STATUS_NOTREADY,
|
||||
EGL_TEX_STATUS_OK,
|
||||
EGL_TEX_STATUS_ERROR
|
||||
}
|
||||
EGL_TexStatus;
|
||||
|
||||
typedef struct EGL_TexSetup
|
||||
{
|
||||
/* the pixel format of the texture */
|
||||
EGL_PixelFormat pixFmt;
|
||||
|
||||
/* the width of the texture in pixels */
|
||||
size_t width;
|
||||
|
||||
/* the height of the texture in pixels */
|
||||
size_t height;
|
||||
|
||||
/* the stide of the texture in bytes */
|
||||
size_t stride;
|
||||
}
|
||||
EGL_TexSetup;
|
||||
|
||||
typedef enum EGL_FilterType
|
||||
{
|
||||
EGL_FILTER_TYPE_EFFECT,
|
||||
EGL_FILTER_TYPE_UPSCALE,
|
||||
EGL_FILTER_TYPE_DOWNSCALE
|
||||
}
|
||||
EGL_FilterType;
|
||||
49
client/renderers/EGL/ffx.c
Normal file
49
client/renderers/EGL/ffx.c
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "ffx.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#define A_CPU
|
||||
#define A_RESTRICT
|
||||
#define A_STATIC inline static
|
||||
#include "shader/ffx_a.h"
|
||||
#include "shader/ffx_cas.h"
|
||||
#include "shader/ffx_fsr1.h"
|
||||
|
||||
void ffxCasConst(uint32_t consts[8], float sharpness, float inputX, float inputY,
|
||||
float outputX, float outputY)
|
||||
{
|
||||
CasSetup(consts + 0, consts + 4, sharpness, inputX, inputY, outputX, outputY);
|
||||
}
|
||||
|
||||
void ffxFsrEasuConst(uint32_t consts[16], float viewportX, float viewportY,
|
||||
float inputX, float inputY, float outputX, float outputY)
|
||||
{
|
||||
FsrEasuCon(consts + 0, consts + 4, consts + 8, consts + 12, viewportX, viewportY,
|
||||
inputX, inputY, outputX, outputY);
|
||||
}
|
||||
|
||||
void ffxFsrRcasConst(uint32_t consts[4], float sharpness)
|
||||
{
|
||||
FsrRcasCon(consts, sharpness);
|
||||
}
|
||||
28
client/renderers/EGL/ffx.h
Normal file
28
client/renderers/EGL/ffx.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
void ffxCasConst(uint32_t consts[8], float sharpness, float inputX, float inputY,
|
||||
float outputX, float outputY);
|
||||
void ffxFsrEasuConst(uint32_t consts[16], float viewportX, float viewportY,
|
||||
float inputX, float inputY, float outputX, float outputY);
|
||||
void ffxFsrRcasConst(uint32_t consts[4], float sharpness);
|
||||
30
client/renderers/EGL/filter.c
Normal file
30
client/renderers/EGL/filter.c
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "filter.h"
|
||||
|
||||
void egl_filterRectsRender(EGL_Shader * shader, EGL_FilterRects * rects)
|
||||
{
|
||||
glUniformMatrix3x2fv(egl_shaderGetUniform(shader, "transform"),
|
||||
1, GL_FALSE, rects->matrix);
|
||||
glUniform2f(egl_shaderGetUniform(shader, "desktopSize"),
|
||||
rects->width, rects->height);
|
||||
egl_desktopRectsRender(rects->rects);
|
||||
}
|
||||
171
client/renderers/EGL/filter.h
Normal file
171
client/renderers/EGL/filter.h
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util.h"
|
||||
#include "shader.h"
|
||||
#include "egltypes.h"
|
||||
#include "desktop_rects.h"
|
||||
#include "model.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
typedef struct EGL_FilterRects
|
||||
{
|
||||
EGL_DesktopRects * rects;
|
||||
GLfloat * matrix;
|
||||
int width, height;
|
||||
}
|
||||
EGL_FilterRects;
|
||||
|
||||
typedef struct EGL_Filter EGL_Filter;
|
||||
|
||||
typedef struct EGL_FilterOps
|
||||
{
|
||||
/* the identifier of this filter */
|
||||
const char * id;
|
||||
|
||||
/* the friendly name of this filter */
|
||||
const char * name;
|
||||
|
||||
/* the type of this filter */
|
||||
EGL_FilterType type;
|
||||
|
||||
/* early initialization for registration of options */
|
||||
void (*earlyInit)(void);
|
||||
|
||||
/* initialize the filter */
|
||||
bool (*init)(EGL_Filter ** filter);
|
||||
|
||||
/* free the filter */
|
||||
void (*free)(EGL_Filter * filter);
|
||||
|
||||
/* render any imgui config
|
||||
* Returns true if a redraw is required */
|
||||
bool (*imguiConfig)(EGL_Filter * filter);
|
||||
|
||||
/* writes filter state to options */
|
||||
void (*saveState)(EGL_Filter * filter);
|
||||
|
||||
/* reads filter state from options */
|
||||
void (*loadState)(EGL_Filter * filter);
|
||||
|
||||
/* set the input format of the filter */
|
||||
bool (*setup)(EGL_Filter * filter, enum EGL_PixelFormat pixFmt,
|
||||
unsigned int width, unsigned int height);
|
||||
|
||||
/* set the output resolution hint for the filter
|
||||
* this is optional and only a hint */
|
||||
void (*setOutputResHint)(EGL_Filter * filter,
|
||||
unsigned int x, unsigned int y);
|
||||
|
||||
/* returns the output resolution of the filter */
|
||||
void (*getOutputRes)(EGL_Filter * filter,
|
||||
unsigned int *x, unsigned int *y);
|
||||
|
||||
/* prepare the shader for use
|
||||
* A filter can return false to bypass it */
|
||||
bool (*prepare)(EGL_Filter * filter);
|
||||
|
||||
/* runs the filter on the provided texture
|
||||
* returns the processed texture as the output */
|
||||
GLuint (*run)(EGL_Filter * filter, EGL_FilterRects * rects,
|
||||
GLuint texture);
|
||||
|
||||
/* called when the filter output is no loger needed so it can release memory
|
||||
* this is optional */
|
||||
void (*release)(EGL_Filter * filter);
|
||||
}
|
||||
EGL_FilterOps;
|
||||
|
||||
typedef struct EGL_Filter
|
||||
{
|
||||
EGL_FilterOps ops;
|
||||
}
|
||||
EGL_Filter;
|
||||
|
||||
static inline bool egl_filterInit(const EGL_FilterOps * ops, EGL_Filter ** filter)
|
||||
{
|
||||
if (!ops->init(filter))
|
||||
return false;
|
||||
|
||||
memcpy(&(*filter)->ops, ops, sizeof(*ops));
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void egl_filterFree(EGL_Filter ** filter)
|
||||
{
|
||||
(*filter)->ops.free(*filter);
|
||||
*filter = NULL;
|
||||
}
|
||||
|
||||
static inline bool egl_filterImguiConfig(EGL_Filter * filter)
|
||||
{
|
||||
return filter->ops.imguiConfig(filter);
|
||||
}
|
||||
|
||||
static inline void egl_filterSaveState(EGL_Filter * filter)
|
||||
{
|
||||
filter->ops.saveState(filter);
|
||||
}
|
||||
|
||||
static inline void egl_filterLoadState(EGL_Filter * filter)
|
||||
{
|
||||
filter->ops.loadState(filter);
|
||||
}
|
||||
|
||||
static inline bool egl_filterSetup(EGL_Filter * filter,
|
||||
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
|
||||
{
|
||||
return filter->ops.setup(filter, pixFmt, width, height);
|
||||
}
|
||||
|
||||
static inline void egl_filterSetOutputResHint(EGL_Filter * filter,
|
||||
unsigned int x, unsigned int y)
|
||||
{
|
||||
if (filter->ops.setOutputResHint)
|
||||
filter->ops.setOutputResHint(filter, x, y);
|
||||
}
|
||||
|
||||
static inline void egl_filterGetOutputRes(EGL_Filter * filter,
|
||||
unsigned int *x, unsigned int *y)
|
||||
{
|
||||
return filter->ops.getOutputRes(filter, x, y);
|
||||
}
|
||||
|
||||
static inline bool egl_filterPrepare(EGL_Filter * filter)
|
||||
{
|
||||
return filter->ops.prepare(filter);
|
||||
}
|
||||
|
||||
static inline GLuint egl_filterRun(EGL_Filter * filter,
|
||||
EGL_FilterRects * rects, GLuint texture)
|
||||
{
|
||||
return filter->ops.run(filter, rects, texture);
|
||||
}
|
||||
|
||||
static inline void egl_filterRelease(EGL_Filter * filter)
|
||||
{
|
||||
if (filter->ops.release)
|
||||
filter->ops.release(filter);
|
||||
}
|
||||
|
||||
void egl_filterRectsRender(EGL_Shader * shader, EGL_FilterRects * rects);
|
||||
442
client/renderers/EGL/filter_downscale.c
Normal file
442
client/renderers/EGL/filter_downscale.c
Normal file
@@ -0,0 +1,442 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "filter.h"
|
||||
#include "framebuffer.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/option.h"
|
||||
#include "cimgui.h"
|
||||
|
||||
#include "basic.vert.h"
|
||||
#include "downscale.frag.h"
|
||||
#include "downscale_lanczos2.frag.h"
|
||||
#include "downscale_linear.frag.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DOWNSCALE_NEAREST = 0,
|
||||
DOWNSCALE_LINEAR,
|
||||
DOWNSCALE_LANCZOS2,
|
||||
}
|
||||
DownscaleFilter;
|
||||
|
||||
#define DOWNSCALE_COUNT (DOWNSCALE_LANCZOS2 + 1)
|
||||
|
||||
const char *filterNames[DOWNSCALE_COUNT] = {
|
||||
"Nearest pixel",
|
||||
"Linear",
|
||||
"Lanczos",
|
||||
};
|
||||
|
||||
typedef struct EGL_FilterDownscale
|
||||
{
|
||||
EGL_Filter base;
|
||||
|
||||
bool enable;
|
||||
EGL_Shader * nearest;
|
||||
EGL_Uniform uNearest;
|
||||
EGL_Shader * linear;
|
||||
EGL_Shader * lanczos2;
|
||||
|
||||
DownscaleFilter filter;
|
||||
enum EGL_PixelFormat pixFmt;
|
||||
unsigned int width, height;
|
||||
float pixelSize;
|
||||
float vOffset, hOffset;
|
||||
bool prepared;
|
||||
|
||||
EGL_Framebuffer * fb;
|
||||
GLuint sampler[2];
|
||||
}
|
||||
EGL_FilterDownscale;
|
||||
|
||||
static void egl_filterDownscaleEarlyInit(void)
|
||||
{
|
||||
static struct Option options[] =
|
||||
{
|
||||
{
|
||||
.module = "eglFilter",
|
||||
.name = "downscale",
|
||||
.description = "Enable downscaling",
|
||||
.preset = true,
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false
|
||||
},
|
||||
{
|
||||
.module = "eglFilter",
|
||||
.name = "downscalePixelSize",
|
||||
.description = "Downscale filter pixel size",
|
||||
.preset = true,
|
||||
.type = OPTION_TYPE_FLOAT,
|
||||
.value.x_float = 2.0f
|
||||
},
|
||||
{
|
||||
.module = "eglFilter",
|
||||
.name = "downscaleHOffset",
|
||||
.description = "Downscale filter horizontal offset",
|
||||
.preset = true,
|
||||
.type = OPTION_TYPE_FLOAT,
|
||||
.value.x_float = 0.0f
|
||||
},
|
||||
{
|
||||
.module = "eglFilter",
|
||||
.name = "downscaleVOffset",
|
||||
.description = "Downscale filter vertical offset",
|
||||
.preset = true,
|
||||
.type = OPTION_TYPE_FLOAT,
|
||||
.value.x_float = 0.0f
|
||||
},
|
||||
{
|
||||
.module = "eglFilter",
|
||||
.name = "downscaleFilter",
|
||||
.description = "Downscale filter type",
|
||||
.preset = true,
|
||||
.type = OPTION_TYPE_INT,
|
||||
.value.x_int = 0
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
option_register(options);
|
||||
}
|
||||
|
||||
static void egl_filterDownscaleSaveState(EGL_Filter * filter)
|
||||
{
|
||||
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
|
||||
|
||||
option_set_bool ("eglFilter", "downscale", this->enable);
|
||||
option_set_float("eglFilter", "downscalePixelSize", this->pixelSize);
|
||||
option_set_float("eglFilter", "downscaleHOffset", this->vOffset);
|
||||
option_set_float("eglFilter", "downscaleVOffset", this->hOffset);
|
||||
option_set_int ("eglFilter", "downscaleFilter", this->filter);
|
||||
}
|
||||
|
||||
static void egl_filterDownscaleLoadState(EGL_Filter * filter)
|
||||
{
|
||||
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
|
||||
|
||||
this->enable = option_get_bool ("eglFilter", "downscale");
|
||||
this->pixelSize = option_get_float("eglFilter", "downscalePixelSize");
|
||||
this->vOffset = option_get_float("eglFilter", "downscaleHOffset");
|
||||
this->hOffset = option_get_float("eglFilter", "downscaleVOffset");
|
||||
this->filter = option_get_int ("eglFilter", "downscaleFilter");
|
||||
|
||||
if (this->filter < 0 || this->filter >= DOWNSCALE_COUNT)
|
||||
this->filter = 0;
|
||||
|
||||
this->prepared = false;
|
||||
}
|
||||
|
||||
static bool egl_filterDownscaleInit(EGL_Filter ** filter)
|
||||
{
|
||||
EGL_FilterDownscale * this = calloc(1, sizeof(*this));
|
||||
if (!this)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate ram");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shaderInit(&this->nearest))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the shader");
|
||||
goto error_this;
|
||||
}
|
||||
|
||||
if (!egl_shaderCompile(this->nearest,
|
||||
b_shader_basic_vert , b_shader_basic_vert_size,
|
||||
b_shader_downscale_frag, b_shader_downscale_frag_size)
|
||||
)
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the shader");
|
||||
goto error_shader;
|
||||
}
|
||||
|
||||
if (!egl_shaderInit(&this->linear))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the shader");
|
||||
goto error_this;
|
||||
}
|
||||
|
||||
if (!egl_shaderCompile(this->linear,
|
||||
b_shader_basic_vert, b_shader_basic_vert_size,
|
||||
b_shader_downscale_linear_frag, b_shader_downscale_linear_frag_size)
|
||||
)
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the shader");
|
||||
goto error_shader;
|
||||
}
|
||||
|
||||
if (!egl_shaderInit(&this->lanczos2))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the shader");
|
||||
goto error_this;
|
||||
}
|
||||
|
||||
if (!egl_shaderCompile(this->lanczos2,
|
||||
b_shader_basic_vert, b_shader_basic_vert_size,
|
||||
b_shader_downscale_lanczos2_frag, b_shader_downscale_lanczos2_frag_size)
|
||||
)
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the shader");
|
||||
goto error_shader;
|
||||
}
|
||||
|
||||
this->uNearest.type = EGL_UNIFORM_TYPE_3F;
|
||||
this->uNearest.location =
|
||||
egl_shaderGetUniform(this->nearest, "uConfig");
|
||||
|
||||
if (!egl_framebufferInit(&this->fb))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the framebuffer");
|
||||
goto error_shader;
|
||||
}
|
||||
|
||||
glGenSamplers(ARRAY_LENGTH(this->sampler), this->sampler);
|
||||
glSamplerParameteri(this->sampler[0], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glSamplerParameteri(this->sampler[0], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glSamplerParameteri(this->sampler[0], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(this->sampler[0], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(this->sampler[1], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(this->sampler[1], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(this->sampler[1], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(this->sampler[1], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
|
||||
|
||||
egl_filterDownscaleLoadState(&this->base);
|
||||
|
||||
*filter = &this->base;
|
||||
return true;
|
||||
|
||||
error_shader:
|
||||
egl_shaderFree(&this->nearest);
|
||||
egl_shaderFree(&this->linear);
|
||||
egl_shaderFree(&this->lanczos2);
|
||||
|
||||
error_this:
|
||||
free(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void egl_filterDownscaleFree(EGL_Filter * filter)
|
||||
{
|
||||
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
|
||||
|
||||
egl_shaderFree(&this->nearest);
|
||||
egl_shaderFree(&this->linear);
|
||||
egl_shaderFree(&this->lanczos2);
|
||||
egl_framebufferFree(&this->fb);
|
||||
glDeleteSamplers(ARRAY_LENGTH(this->sampler), this->sampler);
|
||||
free(this);
|
||||
}
|
||||
|
||||
static bool egl_filterDownscaleImguiConfig(EGL_Filter * filter)
|
||||
{
|
||||
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
|
||||
|
||||
bool redraw = false;
|
||||
bool enable = this->enable;
|
||||
|
||||
igCheckbox("Enable", &enable);
|
||||
if (enable != this->enable)
|
||||
{
|
||||
this->enable = enable;
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
if (igBeginCombo("Filter", filterNames[this->filter], 0))
|
||||
{
|
||||
for (int i = 0; i < DOWNSCALE_COUNT; ++i)
|
||||
{
|
||||
bool selected = i == this->filter;
|
||||
if (igSelectableBool(filterNames[i], selected, 0, (ImVec2) { 0.0f, 0.0f }))
|
||||
{
|
||||
redraw = true;
|
||||
this->filter = i;
|
||||
}
|
||||
if (selected)
|
||||
igSetItemDefaultFocus();
|
||||
}
|
||||
igEndCombo();
|
||||
}
|
||||
|
||||
float pixelSize = this->pixelSize;
|
||||
igInputFloat("Pixel size", &pixelSize, 0.1f, 1.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal);
|
||||
pixelSize = util_clamp(pixelSize, 1.0f, 10.0f);
|
||||
igSliderFloat("##pixelsize", &pixelSize, 1.0f, 10.0f, "%.2f",
|
||||
ImGuiSliderFlags_Logarithmic | ImGuiSliderFlags_NoInput);
|
||||
|
||||
igText("Resolution: %dx%d", this->width, this->height);
|
||||
|
||||
if (pixelSize != this->pixelSize)
|
||||
{
|
||||
this->pixelSize = pixelSize;
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
switch (this->filter)
|
||||
{
|
||||
case DOWNSCALE_NEAREST:
|
||||
{
|
||||
float vOffset = this->vOffset;
|
||||
igSliderFloat("V-Offset", &vOffset, -2, 2, NULL, 0);
|
||||
if (vOffset != this->vOffset)
|
||||
{
|
||||
this->vOffset = vOffset;
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
float hOffset = this->hOffset;
|
||||
igSliderFloat("H-Offset", &hOffset, -2, 2, NULL, 0);
|
||||
if (hOffset != this->hOffset)
|
||||
{
|
||||
this->hOffset = hOffset;
|
||||
redraw = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (redraw)
|
||||
this->prepared = false;
|
||||
|
||||
return redraw;
|
||||
}
|
||||
|
||||
static bool egl_filterDownscaleSetup(EGL_Filter * filter,
|
||||
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
|
||||
{
|
||||
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
|
||||
|
||||
width = (float)width / this->pixelSize;
|
||||
height = (float)height / this->pixelSize;
|
||||
|
||||
if (!this->enable)
|
||||
return false;
|
||||
|
||||
if (this->prepared &&
|
||||
pixFmt == this->pixFmt &&
|
||||
this->width == width &&
|
||||
this->height == height)
|
||||
return this->pixelSize > 1.0f;
|
||||
|
||||
if (!egl_framebufferSetup(this->fb, pixFmt, width, height))
|
||||
return false;
|
||||
|
||||
this->pixFmt = pixFmt;
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
this->prepared = false;
|
||||
|
||||
return this->pixelSize > 1.0f;
|
||||
}
|
||||
|
||||
static void egl_filterDownscaleGetOutputRes(EGL_Filter * filter,
|
||||
unsigned int *width, unsigned int *height)
|
||||
{
|
||||
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
|
||||
*width = this->width;
|
||||
*height = this->height;
|
||||
}
|
||||
|
||||
static bool egl_filterDownscalePrepare(EGL_Filter * filter)
|
||||
{
|
||||
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
|
||||
|
||||
if (this->prepared)
|
||||
return true;
|
||||
|
||||
switch (this->filter)
|
||||
{
|
||||
case DOWNSCALE_NEAREST:
|
||||
this->uNearest.f[0] = this->pixelSize;
|
||||
this->uNearest.f[1] = this->vOffset;
|
||||
this->uNearest.f[2] = this->hOffset;
|
||||
egl_shaderSetUniforms(this->nearest, &this->uNearest, 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this->prepared = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static GLuint egl_filterDownscaleRun(EGL_Filter * filter,
|
||||
EGL_FilterRects * rects, GLuint texture)
|
||||
{
|
||||
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
|
||||
|
||||
egl_framebufferBind(this->fb);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
EGL_Shader * shader;
|
||||
|
||||
switch (this->filter)
|
||||
{
|
||||
case DOWNSCALE_NEAREST:
|
||||
glBindSampler(0, this->sampler[0]);
|
||||
shader = this->nearest;
|
||||
break;
|
||||
|
||||
case DOWNSCALE_LINEAR:
|
||||
glBindSampler(0, this->sampler[1]);
|
||||
shader = this->linear;
|
||||
break;
|
||||
|
||||
case DOWNSCALE_LANCZOS2:
|
||||
glBindSampler(0, this->sampler[0]);
|
||||
shader = this->lanczos2;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_UNREACHABLE();
|
||||
}
|
||||
|
||||
egl_shaderUse(shader);
|
||||
egl_filterRectsRender(shader, rects);
|
||||
|
||||
return egl_framebufferGetTexture(this->fb);
|
||||
}
|
||||
|
||||
EGL_FilterOps egl_filterDownscaleOps =
|
||||
{
|
||||
.id = "downscale",
|
||||
.name = "Downscaler",
|
||||
.type = EGL_FILTER_TYPE_DOWNSCALE,
|
||||
.earlyInit = egl_filterDownscaleEarlyInit,
|
||||
.init = egl_filterDownscaleInit,
|
||||
.free = egl_filterDownscaleFree,
|
||||
.imguiConfig = egl_filterDownscaleImguiConfig,
|
||||
.saveState = egl_filterDownscaleSaveState,
|
||||
.loadState = egl_filterDownscaleLoadState,
|
||||
.setup = egl_filterDownscaleSetup,
|
||||
.getOutputRes = egl_filterDownscaleGetOutputRes,
|
||||
.prepare = egl_filterDownscalePrepare,
|
||||
.run = egl_filterDownscaleRun
|
||||
};
|
||||
297
client/renderers/EGL/filter_ffx_cas.c
Normal file
297
client/renderers/EGL/filter_ffx_cas.c
Normal file
@@ -0,0 +1,297 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "filter.h"
|
||||
#include "framebuffer.h"
|
||||
|
||||
#include "common/countedbuffer.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/option.h"
|
||||
#include "cimgui.h"
|
||||
#include "ffx.h"
|
||||
|
||||
#include "basic.vert.h"
|
||||
#include "ffx_cas.frag.h"
|
||||
|
||||
typedef struct EGL_FilterFFXCAS
|
||||
{
|
||||
EGL_Filter base;
|
||||
|
||||
EGL_Shader * shader;
|
||||
bool enable;
|
||||
|
||||
enum EGL_PixelFormat pixFmt;
|
||||
unsigned int width, height;
|
||||
float sharpness;
|
||||
CountedBuffer * consts;
|
||||
bool prepared;
|
||||
|
||||
EGL_Framebuffer * fb;
|
||||
GLuint sampler;
|
||||
}
|
||||
EGL_FilterFFXCAS;
|
||||
|
||||
static void egl_filterFFXCASEarlyInit(void)
|
||||
{
|
||||
static struct Option options[] =
|
||||
{
|
||||
{
|
||||
.module = "eglFilter",
|
||||
.name = "ffxCAS",
|
||||
.description = "AMD FidelityFX CAS",
|
||||
.preset = true,
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false
|
||||
},
|
||||
{
|
||||
.module = "eglFilter",
|
||||
.name = "ffxCASSharpness",
|
||||
.description = "AMD FidelityFX CAS Sharpness",
|
||||
.preset = true,
|
||||
.type = OPTION_TYPE_FLOAT,
|
||||
.value.x_float = 0.0f
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
option_register(options);
|
||||
}
|
||||
|
||||
static void casUpdateConsts(EGL_FilterFFXCAS * this)
|
||||
{
|
||||
ffxCasConst((uint32_t *) this->consts->data, this->sharpness,
|
||||
this->width, this->height,
|
||||
this->width, this->height);
|
||||
}
|
||||
|
||||
static void egl_filterFFXCASSaveState(EGL_Filter * filter)
|
||||
{
|
||||
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
|
||||
|
||||
option_set_bool ("eglFilter", "ffxCAS", this->enable);
|
||||
option_set_float("eglFilter", "ffxCASSharpness", this->sharpness);
|
||||
}
|
||||
|
||||
static void egl_filterFFXCASLoadState(EGL_Filter * filter)
|
||||
{
|
||||
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
|
||||
|
||||
this->enable = option_get_bool ("eglFilter", "ffxCAS");
|
||||
this->sharpness = option_get_float("eglFilter", "ffxCASSharpness");
|
||||
}
|
||||
|
||||
static bool egl_filterFFXCASInit(EGL_Filter ** filter)
|
||||
{
|
||||
EGL_FilterFFXCAS * this = calloc(1, sizeof(*this));
|
||||
if (!this)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate ram");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shaderInit(&this->shader))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the shader");
|
||||
goto error_this;
|
||||
}
|
||||
|
||||
if (!egl_shaderCompile(this->shader,
|
||||
b_shader_basic_vert , b_shader_basic_vert_size,
|
||||
b_shader_ffx_cas_frag, b_shader_ffx_cas_frag_size)
|
||||
)
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the shader");
|
||||
goto error_shader;
|
||||
}
|
||||
|
||||
this->consts = countedBufferNew(8 * sizeof(GLuint));
|
||||
if (!this->consts)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate consts buffer");
|
||||
goto error_shader;
|
||||
}
|
||||
|
||||
egl_shaderSetUniforms(this->shader, &(EGL_Uniform) {
|
||||
.type = EGL_UNIFORM_TYPE_4UIV,
|
||||
.location = egl_shaderGetUniform(this->shader, "uConsts"),
|
||||
.v = this->consts,
|
||||
}, 1);
|
||||
|
||||
egl_filterFFXCASLoadState(&this->base);
|
||||
|
||||
if (!egl_framebufferInit(&this->fb))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the framebuffer");
|
||||
goto error_consts;
|
||||
}
|
||||
|
||||
glGenSamplers(1, &this->sampler);
|
||||
glSamplerParameteri(this->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(this->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
|
||||
|
||||
*filter = &this->base;
|
||||
return true;
|
||||
|
||||
error_consts:
|
||||
countedBufferRelease(&this->consts);
|
||||
|
||||
error_shader:
|
||||
egl_shaderFree(&this->shader);
|
||||
|
||||
error_this:
|
||||
free(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void egl_filterFFXCASFree(EGL_Filter * filter)
|
||||
{
|
||||
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
|
||||
|
||||
egl_shaderFree(&this->shader);
|
||||
countedBufferRelease(&this->consts);
|
||||
egl_framebufferFree(&this->fb);
|
||||
glDeleteSamplers(1, &this->sampler);
|
||||
free(this);
|
||||
}
|
||||
|
||||
static bool egl_filterFFXCASImguiConfig(EGL_Filter * filter)
|
||||
{
|
||||
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
|
||||
|
||||
bool redraw = false;
|
||||
bool cas = this->enable;
|
||||
float casSharpness = this->sharpness;
|
||||
|
||||
igCheckbox("Enabled", &cas);
|
||||
if (cas != this->enable)
|
||||
{
|
||||
this->enable = cas;
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
igText("Sharpness:");
|
||||
igSameLine(0.0f, -1.0f);
|
||||
igPushItemWidth(igGetWindowWidth() - igGetCursorPosX() -
|
||||
igGetStyle()->WindowPadding.x);
|
||||
|
||||
igSliderFloat("##casSharpness", &casSharpness, 0.0f, 1.0f, NULL, 0);
|
||||
casSharpness = util_clamp(casSharpness, 0.0f, 1.0f);
|
||||
if (igIsItemHovered(ImGuiHoveredFlags_None))
|
||||
igSetTooltip("Ctrl+Click to enter a value");
|
||||
igPopItemWidth();
|
||||
|
||||
if (casSharpness != this->sharpness)
|
||||
{
|
||||
// enable CAS if the sharpness was changed
|
||||
if (!cas)
|
||||
{
|
||||
cas = true;
|
||||
this->enable = true;
|
||||
}
|
||||
|
||||
this->sharpness = casSharpness;
|
||||
casUpdateConsts(this);
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
if (redraw)
|
||||
this->prepared = false;
|
||||
|
||||
return redraw;
|
||||
}
|
||||
|
||||
static bool egl_filterFFXCASSetup(EGL_Filter * filter,
|
||||
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
|
||||
{
|
||||
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
|
||||
|
||||
if (!this->enable)
|
||||
return false;
|
||||
|
||||
if (pixFmt == this->pixFmt && this->width == width && this->height == height)
|
||||
return true;
|
||||
|
||||
if (!egl_framebufferSetup(this->fb, pixFmt, width, height))
|
||||
return false;
|
||||
|
||||
this->pixFmt = pixFmt;
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
this->prepared = false;
|
||||
casUpdateConsts(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void egl_filterFFXCASGetOutputRes(EGL_Filter * filter,
|
||||
unsigned int *width, unsigned int *height)
|
||||
{
|
||||
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
|
||||
*width = this->width;
|
||||
*height = this->height;
|
||||
}
|
||||
|
||||
static bool egl_filterFFXCASPrepare(EGL_Filter * filter)
|
||||
{
|
||||
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
|
||||
|
||||
if (this->prepared)
|
||||
return true;
|
||||
|
||||
this->prepared = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static GLuint egl_filterFFXCASRun(EGL_Filter * filter,
|
||||
EGL_FilterRects * rects, GLuint texture)
|
||||
{
|
||||
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
|
||||
|
||||
egl_framebufferBind(this->fb);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glBindSampler(0, this->sampler);
|
||||
|
||||
egl_shaderUse(this->shader);
|
||||
egl_filterRectsRender(this->shader, rects);
|
||||
|
||||
return egl_framebufferGetTexture(this->fb);
|
||||
}
|
||||
|
||||
EGL_FilterOps egl_filterFFXCASOps =
|
||||
{
|
||||
.id = "ffxCAS",
|
||||
.name = "AMD FidelityFX CAS",
|
||||
.type = EGL_FILTER_TYPE_EFFECT,
|
||||
.earlyInit = egl_filterFFXCASEarlyInit,
|
||||
.init = egl_filterFFXCASInit,
|
||||
.free = egl_filterFFXCASFree,
|
||||
.imguiConfig = egl_filterFFXCASImguiConfig,
|
||||
.saveState = egl_filterFFXCASSaveState,
|
||||
.loadState = egl_filterFFXCASLoadState,
|
||||
.setup = egl_filterFFXCASSetup,
|
||||
.getOutputRes = egl_filterFFXCASGetOutputRes,
|
||||
.prepare = egl_filterFFXCASPrepare,
|
||||
.run = egl_filterFFXCASRun
|
||||
};
|
||||
440
client/renderers/EGL/filter_ffx_fsr1.c
Normal file
440
client/renderers/EGL/filter_ffx_fsr1.c
Normal file
@@ -0,0 +1,440 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "filter.h"
|
||||
#include "framebuffer.h"
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/countedbuffer.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/option.h"
|
||||
#include "cimgui.h"
|
||||
#include "ffx.h"
|
||||
|
||||
#include "basic.vert.h"
|
||||
#include "ffx_fsr1_easu.frag.h"
|
||||
#include "ffx_fsr1_rcas.frag.h"
|
||||
|
||||
typedef struct EGL_FilterFFXFSR1
|
||||
{
|
||||
EGL_Filter base;
|
||||
|
||||
EGL_Shader * easu, * rcas;
|
||||
bool enable, active;
|
||||
float sharpness;
|
||||
CountedBuffer * consts;
|
||||
EGL_Uniform easuUniform[2], rcasUniform;
|
||||
|
||||
enum EGL_PixelFormat pixFmt;
|
||||
unsigned int width, height;
|
||||
unsigned int inWidth, inHeight;
|
||||
bool sizeChanged;
|
||||
bool prepared;
|
||||
|
||||
EGL_Framebuffer * easuFb, * rcasFb;
|
||||
GLuint sampler;
|
||||
}
|
||||
EGL_FilterFFXFSR1;
|
||||
|
||||
static void egl_filterFFXFSR1EarlyInit(void)
|
||||
{
|
||||
static struct Option options[] =
|
||||
{
|
||||
{
|
||||
.module = "eglFilter",
|
||||
.name = "ffxFSR",
|
||||
.description = "AMD FidelityFX FSR",
|
||||
.preset = true,
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false
|
||||
},
|
||||
{
|
||||
.module = "eglFilter",
|
||||
.name = "ffxFSRSharpness",
|
||||
.description = "AMD FidelityFX FSR Sharpness",
|
||||
.preset = true,
|
||||
.type = OPTION_TYPE_FLOAT,
|
||||
.value.x_float = 1.0f
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
option_register(options);
|
||||
}
|
||||
|
||||
static void rcasUpdateUniform(EGL_FilterFFXFSR1 * this)
|
||||
{
|
||||
ffxFsrRcasConst(this->rcasUniform.ui, 2.0f - this->sharpness * 2.0f);
|
||||
}
|
||||
|
||||
static void egl_filterFFXFSR1SaveState(EGL_Filter * filter)
|
||||
{
|
||||
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
|
||||
|
||||
option_set_bool ("eglFilter", "ffxFSR", this->enable);
|
||||
option_set_float("eglFilter", "ffxFSRSharpness", this->sharpness);
|
||||
}
|
||||
|
||||
static void egl_filterFFXFSR1LoadState(EGL_Filter * filter)
|
||||
{
|
||||
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
|
||||
|
||||
this->enable = option_get_bool ("eglFilter", "ffxFSR");
|
||||
this->sharpness = option_get_float("eglFilter", "ffxFSRSharpness");
|
||||
}
|
||||
|
||||
static bool egl_filterFFXFSR1Init(EGL_Filter ** filter)
|
||||
{
|
||||
EGL_FilterFFXFSR1 * this = calloc(1, sizeof(*this));
|
||||
if (!this)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate ram");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shaderInit(&this->easu))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the Easu shader");
|
||||
goto error_this;
|
||||
}
|
||||
|
||||
if (!egl_shaderInit(&this->rcas))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the Rcas shader");
|
||||
goto error_esau;
|
||||
}
|
||||
|
||||
if (!egl_shaderCompile(this->easu,
|
||||
b_shader_basic_vert , b_shader_basic_vert_size,
|
||||
b_shader_ffx_fsr1_easu_frag, b_shader_ffx_fsr1_easu_frag_size)
|
||||
)
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the Easu shader");
|
||||
goto error_rcas;
|
||||
}
|
||||
|
||||
if (!egl_shaderCompile(this->rcas,
|
||||
b_shader_basic_vert , b_shader_basic_vert_size,
|
||||
b_shader_ffx_fsr1_rcas_frag, b_shader_ffx_fsr1_rcas_frag_size)
|
||||
)
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the Rcas shader");
|
||||
goto error_rcas;
|
||||
}
|
||||
|
||||
this->consts = countedBufferNew(16 * sizeof(GLuint));
|
||||
if (!this->consts)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate consts buffer");
|
||||
goto error_rcas;
|
||||
}
|
||||
|
||||
egl_filterFFXFSR1LoadState(&this->base);
|
||||
|
||||
this->easuUniform[0].type = EGL_UNIFORM_TYPE_4UIV;
|
||||
this->easuUniform[0].location =
|
||||
egl_shaderGetUniform(this->easu, "uConsts");
|
||||
this->easuUniform[0].v = this->consts;
|
||||
this->easuUniform[1].type = EGL_UNIFORM_TYPE_2F;
|
||||
this->easuUniform[1].location =
|
||||
egl_shaderGetUniform(this->easu, "uOutRes");
|
||||
|
||||
this->rcasUniform.type = EGL_UNIFORM_TYPE_4UI;
|
||||
this->rcasUniform.location = egl_shaderGetUniform(this->rcas, "uConsts");
|
||||
rcasUpdateUniform(this);
|
||||
|
||||
if (!egl_framebufferInit(&this->easuFb))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the Easu framebuffer");
|
||||
goto error_consts;
|
||||
}
|
||||
|
||||
if (!egl_framebufferInit(&this->rcasFb))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the Rcas framebuffer");
|
||||
goto error_easuFb;
|
||||
}
|
||||
|
||||
glGenSamplers(1, &this->sampler);
|
||||
glSamplerParameteri(this->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(this->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
|
||||
|
||||
*filter = &this->base;
|
||||
return true;
|
||||
|
||||
error_easuFb:
|
||||
egl_framebufferFree(&this->rcasFb);
|
||||
|
||||
error_consts:
|
||||
countedBufferRelease(&this->consts);
|
||||
|
||||
error_rcas:
|
||||
egl_shaderFree(&this->rcas);
|
||||
|
||||
error_esau:
|
||||
egl_shaderFree(&this->easu);
|
||||
|
||||
error_this:
|
||||
free(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void egl_filterFFXFSR1Free(EGL_Filter * filter)
|
||||
{
|
||||
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
|
||||
|
||||
egl_shaderFree(&this->easu);
|
||||
egl_shaderFree(&this->rcas);
|
||||
countedBufferRelease(&this->consts);
|
||||
egl_framebufferFree(&this->easuFb);
|
||||
egl_framebufferFree(&this->rcasFb);
|
||||
glDeleteSamplers(1, &this->sampler);
|
||||
free(this);
|
||||
}
|
||||
|
||||
static bool egl_filterFFXFSR1ImguiConfig(EGL_Filter * filter)
|
||||
{
|
||||
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
|
||||
|
||||
bool redraw = false;
|
||||
bool enable = this->enable;
|
||||
float sharpness = this->sharpness;
|
||||
|
||||
igCheckbox("Enabled", &enable);
|
||||
if (enable != this->enable)
|
||||
{
|
||||
this->enable = enable;
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
if (this->active)
|
||||
{
|
||||
double dimScale = (double) this->width / this->inWidth;
|
||||
const char * name;
|
||||
if (dimScale < 1.29)
|
||||
name = "better than Ultra Quality";
|
||||
else if (dimScale < 1.31)
|
||||
name = "Ultra Quality";
|
||||
else if (dimScale < 1.4)
|
||||
name = "slightly worse than Ultra Quality";
|
||||
else if (dimScale < 1.49)
|
||||
name = "slightly better than Quality";
|
||||
else if (dimScale < 1.51)
|
||||
name = "Quality";
|
||||
else if (dimScale < 1.6)
|
||||
name = "slightly worse than Quality";
|
||||
else if (dimScale < 1.69)
|
||||
name = "slightly better than Balanced";
|
||||
else if (dimScale < 1.71)
|
||||
name = "Balanced";
|
||||
else if (dimScale < 1.85)
|
||||
name = "slightly worse than Balanced";
|
||||
else if (dimScale < 1.99)
|
||||
name = "slightly better than Performance";
|
||||
else if (dimScale < 2.01)
|
||||
name = "Performance";
|
||||
else
|
||||
name = "worse than Performance";
|
||||
igText("Equivalent quality mode: %s%s", name, this->enable ? "" : ", inactive");
|
||||
}
|
||||
else
|
||||
igText("Equivalent quality mode: not upscaling, inactive");
|
||||
|
||||
if (igIsItemHovered(ImGuiHoveredFlags_None))
|
||||
{
|
||||
igBeginTooltip();
|
||||
igText(
|
||||
"Equivalent quality mode is decided by the resolution in the guest VM or the output\n"
|
||||
"of the previous filter in the chain.\n\n"
|
||||
"Here are the input resolutions needed for each quality mode at current window size:\n"
|
||||
);
|
||||
|
||||
if (igBeginTable("Resolutions", 2, 0, (ImVec2) { 0.0f, 0.0f }, 0.0f))
|
||||
{
|
||||
igTableNextColumn();
|
||||
igText("Ultra Quality");
|
||||
igTableNextColumn();
|
||||
igText("%.0fx%.0f", this->width / 1.3, this->height / 1.3);
|
||||
igTableNextColumn();
|
||||
igText("Quality");
|
||||
igTableNextColumn();
|
||||
igText("%.0fx%.0f", this->width / 1.5, this->height / 1.5);
|
||||
igTableNextColumn();
|
||||
igText("Balanced");
|
||||
igTableNextColumn();
|
||||
igText("%.0fx%.0f", this->width / 1.7, this->height / 1.7);
|
||||
igTableNextColumn();
|
||||
igText("Performance");
|
||||
igTableNextColumn();
|
||||
igText("%.0fx%.0f", this->width / 2.0, this->height / 2.0);
|
||||
igEndTable();
|
||||
}
|
||||
igEndTooltip();
|
||||
}
|
||||
|
||||
igText("Sharpness:");
|
||||
igSameLine(0.0f, -1.0f);
|
||||
igPushItemWidth(igGetWindowWidth() - igGetCursorPosX() -
|
||||
igGetStyle()->WindowPadding.x);
|
||||
igSliderFloat("##fsr1Sharpness", &sharpness, 0.0f, 1.0f, NULL, 0);
|
||||
sharpness = util_clamp(sharpness, 0.0f, 1.0f);
|
||||
if (igIsItemHovered(ImGuiHoveredFlags_None))
|
||||
igSetTooltip("Ctrl+Click to enter a value");
|
||||
igPopItemWidth();
|
||||
|
||||
if (sharpness != this->sharpness)
|
||||
{
|
||||
// enable FSR1 if the sharpness was changed
|
||||
if (!enable)
|
||||
{
|
||||
enable = true;
|
||||
this->enable = true;
|
||||
}
|
||||
|
||||
this->sharpness = sharpness;
|
||||
rcasUpdateUniform(this);
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
if (redraw)
|
||||
this->prepared = false;
|
||||
|
||||
return redraw;
|
||||
}
|
||||
|
||||
static void egl_filterFFXFSR1SetOutputResHint(EGL_Filter * filter,
|
||||
unsigned int width, unsigned int height)
|
||||
{
|
||||
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
|
||||
if (this->width == width && this->height == height)
|
||||
return;
|
||||
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
this->sizeChanged = true;
|
||||
this->prepared = false;
|
||||
}
|
||||
|
||||
static bool egl_filterFFXFSR1Setup(EGL_Filter * filter,
|
||||
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
|
||||
{
|
||||
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
|
||||
|
||||
if (!this->enable)
|
||||
return false;
|
||||
|
||||
this->active = this->width > width && this->height > height;
|
||||
if (!this->active)
|
||||
return false;
|
||||
|
||||
if (pixFmt == this->pixFmt && !this->sizeChanged &&
|
||||
width == this->inWidth && height == this->inHeight)
|
||||
return true;
|
||||
|
||||
if (!egl_framebufferSetup(this->easuFb, pixFmt, this->width, this->height))
|
||||
return false;
|
||||
|
||||
if (!egl_framebufferSetup(this->rcasFb, pixFmt, this->width, this->height))
|
||||
return false;
|
||||
|
||||
this->inWidth = width;
|
||||
this->inHeight = height;
|
||||
this->sizeChanged = false;
|
||||
this->pixFmt = pixFmt;
|
||||
this->prepared = false;
|
||||
|
||||
this->easuUniform[1].f[0] = this->width;
|
||||
this->easuUniform[1].f[1] = this->height;
|
||||
ffxFsrEasuConst((uint32_t *)this->consts->data, this->inWidth, this->inHeight,
|
||||
this->inWidth, this->inHeight, this->width, this->height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void egl_filterFFXFSR1GetOutputRes(EGL_Filter * filter,
|
||||
unsigned int *width, unsigned int *height)
|
||||
{
|
||||
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
|
||||
*width = this->width;
|
||||
*height = this->height;
|
||||
}
|
||||
|
||||
static bool egl_filterFFXFSR1Prepare(EGL_Filter * filter)
|
||||
{
|
||||
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
|
||||
|
||||
if (!this->active)
|
||||
return false;
|
||||
|
||||
if (this->prepared)
|
||||
return true;
|
||||
|
||||
egl_shaderSetUniforms(this->easu, this->easuUniform, ARRAY_LENGTH(this->easuUniform));
|
||||
egl_shaderSetUniforms(this->rcas, &this->rcasUniform, 1);
|
||||
this->prepared = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static GLuint egl_filterFFXFSR1Run(EGL_Filter * filter,
|
||||
EGL_FilterRects * rects, GLuint texture)
|
||||
{
|
||||
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
|
||||
|
||||
// pass 1, Easu
|
||||
egl_framebufferBind(this->easuFb);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glBindSampler(0, this->sampler);
|
||||
egl_shaderUse(this->easu);
|
||||
egl_filterRectsRender(this->easu, rects);
|
||||
texture = egl_framebufferGetTexture(this->easuFb);
|
||||
|
||||
// pass 2, Rcas
|
||||
egl_framebufferBind(this->rcasFb);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glBindSampler(0, this->sampler);
|
||||
egl_shaderUse(this->rcas);
|
||||
egl_filterRectsRender(this->rcas, rects);
|
||||
texture = egl_framebufferGetTexture(this->rcasFb);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
EGL_FilterOps egl_filterFFXFSR1Ops =
|
||||
{
|
||||
.id = "ffxFSR1",
|
||||
.name = "AMD FidelityFX FSR",
|
||||
.type = EGL_FILTER_TYPE_UPSCALE,
|
||||
.earlyInit = egl_filterFFXFSR1EarlyInit,
|
||||
.init = egl_filterFFXFSR1Init,
|
||||
.free = egl_filterFFXFSR1Free,
|
||||
.imguiConfig = egl_filterFFXFSR1ImguiConfig,
|
||||
.saveState = egl_filterFFXFSR1SaveState,
|
||||
.loadState = egl_filterFFXFSR1LoadState,
|
||||
.setup = egl_filterFFXFSR1Setup,
|
||||
.setOutputResHint = egl_filterFFXFSR1SetOutputResHint,
|
||||
.getOutputRes = egl_filterFFXFSR1GetOutputRes,
|
||||
.prepare = egl_filterFFXFSR1Prepare,
|
||||
.run = egl_filterFFXFSR1Run
|
||||
};
|
||||
25
client/renderers/EGL/filters.h
Normal file
25
client/renderers/EGL/filters.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
extern EGL_FilterOps egl_filterDownscaleOps;
|
||||
extern EGL_FilterOps egl_filterFFXCASOps;
|
||||
extern EGL_FilterOps egl_filterFFXFSR1Ops;
|
||||
@@ -1,205 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
cahe terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "fps.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
#include "model.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// these headers are auto generated by cmake
|
||||
#include "fps.vert.h"
|
||||
#include "fps.frag.h"
|
||||
#include "fps_bg.frag.h"
|
||||
|
||||
struct EGL_FPS
|
||||
{
|
||||
const LG_Font * font;
|
||||
LG_FontObj fontObj;
|
||||
|
||||
EGL_Texture * texture;
|
||||
EGL_Shader * shader;
|
||||
EGL_Shader * shaderBG;
|
||||
EGL_Model * model;
|
||||
|
||||
bool display;
|
||||
bool ready;
|
||||
int iwidth, iheight;
|
||||
float width, height;
|
||||
|
||||
// uniforms
|
||||
GLint uScreen , uSize;
|
||||
GLint uScreenBG, uSizeBG;
|
||||
};
|
||||
|
||||
bool egl_fps_init(EGL_FPS ** fps, const LG_Font * font, LG_FontObj fontObj)
|
||||
{
|
||||
*fps = (EGL_FPS *)malloc(sizeof(EGL_FPS));
|
||||
if (!*fps)
|
||||
{
|
||||
DEBUG_ERROR("Failed to malloc EGL_FPS");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(*fps, 0, sizeof(EGL_FPS));
|
||||
|
||||
(*fps)->font = font;
|
||||
(*fps)->fontObj = fontObj;
|
||||
|
||||
if (!egl_texture_init(&(*fps)->texture, NULL))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the fps texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shader_init(&(*fps)->shader))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the fps shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shader_init(&(*fps)->shaderBG))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the fps bg shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!egl_shader_compile((*fps)->shader,
|
||||
b_shader_fps_vert, b_shader_fps_vert_size,
|
||||
b_shader_fps_frag, b_shader_fps_frag_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the fps shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shader_compile((*fps)->shaderBG,
|
||||
b_shader_fps_vert , b_shader_fps_vert_size,
|
||||
b_shader_fps_bg_frag, b_shader_fps_bg_frag_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the fps shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
(*fps)->uSize = egl_shader_get_uniform_location((*fps)->shader , "size" );
|
||||
(*fps)->uScreen = egl_shader_get_uniform_location((*fps)->shader , "screen");
|
||||
(*fps)->uSizeBG = egl_shader_get_uniform_location((*fps)->shaderBG, "size" );
|
||||
(*fps)->uScreenBG = egl_shader_get_uniform_location((*fps)->shaderBG, "screen");
|
||||
|
||||
if (!egl_model_init(&(*fps)->model))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the fps model");
|
||||
return false;
|
||||
}
|
||||
|
||||
egl_model_set_default((*fps)->model);
|
||||
egl_model_set_texture((*fps)->model, (*fps)->texture);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_fps_free(EGL_FPS ** fps)
|
||||
{
|
||||
if (!*fps)
|
||||
return;
|
||||
|
||||
egl_texture_free(&(*fps)->texture );
|
||||
egl_shader_free (&(*fps)->shader );
|
||||
egl_shader_free (&(*fps)->shaderBG);
|
||||
egl_model_free (&(*fps)->model );
|
||||
|
||||
free(*fps);
|
||||
*fps = NULL;
|
||||
}
|
||||
|
||||
void egl_fps_set_display(EGL_FPS * fps, bool display)
|
||||
{
|
||||
fps->display = display;
|
||||
}
|
||||
|
||||
void egl_fps_update(EGL_FPS * fps, const float avgFPS, const float renderFPS)
|
||||
{
|
||||
if (!fps->display)
|
||||
return;
|
||||
|
||||
char str[128];
|
||||
snprintf(str, sizeof(str), "UPS: %8.4f, FPS: %8.4f", avgFPS, renderFPS);
|
||||
|
||||
LG_FontBitmap * bmp = fps->font->render(fps->fontObj, 0xffffff00, str);
|
||||
if (!bmp)
|
||||
{
|
||||
DEBUG_ERROR("Failed to render fps text");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fps->iwidth != bmp->width || fps->iheight != bmp->height)
|
||||
{
|
||||
fps->iwidth = bmp->width;
|
||||
fps->iheight = bmp->height;
|
||||
fps->width = (float)bmp->width;
|
||||
fps->height = (float)bmp->height;
|
||||
|
||||
egl_texture_setup(
|
||||
fps->texture,
|
||||
EGL_PF_BGRA,
|
||||
bmp->width ,
|
||||
bmp->height,
|
||||
bmp->width * bmp->bpp,
|
||||
false,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
egl_texture_update
|
||||
(
|
||||
fps->texture,
|
||||
bmp->pixels
|
||||
);
|
||||
|
||||
fps->ready = true;
|
||||
fps->font->release(fps->fontObj, bmp);
|
||||
}
|
||||
|
||||
void egl_fps_render(EGL_FPS * fps, const float scaleX, const float scaleY)
|
||||
{
|
||||
if (!fps->display || !fps->ready)
|
||||
return;
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// render the background first
|
||||
egl_shader_use(fps->shaderBG);
|
||||
glUniform2f(fps->uScreenBG, scaleX , scaleY );
|
||||
glUniform2f(fps->uSizeBG , fps->width, fps->height);
|
||||
egl_model_render(fps->model);
|
||||
|
||||
// render the texture over the background
|
||||
egl_shader_use(fps->shader);
|
||||
glUniform2f(fps->uScreen, scaleX , scaleY );
|
||||
glUniform2f(fps->uSize , fps->width, fps->height);
|
||||
egl_model_render(fps->model);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "interface/font.h"
|
||||
|
||||
typedef struct EGL_FPS EGL_FPS;
|
||||
|
||||
bool egl_fps_init(EGL_FPS ** fps, const LG_Font * font, LG_FontObj fontObj);
|
||||
void egl_fps_free(EGL_FPS ** fps);
|
||||
|
||||
void egl_fps_set_display(EGL_FPS * fps, bool display);
|
||||
void egl_fps_update(EGL_FPS * fps, const float avgUPS, const float avgFPS);
|
||||
void egl_fps_render(EGL_FPS * fps, const float scaleX, const float scaleY);
|
||||
108
client/renderers/EGL/framebuffer.c
Normal file
108
client/renderers/EGL/framebuffer.c
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "framebuffer.h"
|
||||
#include "texture.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
|
||||
struct EGL_Framebuffer
|
||||
{
|
||||
GLuint fbo;
|
||||
EGL_Texture * tex;
|
||||
};
|
||||
|
||||
bool egl_framebufferInit(EGL_Framebuffer ** fb)
|
||||
{
|
||||
EGL_Framebuffer * this = calloc(1, sizeof(*this));
|
||||
if (!this)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate ram");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_textureInit(&this->tex, NULL, EGL_TEXTYPE_BUFFER, false))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
glGenFramebuffers(1, &this->fbo);
|
||||
|
||||
*fb = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_framebufferFree(EGL_Framebuffer ** fb)
|
||||
{
|
||||
EGL_Framebuffer * this = *fb;
|
||||
|
||||
egl_textureFree(&this->tex);
|
||||
free(this);
|
||||
*fb = NULL;
|
||||
}
|
||||
|
||||
bool egl_framebufferSetup(EGL_Framebuffer * this, enum EGL_PixelFormat pixFmt,
|
||||
unsigned int width, unsigned int height)
|
||||
{
|
||||
if (!egl_textureSetup(this->tex, pixFmt, width, height, 0))
|
||||
{
|
||||
DEBUG_ERROR("Failed to setup the texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
GLuint tex;
|
||||
egl_textureGet(this->tex, &tex, NULL, NULL);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, this->fbo);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D, tex, 0);
|
||||
glDrawBuffers(1, &(GLenum){GL_COLOR_ATTACHMENT0});
|
||||
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
DEBUG_ERROR("Failed to setup the framebuffer: 0x%x", status);
|
||||
return false;
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_framebufferBind(EGL_Framebuffer * this)
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, this->fbo);
|
||||
glViewport(0, 0, this->tex->format.width, this->tex->format.height);
|
||||
}
|
||||
|
||||
GLuint egl_framebufferGetTexture(EGL_Framebuffer * this)
|
||||
{
|
||||
GLuint output;
|
||||
egl_textureGet(this->tex, &output, NULL, NULL);
|
||||
return output;
|
||||
}
|
||||
35
client/renderers/EGL/framebuffer.h
Normal file
35
client/renderers/EGL/framebuffer.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "texture.h"
|
||||
|
||||
typedef struct EGL_Framebuffer EGL_Framebuffer;
|
||||
|
||||
bool egl_framebufferInit(EGL_Framebuffer ** fb);
|
||||
void egl_framebufferFree(EGL_Framebuffer ** fb);
|
||||
|
||||
bool egl_framebufferSetup(EGL_Framebuffer * this, enum EGL_PixelFormat pixFmt,
|
||||
unsigned int width, unsigned int height);
|
||||
|
||||
void egl_framebufferBind(EGL_Framebuffer * this);
|
||||
|
||||
GLuint egl_framebufferGetTexture(EGL_Framebuffer * this);
|
||||
13
client/renderers/EGL/glsl.include.awk
Normal file
13
client/renderers/EGL/glsl.include.awk
Normal file
@@ -0,0 +1,13 @@
|
||||
BEGIN { FS="\"" }
|
||||
|
||||
function process(line, second) {
|
||||
if (line ~ /^#include[ \t]*".+"[ \t\r]*$/) {
|
||||
while (getline < second) {
|
||||
process($0, $2)
|
||||
}
|
||||
} else {
|
||||
print line
|
||||
}
|
||||
}
|
||||
|
||||
{ process($0, $2) }
|
||||
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2021 Guanzhong Chen <quantum2048@gmail.com>
|
||||
https://looking-glass.io
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "help.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
#include "model.h"
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// these headers are auto generated by cmake
|
||||
#include "help.vert.h"
|
||||
#include "help.frag.h"
|
||||
#include "help_bg.frag.h"
|
||||
|
||||
struct EGL_Help
|
||||
{
|
||||
const LG_Font * font;
|
||||
LG_FontObj fontObj;
|
||||
|
||||
EGL_Texture * texture;
|
||||
EGL_Shader * shader;
|
||||
EGL_Shader * shaderBG;
|
||||
EGL_Model * model;
|
||||
|
||||
_Atomic(LG_FontBitmap *) bmp;
|
||||
|
||||
bool shouldRender;
|
||||
int iwidth, iheight;
|
||||
float width, height;
|
||||
|
||||
// uniforms
|
||||
GLint uScreen , uSize;
|
||||
GLint uScreenBG, uSizeBG;
|
||||
};
|
||||
|
||||
bool egl_help_init(EGL_Help ** help, const LG_Font * font, LG_FontObj fontObj)
|
||||
{
|
||||
*help = (EGL_Help *)malloc(sizeof(EGL_Help));
|
||||
if (!*help)
|
||||
{
|
||||
DEBUG_ERROR("Failed to malloc EGL_Help");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(*help, 0, sizeof(EGL_Help));
|
||||
|
||||
(*help)->font = font;
|
||||
(*help)->fontObj = fontObj;
|
||||
|
||||
if (!egl_texture_init(&(*help)->texture, NULL))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the help texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shader_init(&(*help)->shader))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the help shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shader_init(&(*help)->shaderBG))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the help bg shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!egl_shader_compile((*help)->shader,
|
||||
b_shader_help_vert, b_shader_help_vert_size,
|
||||
b_shader_help_frag, b_shader_help_frag_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the help shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shader_compile((*help)->shaderBG,
|
||||
b_shader_help_vert , b_shader_help_vert_size,
|
||||
b_shader_help_bg_frag, b_shader_help_bg_frag_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the help shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
(*help)->uSize = egl_shader_get_uniform_location((*help)->shader , "size" );
|
||||
(*help)->uScreen = egl_shader_get_uniform_location((*help)->shader , "screen");
|
||||
(*help)->uSizeBG = egl_shader_get_uniform_location((*help)->shaderBG, "size" );
|
||||
(*help)->uScreenBG = egl_shader_get_uniform_location((*help)->shaderBG, "screen");
|
||||
|
||||
if (!egl_model_init(&(*help)->model))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the fps model");
|
||||
return false;
|
||||
}
|
||||
|
||||
egl_model_set_default((*help)->model);
|
||||
egl_model_set_texture((*help)->model, (*help)->texture);
|
||||
|
||||
atomic_init(&(*help)->bmp, NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_help_free(EGL_Help ** help)
|
||||
{
|
||||
if (!*help)
|
||||
return;
|
||||
|
||||
egl_texture_free(&(*help)->texture );
|
||||
egl_shader_free (&(*help)->shader );
|
||||
egl_shader_free (&(*help)->shaderBG);
|
||||
egl_model_free (&(*help)->model );
|
||||
|
||||
free(*help);
|
||||
*help = NULL;
|
||||
}
|
||||
|
||||
void egl_help_set_text(EGL_Help * help, const char * help_text)
|
||||
{
|
||||
LG_FontBitmap * bmp = NULL;
|
||||
if (help_text)
|
||||
{
|
||||
bmp = help->font->render(help->fontObj, 0xffffff00, help_text);
|
||||
if (!bmp)
|
||||
DEBUG_ERROR("Failed to render help text");
|
||||
} else
|
||||
help->shouldRender = false;
|
||||
|
||||
bmp = atomic_exchange(&help->bmp, bmp);
|
||||
if (bmp)
|
||||
{
|
||||
help->font->release(help->fontObj, bmp);
|
||||
}
|
||||
}
|
||||
|
||||
void egl_help_render(EGL_Help * help, const float scaleX, const float scaleY)
|
||||
{
|
||||
LG_FontBitmap * bmp = atomic_exchange(&help->bmp, NULL);
|
||||
if (bmp)
|
||||
{
|
||||
if (help->iwidth != bmp->width || help->iheight != bmp->height)
|
||||
{
|
||||
help->iwidth = bmp->width;
|
||||
help->iheight = bmp->height;
|
||||
help->width = (float)bmp->width;
|
||||
help->height = (float)bmp->height;
|
||||
|
||||
egl_texture_setup(
|
||||
help->texture,
|
||||
EGL_PF_BGRA,
|
||||
bmp->width ,
|
||||
bmp->height,
|
||||
bmp->width * bmp->bpp,
|
||||
false,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
egl_texture_update
|
||||
(
|
||||
help->texture,
|
||||
bmp->pixels
|
||||
);
|
||||
|
||||
help->shouldRender = true;
|
||||
help->font->release(help->fontObj, bmp);
|
||||
}
|
||||
|
||||
if (!help->shouldRender)
|
||||
return;
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// render the background first
|
||||
egl_shader_use(help->shaderBG);
|
||||
glUniform2f(help->uScreenBG, scaleX , scaleY );
|
||||
glUniform2f(help->uSizeBG , help->width, help->height);
|
||||
egl_model_render(help->model);
|
||||
|
||||
// render the texture over the background
|
||||
egl_shader_use(help->shader);
|
||||
glUniform2f(help->uScreen, scaleX , scaleY );
|
||||
glUniform2f(help->uSize , help->width, help->height);
|
||||
egl_model_render(help->model);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2021 Guanzhong Chen <quantum2048@gmail.com>
|
||||
https://looking-glass.io
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "interface/font.h"
|
||||
|
||||
typedef struct EGL_Help EGL_Help;
|
||||
|
||||
bool egl_help_init(EGL_Help ** help, const LG_Font * font, LG_FontObj fontObj);
|
||||
void egl_help_free(EGL_Help ** help);
|
||||
|
||||
void egl_help_set_text(EGL_Help * help, const char * help_text);
|
||||
void egl_help_render(EGL_Help * help, const float scaleX, const float scaleY);
|
||||
@@ -1,21 +1,22 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "model.h"
|
||||
#include "shader.h"
|
||||
@@ -27,7 +28,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct EGL_Model
|
||||
{
|
||||
@@ -36,8 +36,8 @@ struct EGL_Model
|
||||
size_t vertexCount;
|
||||
bool finish;
|
||||
|
||||
bool hasBuffer;
|
||||
GLuint buffer;
|
||||
GLuint vao;
|
||||
|
||||
EGL_Shader * shader;
|
||||
EGL_Texture * texture;
|
||||
@@ -52,23 +52,23 @@ struct FloatList
|
||||
|
||||
void update_uniform_bindings(EGL_Model * model);
|
||||
|
||||
bool egl_model_init(EGL_Model ** model)
|
||||
bool egl_modelInit(EGL_Model ** model)
|
||||
{
|
||||
*model = (EGL_Model *)malloc(sizeof(EGL_Model));
|
||||
*model = malloc(sizeof(**model));
|
||||
if (!*model)
|
||||
{
|
||||
DEBUG_ERROR("Failed to malloc EGL_Model");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(*model, 0, sizeof(EGL_Model));
|
||||
memset(*model, 0, sizeof(**model));
|
||||
|
||||
(*model)->verticies = ll_new();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_model_free(EGL_Model ** model)
|
||||
void egl_modelFree(EGL_Model ** model)
|
||||
{
|
||||
if (!*model)
|
||||
return;
|
||||
@@ -82,14 +82,17 @@ void egl_model_free(EGL_Model ** model)
|
||||
}
|
||||
ll_free((*model)->verticies);
|
||||
|
||||
if ((*model)->hasBuffer)
|
||||
if ((*model)->buffer)
|
||||
glDeleteBuffers(1, &(*model)->buffer);
|
||||
|
||||
if ((*model)->vao)
|
||||
glDeleteVertexArrays(1, &(*model)->vao);
|
||||
|
||||
free(*model);
|
||||
*model = NULL;
|
||||
}
|
||||
|
||||
void egl_model_set_default(EGL_Model * model)
|
||||
void egl_modelSetDefault(EGL_Model * model, bool flipped)
|
||||
{
|
||||
static const GLfloat square[] =
|
||||
{
|
||||
@@ -99,7 +102,15 @@ void egl_model_set_default(EGL_Model * model)
|
||||
1.0f, 1.0f, 0.0f
|
||||
};
|
||||
|
||||
static const GLfloat uvs[] =
|
||||
static const GLfloat uvsNormal[] =
|
||||
{
|
||||
0.0f, 0.0f,
|
||||
1.0f, 0.0f,
|
||||
0.0f, 1.0f,
|
||||
1.0f, 1.0f
|
||||
};
|
||||
|
||||
static const GLfloat uvsFlipped[] =
|
||||
{
|
||||
0.0f, 1.0f,
|
||||
1.0f, 1.0f,
|
||||
@@ -107,16 +118,16 @@ void egl_model_set_default(EGL_Model * model)
|
||||
1.0f, 0.0f
|
||||
};
|
||||
|
||||
egl_model_add_verticies(model, square, uvs, 4);
|
||||
egl_modelAddVerts(model, square, flipped ? uvsFlipped : uvsNormal, 4);
|
||||
}
|
||||
|
||||
void egl_model_add_verticies(EGL_Model * model, const GLfloat * verticies, const GLfloat * uvs, const size_t count)
|
||||
void egl_modelAddVerts(EGL_Model * model, const GLfloat * verticies, const GLfloat * uvs, const size_t count)
|
||||
{
|
||||
struct FloatList * fl = (struct FloatList *)malloc(sizeof(struct FloatList));
|
||||
struct FloatList * fl = malloc(sizeof(*fl));
|
||||
|
||||
fl->count = count;
|
||||
fl->v = (GLfloat *)malloc(sizeof(GLfloat) * count * 3);
|
||||
fl->u = (GLfloat *)malloc(sizeof(GLfloat) * count * 2);
|
||||
fl->v = malloc(sizeof(GLfloat) * count * 3);
|
||||
fl->u = malloc(sizeof(GLfloat) * count * 2);
|
||||
memcpy(fl->v, verticies, sizeof(GLfloat) * count * 3);
|
||||
|
||||
if (uvs)
|
||||
@@ -129,16 +140,21 @@ void egl_model_add_verticies(EGL_Model * model, const GLfloat * verticies, const
|
||||
model->vertexCount += count;
|
||||
}
|
||||
|
||||
void egl_model_render(EGL_Model * model)
|
||||
void egl_modelRender(EGL_Model * model)
|
||||
{
|
||||
if (!model->vertexCount)
|
||||
return;
|
||||
|
||||
if (model->rebuild)
|
||||
{
|
||||
if (model->hasBuffer)
|
||||
if (model->buffer)
|
||||
glDeleteBuffers(1, &model->buffer);
|
||||
|
||||
if (!model->vao)
|
||||
glGenVertexArrays(1, &model->vao);
|
||||
|
||||
glBindVertexArray(model->vao);
|
||||
|
||||
/* create a buffer large enough */
|
||||
glGenBuffers(1, &model->buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, model->buffer);
|
||||
@@ -161,22 +177,24 @@ void egl_model_render(EGL_Model * model)
|
||||
offset += sizeof(GLfloat) * fl->count * 2;
|
||||
}
|
||||
|
||||
/* set up vertex arrays in the VAO */
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(GLfloat) * model->vertexCount * 3));
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
model->rebuild = false;
|
||||
}
|
||||
|
||||
/* bind the model buffer and setup the pointers */
|
||||
glBindBuffer(GL_ARRAY_BUFFER, model->buffer);
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(GLfloat) * model->vertexCount * 3));
|
||||
glBindVertexArray(model->vao);
|
||||
|
||||
if (model->shader)
|
||||
egl_shader_use(model->shader);
|
||||
egl_shaderUse(model->shader);
|
||||
|
||||
if (model->texture)
|
||||
egl_texture_bind(model->texture);
|
||||
egl_textureBind(model->texture);
|
||||
|
||||
/* draw the arrays */
|
||||
GLint offset = 0;
|
||||
@@ -189,18 +207,17 @@ void egl_model_render(EGL_Model * model)
|
||||
|
||||
/* unbind and cleanup */
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDisableVertexAttribArray(0);
|
||||
glDisableVertexAttribArray(1);
|
||||
glBindVertexArray(0);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
void egl_model_set_shader(EGL_Model * model, EGL_Shader * shader)
|
||||
void egl_modelSetShader(EGL_Model * model, EGL_Shader * shader)
|
||||
{
|
||||
model->shader = shader;
|
||||
update_uniform_bindings(model);
|
||||
}
|
||||
|
||||
void egl_model_set_texture(EGL_Model * model, EGL_Texture * texture)
|
||||
void egl_modelSetTexture(EGL_Model * model, EGL_Texture * texture)
|
||||
{
|
||||
model->texture = texture;
|
||||
update_uniform_bindings(model);
|
||||
@@ -211,6 +228,5 @@ void update_uniform_bindings(EGL_Model * model)
|
||||
if (!model->shader || !model->texture)
|
||||
return;
|
||||
|
||||
const int count = egl_texture_count(model->texture);
|
||||
egl_shader_associate_textures(model->shader, count);
|
||||
egl_shaderAssocTextures(model->shader, 1);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -23,16 +24,17 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "shader.h"
|
||||
#include "texture.h"
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include <GLES3/gl3.h>
|
||||
|
||||
typedef struct EGL_Model EGL_Model;
|
||||
typedef struct EGL_Texture EGL_Texture;
|
||||
|
||||
bool egl_model_init(EGL_Model ** model);
|
||||
void egl_model_free(EGL_Model ** model);
|
||||
bool egl_modelInit(EGL_Model ** model);
|
||||
void egl_modelFree(EGL_Model ** model);
|
||||
|
||||
void egl_model_set_default (EGL_Model * model);
|
||||
void egl_model_add_verticies(EGL_Model * model, const GLfloat * verticies, const GLfloat * uvs, const size_t count);
|
||||
void egl_model_set_shader (EGL_Model * model, EGL_Shader * shader);
|
||||
void egl_model_set_texture (EGL_Model * model, EGL_Texture * texture);
|
||||
void egl_modelSetDefault (EGL_Model * model, bool flipped);
|
||||
void egl_modelAddVerts(EGL_Model * model, const GLfloat * verticies, const GLfloat * uvs, const size_t count);
|
||||
void egl_modelSetShader (EGL_Model * model, EGL_Shader * shader);
|
||||
void egl_modelSetTexture (EGL_Model * model, EGL_Texture * texture);
|
||||
|
||||
void egl_model_render(EGL_Model * model);
|
||||
void egl_modelRender(EGL_Model * model);
|
||||
|
||||
645
client/renderers/EGL/postprocess.c
Normal file
645
client/renderers/EGL/postprocess.c
Normal file
@@ -0,0 +1,645 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include "postprocess.h"
|
||||
#include "filters.h"
|
||||
#include "app.h"
|
||||
#include "cimgui.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <stdatomic.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/array.h"
|
||||
#include "common/option.h"
|
||||
#include "common/paths.h"
|
||||
#include "common/stringlist.h"
|
||||
#include "common/stringutils.h"
|
||||
#include "common/vector.h"
|
||||
|
||||
static const EGL_FilterOps * EGL_Filters[] =
|
||||
{
|
||||
&egl_filterDownscaleOps,
|
||||
&egl_filterFFXFSR1Ops,
|
||||
&egl_filterFFXCASOps
|
||||
};
|
||||
|
||||
struct EGL_PostProcess
|
||||
{
|
||||
Vector filters;
|
||||
GLuint output;
|
||||
unsigned int outputX, outputY;
|
||||
_Atomic(bool) modified;
|
||||
|
||||
EGL_DesktopRects * rects;
|
||||
|
||||
StringList presets;
|
||||
char * presetDir;
|
||||
int activePreset;
|
||||
char presetEdit[128];
|
||||
char * presetError;
|
||||
};
|
||||
|
||||
void egl_postProcessEarlyInit(void)
|
||||
{
|
||||
static struct Option options[] =
|
||||
{
|
||||
{
|
||||
.module = "eglFilter",
|
||||
.name = "order",
|
||||
.description = "The order of filters to use",
|
||||
.preset = true,
|
||||
.type = OPTION_TYPE_STRING,
|
||||
.value.x_string = ""
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
option_register(options);
|
||||
|
||||
for (int i = 0; i < ARRAY_LENGTH(EGL_Filters); ++i)
|
||||
EGL_Filters[i]->earlyInit();
|
||||
}
|
||||
|
||||
static void loadPresetList(struct EGL_PostProcess * this)
|
||||
{
|
||||
DIR * dir = NULL;
|
||||
|
||||
alloc_sprintf(&this->presetDir, "%s/presets", lgConfigDir());
|
||||
if (!this->presetDir)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory for presets");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mkdir(this->presetDir, S_IRWXU) < 0 && errno != EEXIST)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create presets directory: %s", this->presetDir);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dir = opendir(this->presetDir);
|
||||
if (!dir)
|
||||
{
|
||||
DEBUG_ERROR("Failed to open presets directory: %s", this->presetDir);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
this->presets = stringlist_new(true);
|
||||
if (!this->presets)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory for preset list");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
struct dirent * entry;
|
||||
while ((entry = readdir(dir)) != NULL)
|
||||
{
|
||||
if (entry->d_type != DT_REG)
|
||||
continue;
|
||||
|
||||
DEBUG_INFO("Found preset: %s", entry->d_name);
|
||||
char * name = strdup(entry->d_name);
|
||||
if (!name)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory");
|
||||
goto fail;
|
||||
}
|
||||
stringlist_push(this->presets, name);
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
this->activePreset = -1;
|
||||
return;
|
||||
|
||||
fail:
|
||||
free(this->presetDir);
|
||||
this->presetDir = NULL;
|
||||
if (dir)
|
||||
closedir(dir);
|
||||
if (this->presets)
|
||||
stringlist_free(&this->presets);
|
||||
}
|
||||
|
||||
static void presetError(struct EGL_PostProcess * this, char * message)
|
||||
{
|
||||
free(this->presetError);
|
||||
this->presetError = message;
|
||||
}
|
||||
|
||||
static bool savePreset(struct EGL_PostProcess * this, const char * name)
|
||||
{
|
||||
EGL_Filter * filter;
|
||||
vector_forEach(filter, &this->filters)
|
||||
egl_filterSaveState(filter);
|
||||
|
||||
size_t orderLen = 0;
|
||||
vector_forEach(filter, &this->filters)
|
||||
orderLen += strlen(filter->ops.id) + 1;
|
||||
|
||||
char order[orderLen];
|
||||
char * p = order;
|
||||
vector_forEach(filter, &this->filters)
|
||||
{
|
||||
strcpy(p, filter->ops.id);
|
||||
p += strlen(filter->ops.id);
|
||||
*p++ = ';';
|
||||
}
|
||||
if (p > order)
|
||||
p[-1] = '\0';
|
||||
option_set_string("eglFilter", "order", order);
|
||||
|
||||
char * path;
|
||||
alloc_sprintf(&path, "%s/%s", this->presetDir, name);
|
||||
if (!path)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE * file = fopen(path, "w");
|
||||
if (!file)
|
||||
{
|
||||
const char * strError = strerror(errno);
|
||||
DEBUG_ERROR("Failed to open preset \"%s\" for writing: %s", name, strError);
|
||||
free(path);
|
||||
|
||||
char * error;
|
||||
alloc_sprintf(&error, "Failed to save preset: %s\nError: %s", name, strError);
|
||||
if (error)
|
||||
presetError(this, error);
|
||||
return false;
|
||||
}
|
||||
free(path);
|
||||
|
||||
DEBUG_INFO("Saving preset: %s", name);
|
||||
option_dump_preset(file);
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int stringListIndex(StringList list, const char * str)
|
||||
{
|
||||
unsigned int count = stringlist_count(list);
|
||||
for (unsigned int i = 0; i < count; ++i)
|
||||
if (strcmp(stringlist_at(list, i), str) == 0)
|
||||
return i;
|
||||
return INT_MAX;
|
||||
}
|
||||
|
||||
static int compareFilterOrder(const void * a_, const void * b_, void * opaque)
|
||||
{
|
||||
const EGL_Filter * a = *(const EGL_Filter **)a_;
|
||||
const EGL_Filter * b = *(const EGL_Filter **)b_;
|
||||
StringList order = opaque;
|
||||
|
||||
return stringListIndex(order, a->ops.id) - stringListIndex(order, b->ops.id);
|
||||
}
|
||||
|
||||
static void reorderFilters(struct EGL_PostProcess * this)
|
||||
{
|
||||
StringList order = stringlist_new(false);
|
||||
if (!order)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory");
|
||||
return;
|
||||
}
|
||||
|
||||
char * orderStr = strdup(option_get_string("eglFilter", "order"));
|
||||
if (!orderStr)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory");
|
||||
stringlist_free(&order);
|
||||
return;
|
||||
}
|
||||
|
||||
char * p = orderStr;
|
||||
while (*p)
|
||||
{
|
||||
stringlist_push(order, p);
|
||||
char * end = strchr(p, ';');
|
||||
if (!end)
|
||||
break;
|
||||
*end = '\0';
|
||||
p = end + 1;
|
||||
}
|
||||
|
||||
qsort_r(vector_data(&this->filters), vector_size(&this->filters),
|
||||
sizeof(EGL_Filter *), compareFilterOrder, order);
|
||||
|
||||
stringlist_free(&order);
|
||||
free(orderStr);
|
||||
}
|
||||
|
||||
static void loadPreset(struct EGL_PostProcess * this, const char * name)
|
||||
{
|
||||
char * path;
|
||||
alloc_sprintf(&path, "%s/%s", this->presetDir, name);
|
||||
if (!path)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!option_load(path))
|
||||
{
|
||||
DEBUG_ERROR("Failed to load preset: %s", name);
|
||||
free(path);
|
||||
|
||||
char * error;
|
||||
alloc_sprintf(&error, "Failed to load preset: %s", name);
|
||||
if (error)
|
||||
presetError(this, error);
|
||||
return;
|
||||
}
|
||||
free(path);
|
||||
|
||||
DEBUG_INFO("Loading preset: %s", name);
|
||||
EGL_Filter * filter;
|
||||
vector_forEach(filter, &this->filters)
|
||||
egl_filterLoadState(filter);
|
||||
reorderFilters(this);
|
||||
}
|
||||
|
||||
static void savePresetAs(struct EGL_PostProcess * this)
|
||||
{
|
||||
if (!savePreset(this, this->presetEdit))
|
||||
return;
|
||||
|
||||
for (unsigned i = 0; i < stringlist_count(this->presets); ++i)
|
||||
{
|
||||
DEBUG_INFO("Saw preset: %s", stringlist_at(this->presets, i));
|
||||
if (strcmp(stringlist_at(this->presets, i), this->presetEdit) == 0)
|
||||
{
|
||||
this->activePreset = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->activePreset = stringlist_push(this->presets, strdup(this->presetEdit));
|
||||
}
|
||||
|
||||
static void deletePreset(struct EGL_PostProcess * this)
|
||||
{
|
||||
char * path;
|
||||
alloc_sprintf(&path, "%s/%s", this->presetDir,
|
||||
stringlist_at(this->presets, this->activePreset));
|
||||
if (!path)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory");
|
||||
return;
|
||||
}
|
||||
|
||||
unlink(path);
|
||||
free(path);
|
||||
stringlist_remove(this->presets, this->activePreset);
|
||||
if (this->activePreset >= stringlist_count(this->presets))
|
||||
this->activePreset = stringlist_count(this->presets) - 1;
|
||||
}
|
||||
|
||||
static bool presetsUI(struct EGL_PostProcess * this)
|
||||
{
|
||||
if (!this->presets)
|
||||
return false;
|
||||
|
||||
bool redraw = false;
|
||||
const char * active = "<none>";
|
||||
|
||||
if (this->activePreset >= 0)
|
||||
active = stringlist_at(this->presets, this->activePreset);
|
||||
|
||||
if (igBeginCombo("Preset name", active, 0))
|
||||
{
|
||||
for (unsigned i = 0; i < stringlist_count(this->presets); ++i)
|
||||
{
|
||||
bool selected = i == this->activePreset;
|
||||
if (igSelectableBool(stringlist_at(this->presets, i), selected, 0, (ImVec2) { 0.0f, 0.0f }))
|
||||
{
|
||||
this->activePreset = i;
|
||||
redraw = true;
|
||||
loadPreset(this, stringlist_at(this->presets, this->activePreset));
|
||||
}
|
||||
if (selected)
|
||||
igSetItemDefaultFocus();
|
||||
}
|
||||
igEndCombo();
|
||||
}
|
||||
if (igIsItemHovered(ImGuiHoveredFlags_None))
|
||||
igSetTooltip("Selecting a preset will load it");
|
||||
|
||||
if (igButton("Save preset", (ImVec2) { 0.0f, 0.0f }))
|
||||
{
|
||||
if (this->activePreset >= 0)
|
||||
savePreset(this, stringlist_at(this->presets, this->activePreset));
|
||||
else
|
||||
presetError(this, strdup("You must select a preset to save."));
|
||||
}
|
||||
|
||||
if (igIsItemHovered(ImGuiHoveredFlags_None) && this->activePreset >= 0)
|
||||
igSetTooltip("This will overwrite the preset named: %s",
|
||||
stringlist_at(this->presets, this->activePreset));
|
||||
|
||||
igSameLine(0.0f, -1.0f);
|
||||
|
||||
if (igButton("Save preset as...", (ImVec2) { 0.0f, 0.0f }))
|
||||
{
|
||||
this->presetEdit[0] = '\0';
|
||||
igOpenPopup("Save preset as...", ImGuiPopupFlags_None);
|
||||
}
|
||||
|
||||
igSameLine(0.0f, -1.0f);
|
||||
|
||||
if (igButton("Delete preset", (ImVec2) { 0.0f, 0.0f }) && this->activePreset >= 0)
|
||||
deletePreset(this);
|
||||
|
||||
if (igBeginPopupModal("Save preset as...", NULL, ImGuiWindowFlags_AlwaysAutoResize))
|
||||
{
|
||||
igText("Enter a name for the new preset:");
|
||||
|
||||
if (!igIsAnyItemActive())
|
||||
igSetKeyboardFocusHere(0);
|
||||
|
||||
if (igInputText("##name", this->presetEdit, sizeof(this->presetEdit),
|
||||
ImGuiInputTextFlags_EnterReturnsTrue, NULL, NULL))
|
||||
{
|
||||
savePresetAs(this);
|
||||
igCloseCurrentPopup();
|
||||
}
|
||||
|
||||
if (igButton("Save", (ImVec2) { 0.0f, 0.0f }))
|
||||
{
|
||||
savePresetAs(this);
|
||||
igCloseCurrentPopup();
|
||||
}
|
||||
|
||||
igSameLine(0.0f, -1.0f);
|
||||
if (igButton("Cancel", (ImVec2) { 0.0f, 0.0f }))
|
||||
igCloseCurrentPopup();
|
||||
|
||||
igEndPopup();
|
||||
}
|
||||
|
||||
if (this->presetError)
|
||||
igOpenPopup("Preset error", ImGuiPopupFlags_None);
|
||||
|
||||
if (igBeginPopupModal("Preset error", NULL, ImGuiWindowFlags_AlwaysAutoResize))
|
||||
{
|
||||
igText("%s", this->presetError);
|
||||
|
||||
if (!igIsAnyItemActive())
|
||||
igSetKeyboardFocusHere(0);
|
||||
|
||||
if (igButton("OK", (ImVec2) { 0.0f, 0.0f }))
|
||||
{
|
||||
free(this->presetError);
|
||||
this->presetError = NULL;
|
||||
igCloseCurrentPopup();
|
||||
}
|
||||
|
||||
igEndPopup();
|
||||
}
|
||||
|
||||
return redraw;
|
||||
}
|
||||
|
||||
static void drawDropTarget(void)
|
||||
{
|
||||
igPushStyleColorVec4(ImGuiCol_Separator, (ImVec4) { 1.0f, 1.0f, 0.0f, 1.0f });
|
||||
igSeparator();
|
||||
igPopStyleColor(1);
|
||||
}
|
||||
|
||||
static void configUI(void * opaque, int * id)
|
||||
{
|
||||
struct EGL_PostProcess * this = opaque;
|
||||
|
||||
bool redraw = false;
|
||||
redraw |= presetsUI(this);
|
||||
igSeparator();
|
||||
|
||||
static size_t mouseIdx = -1;
|
||||
static bool moving = false;
|
||||
static size_t moveIdx = 0;
|
||||
bool doMove = false;
|
||||
|
||||
ImVec2 window, pos;
|
||||
igGetWindowPos(&window);
|
||||
igGetMousePos(&pos);
|
||||
|
||||
EGL_Filter ** filters = vector_data(&this->filters);
|
||||
size_t count = vector_size(&this->filters);
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
EGL_Filter * filter = filters[i];
|
||||
|
||||
if (moving && mouseIdx < moveIdx && i == mouseIdx)
|
||||
drawDropTarget();
|
||||
|
||||
igPushIDPtr(filter);
|
||||
bool draw = igCollapsingHeaderBoolPtr(filter->ops.name, NULL, 0);
|
||||
if (igIsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
|
||||
mouseIdx = i;
|
||||
|
||||
bool active = igIsItemActive();
|
||||
if (draw)
|
||||
redraw |= egl_filterImguiConfig(filter);
|
||||
igPopID();
|
||||
|
||||
if (moving)
|
||||
{
|
||||
if (!igIsMouseDragging(ImGuiMouseButton_Left, -1.0f))
|
||||
{
|
||||
moving = false;
|
||||
doMove = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (active && igIsMouseDragging(ImGuiMouseButton_Left, -1.0f))
|
||||
{
|
||||
moveIdx = mouseIdx;
|
||||
moving = true;
|
||||
}
|
||||
|
||||
if (moving && mouseIdx > moveIdx && i == mouseIdx)
|
||||
drawDropTarget();
|
||||
}
|
||||
|
||||
if (moving)
|
||||
{
|
||||
igSetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
igSetTooltip(filters[moveIdx]->ops.name);
|
||||
}
|
||||
|
||||
if (doMove)
|
||||
{
|
||||
EGL_Filter * tmp = filters[moveIdx];
|
||||
if (mouseIdx > moveIdx) // moving down
|
||||
memmove(filters + moveIdx, filters + moveIdx + 1, (mouseIdx - moveIdx) * sizeof(EGL_Filter *));
|
||||
else // moving up
|
||||
memmove(filters + mouseIdx + 1, filters + mouseIdx, (moveIdx - mouseIdx) * sizeof(EGL_Filter *));
|
||||
filters[mouseIdx] = tmp;
|
||||
}
|
||||
|
||||
if (redraw)
|
||||
{
|
||||
atomic_store(&this->modified, true);
|
||||
app_invalidateWindow(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool egl_postProcessInit(EGL_PostProcess ** pp)
|
||||
{
|
||||
EGL_PostProcess * this = calloc(1, sizeof(*this));
|
||||
if (!this)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!vector_create(&this->filters, sizeof(EGL_Filter *), ARRAY_LENGTH(EGL_Filters)))
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate the filter list");
|
||||
goto error_this;
|
||||
}
|
||||
|
||||
if (!egl_desktopRectsInit(&this->rects, 1))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the desktop rects");
|
||||
goto error_filters;
|
||||
}
|
||||
|
||||
loadPresetList(this);
|
||||
reorderFilters(this);
|
||||
app_overlayConfigRegisterTab("EGL Filters", configUI, this);
|
||||
|
||||
*pp = this;
|
||||
return true;
|
||||
|
||||
error_filters:
|
||||
vector_destroy(&this->filters);
|
||||
|
||||
error_this:
|
||||
free(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
void egl_postProcessFree(EGL_PostProcess ** pp)
|
||||
{
|
||||
if (!*pp)
|
||||
return;
|
||||
|
||||
EGL_PostProcess * this = *pp;
|
||||
|
||||
EGL_Filter ** filter;
|
||||
vector_forEachRef(filter, &this->filters)
|
||||
egl_filterFree(filter);
|
||||
vector_destroy(&this->filters);
|
||||
|
||||
free(this->presetDir);
|
||||
if (this->presets)
|
||||
stringlist_free(&this->presets);
|
||||
|
||||
egl_desktopRectsFree(&this->rects);
|
||||
free(this->presetError);
|
||||
free(this);
|
||||
*pp = NULL;
|
||||
}
|
||||
|
||||
bool egl_postProcessAdd(EGL_PostProcess * this, const EGL_FilterOps * ops)
|
||||
{
|
||||
EGL_Filter * filter;
|
||||
if (!egl_filterInit(ops, &filter))
|
||||
return false;
|
||||
|
||||
vector_push(&this->filters, &filter);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_postProcessConfigModified(EGL_PostProcess * this)
|
||||
{
|
||||
return atomic_load(&this->modified);
|
||||
}
|
||||
|
||||
bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
|
||||
EGL_DesktopRects * rects, int desktopWidth, int desktopHeight,
|
||||
unsigned int targetX, unsigned int targetY)
|
||||
{
|
||||
if (targetX == 0 && targetY == 0)
|
||||
DEBUG_FATAL("targetX || targetY == 0");
|
||||
|
||||
EGL_Filter * lastFilter = NULL;
|
||||
unsigned int sizeX, sizeY;
|
||||
|
||||
GLuint texture;
|
||||
if (egl_textureGet(tex, &texture, &sizeX, &sizeY) != EGL_TEX_STATUS_OK)
|
||||
return false;
|
||||
|
||||
if (atomic_exchange(&this->modified, false))
|
||||
{
|
||||
rects = this->rects;
|
||||
egl_desktopRectsUpdate(rects, NULL, desktopWidth, desktopHeight);
|
||||
}
|
||||
|
||||
GLfloat matrix[6];
|
||||
egl_desktopRectsMatrix(matrix, desktopWidth, desktopHeight, 0.0f, 0.0f,
|
||||
1.0f, 1.0f, LG_ROTATE_0);
|
||||
|
||||
EGL_FilterRects filterRects = {
|
||||
.rects = rects,
|
||||
.matrix = matrix,
|
||||
.width = desktopWidth,
|
||||
.height = desktopHeight,
|
||||
};
|
||||
|
||||
EGL_Filter * filter;
|
||||
vector_forEach(filter, &this->filters)
|
||||
{
|
||||
egl_filterSetOutputResHint(filter, targetX, targetY);
|
||||
|
||||
if (!egl_filterSetup(filter, tex->format.pixFmt, sizeX, sizeY) ||
|
||||
!egl_filterPrepare(filter))
|
||||
continue;
|
||||
|
||||
texture = egl_filterRun(filter, &filterRects, texture);
|
||||
egl_filterGetOutputRes(filter, &sizeX, &sizeY);
|
||||
|
||||
if (lastFilter)
|
||||
egl_filterRelease(lastFilter);
|
||||
|
||||
lastFilter = filter;
|
||||
}
|
||||
|
||||
this->output = texture;
|
||||
this->outputX = sizeX;
|
||||
this->outputY = sizeY;
|
||||
return true;
|
||||
}
|
||||
|
||||
GLuint egl_postProcessGetOutput(EGL_PostProcess * this,
|
||||
unsigned int * outputX, unsigned int * outputY)
|
||||
{
|
||||
*outputX = this->outputX;
|
||||
*outputY = this->outputY;
|
||||
return this->output;
|
||||
}
|
||||
47
client/renderers/EGL/postprocess.h
Normal file
47
client/renderers/EGL/postprocess.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "desktop_rects.h"
|
||||
#include "filter.h"
|
||||
#include "texture.h"
|
||||
|
||||
typedef struct EGL_PostProcess EGL_PostProcess;
|
||||
|
||||
void egl_postProcessEarlyInit(void);
|
||||
|
||||
bool egl_postProcessInit(EGL_PostProcess ** pp);
|
||||
void egl_postProcessFree(EGL_PostProcess ** pp);
|
||||
|
||||
/* create and add a filter to this processor */
|
||||
bool egl_postProcessAdd(EGL_PostProcess * this, const EGL_FilterOps * ops);
|
||||
|
||||
/* returns true if the configuration was modified since the last run */
|
||||
bool egl_postProcessConfigModified(EGL_PostProcess * this);
|
||||
|
||||
/* apply the filters to the supplied texture
|
||||
* targetX/Y is the final target output dimension hint if scalers are present */
|
||||
bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
|
||||
EGL_DesktopRects * rects, int desktopWidth, int desktopHeight,
|
||||
unsigned int targetX, unsigned int targetY);
|
||||
|
||||
GLuint egl_postProcessGetOutput(EGL_PostProcess * this,
|
||||
unsigned int * outputX, unsigned int * outputY);
|
||||
@@ -1,21 +1,22 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "shader.h"
|
||||
#include "common/debug.h"
|
||||
@@ -29,34 +30,41 @@ struct EGL_Shader
|
||||
{
|
||||
bool hasShader;
|
||||
GLuint shader;
|
||||
|
||||
EGL_Uniform * uniforms;
|
||||
int uniformCount;
|
||||
int uniformUsed;
|
||||
};
|
||||
|
||||
bool egl_shader_init(EGL_Shader ** this)
|
||||
bool egl_shaderInit(EGL_Shader ** this)
|
||||
{
|
||||
*this = (EGL_Shader *)malloc(sizeof(EGL_Shader));
|
||||
*this = calloc(1, sizeof(EGL_Shader));
|
||||
if (!*this)
|
||||
{
|
||||
DEBUG_ERROR("Failed to malloc EGL_Shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(*this, 0, sizeof(EGL_Shader));
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_shader_free(EGL_Shader ** this)
|
||||
void egl_shaderFree(EGL_Shader ** shader)
|
||||
{
|
||||
if (!*this)
|
||||
EGL_Shader * this = *shader;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
if ((*this)->hasShader)
|
||||
glDeleteProgram((*this)->shader);
|
||||
if (this->hasShader)
|
||||
glDeleteProgram(this->shader);
|
||||
|
||||
free(*this);
|
||||
*this = NULL;
|
||||
egl_shaderFreeUniforms(this);
|
||||
free(this->uniforms);
|
||||
|
||||
free(this);
|
||||
*shader = NULL;
|
||||
}
|
||||
|
||||
bool egl_shader_load(EGL_Shader * this, const char * vertex_file, const char * fragment_file)
|
||||
bool egl_shaderLoad(EGL_Shader * this, const char * vertex_file, const char * fragment_file)
|
||||
{
|
||||
char * vertex_code, * fragment_code;
|
||||
size_t vertex_size, fragment_size;
|
||||
@@ -78,13 +86,14 @@ bool egl_shader_load(EGL_Shader * this, const char * vertex_file, const char * f
|
||||
|
||||
DEBUG_INFO("Loaded fragment shader: %s", fragment_file);
|
||||
|
||||
bool ret = egl_shader_compile(this, vertex_code, vertex_size, fragment_code, fragment_size);
|
||||
bool ret = egl_shaderCompile(this, vertex_code, vertex_size, fragment_code, fragment_size);
|
||||
free(vertex_code);
|
||||
free(fragment_code);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool egl_shader_compile(EGL_Shader * this, const char * vertex_code, size_t vertex_size, const char * fragment_code, size_t fragment_size)
|
||||
bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
|
||||
size_t vertex_size, const char * fragment_code, size_t fragment_size)
|
||||
{
|
||||
if (this->hasShader)
|
||||
{
|
||||
@@ -185,15 +194,280 @@ bool egl_shader_compile(EGL_Shader * this, const char * vertex_code, size_t vert
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_shader_use(EGL_Shader * this)
|
||||
void egl_shaderSetUniforms(EGL_Shader * this, EGL_Uniform * uniforms, int count)
|
||||
{
|
||||
egl_shaderFreeUniforms(this);
|
||||
if (count > this->uniformCount)
|
||||
{
|
||||
free(this->uniforms);
|
||||
this->uniforms = malloc(sizeof(*this->uniforms) * count);
|
||||
this->uniformCount = count;
|
||||
}
|
||||
|
||||
this->uniformUsed = count;
|
||||
memcpy(this->uniforms, uniforms, sizeof(*this->uniforms) * count);
|
||||
|
||||
for(int i = 0; i < this->uniformUsed; ++i)
|
||||
{
|
||||
switch(this->uniforms[i].type)
|
||||
{
|
||||
case EGL_UNIFORM_TYPE_1FV:
|
||||
case EGL_UNIFORM_TYPE_2FV:
|
||||
case EGL_UNIFORM_TYPE_3FV:
|
||||
case EGL_UNIFORM_TYPE_4FV:
|
||||
case EGL_UNIFORM_TYPE_1IV:
|
||||
case EGL_UNIFORM_TYPE_2IV:
|
||||
case EGL_UNIFORM_TYPE_3IV:
|
||||
case EGL_UNIFORM_TYPE_4IV:
|
||||
case EGL_UNIFORM_TYPE_1UIV:
|
||||
case EGL_UNIFORM_TYPE_2UIV:
|
||||
case EGL_UNIFORM_TYPE_3UIV:
|
||||
case EGL_UNIFORM_TYPE_4UIV:
|
||||
countedBufferAddRef(this->uniforms[i].v);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_M2FV:
|
||||
case EGL_UNIFORM_TYPE_M3FV:
|
||||
case EGL_UNIFORM_TYPE_M4FV:
|
||||
case EGL_UNIFORM_TYPE_M2x3FV:
|
||||
case EGL_UNIFORM_TYPE_M3x2FV:
|
||||
case EGL_UNIFORM_TYPE_M2x4FV:
|
||||
case EGL_UNIFORM_TYPE_M4x2FV:
|
||||
case EGL_UNIFORM_TYPE_M3x4FV:
|
||||
case EGL_UNIFORM_TYPE_M4x3FV:
|
||||
countedBufferAddRef(this->uniforms[i].m.v);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void egl_shaderFreeUniforms(EGL_Shader * this)
|
||||
{
|
||||
for(int i = 0; i < this->uniformUsed; ++i)
|
||||
{
|
||||
switch(this->uniforms[i].type)
|
||||
{
|
||||
case EGL_UNIFORM_TYPE_1FV:
|
||||
case EGL_UNIFORM_TYPE_2FV:
|
||||
case EGL_UNIFORM_TYPE_3FV:
|
||||
case EGL_UNIFORM_TYPE_4FV:
|
||||
case EGL_UNIFORM_TYPE_1IV:
|
||||
case EGL_UNIFORM_TYPE_2IV:
|
||||
case EGL_UNIFORM_TYPE_3IV:
|
||||
case EGL_UNIFORM_TYPE_4IV:
|
||||
case EGL_UNIFORM_TYPE_1UIV:
|
||||
case EGL_UNIFORM_TYPE_2UIV:
|
||||
case EGL_UNIFORM_TYPE_3UIV:
|
||||
case EGL_UNIFORM_TYPE_4UIV:
|
||||
countedBufferRelease(&this->uniforms[i].v);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_M2FV:
|
||||
case EGL_UNIFORM_TYPE_M3FV:
|
||||
case EGL_UNIFORM_TYPE_M4FV:
|
||||
case EGL_UNIFORM_TYPE_M2x3FV:
|
||||
case EGL_UNIFORM_TYPE_M3x2FV:
|
||||
case EGL_UNIFORM_TYPE_M2x4FV:
|
||||
case EGL_UNIFORM_TYPE_M4x2FV:
|
||||
case EGL_UNIFORM_TYPE_M3x4FV:
|
||||
case EGL_UNIFORM_TYPE_M4x3FV:
|
||||
countedBufferRelease(&this->uniforms[i].m.v);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->uniformUsed = 0;
|
||||
}
|
||||
|
||||
void egl_shaderUse(EGL_Shader * this)
|
||||
{
|
||||
if (this->hasShader)
|
||||
glUseProgram(this->shader);
|
||||
else
|
||||
DEBUG_ERROR("Shader program has not been compiled");
|
||||
|
||||
for(int i = 0; i < this->uniformUsed; ++i)
|
||||
{
|
||||
EGL_Uniform * uniform = &this->uniforms[i];
|
||||
switch(uniform->type)
|
||||
{
|
||||
case EGL_UNIFORM_TYPE_1F:
|
||||
glUniform1f(uniform->location, uniform->f[0]);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_2F:
|
||||
glUniform2f(uniform->location, uniform->f[0], uniform->f[1]);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_3F:
|
||||
glUniform3f(uniform->location, uniform->f[0], uniform->f[1],
|
||||
uniform->f[2]);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_4F:
|
||||
glUniform4f(uniform->location, uniform->f[0], uniform->f[1],
|
||||
uniform->f[2], uniform->f[3]);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_1I:
|
||||
glUniform1i(uniform->location, uniform->i[0]);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_2I:
|
||||
glUniform2i(uniform->location, uniform->i[0], uniform->i[1]);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_3I:
|
||||
glUniform3i(uniform->location, uniform->i[0], uniform->i[1],
|
||||
uniform->i[2]);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_4I:
|
||||
glUniform4i(uniform->location, uniform->i[0], uniform->i[1],
|
||||
uniform->i[2], uniform->i[3]);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_1UI:
|
||||
glUniform1ui(uniform->location, uniform->ui[0]);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_2UI:
|
||||
glUniform2ui(uniform->location, uniform->ui[0], uniform->ui[1]);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_3UI:
|
||||
glUniform3ui(uniform->location, uniform->ui[0], uniform->ui[1],
|
||||
uniform->ui[2]);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_4UI:
|
||||
glUniform4ui(uniform->location, uniform->ui[0], uniform->ui[1],
|
||||
uniform->ui[2], uniform->ui[3]);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_1FV:
|
||||
glUniform1fv(uniform->location, uniform->v->size / sizeof(GLfloat),
|
||||
(const GLfloat *)uniform->v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_2FV:
|
||||
glUniform2fv(uniform->location, uniform->v->size / sizeof(GLfloat) / 2,
|
||||
(const GLfloat *)uniform->v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_3FV:
|
||||
glUniform3fv(uniform->location, uniform->v->size / sizeof(GLfloat) / 3,
|
||||
(const GLfloat *)uniform->v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_4FV:
|
||||
glUniform4fv(uniform->location, uniform->v->size / sizeof(GLfloat) / 4,
|
||||
(const GLfloat *)uniform->v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_1IV:
|
||||
glUniform1iv(uniform->location, uniform->v->size / sizeof(GLint),
|
||||
(const GLint *)uniform->v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_2IV:
|
||||
glUniform2iv(uniform->location, uniform->v->size / sizeof(GLint) / 2,
|
||||
(const GLint *)uniform->v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_3IV:
|
||||
glUniform3iv(uniform->location, uniform->v->size / sizeof(GLint) / 3,
|
||||
(const GLint *)uniform->v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_4IV:
|
||||
glUniform4iv(uniform->location, uniform->v->size / sizeof(GLint) / 4,
|
||||
(const GLint *)uniform->v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_1UIV:
|
||||
glUniform1uiv(uniform->location, uniform->v->size / sizeof(GLuint),
|
||||
(const GLuint *)uniform->v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_2UIV:
|
||||
glUniform2uiv(uniform->location, uniform->v->size / sizeof(GLuint) / 2,
|
||||
(const GLuint *)uniform->v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_3UIV:
|
||||
glUniform3uiv(uniform->location, uniform->v->size / sizeof(GLuint) / 3,
|
||||
(const GLuint *)uniform->v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_4UIV:
|
||||
glUniform4uiv(uniform->location, uniform->v->size / sizeof(GLuint) / 4,
|
||||
(const GLuint *)uniform->v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_M2FV:
|
||||
glUniformMatrix2fv(uniform->location,
|
||||
uniform->v->size / sizeof(GLfloat) / 2,
|
||||
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_M3FV:
|
||||
glUniformMatrix3fv(uniform->location,
|
||||
uniform->v->size / sizeof(GLfloat) / 3,
|
||||
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_M4FV:
|
||||
glUniformMatrix4fv(uniform->location,
|
||||
uniform->v->size / sizeof(GLfloat) / 4,
|
||||
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_M2x3FV:
|
||||
glUniformMatrix2x3fv(uniform->location,
|
||||
uniform->v->size / sizeof(GLfloat) / 6,
|
||||
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_M3x2FV:
|
||||
glUniformMatrix3x2fv(uniform->location,
|
||||
uniform->v->size / sizeof(GLfloat) / 6,
|
||||
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_M2x4FV:
|
||||
glUniformMatrix2x4fv(uniform->location,
|
||||
uniform->v->size / sizeof(GLfloat) / 8,
|
||||
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_M4x2FV:
|
||||
glUniformMatrix4x2fv(uniform->location,
|
||||
uniform->v->size / sizeof(GLfloat) / 8,
|
||||
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_M3x4FV:
|
||||
glUniformMatrix3x4fv(uniform->location,
|
||||
uniform->v->size / sizeof(GLfloat) / 12,
|
||||
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
|
||||
break;
|
||||
|
||||
case EGL_UNIFORM_TYPE_M4x3FV:
|
||||
glUniformMatrix4x3fv(uniform->location,
|
||||
uniform->v->size / sizeof(GLfloat) / 12,
|
||||
uniform->m.transpose, (const GLfloat *)uniform->m.v->data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void egl_shader_associate_textures(EGL_Shader * this, const int count)
|
||||
void egl_shaderAssocTextures(EGL_Shader * this, const int count)
|
||||
{
|
||||
char name[] = "sampler1";
|
||||
glUseProgram(this->shader);
|
||||
@@ -211,7 +485,7 @@ void egl_shader_associate_textures(EGL_Shader * this, const int count)
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
GLint egl_shader_get_uniform_location(EGL_Shader * this, const char * name)
|
||||
GLint egl_shaderGetUniform(EGL_Shader * this, const char * name)
|
||||
{
|
||||
if (!this->shader)
|
||||
{
|
||||
|
||||
@@ -1,37 +1,113 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 2 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2021 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include <GLES3/gl3.h>
|
||||
|
||||
#include "common/countedbuffer.h"
|
||||
|
||||
typedef struct EGL_Shader EGL_Shader;
|
||||
|
||||
bool egl_shader_init(EGL_Shader ** shader);
|
||||
void egl_shader_free(EGL_Shader ** shader);
|
||||
enum EGL_UniformType
|
||||
{
|
||||
EGL_UNIFORM_TYPE_1F,
|
||||
EGL_UNIFORM_TYPE_2F,
|
||||
EGL_UNIFORM_TYPE_3F,
|
||||
EGL_UNIFORM_TYPE_4F,
|
||||
EGL_UNIFORM_TYPE_1I,
|
||||
EGL_UNIFORM_TYPE_2I,
|
||||
EGL_UNIFORM_TYPE_3I,
|
||||
EGL_UNIFORM_TYPE_4I,
|
||||
EGL_UNIFORM_TYPE_1UI,
|
||||
EGL_UNIFORM_TYPE_2UI,
|
||||
EGL_UNIFORM_TYPE_3UI,
|
||||
EGL_UNIFORM_TYPE_4UI,
|
||||
|
||||
bool egl_shader_load (EGL_Shader * model, const char * vertex_file, const char * fragment_file);
|
||||
bool egl_shader_compile(EGL_Shader * model, const char * vertex_code, size_t vertex_size, const char * fragment_code, size_t fragment_size);
|
||||
void egl_shader_use (EGL_Shader * shader);
|
||||
// vectors
|
||||
EGL_UNIFORM_TYPE_1FV,
|
||||
EGL_UNIFORM_TYPE_2FV,
|
||||
EGL_UNIFORM_TYPE_3FV,
|
||||
EGL_UNIFORM_TYPE_4FV,
|
||||
EGL_UNIFORM_TYPE_1IV,
|
||||
EGL_UNIFORM_TYPE_2IV,
|
||||
EGL_UNIFORM_TYPE_3IV,
|
||||
EGL_UNIFORM_TYPE_4IV,
|
||||
EGL_UNIFORM_TYPE_1UIV,
|
||||
EGL_UNIFORM_TYPE_2UIV,
|
||||
EGL_UNIFORM_TYPE_3UIV,
|
||||
EGL_UNIFORM_TYPE_4UIV,
|
||||
|
||||
void egl_shader_associate_textures(EGL_Shader * shader, const int count);
|
||||
GLint egl_shader_get_uniform_location(EGL_Shader * shader, const char * name);
|
||||
// matrices
|
||||
EGL_UNIFORM_TYPE_M2FV,
|
||||
EGL_UNIFORM_TYPE_M3FV,
|
||||
EGL_UNIFORM_TYPE_M4FV,
|
||||
EGL_UNIFORM_TYPE_M2x3FV,
|
||||
EGL_UNIFORM_TYPE_M3x2FV,
|
||||
EGL_UNIFORM_TYPE_M2x4FV,
|
||||
EGL_UNIFORM_TYPE_M4x2FV,
|
||||
EGL_UNIFORM_TYPE_M3x4FV,
|
||||
EGL_UNIFORM_TYPE_M4x3FV
|
||||
};
|
||||
|
||||
typedef struct EGL_Uniform
|
||||
{
|
||||
enum EGL_UniformType type;
|
||||
GLint location;
|
||||
|
||||
union
|
||||
{
|
||||
GLfloat f [4];
|
||||
GLint i [4];
|
||||
GLuint ui[4];
|
||||
|
||||
CountedBuffer * v;
|
||||
|
||||
struct
|
||||
{
|
||||
CountedBuffer * v;
|
||||
GLboolean transpose;
|
||||
}
|
||||
m;
|
||||
};
|
||||
}
|
||||
EGL_Uniform;
|
||||
|
||||
bool egl_shaderInit(EGL_Shader ** shader);
|
||||
void egl_shaderFree(EGL_Shader ** shader);
|
||||
|
||||
bool egl_shaderLoad(EGL_Shader * model, const char * vertex_file,
|
||||
const char * fragment_file);
|
||||
|
||||
bool egl_shaderCompile(EGL_Shader * model, const char * vertex_code,
|
||||
size_t vertex_size, const char * fragment_code, size_t fragment_size);
|
||||
|
||||
void egl_shaderSetUniforms(EGL_Shader * shader, EGL_Uniform * uniforms,
|
||||
int count);
|
||||
void egl_shaderFreeUniforms(EGL_Shader * shader);
|
||||
|
||||
void egl_shaderUse(EGL_Shader * shader);
|
||||
|
||||
void egl_shaderAssocTextures(EGL_Shader * shader, const int count);
|
||||
|
||||
GLint egl_shaderGetUniform(EGL_Shader * shader, const char * name);
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#version 300 es
|
||||
|
||||
in highp vec2 uv;
|
||||
in highp vec2 sz;
|
||||
out highp vec4 color;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = texelFetch(sampler1, ivec2(uv * sz), 0);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#version 300 es
|
||||
|
||||
layout(location = 0) in vec3 vertexPosition_modelspace;
|
||||
layout(location = 1) in vec2 vertexUV;
|
||||
|
||||
uniform vec2 screen;
|
||||
uniform ivec2 size;
|
||||
uniform vec4 color;
|
||||
|
||||
out highp vec2 uv;
|
||||
out highp vec2 sz;
|
||||
out highp vec4 c;
|
||||
|
||||
void main()
|
||||
{
|
||||
sz = vec2(size) + 0.5;
|
||||
|
||||
gl_Position.xyz = vertexPosition_modelspace;
|
||||
gl_Position.w = 1.0;
|
||||
gl_Position.xy *= screen.xy * sz;
|
||||
|
||||
uv = vertexUV;
|
||||
c = color;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#version 300 es
|
||||
|
||||
in highp vec4 c;
|
||||
out highp vec4 color;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = c;
|
||||
}
|
||||
15
client/renderers/EGL/shader/basic.vert
Normal file
15
client/renderers/EGL/shader/basic.vert
Normal file
@@ -0,0 +1,15 @@
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
|
||||
layout(location = 0) in vec2 vertex;
|
||||
out vec2 fragCoord;
|
||||
|
||||
uniform vec2 desktopSize;
|
||||
uniform mat3x2 transform;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 pos = transform * vec3(vertex, 1.0);
|
||||
gl_Position = vec4(pos.x, -pos.y, 0.0, 1.0);
|
||||
fragCoord = vertex / desktopSize;
|
||||
}
|
||||
37
client/renderers/EGL/shader/color_blind.h
Normal file
37
client/renderers/EGL/shader/color_blind.h
Normal file
@@ -0,0 +1,37 @@
|
||||
vec4 cbTransform(vec4 color, int cbMode)
|
||||
{
|
||||
float L = (17.8824000 * color.r) + (43.516100 * color.g) + (4.11935 * color.b);
|
||||
float M = (03.4556500 * color.r) + (27.155400 * color.g) + (3.86714 * color.b);
|
||||
float S = (00.0299566 * color.r) + (00.184309 * color.g) + (1.46709 * color.b);
|
||||
float l, m, s;
|
||||
|
||||
if (cbMode == 1) // Protanope
|
||||
{
|
||||
l = 0.0f * L + 2.02344f * M + -2.52581f * S;
|
||||
m = 0.0f * L + 1.0f * M + 0.0f * S;
|
||||
s = 0.0f * L + 0.0f * M + 1.0f * S;
|
||||
}
|
||||
else if (cbMode == 2) // Deuteranope
|
||||
{
|
||||
l = 1.000000 * L + 0.0f * M + 0.00000 * S;
|
||||
m = 0.494207 * L + 0.0f * M + 1.24827 * S;
|
||||
s = 0.000000 * L + 0.0f * M + 1.00000 * S;
|
||||
}
|
||||
else if (cbMode == 3) // Tritanope
|
||||
{
|
||||
l = 1.000000 * L + 0.000000 * M + 0.0 * S;
|
||||
m = 0.000000 * L + 1.000000 * M + 0.0 * S;
|
||||
s = -0.395913 * L + 0.801109 * M + 0.0 * S;
|
||||
}
|
||||
|
||||
vec4 error;
|
||||
error.r = ( 0.080944447900 * l) + (-0.13050440900 * m) + ( 0.116721066 * s);
|
||||
error.g = (-0.010248533500 * l) + ( 0.05401932660 * m) + (-0.113614708 * s);
|
||||
error.b = (-0.000365296938 * l) + (-0.00412161469 * m) + ( 0.693511405 * s);
|
||||
error.a = 0.0;
|
||||
|
||||
error = color - error;
|
||||
color.g += (error.r * 0.7) + (error.g * 1.0);
|
||||
color.b += (error.r * 0.7) + (error.b * 1.0);
|
||||
return color;
|
||||
}
|
||||
32
client/renderers/EGL/shader/compat.h
Normal file
32
client/renderers/EGL/shader/compat.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#if __VERSION__ == 300
|
||||
vec4 textureGather(sampler2D tex, vec2 uv, int comp)
|
||||
{
|
||||
vec4 c0 = textureOffset(tex, uv, ivec2(0,1));
|
||||
vec4 c1 = textureOffset(tex, uv, ivec2(1,1));
|
||||
vec4 c2 = textureOffset(tex, uv, ivec2(1,0));
|
||||
vec4 c3 = textureOffset(tex, uv, ivec2(0,0));
|
||||
return vec4(c0[comp], c1[comp], c2[comp],c3[comp]);
|
||||
}
|
||||
#elif __VERSION__ < 300
|
||||
vec4 textureGather(sampler2D tex, vec2 uv, int comp)
|
||||
{
|
||||
vec4 c3 = texture2D(tex, uv);
|
||||
return vec4(c3[comp], c3[comp], c3[comp],c3[comp]);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __VERSION__ < 310
|
||||
uint bitfieldExtract(uint val, int off, int size)
|
||||
{
|
||||
uint mask = uint((1 << size) - 1);
|
||||
return uint(val >> off) & mask;
|
||||
}
|
||||
|
||||
uint bitfieldInsert(uint a, uint b, int c, int d)
|
||||
{
|
||||
uint mask = ~(0xffffffffu << d) << c;
|
||||
mask = ~mask;
|
||||
a &= mask;
|
||||
return a | (b << c);
|
||||
}
|
||||
#endif
|
||||
@@ -1,12 +1,13 @@
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
|
||||
layout(location = 0) in vec3 vertexPosition_modelspace;
|
||||
layout(location = 1) in vec2 vertexUV;
|
||||
|
||||
uniform vec4 mouse;
|
||||
uniform lowp int rotate;
|
||||
uniform int rotate;
|
||||
|
||||
out highp vec2 uv;
|
||||
out vec2 uv;
|
||||
|
||||
void main()
|
||||
{
|
||||
@@ -38,6 +39,6 @@ void main()
|
||||
gl_Position.y = muv.x;
|
||||
}
|
||||
|
||||
gl_Position.w = 1.0;
|
||||
gl_Position.zw = vec2(0.0, 1.0);
|
||||
uv = vertexUV;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
|
||||
in highp vec2 uv;
|
||||
out highp vec4 color;
|
||||
in vec2 uv;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp vec4 tmp = texture(sampler1, uv);
|
||||
vec4 tmp = texture(sampler1, uv);
|
||||
if (tmp.rgb == vec3(0.0, 0.0, 0.0))
|
||||
discard;
|
||||
color = tmp;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
|
||||
in highp vec2 uv;
|
||||
out highp vec4 color;
|
||||
#include "color_blind.h"
|
||||
|
||||
in vec2 uv;
|
||||
out vec4 color;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
|
||||
uniform lowp int rotate;
|
||||
uniform int cbMode;
|
||||
|
||||
void main()
|
||||
@@ -13,39 +15,5 @@ void main()
|
||||
color = texture(sampler1, uv);
|
||||
|
||||
if (cbMode > 0)
|
||||
{
|
||||
highp float L = (17.8824000 * color.r) + (43.516100 * color.g) + (4.11935 * color.b);
|
||||
highp float M = (03.4556500 * color.r) + (27.155400 * color.g) + (3.86714 * color.b);
|
||||
highp float S = (00.0299566 * color.r) + (00.184309 * color.g) + (1.46709 * color.b);
|
||||
highp float l, m, s;
|
||||
|
||||
if (cbMode == 1) // Protanope
|
||||
{
|
||||
l = 0.0f * L + 2.02344f * M + -2.52581f * S;
|
||||
m = 0.0f * L + 1.0f * M + 0.0f * S;
|
||||
s = 0.0f * L + 0.0f * M + 1.0f * S;
|
||||
}
|
||||
else if (cbMode == 2) // Deuteranope
|
||||
{
|
||||
l = 1.000000 * L + 0.0f * M + 0.00000 * S;
|
||||
m = 0.494207 * L + 0.0f * M + 1.24827 * S;
|
||||
s = 0.000000 * L + 0.0f * M + 1.00000 * S;
|
||||
}
|
||||
else if (cbMode == 3) // Tritanope
|
||||
{
|
||||
l = 1.000000 * L + 0.000000 * M + 0.0 * S;
|
||||
m = 0.000000 * L + 1.000000 * M + 0.0 * S;
|
||||
s = -0.395913 * L + 0.801109 * M + 0.0 * S;
|
||||
}
|
||||
|
||||
highp vec4 error;
|
||||
error.r = ( 0.080944447900 * l) + (-0.13050440900 * m) + ( 0.116721066 * s);
|
||||
error.g = (-0.010248533500 * l) + ( 0.05401932660 * m) + (-0.113614708 * s);
|
||||
error.b = (-0.000365296938 * l) + (-0.00412161469 * m) + ( 0.693511405 * s);
|
||||
error.a = 0.0;
|
||||
|
||||
error = color - error;
|
||||
color.g += (error.r * 0.7) + (error.g * 1.0);
|
||||
color.b += (error.r * 0.7) + (error.b * 1.0);
|
||||
}
|
||||
color = cbTransform(color, cbMode);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user