mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-02-18 16:49:48 +00:00
Compare commits
570 Commits
a12
...
Release/B2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76710ef201 | ||
|
|
e20c8a5cc7 | ||
|
|
4f4d2dbf42 | ||
|
|
8692e9af80 | ||
|
|
7d2b39058c | ||
|
|
6927dbecd2 | ||
|
|
f9b6dcc986 | ||
|
|
5c912e3c27 | ||
|
|
7e362050f7 | ||
|
|
10fbdeb294 | ||
|
|
72d70e8322 | ||
|
|
c66a339bbc | ||
|
|
1c7961daeb | ||
|
|
cdc3384883 | ||
|
|
969effedde | ||
|
|
dc4d1d49fa | ||
|
|
4e1f947a09 | ||
|
|
15d1a74291 | ||
|
|
7dba6b9b08 | ||
|
|
a5ad531004 | ||
|
|
c119b3dcca | ||
|
|
e2f2437ef4 | ||
|
|
b2980fea63 | ||
|
|
2b518690b8 | ||
|
|
92aca75792 | ||
|
|
64fdb8b7bb | ||
|
|
431ae3fc55 | ||
|
|
380b5df9f9 | ||
|
|
c7330167cf | ||
|
|
ca02e1aba9 | ||
|
|
ca4b1f5592 | ||
|
|
0cf1e27709 | ||
|
|
045932ce77 | ||
|
|
bf5481446b | ||
|
|
e3f97e384b | ||
|
|
76e119f8ad | ||
|
|
bfb12c74fb | ||
|
|
fa50b7824c | ||
|
|
da8b2d0cec | ||
|
|
74649ddb96 | ||
|
|
4619ddef5d | ||
|
|
ea74ee6e25 | ||
|
|
ecd73aa670 | ||
|
|
10d9678b3d | ||
|
|
e08d3afdbc | ||
|
|
9a6b598438 | ||
|
|
d9a80b16f0 | ||
|
|
90d0cd873d | ||
|
|
82e0b7b6ab | ||
|
|
2e1b0f2550 | ||
|
|
3302d353cf | ||
|
|
1899d9f1da | ||
|
|
fb9b772db0 | ||
|
|
302b988524 | ||
|
|
19c2fe9b5e | ||
|
|
88d25ee98c | ||
|
|
0f2ecdf5f1 | ||
|
|
3511fb8d59 | ||
|
|
1d6d640b6e | ||
|
|
977d7b277d | ||
|
|
be7820303f | ||
|
|
43503222c7 | ||
|
|
85b8c12abf | ||
|
|
7af053497e | ||
|
|
9e3a42cb62 | ||
|
|
aa32c5ffad | ||
|
|
62d1bd1ea2 | ||
|
|
2329e993ee | ||
|
|
da655b86c3 | ||
|
|
c5ff8bd4ce | ||
|
|
06aee158de | ||
|
|
bd42445ea7 | ||
|
|
ede96fa486 | ||
|
|
67dec216d2 | ||
|
|
fcbdf7ba4f | ||
|
|
e8c949c1e7 | ||
|
|
28c93ef5ac | ||
|
|
d7921c5d5f | ||
|
|
6d296f2b44 | ||
|
|
553e2830bb | ||
|
|
667ab981ba | ||
|
|
bc7871f630 | ||
|
|
d579705b10 | ||
|
|
94d383a8c1 | ||
|
|
08062e3fc3 | ||
|
|
4441427943 | ||
|
|
f5da432d38 | ||
|
|
60f665a65c | ||
|
|
9b6174793a | ||
|
|
dedab38b99 | ||
|
|
4580b18b04 | ||
|
|
88dad36449 | ||
|
|
075c82b32c | ||
|
|
ae2ffd0a28 | ||
|
|
26eea64689 | ||
|
|
c9ff1e1949 | ||
|
|
e31f38eadc | ||
|
|
756b57400b | ||
|
|
01bfd2e090 | ||
|
|
dc3e89e65c | ||
|
|
240d0ff263 | ||
|
|
3b47a4113f | ||
|
|
a6d6a49f82 | ||
|
|
f8ff3faf78 | ||
|
|
d899c26617 | ||
|
|
73ba325072 | ||
|
|
aff19e13c7 | ||
|
|
007122df43 | ||
|
|
06f8911ee1 | ||
|
|
f96f0fecda | ||
|
|
21987cb423 | ||
|
|
18cc8d7cab | ||
|
|
fc0dbd8782 | ||
|
|
b7ca3d7e37 | ||
|
|
c4bf992c0c | ||
|
|
dcce288a98 | ||
|
|
cfd8126e5d | ||
|
|
7a96642498 | ||
|
|
8d5a42c233 | ||
|
|
00a41be413 | ||
|
|
fdb9a9cca8 | ||
|
|
e7f088ef52 | ||
|
|
243efcd51a | ||
|
|
e3cbdd18a0 | ||
|
|
b9cdaf8e19 | ||
|
|
4758caa772 | ||
|
|
4058522f68 | ||
|
|
80437c564d | ||
|
|
503fc7c312 | ||
|
|
f6691a90c0 | ||
|
|
ead09ed110 | ||
|
|
ac1ecd2e7b | ||
|
|
3538e7f6f4 | ||
|
|
75bc038144 | ||
|
|
7018a3e737 | ||
|
|
d3836d4548 | ||
|
|
dbd7db7787 | ||
|
|
1222fd40b7 | ||
|
|
b5f4c639fd | ||
|
|
cddeeff3fc | ||
|
|
94a35a6558 | ||
|
|
b953b2b807 | ||
|
|
367a73d033 | ||
|
|
1ac13658e1 | ||
|
|
2440272307 | ||
|
|
582ed6b5d1 | ||
|
|
e2adbaa5c1 | ||
|
|
4acf800ace | ||
|
|
7cc305c2f5 | ||
|
|
95f5962186 | ||
|
|
f4c2996a3a | ||
|
|
10c4037694 | ||
|
|
52be6deccf | ||
|
|
0d736efc88 | ||
|
|
9cc21c2a62 | ||
|
|
0b7f422d5d | ||
|
|
0ca760fad6 | ||
|
|
3feed7ba07 | ||
|
|
57f1f2d1fe | ||
|
|
b0f9f15a60 | ||
|
|
dc4d820666 | ||
|
|
e30b54ddb2 | ||
|
|
939bb07603 | ||
|
|
cc2c49644d | ||
|
|
29f221d547 | ||
|
|
2e32ceb6e0 | ||
|
|
2cbc9b6426 | ||
|
|
3f3a8f898d | ||
|
|
6e62ea5364 | ||
|
|
5d39b6160a | ||
|
|
a9e8187f28 | ||
|
|
228f5bfdff | ||
|
|
29e5f193f0 | ||
|
|
8f8ebab712 | ||
|
|
418149c9a6 | ||
|
|
e30e5da75a | ||
|
|
fc6681306e | ||
|
|
60acc3ef44 | ||
|
|
9958e557b7 | ||
|
|
8dbc1daaf4 | ||
|
|
5a23d048bd | ||
|
|
b658ea6459 | ||
|
|
dc91a0d807 | ||
|
|
c1fd6552d2 | ||
|
|
6b2e78acdf | ||
|
|
7b11ab04c6 | ||
|
|
bced5f95ff | ||
|
|
9d7f773b9c | ||
|
|
fea0a98b9e | ||
|
|
8745858bcf | ||
|
|
2885c73a9a | ||
|
|
893b23f3cd | ||
|
|
d860d6b891 | ||
|
|
dcc9625803 | ||
|
|
b7e4426002 | ||
|
|
b4cf8f76c8 | ||
|
|
687eddcc63 | ||
|
|
9d6d137b50 | ||
|
|
a75b95694b | ||
|
|
c7aa8871e4 | ||
|
|
f9d919bdbb | ||
|
|
4d0f019ad5 | ||
|
|
e6154e685f | ||
|
|
2c59b5f557 | ||
|
|
4746c89227 | ||
|
|
278d851c7c | ||
|
|
406e22a681 | ||
|
|
17e05c6fd5 | ||
|
|
9846762991 | ||
|
|
17df1ebc6b | ||
|
|
ad8a8b52be | ||
|
|
0d29527758 | ||
|
|
7a96c9fe24 | ||
|
|
c71e5c63ca | ||
|
|
f82a164d75 | ||
|
|
5d4e9b1ead | ||
|
|
788f885759 | ||
|
|
6aeafc6651 | ||
|
|
1aadf91901 | ||
|
|
7de030bb69 | ||
|
|
b5d91ccc21 | ||
|
|
0eafa7de5d | ||
|
|
e554635e48 | ||
|
|
5e915dd1ff | ||
|
|
13f55011c0 | ||
|
|
05dc713dac | ||
|
|
80f3c7934a | ||
|
|
1341bf8fbd | ||
|
|
5b163063c3 | ||
|
|
c2a15ad89d | ||
|
|
c92312a6c6 | ||
|
|
3253e7fd10 | ||
|
|
e5178793b3 | ||
|
|
bec4f83778 | ||
|
|
22f04a926f | ||
|
|
76fa390e3d | ||
|
|
1ef406bbaf | ||
|
|
0aa8711796 | ||
|
|
bea7c94cae | ||
|
|
e7239c53fd | ||
|
|
6f551c770c | ||
|
|
2d755a45e0 | ||
|
|
7a98a886b6 | ||
|
|
b0fb7177bb | ||
|
|
73e8bc41cd | ||
|
|
0b8f1a18b2 | ||
|
|
8caa220ad5 | ||
|
|
b8203bec53 | ||
|
|
5db4c32035 | ||
|
|
9282ed19b2 | ||
|
|
45ee79014d | ||
|
|
0dc0e6490c | ||
|
|
127113a59b | ||
|
|
49bf115c84 | ||
|
|
2196516e2b | ||
|
|
899dbff7e9 | ||
|
|
4345d94d68 | ||
|
|
074af5d16c | ||
|
|
89d6ea0b5d | ||
|
|
c5baf212c8 | ||
|
|
ba31c78412 | ||
|
|
1c1d2a0568 | ||
|
|
0c6ff6822d | ||
|
|
491ffc3576 | ||
|
|
da5ebee3f7 | ||
|
|
6530ca62da | ||
|
|
0bd19cfd38 | ||
|
|
8ada29e25f | ||
|
|
3b5c1bd09c | ||
|
|
c82a5e0523 | ||
|
|
9c5f9906fa | ||
|
|
db2f5b85a9 | ||
|
|
547598c61c | ||
|
|
711fbc549a | ||
|
|
f85c017184 | ||
|
|
85d46ed2b0 | ||
|
|
2d9f578719 | ||
|
|
e75f3a7278 | ||
|
|
26fa5c8860 | ||
|
|
ed5140568a | ||
|
|
70110b4a5a | ||
|
|
a6f23f00b4 | ||
|
|
30e3a43311 | ||
|
|
dce6aaefea | ||
|
|
4843a278ff | ||
|
|
fe7d611fb9 | ||
|
|
0e7e918e2c | ||
|
|
7d6e061ade | ||
|
|
66891aa536 | ||
|
|
1d7a2ccf82 | ||
|
|
e1bfb1234b | ||
|
|
9377fdfc37 | ||
|
|
5f1d17ba1f | ||
|
|
4c0ca1c8e7 | ||
|
|
8ef1aee35c | ||
|
|
4168cc8d78 | ||
|
|
bca54ab1f6 | ||
|
|
6d2c464436 | ||
|
|
e93bd7a3bf | ||
|
|
da94075e7b | ||
|
|
69522495de | ||
|
|
fce88fc72c | ||
|
|
163a2e5d0a | ||
|
|
b979752989 | ||
|
|
8ad2d5f949 | ||
|
|
745ba66119 | ||
|
|
4cf6dec592 | ||
|
|
d7fa0aeff9 | ||
|
|
2def6346e6 | ||
|
|
607539a2af | ||
|
|
6d24dd52d6 | ||
|
|
e3343cbd01 | ||
|
|
71ffa0a137 | ||
|
|
2b4f8091f9 | ||
|
|
113da121e9 | ||
|
|
dd7413f973 | ||
|
|
0851fd13e6 | ||
|
|
97024041f3 | ||
|
|
22238c3200 | ||
|
|
780bb248f7 | ||
|
|
026bdb00f2 | ||
|
|
373d4ac932 | ||
|
|
7d26027752 | ||
|
|
3d426ccef8 | ||
|
|
b31e8e1cee | ||
|
|
f0923c4ed7 | ||
|
|
aabf19e63b | ||
|
|
f4fc1eb5f6 | ||
|
|
5e201a32ca | ||
|
|
438e9e0969 | ||
|
|
9554e82c47 | ||
|
|
4cf2c7a350 | ||
|
|
664d7dccdb | ||
|
|
21b02efb4d | ||
|
|
d07aa4b29e | ||
|
|
9f33043d17 | ||
|
|
2e6301fca1 | ||
|
|
83c5df2c47 | ||
|
|
759b4ef811 | ||
|
|
437ebf6265 | ||
|
|
bffd02b8c7 | ||
|
|
196b27ee9c | ||
|
|
ff08540fd3 | ||
|
|
07be380f34 | ||
|
|
76d58deefa | ||
|
|
dba9764c5e | ||
|
|
ee5d6c7c3e | ||
|
|
1492196bbf | ||
|
|
9378f69653 | ||
|
|
d2d427b533 | ||
|
|
78a6af8dae | ||
|
|
3585e02993 | ||
|
|
5af88ae61e | ||
|
|
f946117dac | ||
|
|
666a6a218f | ||
|
|
1b031582a4 | ||
|
|
afe072adf1 | ||
|
|
09d4fea9e2 | ||
|
|
58c3fba6b9 | ||
|
|
773dd7773b | ||
|
|
732ce05866 | ||
|
|
108c7d3aaa | ||
|
|
86f4256b5a | ||
|
|
84b2917706 | ||
|
|
fc66a4a19c | ||
|
|
087387087e | ||
|
|
3f404905d2 | ||
|
|
67595d6deb | ||
|
|
77f942711a | ||
|
|
e3c98ddc35 | ||
|
|
db0d966102 | ||
|
|
a29639fceb | ||
|
|
0605b7df8c | ||
|
|
51ca08719e | ||
|
|
ce9b94e93d | ||
|
|
7cc0f7cb99 | ||
|
|
06c229dfd4 | ||
|
|
2d5f6d65ce | ||
|
|
b9841351b4 | ||
|
|
d9b6d115d1 | ||
|
|
cc6dd58778 | ||
|
|
0ba931cbed | ||
|
|
a7daeb2a12 | ||
|
|
2fe9dc7ca1 | ||
|
|
b662128708 | ||
|
|
e22f33a44b | ||
|
|
5d69d2aba9 | ||
|
|
0090580a64 | ||
|
|
538a6dc08e | ||
|
|
5b199d8f25 | ||
|
|
51ddb62126 | ||
|
|
785bc33192 | ||
|
|
522bacb1f0 | ||
|
|
cf030f6f0c | ||
|
|
823164a924 | ||
|
|
2ddae623b8 | ||
|
|
86c7286aad | ||
|
|
9886316e07 | ||
|
|
8a3356859c | ||
|
|
32d5f1db85 | ||
|
|
b5975e0f05 | ||
|
|
53ade56b4e | ||
|
|
5677117c0d | ||
|
|
558ae5dc45 | ||
|
|
83f63f4c42 | ||
|
|
247e92937c | ||
|
|
63314941f6 | ||
|
|
e7345b9711 | ||
|
|
22f9fa3938 | ||
|
|
4617829d41 | ||
|
|
fc907b802f | ||
|
|
ba50fbdc3e | ||
|
|
6f77ba8aea | ||
|
|
972ff93e6c | ||
|
|
338bc2e0dc | ||
|
|
8cedad8241 | ||
|
|
32bd6d96e3 | ||
|
|
611216286e | ||
|
|
d8915dbfc9 | ||
|
|
28b12c85f4 | ||
|
|
bee221c18d | ||
|
|
878eb057d1 | ||
|
|
da7c66419a | ||
|
|
d5ad53dae7 | ||
|
|
a03075416c | ||
|
|
e4d8cf2d76 | ||
|
|
8b47d740a8 | ||
|
|
0cac3e1c40 | ||
|
|
3f13485ced | ||
|
|
24c99c4ff9 | ||
|
|
4002f2716d | ||
|
|
f0758768b9 | ||
|
|
a82b1a2e2f | ||
|
|
ccd0fd8902 | ||
|
|
1fbba5cf2d | ||
|
|
d6805cfa0f | ||
|
|
4dee965fdf | ||
|
|
35094a57cb | ||
|
|
5d254c7751 | ||
|
|
10217fc8d9 | ||
|
|
226dd28be8 | ||
|
|
c6d2b6ea8a | ||
|
|
7fd4ba3aad | ||
|
|
ecfcf11c05 | ||
|
|
30ea57c644 | ||
|
|
c4001c727a | ||
|
|
fd4cfc2ff3 | ||
|
|
03cb61f746 | ||
|
|
8eed25b469 | ||
|
|
ee09594190 | ||
|
|
66c3c0115f | ||
|
|
3e021f3a6b | ||
|
|
b524c077a4 | ||
|
|
10f7efecb2 | ||
|
|
f09ee0bdb3 | ||
|
|
d5a52241b0 | ||
|
|
52c4e15c76 | ||
|
|
fdba14691c | ||
|
|
3d136a28a0 | ||
|
|
db398d41a0 | ||
|
|
7cbaf8b5be | ||
|
|
d1c0d2b5f8 | ||
|
|
909606627f | ||
|
|
80f5d3a660 | ||
|
|
182c4752d5 | ||
|
|
273ef55857 | ||
|
|
88c2e55acf | ||
|
|
496fd79714 | ||
|
|
40a1b860bf | ||
|
|
8120913acb | ||
|
|
935eb0651d | ||
|
|
925a93686b | ||
|
|
6f545483c9 | ||
|
|
a8b018d5da | ||
|
|
6e35033f2e | ||
|
|
f79a1b2533 | ||
|
|
79ce98116a | ||
|
|
942c417cbb | ||
|
|
8df850023c | ||
|
|
eedde4abcb | ||
|
|
fcc06dfad4 | ||
|
|
ff850c4251 | ||
|
|
20f8c92bb2 | ||
|
|
22dcb39adb | ||
|
|
f572a72c2a | ||
|
|
be736c48e9 | ||
|
|
67c7c79dae | ||
|
|
61108ba760 | ||
|
|
7285f9e9ad | ||
|
|
b29de8f370 | ||
|
|
7a828b3aee | ||
|
|
afc264e846 | ||
|
|
37c1d7ea58 | ||
|
|
4a72dab02a | ||
|
|
22e5b323c8 | ||
|
|
b275ac5765 | ||
|
|
1475845675 | ||
|
|
6d6034870e | ||
|
|
0a3b1e930a | ||
|
|
836e8a5654 | ||
|
|
39ac07bfde | ||
|
|
fc178b40bc | ||
|
|
9170b24fee | ||
|
|
3674b4ed96 | ||
|
|
c9d9205bb8 | ||
|
|
2c54fd2357 | ||
|
|
d881df916e | ||
|
|
6894ed7d5c | ||
|
|
25a2b2d5d3 | ||
|
|
4fd62a58bd | ||
|
|
532dc07c7b | ||
|
|
fb2a2076a2 | ||
|
|
a8622be1c6 | ||
|
|
810fb73362 | ||
|
|
6950379d94 | ||
|
|
f9020659e6 | ||
|
|
c99f4e31c5 | ||
|
|
526c09b7ff | ||
|
|
5a37a53cb0 | ||
|
|
a57d68acd5 | ||
|
|
a33734e2d3 | ||
|
|
e5921b3949 | ||
|
|
5de25f2b43 | ||
|
|
41f4166aed | ||
|
|
4f8fa6e7aa | ||
|
|
dbd09a431a | ||
|
|
8d48dd973a | ||
|
|
c7666b314b | ||
|
|
03628505ed | ||
|
|
b368873f4d | ||
|
|
dd38f3ce13 | ||
|
|
d8b01c0257 | ||
|
|
0a2fbe1f7f | ||
|
|
de0b54ae70 | ||
|
|
54e8cce33c | ||
|
|
08bf01b649 | ||
|
|
1a66c11091 | ||
|
|
689a1de69b | ||
|
|
0dfa7425c1 | ||
|
|
4098db039e | ||
|
|
a7834611d1 | ||
|
|
9dd4e4756b | ||
|
|
108369414e | ||
|
|
00e07c0384 | ||
|
|
1ebee561bc | ||
|
|
ec0db86663 | ||
|
|
3df4bb3a54 | ||
|
|
5bd748680f | ||
|
|
e09ff31c09 | ||
|
|
07e4c1c20f | ||
|
|
daf854c692 | ||
|
|
65c1e0391c | ||
|
|
769edba1a5 | ||
|
|
2567447b24 | ||
|
|
263b412fdf | ||
|
|
037ea5b1fc | ||
|
|
18634fa805 | ||
|
|
473e4716fc | ||
|
|
59cac9c0cc | ||
|
|
92d87d983b | ||
|
|
bfc4a1bc16 | ||
|
|
1ef61f6cd3 | ||
|
|
5518ccb795 | ||
|
|
027b27dda1 | ||
|
|
6e1180ce06 | ||
|
|
e4ae9134ae | ||
|
|
640bc03c6b | ||
|
|
2a86339b1d | ||
|
|
667aed635d | ||
|
|
1d3a23e051 |
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: gnif
|
||||
patreon: gnif
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: lookingglass
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
74
.github/issue_template.md
vendored
74
.github/issue_template.md
vendored
@@ -1,11 +1,69 @@
|
||||
### Required information
|
||||
### Issues are for Bug Reports and Feature Requests Only!
|
||||
|
||||
Host CPU:
|
||||
Host GPU:
|
||||
Guest GPU:
|
||||
Host Kernel version:
|
||||
Host QEMU version:
|
||||
If you are looking for help or support please use one of the following methods
|
||||
|
||||
Please describe what were you doing when the problem occured. If the Windows host application crashed please check for file named `looking-glass-host.dmp` and attach it to this bug report.
|
||||
Create a New Topic on the Level1Tech's forum under the Looking Glass category:
|
||||
* https://forum.level1techs.com/c/software/lookingGlass/142
|
||||
|
||||
**Reports that do no include this information will be ignored and closed**
|
||||
Ask for help in #looking-glass in the VFIO discord server
|
||||
* https://discord.gg/4ahCn4c
|
||||
|
||||
*Issues that are not bug reports or feature requests will be closed & ignored*
|
||||
|
||||
### Errors that are not bugs
|
||||
|
||||
Some errors generated by the LG client are not bugs, but rather issues with your
|
||||
system's configuration and/or timing. Please do not report these, but rather use
|
||||
one of the above resources to ask for advice/help.
|
||||
|
||||
* `LGMP_ERR_QUEUE_UNSUBSCRIBED` - Failure to heed advice on things such as
|
||||
using `isolcpus` and CPU pinning may result in this message, especially if you
|
||||
are over-taxing your CPU.
|
||||
|
||||
* `Could not create an SDL window: *` - Failure to create a SDL window is not an
|
||||
issue with Looking Glass but rather a more substantial issue with your system,
|
||||
such as missing hardware support for the RGBA32 pixmap format, or missing
|
||||
required OpenGL EGL features.
|
||||
|
||||
* `The host application is not compatible with this client` - The Looking Glass
|
||||
Host application in Windows is the incorrect version and is not compatible,
|
||||
you need to make sure you run matching versions of both the host and client
|
||||
applications.
|
||||
|
||||
### Bug Report Required Information
|
||||
|
||||
The entire (not truncated) output from the client application (if applicable).
|
||||
To obtain this run `looking-glass-client` in a terminal.
|
||||
|
||||
```
|
||||
PASTE CLIENT OUTPUT HERE
|
||||
```
|
||||
|
||||
The entire (not truncated) log file from the host application (if applicable).
|
||||
To obtain this locate the log file on your system, it will be in one of the
|
||||
following two locations depending on how you are launching the Looking Glass Host
|
||||
application:
|
||||
|
||||
* C:\Windows\Temp\looking-glass.txt
|
||||
* C:\Users\YOUR_USER\AppData\Local\Temp\looking-glass.txt
|
||||
|
||||
This log may be quite long, please delete the file first and then proceed to
|
||||
launch the host and reproduce the issue so that the log only contains the
|
||||
pertinent information.
|
||||
|
||||
|
||||
```
|
||||
PASTE HOST LOG FILE CONTENTS HERE
|
||||
```
|
||||
|
||||
If the client is unexpetedly exiting without a backtrace, please provide one via
|
||||
gdb with the command `thread apply all bt`. If you are unsure how to do this
|
||||
please watch the video below on how to perform a Debug build and generate this
|
||||
backtrace.
|
||||
|
||||
https://www.youtube.com/watch?v=EqxxJK9Yo64
|
||||
|
||||
|
||||
```
|
||||
PASTE FULL BACKTRACE HERE
|
||||
```
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -4,3 +4,7 @@ module/*.mod.c
|
||||
module/.*
|
||||
module/Module.symvers
|
||||
module/modules.order
|
||||
*.a
|
||||
*.o
|
||||
*.exe
|
||||
*/build
|
||||
|
||||
9
.gitmodules
vendored
9
.gitmodules
vendored
@@ -1,3 +1,6 @@
|
||||
[submodule "vendor/kvm-guest-drivers-windows"]
|
||||
path = vendor/kvm-guest-drivers-windows
|
||||
url = https://github.com/virtio-win/kvm-guest-drivers-windows.git
|
||||
[submodule "LGMP"]
|
||||
path = repos/LGMP
|
||||
url = https://github.com/gnif/LGMP.git
|
||||
[submodule "repos/PureSpice"]
|
||||
path = repos/PureSpice
|
||||
url = https://github.com/gnif/PureSpice
|
||||
|
||||
58
README.md
58
README.md
@@ -1,22 +1,66 @@
|
||||
# Looking Glass
|
||||
An extremely low latency KVMFR (KVM FrameRelay) implementation for guests with VGA PCI Passthrough.
|
||||
|
||||
An extremely low latency KVMFR (KVM FrameRelay) implementation for guests with
|
||||
VGA PCI Passthrough.
|
||||
|
||||
* Project Website: https://looking-glass.hostfission.com
|
||||
* Support Forum: https://forum.level1techs.com/t/looking-glass-guides-help-and-support/122387
|
||||
* Getting Started: https://looking-glass.hostfission.com/wiki/Installation
|
||||
|
||||
## Obtaining and using Looking Glass
|
||||
## Donations
|
||||
|
||||
Please see https://looking-glass.hostfission.com/quickstart
|
||||
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
|
||||
|
||||
** 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:
|
||||
|
||||
https://looking-glass.hostfission.com/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.hostfission.com/wiki, and as such the
|
||||
information in these files may be dated.
|
||||
|
||||
* [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://ci.appveyor.com/project/gnif/lookingglass/build/artifacts
|
||||
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.hostfission.com/downloads
|
||||
|
||||
# Help and support
|
||||
|
||||
## Web
|
||||
https://forum.level1techs.com/t/looking-glass-guides-help-and-support/122387
|
||||
|
||||
https://forum.level1techs.com/t/looking-glass-triage/130952
|
||||
|
||||
## Discord
|
||||
|
||||
https://discord.gg/4ahCn4c
|
||||
|
||||
## IRC
|
||||
|
||||
Join us in the #LookingGlass channel on the FreeNode network
|
||||
|
||||
## Trello
|
||||
|
||||
* https://trello.com/b/tI1Xbwsg/looking-glass
|
||||
|
||||
@@ -1,28 +1,57 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(looking-glass-client C)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
|
||||
|
||||
SET(CMAKE_C_FLAGS "-std=gnu99 -g -O3 -march=native -Wall -Werror -Wfatal-errors -ffast-math -fdata-sections -ffunction-sections")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--gc-sections")
|
||||
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()
|
||||
|
||||
option(ENABLE_OPENGL "Enable the OpenGL renderer" ON)
|
||||
add_feature_info(ENABLE_OPENGL ENABLE_OPENGL "Legacy OpenGL renderer.")
|
||||
|
||||
option(ENABLE_EGL "Enable the EGL renderer" ON)
|
||||
add_feature_info(ENABLE_EGL ENABLE_EGL "EGL renderer.")
|
||||
|
||||
option(ENABLE_CB_X11 "Enable X11 clipboard integration" ON)
|
||||
add_feature_info(ENABLE_CB_X11 ENABLE_CB_X11 "X11 Clipboard Integration.")
|
||||
|
||||
option(ENABLE_BACKTRACE "Enable backtrace support on crash" ON)
|
||||
add_feature_info(ENABLE_BACKTRACE ENABLE_BACKTRACE "Backtrace support.")
|
||||
|
||||
add_compile_options(
|
||||
"-Wall"
|
||||
"-Werror"
|
||||
"-Wfatal-errors"
|
||||
"-ffast-math"
|
||||
"-fdata-sections"
|
||||
"-ffunction-sections"
|
||||
"$<$<CONFIG:DEBUG>:-O0;-g3;-ggdb>"
|
||||
)
|
||||
|
||||
set(EXE_FLAGS "-Wl,--gc-sections")
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(PKGCONFIG REQUIRED
|
||||
sdl2
|
||||
SDL2_ttf
|
||||
gl
|
||||
glu
|
||||
egl
|
||||
spice-protocol
|
||||
fontconfig
|
||||
x11
|
||||
libconfig
|
||||
nettle
|
||||
hogweed
|
||||
)
|
||||
|
||||
pkg_check_modules(PKGCONFIG_OPT
|
||||
xi
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND git describe --always --long --dirty --abbrev=10 --tags
|
||||
COMMAND cat ../VERSION
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE BUILD_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
@@ -31,46 +60,55 @@ execute_process(
|
||||
find_package(GMP)
|
||||
|
||||
add_definitions(-D BUILD_VERSION='"${BUILD_VERSION}"')
|
||||
add_definitions(-D USE_NETTLE)
|
||||
add_definitions(-D ATOMIC_LOCKING)
|
||||
add_definitions(-D GL_GLEXT_PROTOTYPES)
|
||||
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
|
||||
|
||||
include_directories(
|
||||
${PROJECT_SOURCE_DIR}
|
||||
${PROJECT_SOURCE_DIR}/../common
|
||||
${PKGCONFIG_INCLUDE_DIRS}
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
${CMAKE_BINARY_DIR}/include
|
||||
${PKGCONFIG_INCLUDE_DIRS} ${PKGCONFIG_OPT_INCLUDE_DIRS}
|
||||
${GMP_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
link_libraries(
|
||||
${PKGCONFIG_LIBRARIES}
|
||||
${PKGCONFIG_LIBRARIES} ${PKGCONFIG_OPT_LIBRARIES}
|
||||
${GMP_LIBRARIES}
|
||||
rt m
|
||||
${CMAKE_DL_LIBS}
|
||||
rt
|
||||
m
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
main.c
|
||||
lg-renderer.c
|
||||
lg-fonts.c
|
||||
ll.c
|
||||
utils.c
|
||||
spice/rsa.c
|
||||
spice/spice.c
|
||||
decoders/null.c
|
||||
decoders/yuv420.c
|
||||
renderers/opengl.c
|
||||
renderers/egl.c
|
||||
renderers/egl/shader.c
|
||||
renderers/egl/texture.c
|
||||
renderers/egl/model.c
|
||||
renderers/egl/desktop.c
|
||||
renderers/egl/cursor.c
|
||||
renderers/egl/fps.c
|
||||
renderers/egl/draw.c
|
||||
renderers/egl/splash.c
|
||||
renderers/egl/alert.c
|
||||
fonts/sdl.c
|
||||
src/main.c
|
||||
src/app.c
|
||||
src/config.c
|
||||
src/lg-renderer.c
|
||||
src/ll.c
|
||||
src/utils.c
|
||||
)
|
||||
|
||||
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(renderers)
|
||||
add_subdirectory(clipboards)
|
||||
add_subdirectory(fonts)
|
||||
add_subdirectory(decoders)
|
||||
|
||||
add_executable(looking-glass-client ${SOURCES})
|
||||
target_compile_options(looking-glass-client PUBLIC ${PKGCONFIG_CFLAGS_OTHER})
|
||||
target_compile_options(looking-glass-client PUBLIC ${PKGCONFIG_CFLAGS_OTHER} ${PKGCONFIG_OPT_CFLAGS_OTHER})
|
||||
target_link_libraries(looking-glass-client
|
||||
${EXE_FLAGS}
|
||||
lg_common
|
||||
lgmp
|
||||
purespice
|
||||
renderers
|
||||
clipboards
|
||||
fonts
|
||||
)
|
||||
|
||||
install(PROGRAMS ${CMAKE_BINARY_DIR}/looking-glass-client DESTINATION bin/ COMPONENT binary)
|
||||
|
||||
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)
|
||||
|
||||
164
client/README.md
Normal file
164
client/README.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# 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
|
||||
|
||||
#### 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
|
||||
|
||||
### Building
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ../
|
||||
make
|
||||
|
||||
Should this all go well you should be left with the file `looking-glass-client`
|
||||
|
||||
---
|
||||
|
||||
## 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>I</kbd> | Spice keyboard & mouse enable toggle |
|
||||
| <kbd>ScrLk</kbd>+<kbd>N</kbd> | Toggle night vision mode (EGL renderer only!) |
|
||||
| <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 |
|
||||
|
||||
|
||||
|
||||
### 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:shmFile | -f | /dev/shm/looking-glass | The path to the shared memory file |
|
||||
| app:shmSize | -L | 0 | Specify the size in MB of the shared memory file (0 = detect) |
|
||||
| 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 |
|
||||
|-------------------------------------------------------------------------------------------------------------------------|
|
||||
|
||||
|-------------------------------------------------------------------------------------------------------------|
|
||||
| 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 | Aallow the window to be manually resized |
|
||||
| win:keepAspect | -r | yes | Maintain the correct aspect ratio |
|
||||
| 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:fpsLimit | -K | 200 | Frame rate limit (0 = disable - not recommended) |
|
||||
| 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 |
|
||||
|-------------------------------------------------------------------------------------------------------------|
|
||||
|
||||
|---------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Long | Short | Value | Description |
|
||||
|---------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| input:grabKeyboard | -G | yes | Grab the keyboard in capture mode |
|
||||
| input:escapeKey | -m | 71 = ScrollLock | Specify the escape key, see https://wiki.libsdl.org/SDLScancodeLookup for valid values |
|
||||
| input:hideCursor | -M | yes | Hide the local mouse cursor |
|
||||
| input:mouseSens | | 0 | Initial mouse sensitivity when in capture mode (-9 to 9) |
|
||||
|---------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|
||||
|------------------------------------------------------------------------------------------------------------------|
|
||||
| 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 |
|
||||
|------------------------------------------------------------------------------------------------------------------|
|
||||
|
||||
|--------------------------------------------------------------------------|
|
||||
| Long | Short | Value | Description |
|
||||
|--------------------------------------------------------------------------|
|
||||
| egl:vsync | | no | Enable vsync |
|
||||
| egl:nvGainMax | | 1 | The maximum night vision gain |
|
||||
| egl:nvGain | | 0 | The initial night vision gain at startup |
|
||||
|--------------------------------------------------------------------------|
|
||||
|
||||
|------------------------------------------------------------------------------------|
|
||||
| Long | Short | Value | Description |
|
||||
|------------------------------------------------------------------------------------|
|
||||
| opengl:mipmap | | yes | Enable mipmapping |
|
||||
| opengl:vsync | | yes | Enable vsync |
|
||||
| opengl:preventBuffer | | yes | Prevent the driver from buffering frames |
|
||||
| opengl:amdPinnedMem | | yes | Use GL_AMD_pinned_memory if it is available |
|
||||
|------------------------------------------------------------------------------------|
|
||||
```
|
||||
43
client/clipboards/CMakeLists.txt
Normal file
43
client/clipboards/CMakeLists.txt
Normal file
@@ -0,0 +1,43 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(clipboards LANGUAGES C)
|
||||
|
||||
set(CLIPBOARD_H "${CMAKE_BINARY_DIR}/include/dynamic/clipboards.h")
|
||||
set(CLIPBOARD_C "${CMAKE_BINARY_DIR}/src/clipboards.c")
|
||||
|
||||
file(WRITE ${CLIPBOARD_H} "#include \"interface/clipboard.h\"\n\n")
|
||||
file(APPEND ${CLIPBOARD_H} "extern LG_Clipboard * LG_Clipboards[];\n\n")
|
||||
|
||||
file(WRITE ${CLIPBOARD_C} "#include \"interface/clipboard.h\"\n\n")
|
||||
file(APPEND ${CLIPBOARD_C} "#include <stddef.h>\n\n")
|
||||
|
||||
set(CLIPBOARDS "_")
|
||||
set(CLIPBOARDS_LINK "_")
|
||||
function(add_clipboard name)
|
||||
set(CLIPBOARDS "${CLIPBOARDS};${name}" PARENT_SCOPE)
|
||||
set(CLIPBOARDS_LINK "${CLIPBOARDS_LINK};clipboard_${name}" PARENT_SCOPE)
|
||||
add_subdirectory(${name})
|
||||
endfunction()
|
||||
|
||||
# Add/remove clipboards here!
|
||||
if (ENABLE_CB_X11)
|
||||
add_clipboard(X11)
|
||||
endif()
|
||||
|
||||
list(REMOVE_AT CLIPBOARDS 0)
|
||||
list(REMOVE_AT CLIPBOARDS_LINK 0)
|
||||
|
||||
list(LENGTH CLIPBOARDS CLIPBOARD_COUNT)
|
||||
file(APPEND ${CLIPBOARD_H} "#define LG_CLIPBOARD_COUNT ${CLIPBOARD_COUNT}\n")
|
||||
|
||||
foreach(clipboard ${CLIPBOARDS})
|
||||
file(APPEND ${CLIPBOARD_C} "extern LG_Clipboard LGC_${clipboard};\n")
|
||||
endforeach()
|
||||
|
||||
file(APPEND ${CLIPBOARD_C} "\nconst LG_Clipboard * LG_Clipboards[] =\n{\n")
|
||||
foreach(clipboard ${CLIPBOARDS})
|
||||
file(APPEND ${CLIPBOARD_C} " &LGC_${clipboard},\n")
|
||||
endforeach()
|
||||
file(APPEND ${CLIPBOARD_C} " NULL\n};\n\n")
|
||||
|
||||
add_library(clipboards STATIC ${CLIPBOARD_C})
|
||||
target_link_libraries(clipboards ${CLIPBOARDS_LINK})
|
||||
26
client/clipboards/X11/CMakeLists.txt
Normal file
26
client/clipboards/X11/CMakeLists.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(clipboard_X11 LANGUAGES C)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(CLIPBOARD_PKGCONFIG REQUIRED
|
||||
x11
|
||||
xfixes
|
||||
)
|
||||
|
||||
add_library(clipboard_X11 STATIC
|
||||
src/x11.c
|
||||
)
|
||||
|
||||
target_link_libraries(clipboard_X11
|
||||
${CLIPBOARD_PKGCONFIG_LIBRARIES}
|
||||
lg_common
|
||||
)
|
||||
|
||||
target_include_directories(clipboard_X11
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
PRIVATE
|
||||
src
|
||||
${CLIPBOARD_PKGCONFIG_INCLUDE_DIRS}
|
||||
)
|
||||
359
client/clipboards/X11/src/x11.c
Normal file
359
client/clipboards/X11/src/x11.c
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
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 "interface/clipboard.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
|
||||
struct state
|
||||
{
|
||||
Display * display;
|
||||
Window window;
|
||||
Atom aSelection;
|
||||
Atom aCurSelection;
|
||||
Atom aTargets;
|
||||
Atom aSelData;
|
||||
Atom aIncr;
|
||||
Atom aTypes[LG_CLIPBOARD_DATA_NONE];
|
||||
LG_ClipboardReleaseFn releaseFn;
|
||||
LG_ClipboardRequestFn requestFn;
|
||||
LG_ClipboardNotifyFn notifyFn;
|
||||
LG_ClipboardDataFn dataFn;
|
||||
LG_ClipboardData type;
|
||||
|
||||
// XFixes vars
|
||||
int eventBase;
|
||||
int errorBase;
|
||||
};
|
||||
|
||||
static struct state * this = NULL;
|
||||
|
||||
static const char * atomTypes[] =
|
||||
{
|
||||
"UTF8_STRING",
|
||||
"image/png",
|
||||
"image/bmp",
|
||||
"image/tiff",
|
||||
"image/jpeg"
|
||||
};
|
||||
|
||||
static const char * x11_cb_getName()
|
||||
{
|
||||
return "X11";
|
||||
}
|
||||
|
||||
static bool x11_cb_init(
|
||||
SDL_SysWMinfo * wminfo,
|
||||
LG_ClipboardReleaseFn releaseFn,
|
||||
LG_ClipboardNotifyFn notifyFn,
|
||||
LG_ClipboardDataFn dataFn)
|
||||
{
|
||||
// final sanity check
|
||||
if (wminfo->subsystem != SDL_SYSWM_X11)
|
||||
{
|
||||
DEBUG_ERROR("wrong subsystem");
|
||||
return false;
|
||||
}
|
||||
|
||||
this = (struct state *)malloc(sizeof(struct state));
|
||||
memset(this, 0, sizeof(struct state));
|
||||
|
||||
this->display = wminfo->info.x11.display;
|
||||
this->window = wminfo->info.x11.window;
|
||||
this->aSelection = XInternAtom(this->display, "CLIPBOARD", False);
|
||||
this->aTargets = XInternAtom(this->display, "TARGETS" , False);
|
||||
this->aSelData = XInternAtom(this->display, "SEL_DATA" , False);
|
||||
this->aIncr = XInternAtom(this->display, "INCR" , False);
|
||||
this->aCurSelection = BadValue;
|
||||
this->releaseFn = releaseFn;
|
||||
this->notifyFn = notifyFn;
|
||||
this->dataFn = dataFn;
|
||||
|
||||
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
||||
{
|
||||
this->aTypes[i] = XInternAtom(this->display, atomTypes[i], False);
|
||||
if (this->aTypes[i] == BadAlloc || this->aTypes[i] == BadValue)
|
||||
{
|
||||
DEBUG_ERROR("failed to get atom for type: %s", atomTypes[i]);
|
||||
free(this);
|
||||
this = NULL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// we need the raw X events
|
||||
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
|
||||
|
||||
// use xfixes to get clipboard change notifications
|
||||
if (!XFixesQueryExtension(this->display, &this->eventBase, &this->errorBase))
|
||||
{
|
||||
DEBUG_ERROR("failed to initialize xfixes");
|
||||
free(this);
|
||||
this = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
XFixesSelectSelectionInput(this->display, this->window, XA_PRIMARY , XFixesSetSelectionOwnerNotifyMask);
|
||||
XFixesSelectSelectionInput(this->display, this->window, this->aSelection, XFixesSetSelectionOwnerNotifyMask);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void x11_cb_free()
|
||||
{
|
||||
free(this);
|
||||
this = NULL;
|
||||
}
|
||||
|
||||
static void x11_cb_reply_fn(void * opaque, LG_ClipboardData type, uint8_t * data, uint32_t size)
|
||||
{
|
||||
XEvent *s = (XEvent *)opaque;
|
||||
|
||||
XChangeProperty(
|
||||
this->display ,
|
||||
s->xselection.requestor,
|
||||
s->xselection.property ,
|
||||
s->xselection.target ,
|
||||
8,
|
||||
PropModeReplace,
|
||||
data,
|
||||
size);
|
||||
|
||||
XSendEvent(this->display, s->xselection.requestor, 0, 0, s);
|
||||
XFlush(this->display);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void x11_cb_wmevent(SDL_SysWMmsg * msg)
|
||||
{
|
||||
XEvent e = msg->msg.x11.event;
|
||||
|
||||
if (e.type == SelectionRequest)
|
||||
{
|
||||
XEvent * s = (XEvent *)malloc(sizeof(XEvent));
|
||||
s->xselection.type = SelectionNotify;
|
||||
s->xselection.requestor = e.xselectionrequest.requestor;
|
||||
s->xselection.selection = e.xselectionrequest.selection;
|
||||
s->xselection.target = e.xselectionrequest.target;
|
||||
s->xselection.property = e.xselectionrequest.property;
|
||||
s->xselection.time = e.xselectionrequest.time;
|
||||
|
||||
if (!this->requestFn)
|
||||
{
|
||||
s->xselection.property = None;
|
||||
XSendEvent(this->display, e.xselectionrequest.requestor, 0, 0, s);
|
||||
XFlush(this->display);
|
||||
free(s);
|
||||
return;
|
||||
}
|
||||
|
||||
// target list requested
|
||||
if (e.xselectionrequest.target == this->aTargets)
|
||||
{
|
||||
Atom targets[2];
|
||||
targets[0] = this->aTargets;
|
||||
targets[1] = this->aTypes[this->type];
|
||||
|
||||
XChangeProperty(
|
||||
e.xselectionrequest.display,
|
||||
e.xselectionrequest.requestor,
|
||||
e.xselectionrequest.property,
|
||||
XA_ATOM,
|
||||
32,
|
||||
PropModeReplace,
|
||||
(unsigned char*)targets,
|
||||
sizeof(targets) / sizeof(Atom));
|
||||
|
||||
XSendEvent(this->display, e.xselectionrequest.requestor, 0, 0, s);
|
||||
XFlush(this->display);
|
||||
free(s);
|
||||
return;
|
||||
}
|
||||
|
||||
// look to see if we can satisfy the data type
|
||||
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
||||
if (this->aTypes[i] == e.xselectionrequest.target && this->type == i)
|
||||
{
|
||||
// request the data
|
||||
this->requestFn(x11_cb_reply_fn, s);
|
||||
return;
|
||||
}
|
||||
|
||||
// report no data
|
||||
s->xselection.property = None;
|
||||
XSendEvent(this->display, e.xselectionrequest.requestor, 0, 0, s);
|
||||
XFlush(this->display);
|
||||
}
|
||||
|
||||
if (e.type == SelectionClear && (
|
||||
e.xselectionclear.selection == XA_PRIMARY ||
|
||||
e.xselectionclear.selection == this->aSelection)
|
||||
)
|
||||
{
|
||||
this->aCurSelection = BadValue;
|
||||
this->releaseFn();
|
||||
return;
|
||||
}
|
||||
|
||||
// if someone selected data
|
||||
if (e.type == this->eventBase + XFixesSelectionNotify)
|
||||
{
|
||||
XFixesSelectionNotifyEvent * sne = (XFixesSelectionNotifyEvent *)&e;
|
||||
|
||||
// check if the selection is valid and it isn't ourself
|
||||
if (
|
||||
(sne->selection != XA_PRIMARY && sne->selection != this->aSelection) ||
|
||||
sne->owner == this->window ||
|
||||
sne->owner == 0
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// remember which selection we are working with
|
||||
this->aCurSelection = sne->selection;
|
||||
XConvertSelection(
|
||||
this->display,
|
||||
sne->selection,
|
||||
this->aTargets,
|
||||
this->aTargets,
|
||||
this->window,
|
||||
CurrentTime);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.type == SelectionNotify)
|
||||
{
|
||||
if (e.xselection.property == None)
|
||||
return;
|
||||
|
||||
Atom type;
|
||||
int format;
|
||||
unsigned long itemCount, after;
|
||||
unsigned char *data;
|
||||
|
||||
XGetWindowProperty(
|
||||
this->display,
|
||||
this->window,
|
||||
e.xselection.property,
|
||||
0, ~0L, // start and length
|
||||
True , // delete the property
|
||||
AnyPropertyType,
|
||||
&type,
|
||||
&format,
|
||||
&itemCount,
|
||||
&after,
|
||||
&data);
|
||||
|
||||
// the target list
|
||||
if (e.xselection.property == this->aTargets)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
if (data)
|
||||
XFree(data);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 (this->aTypes[n] == targets[i])
|
||||
{
|
||||
// we have a match, so send the notification
|
||||
this->notifyFn(n);
|
||||
XFree(data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// no matches
|
||||
this->notifyFn(LG_CLIPBOARD_DATA_NONE);
|
||||
XFree(data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (format == this->aIncr)
|
||||
{
|
||||
DEBUG_WARN("fixme: large paste buffers are not yet supported");
|
||||
XFree(data);
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i = 0; i < LG_CLIPBOARD_DATA_NONE; ++i)
|
||||
if (this->aTypes[i] == type)
|
||||
{
|
||||
this->dataFn(i, data, itemCount);
|
||||
XFree(data);
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_WARN("clipboard data (%s) not in a supported format", XGetAtomName(this->display, type));
|
||||
XFree(data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void x11_cb_notice(LG_ClipboardRequestFn requestFn, LG_ClipboardData type)
|
||||
{
|
||||
this->requestFn = requestFn;
|
||||
this->type = type;
|
||||
XSetSelectionOwner(this->display, XA_PRIMARY , this->window, CurrentTime);
|
||||
XSetSelectionOwner(this->display, this->aSelection, this->window, CurrentTime);
|
||||
XFlush(this->display);
|
||||
}
|
||||
|
||||
static void x11_cb_release()
|
||||
{
|
||||
this->requestFn = NULL;
|
||||
XSetSelectionOwner(this->display, XA_PRIMARY , None, CurrentTime);
|
||||
XSetSelectionOwner(this->display, this->aSelection, None, CurrentTime);
|
||||
XFlush(this->display);
|
||||
}
|
||||
|
||||
static void x11_cb_request(LG_ClipboardData type)
|
||||
{
|
||||
if (this->aCurSelection == BadValue)
|
||||
return;
|
||||
|
||||
XConvertSelection(
|
||||
this->display,
|
||||
this->aCurSelection,
|
||||
this->aTypes[type],
|
||||
this->aSelData,
|
||||
this->window,
|
||||
CurrentTime);
|
||||
}
|
||||
|
||||
const LG_Clipboard LGC_X11 =
|
||||
{
|
||||
.getName = x11_cb_getName,
|
||||
.init = x11_cb_init,
|
||||
.free = x11_cb_free,
|
||||
.wmevent = x11_cb_wmevent,
|
||||
.notice = x11_cb_notice,
|
||||
.release = x11_cb_release,
|
||||
.request = x11_cb_request
|
||||
};
|
||||
34
client/cmake/MakeObject.cmake
Normal file
34
client/cmake/MakeObject.cmake
Normal file
@@ -0,0 +1,34 @@
|
||||
function(make_object out_var)
|
||||
set(result)
|
||||
set(result_h)
|
||||
foreach(in_f ${ARGN})
|
||||
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})
|
||||
|
||||
add_custom_command(OUTPUT ${out_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}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
COMMENT "Creating object from ${in_f}"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
file(WRITE ${out_h} "extern const char b_${sym_in}[];\n")
|
||||
file(APPEND ${out_h} "extern const char b_${sym_in}_end[];\n")
|
||||
file(APPEND ${out_h} "#define b_${sym_in}_size (b_${sym_in}_end - b_${sym_in})\n")
|
||||
|
||||
get_filename_component(h_dir ${out_h} DIRECTORY)
|
||||
list(APPEND result_h ${h_dir})
|
||||
list(APPEND result ${out_f})
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES result_h)
|
||||
|
||||
set(${out_var}_OBJS "${result}" PARENT_SCOPE)
|
||||
set(${out_var}_INCS "${result_h}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
25
client/decoders/CMakeLists.txt
Normal file
25
client/decoders/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(decoders LANGUAGES C)
|
||||
|
||||
#find_package(PkgConfig)
|
||||
#pkg_check_modules(DECODERS_PKGCONFIG REQUIRED
|
||||
#)
|
||||
|
||||
add_library(decoders STATIC
|
||||
src/null.c
|
||||
src/yuv420.c
|
||||
)
|
||||
|
||||
target_link_libraries(decoders
|
||||
lg_common
|
||||
${DECODERS_PKGCONFIG_LIBRARIES}
|
||||
)
|
||||
|
||||
target_include_directories(decoders
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
PRIVATE
|
||||
src
|
||||
${DECODERS_PKGCONFIG_INCLUDE_DIRS}
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -17,10 +17,10 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lg-decoder.h"
|
||||
#include "interface/decoder.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "memcpySSE.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/memcpySSE.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -17,10 +17,10 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lg-decoder.h"
|
||||
#include "interface/decoder.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "memcpySSE.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/memcpySSE.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
41
client/fonts/CMakeLists.txt
Normal file
41
client/fonts/CMakeLists.txt
Normal file
@@ -0,0 +1,41 @@
|
||||
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})
|
||||
26
client/fonts/SDL/CMakeLists.txt
Normal file
26
client/fonts/SDL/CMakeLists.txt
Normal file
@@ -0,0 +1,26 @@
|
||||
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,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -20,8 +20,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "lg-font.h"
|
||||
#include "debug.h"
|
||||
#include "interface/font.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
#include <fontconfig/fontconfig.h>
|
||||
58
client/include/interface/app.h
Normal file
58
client/include/interface/app.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
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 <SDL2/SDL.h>
|
||||
|
||||
typedef enum LG_MsgAlert
|
||||
{
|
||||
LG_ALERT_INFO ,
|
||||
LG_ALERT_SUCCESS,
|
||||
LG_ALERT_WARNING,
|
||||
LG_ALERT_ERROR
|
||||
}
|
||||
LG_MsgAlert;
|
||||
|
||||
typedef struct KeybindHandle * KeybindHandle;
|
||||
typedef void (*SuperEventFn)(SDL_Scancode key, void * opaque);
|
||||
|
||||
/**
|
||||
* Show an alert on screen
|
||||
* @param type The alert type
|
||||
* param fmt The alert message format
|
||||
@ param ... formatted message values
|
||||
*/
|
||||
void app_alert(LG_MsgAlert type, const char * fmt, ...);
|
||||
|
||||
/**
|
||||
* Register a handler for the <super>+<key> combination
|
||||
* @param key The scancode to register
|
||||
* @param callback The function to be called when the combination is pressed
|
||||
* @param opaque A pointer to be passed to the callback, may be NULL
|
||||
* @retval A handle for the binding or NULL on failure.
|
||||
* The caller is required to release the handle via `app_release_keybind` when it is no longer required
|
||||
*/
|
||||
KeybindHandle app_register_keybind(SDL_Scancode key, SuperEventFn callback, void * opaque);
|
||||
|
||||
/**
|
||||
* Release an existing key binding
|
||||
* @param handle A pointer to the keybind handle to release, may be NULL
|
||||
*/
|
||||
void app_release_keybind(KeybindHandle * handle);
|
||||
62
client/include/interface/clipboard.h
Normal file
62
client/include/interface/clipboard.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
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 <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_syswm.h>
|
||||
|
||||
typedef enum LG_ClipboardData
|
||||
{
|
||||
LG_CLIPBOARD_DATA_TEXT = 0,
|
||||
LG_CLIPBOARD_DATA_PNG,
|
||||
LG_CLIPBOARD_DATA_BMP,
|
||||
LG_CLIPBOARD_DATA_TIFF,
|
||||
LG_CLIPBOARD_DATA_JPEG,
|
||||
|
||||
LG_CLIPBOARD_DATA_NONE // enum max, not a data type
|
||||
}
|
||||
LG_ClipboardData;
|
||||
|
||||
typedef void (* LG_ClipboardReplyFn )(void * opaque, const LG_ClipboardData type, uint8_t * data, uint32_t size);
|
||||
typedef void (* LG_ClipboardRequestFn)(LG_ClipboardReplyFn replyFn, void * opaque);
|
||||
typedef void (* LG_ClipboardReleaseFn)();
|
||||
typedef void (* LG_ClipboardNotifyFn)(LG_ClipboardData type);
|
||||
typedef void (* LG_ClipboardDataFn )(const LG_ClipboardData type, uint8_t * data, size_t size);
|
||||
|
||||
typedef const char * (* LG_ClipboardGetName)();
|
||||
typedef bool (* LG_ClipboardInit)(SDL_SysWMinfo * wminfo, LG_ClipboardReleaseFn releaseFn, LG_ClipboardNotifyFn notifyFn, LG_ClipboardDataFn dataFn);
|
||||
typedef void (* LG_ClipboardFree)();
|
||||
typedef void (* LG_ClipboardWMEvent)(SDL_SysWMmsg * msg);
|
||||
typedef void (* LG_ClipboardNotice)(LG_ClipboardRequestFn requestFn, LG_ClipboardData type);
|
||||
typedef void (* LG_ClipboardRelease)();
|
||||
typedef void (* LG_ClipboardRequest)(LG_ClipboardData type);
|
||||
|
||||
typedef struct LG_Clipboard
|
||||
{
|
||||
LG_ClipboardGetName getName;
|
||||
LG_ClipboardInit init;
|
||||
LG_ClipboardFree free;
|
||||
LG_ClipboardWMEvent wmevent;
|
||||
LG_ClipboardNotice notice;
|
||||
LG_ClipboardRelease release;
|
||||
LG_ClipboardRequest request;
|
||||
}
|
||||
LG_Clipboard;
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -19,7 +19,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "lg-renderer.h"
|
||||
#include "renderer.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -24,13 +24,16 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
|
||||
#include "KVMFR.h"
|
||||
#include "app.h"
|
||||
#include "common/KVMFR.h"
|
||||
#include "common/framebuffer.h"
|
||||
|
||||
#define IS_LG_RENDERER_VALID(x) \
|
||||
((x)->get_name && \
|
||||
(x)->create && \
|
||||
(x)->initialize && \
|
||||
(x)->deinitialize && \
|
||||
(x)->on_restart && \
|
||||
(x)->on_resize && \
|
||||
(x)->on_mouse_shape && \
|
||||
(x)->on_mouse_event && \
|
||||
@@ -39,28 +42,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
(x)->render && \
|
||||
(x)->update_fps)
|
||||
|
||||
#define LGR_OPTION_COUNT(x) (sizeof(x) / sizeof(LG_RendererOpt))
|
||||
|
||||
typedef bool(* LG_RendererOptValidator)(const char * value);
|
||||
typedef void(* LG_RendererOptHandler )(void * opaque, const char * value);
|
||||
|
||||
typedef struct LG_RendererOpt
|
||||
{
|
||||
const char * name;
|
||||
const char * desc;
|
||||
LG_RendererOptValidator validator;
|
||||
LG_RendererOptHandler handler;
|
||||
}
|
||||
LG_RendererOpt;
|
||||
|
||||
typedef struct LG_RendererOptValue
|
||||
{
|
||||
const LG_RendererOpt * opt;
|
||||
char * value;
|
||||
} LG_RendererOptValue;
|
||||
|
||||
typedef LG_RendererOpt * LG_RendererOptions;
|
||||
|
||||
typedef struct LG_RendererParams
|
||||
{
|
||||
// TTF_Font * font;
|
||||
@@ -98,35 +79,33 @@ typedef enum LG_RendererCursor
|
||||
}
|
||||
LG_RendererCursor;
|
||||
|
||||
typedef enum LG_RendererOnAlert
|
||||
{
|
||||
LG_ALERT_INFO ,
|
||||
LG_ALERT_SUCCESS,
|
||||
LG_ALERT_WARNING,
|
||||
LG_ALERT_ERROR
|
||||
}
|
||||
LG_RendererAlert;
|
||||
|
||||
// returns the friendly name of the renderer
|
||||
typedef const char * (* LG_RendererGetName)();
|
||||
|
||||
// called pre-creation to allow the renderer to register any options it might have
|
||||
typedef void (* LG_RendererSetup)();
|
||||
|
||||
typedef bool (* LG_RendererCreate )(void ** opaque, const LG_RendererParams params);
|
||||
typedef bool (* LG_RendererInitialize )(void * opaque, Uint32 * sdlFlags);
|
||||
typedef void (* LG_RendererDeInitialize)(void * opaque);
|
||||
typedef void (* LG_RendererOnRestart )(void * opaque);
|
||||
typedef void (* LG_RendererOnResize )(void * opaque, const int width, const int height, const LG_RendererRect destRect);
|
||||
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_RendererOnFrameEvent)(void * opaque, const LG_RendererFormat format, const uint8_t * data);
|
||||
typedef void (* LG_RendererOnAlert )(void * opaque, const LG_RendererAlert alert, const char * message, bool ** closeFlag);
|
||||
typedef bool (* LG_RendererOnFrameEvent)(void * opaque, const LG_RendererFormat format, const FrameBuffer * frame);
|
||||
typedef void (* LG_RendererOnAlert )(void * opaque, const LG_MsgAlert alert, const char * message, bool ** closeFlag);
|
||||
typedef bool (* LG_RendererRender )(void * opaque, SDL_Window *window);
|
||||
typedef void (* LG_RendererUpdateFPS )(void * opaque, const float avgUPS, const float avgFPS);
|
||||
|
||||
typedef struct LG_Renderer
|
||||
{
|
||||
LG_RendererCreate create;
|
||||
LG_RendererGetName get_name;
|
||||
LG_RendererOptions options;
|
||||
unsigned int option_count;
|
||||
LG_RendererSetup setup;
|
||||
|
||||
LG_RendererCreate create;
|
||||
LG_RendererInitialize initialize;
|
||||
LG_RendererDeInitialize deinitialize;
|
||||
LG_RendererOnRestart on_restart;
|
||||
LG_RendererOnResize on_resize;
|
||||
LG_RendererOnMouseShape on_mouse_shape;
|
||||
LG_RendererOnMouseEvent on_mouse_event;
|
||||
@@ -137,7 +116,3 @@ typedef struct LG_Renderer
|
||||
LG_RendererUpdateFPS update_fps;
|
||||
}
|
||||
LG_Renderer;
|
||||
|
||||
// generic option helpers
|
||||
bool LG_RendererValidatorBool(const char * value);
|
||||
bool LG_RendererValueToBool (const char * value);
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -18,7 +18,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "lg-decoder.h"
|
||||
#include "interface/decoder.h"
|
||||
|
||||
extern const LG_Decoder LGD_NULL;
|
||||
extern const LG_Decoder LGD_YUV420;
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KVMGFX Client - A KVM Client for VGA Passthrough
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -17,15 +17,11 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lg-fonts.h"
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
extern const LG_Font LGF_SDL;
|
||||
|
||||
const LG_Font * LG_Fonts[] =
|
||||
{
|
||||
&LGF_SDL,
|
||||
NULL // end of array sentinal
|
||||
};
|
||||
|
||||
#define LG_FONT_COUNT ((sizeof(LG_Fonts) / sizeof(LG_Font *)) - 1)
|
||||
// reads the specified file into a new buffer
|
||||
// the callee must free the buffer
|
||||
bool file_get_contents(const char * filename, char ** buffer, size_t * length);
|
||||
1636
client/main.c
1636
client/main.c
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
|
||||
46
client/renderers/CMakeLists.txt
Normal file
46
client/renderers/CMakeLists.txt
Normal file
@@ -0,0 +1,46 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(renderers LANGUAGES C)
|
||||
|
||||
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(WRITE ${RENDERER_C} "#include \"interface/renderer.h\"\n\n")
|
||||
file(APPEND ${RENDERER_C} "#include <stddef.h>\n\n")
|
||||
|
||||
set(RENDERERS "_")
|
||||
set(RENDERERS_LINK "_")
|
||||
function(add_renderer name)
|
||||
set(RENDERERS "${RENDERERS};${name}" PARENT_SCOPE)
|
||||
set(RENDERERS_LINK "${RENDERERS_LINK};renderer_${name}" PARENT_SCOPE)
|
||||
add_subdirectory(${name})
|
||||
endfunction()
|
||||
|
||||
# Add/remove renderers here!
|
||||
if(ENABLE_EGL)
|
||||
add_renderer(EGL)
|
||||
endif()
|
||||
if (ENABLE_OPENGL)
|
||||
add_renderer(OpenGL)
|
||||
endif()
|
||||
|
||||
list(REMOVE_AT RENDERERS 0)
|
||||
list(REMOVE_AT RENDERERS_LINK 0)
|
||||
|
||||
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")
|
||||
endforeach()
|
||||
|
||||
file(APPEND ${RENDERER_C} "\nconst LG_Renderer * LG_Renderers[] =\n{\n")
|
||||
foreach(renderer ${RENDERERS})
|
||||
file(APPEND ${RENDERER_C} " &LGR_${renderer},\n")
|
||||
endforeach()
|
||||
file(APPEND ${RENDERER_C} " NULL\n};")
|
||||
|
||||
add_library(renderers STATIC ${RENDERER_C})
|
||||
target_link_libraries(renderers ${RENDERERS_LINK})
|
||||
63
client/renderers/EGL/CMakeLists.txt
Normal file
63
client/renderers/EGL/CMakeLists.txt
Normal file
@@ -0,0 +1,63 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(renderer_EGL LANGUAGES C)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(RENDERER_EGL_PKGCONFIG REQUIRED
|
||||
egl
|
||||
gl
|
||||
)
|
||||
|
||||
pkg_check_modules(RENDERER_EGL_OPT_PKGCONFIG
|
||||
wayland-egl
|
||||
)
|
||||
|
||||
include(MakeObject)
|
||||
make_object(
|
||||
EGL_SHADER
|
||||
shader/desktop.vert
|
||||
shader/desktop_rgb.frag
|
||||
shader/desktop_yuv.frag
|
||||
shader/cursor.vert
|
||||
shader/cursor_rgb.frag
|
||||
shader/cursor_mono.frag
|
||||
shader/fps.vert
|
||||
shader/fps.frag
|
||||
shader/fps_bg.frag
|
||||
shader/alert.vert
|
||||
shader/alert.frag
|
||||
shader/alert_bg.frag
|
||||
shader/splash_bg.vert
|
||||
shader/splash_bg.frag
|
||||
shader/splash_logo.vert
|
||||
shader/splash_logo.frag
|
||||
)
|
||||
|
||||
add_library(renderer_EGL STATIC
|
||||
egl.c
|
||||
debug.c
|
||||
shader.c
|
||||
texture.c
|
||||
model.c
|
||||
desktop.c
|
||||
cursor.c
|
||||
fps.c
|
||||
draw.c
|
||||
splash.c
|
||||
alert.c
|
||||
${EGL_SHADER_OBJS}
|
||||
)
|
||||
|
||||
target_link_libraries(renderer_EGL
|
||||
${RENDERER_EGL_PKGCONFIG_LIBRARIES}
|
||||
${RENDERER_EGL_OPT_PKGCONFIG_LIBRARIES}
|
||||
lg_common
|
||||
fonts
|
||||
)
|
||||
|
||||
target_include_directories(renderer_EGL
|
||||
PRIVATE
|
||||
src
|
||||
${EGL_SHADER_INCS}
|
||||
${RENDERER_EGL_PKGCONFIG_INCLUDE_DIRS}
|
||||
${RENDERER_EGL_OPT_PKGCONFIG_INCLUDE_DIRS}
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -18,8 +18,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "alert.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/locking.h"
|
||||
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
@@ -28,6 +28,11 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#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;
|
||||
@@ -44,6 +49,7 @@ struct EGL_Alert
|
||||
|
||||
bool ready;
|
||||
float width , height ;
|
||||
float bgWidth, bgHeight;
|
||||
float r, g, b, a;
|
||||
|
||||
// uniforms
|
||||
@@ -51,57 +57,6 @@ struct EGL_Alert
|
||||
GLint uScreenBG, uSizeBG, uColorBG;
|
||||
};
|
||||
|
||||
static const char vertex_shader[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
layout(location = 0) in vec3 vertexPosition_modelspace;\
|
||||
layout(location = 1) in vec2 vertexUV;\
|
||||
\
|
||||
uniform vec2 screen;\
|
||||
uniform vec2 size;\
|
||||
uniform vec4 color;\
|
||||
\
|
||||
out highp vec2 uv;\
|
||||
out highp vec4 c;\
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
gl_Position.xyz = vertexPosition_modelspace; \
|
||||
gl_Position.w = 1.0; \
|
||||
gl_Position.xy *= screen.xy * size.xy; \
|
||||
\
|
||||
uv = vertexUV;\
|
||||
c = color;\
|
||||
}\
|
||||
";
|
||||
|
||||
static const char frag_shader[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
in highp vec2 uv;\
|
||||
out highp vec4 color;\
|
||||
\
|
||||
uniform sampler2D sampler1;\
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
color = texture(sampler1, uv);\
|
||||
}\
|
||||
";
|
||||
|
||||
static const char frag_shaderBG[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
in highp vec4 c;\
|
||||
out highp vec4 color;\
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
color = c;\
|
||||
}\
|
||||
";
|
||||
|
||||
|
||||
bool egl_alert_init(EGL_Alert ** alert, const LG_Font * font, LG_FontObj fontObj)
|
||||
{
|
||||
*alert = (EGL_Alert *)malloc(sizeof(EGL_Alert));
|
||||
@@ -137,16 +92,16 @@ bool egl_alert_init(EGL_Alert ** alert, const LG_Font * font, LG_FontObj fontObj
|
||||
|
||||
|
||||
if (!egl_shader_compile((*alert)->shader,
|
||||
vertex_shader, sizeof(vertex_shader),
|
||||
frag_shader , sizeof(frag_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,
|
||||
vertex_shader, sizeof(vertex_shader),
|
||||
frag_shaderBG, sizeof(frag_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;
|
||||
@@ -219,14 +174,19 @@ void egl_alert_render(EGL_Alert * alert, const float scaleX, const float scaleY)
|
||||
EGL_PF_BGRA,
|
||||
alert->bmp->width ,
|
||||
alert->bmp->height,
|
||||
alert->bmp->width * alert->bmp->height * alert->bmp->bpp,
|
||||
alert->bmp->width * alert->bmp->bpp,
|
||||
false
|
||||
);
|
||||
|
||||
egl_texture_update(alert->texture, alert->bmp->pixels);
|
||||
|
||||
alert->width = alert->bmp->width;
|
||||
alert->height = alert->bmp->height;
|
||||
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);
|
||||
@@ -244,14 +204,14 @@ void egl_alert_render(EGL_Alert * alert, const float scaleX, const float scaleY)
|
||||
// render the background first
|
||||
egl_shader_use(alert->shaderBG);
|
||||
glUniform2f(alert->uScreenBG, scaleX , scaleY );
|
||||
glUniform2f(alert->uSizeBG , alert->width, alert->height);
|
||||
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 );
|
||||
glUniform2f(alert->uSize , alert->width, alert->height);
|
||||
glUniform2i(alert->uSize , alert->width, alert->height);
|
||||
egl_model_render(alert->model);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -21,7 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "lg-fonts.h"
|
||||
#include "interface/font.h"
|
||||
|
||||
typedef struct EGL_Alert EGL_Alert;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -18,8 +18,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cursor.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/locking.h"
|
||||
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
@@ -28,13 +28,18 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// these headers are auto generated by cmake
|
||||
#include "cursor.vert.h"
|
||||
#include "cursor_rgb.frag.h"
|
||||
#include "cursor_mono.frag.h"
|
||||
|
||||
struct EGL_Cursor
|
||||
{
|
||||
LG_Lock lock;
|
||||
LG_RendererCursor type;
|
||||
int width;
|
||||
int height;
|
||||
int pitch;
|
||||
int stride;
|
||||
uint8_t * data;
|
||||
size_t dataSize;
|
||||
bool update;
|
||||
@@ -59,65 +64,6 @@ struct EGL_Cursor
|
||||
struct EGL_Model * model;
|
||||
};
|
||||
|
||||
static const char vertex_shader[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
layout(location = 0) in vec3 vertexPosition_modelspace;\
|
||||
layout(location = 1) in vec2 vertexUV;\
|
||||
\
|
||||
uniform vec4 mouse;\
|
||||
\
|
||||
out highp vec2 uv;\
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
gl_Position.xyz = vertexPosition_modelspace;\
|
||||
gl_Position.w = 1.0;\
|
||||
\
|
||||
gl_Position.x += 1.0f;\
|
||||
gl_Position.y -= 1.0f;\
|
||||
\
|
||||
gl_Position.x *= mouse.z;\
|
||||
gl_Position.y *= mouse.w;\
|
||||
\
|
||||
gl_Position.x += mouse.x;\
|
||||
gl_Position.y -= mouse.y;\
|
||||
\
|
||||
uv = vertexUV;\
|
||||
}\
|
||||
";
|
||||
|
||||
static const char frag_mouse_mono[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
in highp vec2 uv;\
|
||||
out highp vec4 color;\
|
||||
\
|
||||
uniform sampler2D sampler1;\
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
highp vec4 tmp = texture(sampler1, uv);\
|
||||
if (tmp.rgb == vec3(0.0, 0.0, 0.0))\
|
||||
discard;\
|
||||
color = tmp;\
|
||||
}\
|
||||
";
|
||||
|
||||
static const char frag_rgba[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
in highp vec2 uv;\
|
||||
out highp vec4 color;\
|
||||
\
|
||||
uniform sampler2D sampler1;\
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
color = texture(sampler1, uv);\
|
||||
}\
|
||||
";
|
||||
|
||||
bool egl_cursor_init(EGL_Cursor ** cursor)
|
||||
{
|
||||
*cursor = (EGL_Cursor *)malloc(sizeof(EGL_Cursor));
|
||||
@@ -156,8 +102,8 @@ bool egl_cursor_init(EGL_Cursor ** cursor)
|
||||
|
||||
if (!egl_shader_compile(
|
||||
(*cursor)->shader,
|
||||
vertex_shader, sizeof(vertex_shader),
|
||||
frag_rgba , sizeof(frag_rgba )))
|
||||
b_shader_cursor_vert , b_shader_cursor_vert_size,
|
||||
b_shader_cursor_rgb_frag, b_shader_cursor_rgb_frag_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the cursor shader");
|
||||
return false;
|
||||
@@ -165,8 +111,8 @@ bool egl_cursor_init(EGL_Cursor ** cursor)
|
||||
|
||||
if (!egl_shader_compile(
|
||||
(*cursor)->shaderMono,
|
||||
vertex_shader , sizeof(vertex_shader ),
|
||||
frag_mouse_mono, sizeof(frag_mouse_mono)))
|
||||
b_shader_cursor_vert , b_shader_cursor_vert_size,
|
||||
b_shader_cursor_mono_frag, b_shader_cursor_mono_frag_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the cursor mono shader");
|
||||
return false;
|
||||
@@ -204,16 +150,16 @@ void egl_cursor_free(EGL_Cursor ** cursor)
|
||||
*cursor = NULL;
|
||||
}
|
||||
|
||||
bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type, const int width, const int height, const int pitch, const uint8_t * data)
|
||||
bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type, const int width, const int height, const int stride, const uint8_t * data)
|
||||
{
|
||||
LG_LOCK(cursor->lock);
|
||||
|
||||
cursor->type = type;
|
||||
cursor->width = width;
|
||||
cursor->height = (type == LG_CURSOR_MONOCHROME ? height / 2 : height);
|
||||
cursor->pitch = pitch;
|
||||
cursor->stride = stride;
|
||||
|
||||
const size_t size = height * pitch;
|
||||
const size_t size = height * stride;
|
||||
if (size > cursor->dataSize)
|
||||
{
|
||||
if (cursor->data)
|
||||
@@ -261,28 +207,14 @@ void egl_cursor_render(EGL_Cursor * cursor)
|
||||
|
||||
uint8_t * data = cursor->data;
|
||||
|
||||
// tmp buffer for masked colour
|
||||
uint32_t tmp[cursor->width * cursor->height];
|
||||
|
||||
switch(cursor->type)
|
||||
{
|
||||
case LG_CURSOR_MASKED_COLOR:
|
||||
{
|
||||
for(int i = 0; i < cursor->width * cursor->height; ++i)
|
||||
{
|
||||
const uint32_t c = ((uint32_t *)data)[i];
|
||||
tmp[i] = (c & ~0xFF000000) | (c & 0xFF000000 ? 0x0 : 0xFF000000);
|
||||
}
|
||||
data = (uint8_t *)tmp;
|
||||
// fall through to LG_CURSOR_COLOR
|
||||
//
|
||||
// technically we should also create an XOR texture from the data but this
|
||||
// usage seems very rare in modern software.
|
||||
}
|
||||
// fall through
|
||||
|
||||
case LG_CURSOR_COLOR:
|
||||
{
|
||||
egl_texture_setup(cursor->texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * cursor->height * 4, false);
|
||||
egl_texture_setup(cursor->texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->stride, false);
|
||||
egl_texture_update(cursor->texture, data);
|
||||
egl_model_set_texture(cursor->model, cursor->texture);
|
||||
break;
|
||||
@@ -296,8 +228,8 @@ void egl_cursor_render(EGL_Cursor * cursor)
|
||||
for(int y = 0; y < cursor->height; ++y)
|
||||
for(int x = 0; x < cursor->width; ++x)
|
||||
{
|
||||
const uint8_t * srcAnd = data + (cursor->pitch * y) + (x / 8);
|
||||
const uint8_t * srcXor = srcAnd + cursor->pitch * cursor->height;
|
||||
const uint8_t * srcAnd = data + (cursor->stride * y) + (x / 8);
|
||||
const uint8_t * srcXor = srcAnd + cursor->stride * cursor->height;
|
||||
const uint8_t mask = 0x80 >> (x % 8);
|
||||
const uint32_t andMask = (*srcAnd & mask) ? 0xFFFFFFFF : 0xFF000000;
|
||||
const uint32_t xorMask = (*srcXor & mask) ? 0x00FFFFFF : 0x00000000;
|
||||
@@ -306,8 +238,8 @@ void egl_cursor_render(EGL_Cursor * cursor)
|
||||
xor[y * cursor->width + x] = xorMask;
|
||||
}
|
||||
|
||||
egl_texture_setup (cursor->texture , EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * cursor->height * 4, false);
|
||||
egl_texture_setup (cursor->textureMono, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * cursor->height * 4, false);
|
||||
egl_texture_setup (cursor->texture , EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false);
|
||||
egl_texture_setup (cursor->textureMono, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false);
|
||||
egl_texture_update(cursor->texture , (uint8_t *)and);
|
||||
egl_texture_update(cursor->textureMono, (uint8_t *)xor);
|
||||
break;
|
||||
@@ -316,10 +248,11 @@ void egl_cursor_render(EGL_Cursor * cursor)
|
||||
LG_UNLOCK(cursor->lock);
|
||||
}
|
||||
|
||||
if (cursor->type == LG_CURSOR_MONOCHROME)
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
switch(cursor->type)
|
||||
{
|
||||
case LG_CURSOR_MONOCHROME:
|
||||
{
|
||||
egl_shader_use(cursor->shader);
|
||||
glUniform4f(cursor->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h / 2);
|
||||
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
|
||||
@@ -331,18 +264,26 @@ void egl_cursor_render(EGL_Cursor * cursor)
|
||||
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
|
||||
egl_model_set_texture(cursor->model, cursor->textureMono);
|
||||
egl_model_render(cursor->model);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
case LG_CURSOR_COLOR:
|
||||
{
|
||||
egl_shader_use(cursor->shader);
|
||||
glUniform4f(cursor->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
egl_model_render(cursor->model);
|
||||
break;
|
||||
}
|
||||
|
||||
case LG_CURSOR_MASKED_COLOR:
|
||||
{
|
||||
egl_shader_use(cursor->shaderMono);
|
||||
glUniform4f(cursor->uMousePos, cursor->x, cursor->y, cursor->w, cursor->h);
|
||||
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
|
||||
egl_model_render(cursor->model);
|
||||
break;
|
||||
}
|
||||
}
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -20,14 +20,15 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "lg-renderer.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);
|
||||
|
||||
bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type, const int width, const int height, const int pitch, const uint8_t * data);
|
||||
bool egl_cursor_set_shape(EGL_Cursor * cursor, const LG_RendererCursor type, const int width, const int height, const int stride, const uint8_t * data);
|
||||
void egl_cursor_set_size (EGL_Cursor * cursor, const float x, const float y);
|
||||
void egl_cursor_set_state(EGL_Cursor * cursor, const bool visible, const float x, const float y);
|
||||
void egl_cursor_render (EGL_Cursor * cursor);
|
||||
58
client/renderers/EGL/debug.c
Normal file
58
client/renderers/EGL/debug.c
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
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 <GL/gl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void egl_debug_printf(char * format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
|
||||
GLenum error = glGetError();
|
||||
switch(error)
|
||||
{
|
||||
case GL_NO_ERROR:
|
||||
fprintf(stderr, " (GL_NO_ERROR)\n");
|
||||
break;
|
||||
|
||||
case GL_INVALID_ENUM:
|
||||
fprintf(stderr, " (GL_INVALID_ENUM)\n");
|
||||
break;
|
||||
|
||||
case GL_INVALID_VALUE:
|
||||
fprintf(stderr, " (GL_INVALID_VALUE)\n");
|
||||
break;
|
||||
|
||||
case GL_INVALID_OPERATION:
|
||||
fprintf(stderr, " (GL_INVALID_OPERATION)\n");
|
||||
break;
|
||||
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||
fprintf(stderr, " (GL_INVALID_FRAMEBUFFER_OPERATION)\n");
|
||||
break;
|
||||
|
||||
case GL_OUT_OF_MEMORY:
|
||||
fprintf(stderr, " (GL_OUT_OF_MEMORY)\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
27
client/renderers/EGL/debug.h
Normal file
27
client/renderers/EGL/debug.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
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 <common/debug.h>
|
||||
|
||||
#define EGL_DEBUG_PRINT(type, fmt, ...) do {egl_debug_printf(type " %20s:%-4u | %-30s | " fmt, STRIPPATH(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__);} while (0)
|
||||
#define EGL_ERROR(fmt, ...) EGL_DEBUG_PRINT("[E]", fmt, ##__VA_ARGS__)
|
||||
|
||||
void egl_debug_printf(char * format, ...);
|
||||
261
client/renderers/EGL/desktop.c
Normal file
261
client/renderers/EGL/desktop.c
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
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 "desktop.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/option.h"
|
||||
#include "common/locking.h"
|
||||
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
#include "model.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "interface/app.h"
|
||||
|
||||
// these headers are auto generated by cmake
|
||||
#include "desktop.vert.h"
|
||||
#include "desktop_rgb.frag.h"
|
||||
#include "desktop_yuv.frag.h"
|
||||
|
||||
struct DesktopShader
|
||||
{
|
||||
EGL_Shader * shader;
|
||||
GLint uDesktopPos;
|
||||
GLint uDesktopSize;
|
||||
GLint uNearest;
|
||||
GLint uNV, uNVGain;
|
||||
};
|
||||
|
||||
struct EGL_Desktop
|
||||
{
|
||||
void * egl;
|
||||
|
||||
EGL_Texture * texture;
|
||||
struct DesktopShader * shader; // the active shader
|
||||
EGL_Model * model;
|
||||
|
||||
// internals
|
||||
int width, height;
|
||||
|
||||
// shader instances
|
||||
struct DesktopShader shader_generic;
|
||||
struct DesktopShader shader_yuv;
|
||||
|
||||
// night vision
|
||||
KeybindHandle kbNV;
|
||||
int nvMax;
|
||||
int nvGain;
|
||||
};
|
||||
|
||||
// forwards
|
||||
void egl_desktop_toggle_nv(SDL_Scancode key, void * opaque);
|
||||
|
||||
static bool egl_init_desktop_shader(
|
||||
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))
|
||||
return false;
|
||||
|
||||
if (!egl_shader_compile(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->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" );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_desktop_init(void * egl, EGL_Desktop ** desktop)
|
||||
{
|
||||
*desktop = (EGL_Desktop *)malloc(sizeof(EGL_Desktop));
|
||||
if (!*desktop)
|
||||
{
|
||||
DEBUG_ERROR("Failed to malloc EGL_Desktop");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(*desktop, 0, sizeof(EGL_Desktop));
|
||||
|
||||
if (!egl_texture_init(&(*desktop)->texture))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the desktop texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_init_desktop_shader(
|
||||
&(*desktop)->shader_generic,
|
||||
b_shader_desktop_vert , b_shader_desktop_vert_size,
|
||||
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the generic desktop shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_init_desktop_shader(
|
||||
&(*desktop)->shader_yuv,
|
||||
b_shader_desktop_vert , b_shader_desktop_vert_size,
|
||||
b_shader_desktop_yuv_frag, b_shader_desktop_yuv_frag_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the yuv desktop shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_model_init(&(*desktop)->model))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the desktop model");
|
||||
return false;
|
||||
}
|
||||
|
||||
egl_model_set_default((*desktop)->model);
|
||||
egl_model_set_texture((*desktop)->model, (*desktop)->texture);
|
||||
|
||||
(*desktop)->egl = egl;
|
||||
(*desktop)->kbNV = app_register_keybind(SDL_SCANCODE_N, egl_desktop_toggle_nv, *desktop);
|
||||
|
||||
(*desktop)->nvMax = option_get_int("egl", "nvGainMax");
|
||||
(*desktop)->nvGain = option_get_int("egl", "nvGain" );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void egl_desktop_toggle_nv(SDL_Scancode key, void * opaque)
|
||||
{
|
||||
EGL_Desktop * desktop = (EGL_Desktop *)opaque;
|
||||
if (desktop->nvGain++ == desktop->nvMax)
|
||||
desktop->nvGain = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void egl_desktop_free(EGL_Desktop ** desktop)
|
||||
{
|
||||
if (!*desktop)
|
||||
return;
|
||||
|
||||
egl_texture_free(&(*desktop)->texture );
|
||||
egl_shader_free (&(*desktop)->shader_generic.shader);
|
||||
egl_shader_free (&(*desktop)->shader_yuv.shader );
|
||||
egl_model_free (&(*desktop)->model );
|
||||
|
||||
app_release_keybind(&(*desktop)->kbNV);
|
||||
|
||||
free(*desktop);
|
||||
*desktop = NULL;
|
||||
}
|
||||
|
||||
bool egl_desktop_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const FrameBuffer * frame)
|
||||
{
|
||||
if (sourceChanged)
|
||||
{
|
||||
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_YUV420:
|
||||
pixFmt = EGL_PF_YUV420;
|
||||
desktop->shader = &desktop->shader_yuv;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Unsupported frame format");
|
||||
return false;
|
||||
}
|
||||
|
||||
desktop->width = format.width;
|
||||
desktop->height = format.height;
|
||||
|
||||
if (!egl_texture_setup(
|
||||
desktop->texture,
|
||||
pixFmt,
|
||||
format.width,
|
||||
format.height,
|
||||
format.pitch,
|
||||
true // streaming texture
|
||||
))
|
||||
{
|
||||
DEBUG_ERROR("Failed to setup the desktop texture");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!egl_texture_update_from_frame(desktop->texture, frame))
|
||||
return false;
|
||||
|
||||
enum EGL_TexStatus status;
|
||||
if ((status = egl_texture_process(desktop->texture)) != EGL_TEX_STATUS_OK)
|
||||
{
|
||||
if (status != EGL_TEX_STATUS_NOTREADY)
|
||||
DEBUG_ERROR("Failed to process the desktop texture");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_desktop_render(EGL_Desktop * desktop, const float x, const float y, const float scaleX, const float scaleY, const bool nearest)
|
||||
{
|
||||
if (!desktop->shader)
|
||||
return false;
|
||||
|
||||
const struct DesktopShader * shader = desktop->shader;
|
||||
egl_shader_use(shader->shader);
|
||||
glUniform4f(shader->uDesktopPos , x, y, scaleX, scaleY);
|
||||
glUniform1i(shader->uNearest , nearest ? 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);
|
||||
|
||||
egl_model_render(desktop->model);
|
||||
return true;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -21,13 +21,12 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "lg-renderer.h"
|
||||
#include "interface/renderer.h"
|
||||
|
||||
typedef struct EGL_Desktop EGL_Desktop;
|
||||
|
||||
bool egl_desktop_init(EGL_Desktop ** desktop);
|
||||
bool egl_desktop_init(void * egl, EGL_Desktop ** desktop);
|
||||
void egl_desktop_free(EGL_Desktop ** desktop);
|
||||
|
||||
bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const uint8_t * data);
|
||||
bool egl_desktop_perform_update(EGL_Desktop * desktop, const bool sourceChanged);
|
||||
void egl_desktop_render(EGL_Desktop * desktop, const float x, const float y, const float scaleX, const float scaleY);
|
||||
bool egl_desktop_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const FrameBuffer * frame);
|
||||
bool egl_desktop_render(EGL_Desktop * desktop, const float x, const float y, const float scaleX, const float scaleY, const bool nearest);
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -17,22 +17,30 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lg-renderer.h"
|
||||
#include "interface/renderer.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/option.h"
|
||||
#include "common/sysinfo.h"
|
||||
#include "common/time.h"
|
||||
#include "common/locking.h"
|
||||
#include "utils.h"
|
||||
#include "lg-fonts.h"
|
||||
#include "dynamic/fonts.h"
|
||||
|
||||
#include <SDL2/SDL_syswm.h>
|
||||
#include <SDL2/SDL_egl.h>
|
||||
|
||||
#include "egl/model.h"
|
||||
#include "egl/shader.h"
|
||||
#include "egl/desktop.h"
|
||||
#include "egl/cursor.h"
|
||||
#include "egl/fps.h"
|
||||
#include "egl/splash.h"
|
||||
#include "egl/alert.h"
|
||||
#if defined(SDL_VIDEO_DRIVER_WAYLAND)
|
||||
#include <wayland-egl.h>
|
||||
#endif
|
||||
|
||||
#include "model.h"
|
||||
#include "shader.h"
|
||||
#include "desktop.h"
|
||||
#include "cursor.h"
|
||||
#include "fps.h"
|
||||
#include "splash.h"
|
||||
#include "alert.h"
|
||||
|
||||
#define SPLASH_FADE_TIME 1000000
|
||||
#define ALERT_TIMEOUT 2000000
|
||||
@@ -42,22 +50,16 @@ struct Options
|
||||
bool vsync;
|
||||
};
|
||||
|
||||
static struct Options defaultOptions =
|
||||
{
|
||||
.vsync = false
|
||||
};
|
||||
|
||||
struct Inst
|
||||
{
|
||||
LG_RendererParams params;
|
||||
struct Options opt;
|
||||
|
||||
Display * xDisplay;
|
||||
Window xWindow;
|
||||
EGLNativeWindowType nativeWind;
|
||||
EGLDisplay display;
|
||||
EGLConfig configs;
|
||||
EGLSurface surface;
|
||||
EGLContext context;
|
||||
EGLContext context, frameContext;
|
||||
|
||||
EGL_Desktop * desktop; // the desktop
|
||||
EGL_Cursor * cursor; // the mouse cursor
|
||||
@@ -66,7 +68,7 @@ struct Inst
|
||||
EGL_Alert * alert; // the alert display
|
||||
|
||||
LG_RendererFormat format;
|
||||
bool sourceChanged;
|
||||
bool start;
|
||||
uint64_t waitFadeTime;
|
||||
bool waitDone;
|
||||
|
||||
@@ -82,7 +84,10 @@ struct Inst
|
||||
float scaleX , scaleY;
|
||||
float splashRatio;
|
||||
float screenScaleX, screenScaleY;
|
||||
bool useNearest;
|
||||
|
||||
bool cursorVisible;
|
||||
int cursorX , cursorY;
|
||||
float mouseWidth , mouseHeight;
|
||||
float mouseScaleX, mouseScaleY;
|
||||
|
||||
@@ -91,6 +96,46 @@ struct Inst
|
||||
};
|
||||
|
||||
|
||||
static struct Option egl_options[] =
|
||||
{
|
||||
{
|
||||
.module = "egl",
|
||||
.name = "vsync",
|
||||
.description = "Enable vsync",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false
|
||||
},
|
||||
{
|
||||
.module = "egl",
|
||||
.name = "doubleBuffer",
|
||||
.description = "Enable double buffering",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false
|
||||
},
|
||||
{
|
||||
.module = "egl",
|
||||
.name = "multisample",
|
||||
.description = "Enable Multisampling",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
{
|
||||
.module = "egl",
|
||||
.name = "nvGainMax",
|
||||
.description = "The maximum night vision gain",
|
||||
.type = OPTION_TYPE_INT,
|
||||
.value.x_int = 1
|
||||
},
|
||||
{
|
||||
.module = "egl",
|
||||
.name = "nvGain",
|
||||
.description = "The initial night vision gain at startup",
|
||||
.type = OPTION_TYPE_INT,
|
||||
.value.x_int = 0
|
||||
},
|
||||
{0}
|
||||
};
|
||||
|
||||
void update_mouse_shape(struct Inst * this);
|
||||
|
||||
const char * egl_get_name()
|
||||
@@ -98,6 +143,11 @@ const char * egl_get_name()
|
||||
return "EGL";
|
||||
}
|
||||
|
||||
void egl_setup()
|
||||
{
|
||||
option_register(egl_options);
|
||||
}
|
||||
|
||||
bool egl_create(void ** opaque, const LG_RendererParams params)
|
||||
{
|
||||
// create our local storage
|
||||
@@ -112,7 +162,8 @@ bool egl_create(void ** opaque, const LG_RendererParams params)
|
||||
// safe off parameteres and init our default option values
|
||||
struct Inst * this = (struct Inst *)*opaque;
|
||||
memcpy(&this->params, ¶ms, sizeof(LG_RendererParams));
|
||||
memcpy(&this->opt , &defaultOptions, sizeof(struct Options ));
|
||||
|
||||
this->opt.vsync = option_get_bool("egl", "vsync");
|
||||
|
||||
this->translateX = 0;
|
||||
this->translateY = 0;
|
||||
@@ -122,7 +173,7 @@ bool egl_create(void ** opaque, const LG_RendererParams params)
|
||||
this->screenScaleY = 1.0f;
|
||||
|
||||
this->font = LG_Fonts[0];
|
||||
if (!this->font->create(&this->fontObj, NULL, 14))
|
||||
if (!this->font->create(&this->fontObj, NULL, 16))
|
||||
{
|
||||
DEBUG_ERROR("Failed to create a font instance");
|
||||
return false;
|
||||
@@ -133,12 +184,27 @@ bool egl_create(void ** opaque, const LG_RendererParams params)
|
||||
|
||||
bool egl_initialize(void * opaque, Uint32 * sdlFlags)
|
||||
{
|
||||
const bool doubleBuffer = option_get_bool("egl", "doubleBuffer");
|
||||
DEBUG_INFO("Double buffering is %s", doubleBuffer ? "on" : "off");
|
||||
|
||||
*sdlFlags = SDL_WINDOW_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_DOUBLEBUFFER , doubleBuffer ? 1 : 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
|
||||
if (option_get_bool("egl", "multisample"))
|
||||
{
|
||||
int maxSamples = sysinfo_gfx_max_multisample();
|
||||
if (maxSamples > 1)
|
||||
{
|
||||
if (maxSamples > 4)
|
||||
maxSamples = 4;
|
||||
|
||||
DEBUG_INFO("Multisampling enabled, max samples: %d", maxSamples);
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, maxSamples);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -155,9 +221,20 @@ void egl_deinitialize(void * opaque)
|
||||
egl_splash_free (&this->splash);
|
||||
egl_alert_free (&this->alert );
|
||||
|
||||
LG_LOCK_FREE(this->lock);
|
||||
|
||||
free(this);
|
||||
}
|
||||
|
||||
void egl_on_restart(void * opaque)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
|
||||
eglDestroyContext(this->display, this->frameContext);
|
||||
this->frameContext = NULL;
|
||||
this->start = false;
|
||||
}
|
||||
|
||||
void egl_on_resize(void * opaque, const int width, const int height, const LG_RendererRect destRect)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
@@ -186,6 +263,13 @@ void egl_on_resize(void * opaque, const int width, const int height, const LG_Re
|
||||
this->splashRatio = (float)width / (float)height;
|
||||
this->screenScaleX = 1.0f / width;
|
||||
this->screenScaleY = 1.0f / height;
|
||||
|
||||
egl_cursor_set_state(
|
||||
this->cursor,
|
||||
this->cursorVisible,
|
||||
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleX,
|
||||
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleY
|
||||
);
|
||||
}
|
||||
|
||||
bool egl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data)
|
||||
@@ -210,44 +294,67 @@ bool egl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, const int
|
||||
bool egl_on_mouse_event(void * opaque, const bool visible, const int x, const int y)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
this->cursorVisible = visible;
|
||||
this->cursorX = x;
|
||||
this->cursorY = y;
|
||||
|
||||
egl_cursor_set_state(
|
||||
this->cursor,
|
||||
visible,
|
||||
(((float)x * this->mouseScaleX) - 1.0f) * this->scaleX,
|
||||
(((float)y * this->mouseScaleY) - 1.0f) * this->scaleY
|
||||
this->cursorVisible,
|
||||
(((float)this->cursorX * this->mouseScaleX) - 1.0f) * this->scaleX,
|
||||
(((float)this->cursorY * this->mouseScaleY) - 1.0f) * this->scaleY
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_on_frame_event(void * opaque, const LG_RendererFormat format, const uint8_t * data)
|
||||
bool egl_on_frame_event(void * opaque, const LG_RendererFormat format, const FrameBuffer * frame)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
this->sourceChanged = (
|
||||
this->sourceChanged ||
|
||||
const bool sourceChanged = (
|
||||
this->format.type != format.type ||
|
||||
this->format.width != format.width ||
|
||||
this->format.height != format.height ||
|
||||
this->format.pitch != format.pitch
|
||||
);
|
||||
|
||||
if (this->sourceChanged)
|
||||
if (sourceChanged)
|
||||
memcpy(&this->format, &format, sizeof(LG_RendererFormat));
|
||||
|
||||
if (!egl_desktop_prepare_update(this->desktop, this->sourceChanged, format, data))
|
||||
this->useNearest = this->width < format.width || this->height < format.height;
|
||||
|
||||
/* this event runs in a second thread so we need to init it here */
|
||||
if (!this->frameContext)
|
||||
{
|
||||
DEBUG_INFO("Failed to prepare to update the desktop");
|
||||
static EGLint attrs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
if (!(this->frameContext = eglCreateContext(this->display, this->configs, this->context, attrs)))
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the frame context");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->waitFadeTime)
|
||||
this->waitFadeTime = microtime() + SPLASH_FADE_TIME;
|
||||
if (!eglMakeCurrent(this->display, EGL_NO_SURFACE, EGL_NO_SURFACE, this->frameContext))
|
||||
{
|
||||
DEBUG_ERROR("Failed to make the frame context current");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!egl_desktop_update(this->desktop, sourceChanged, format, frame))
|
||||
{
|
||||
DEBUG_INFO("Failed to to update the desktop");
|
||||
return false;
|
||||
}
|
||||
|
||||
this->start = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_on_alert(void * opaque, const LG_RendererAlert alert, const char * message, bool ** closeFlag)
|
||||
void egl_on_alert(void * opaque, const LG_MsgAlert alert, const char * message, bool ** closeFlag)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
|
||||
@@ -294,10 +401,52 @@ bool egl_render_startup(void * opaque, SDL_Window * window)
|
||||
return false;
|
||||
}
|
||||
|
||||
this->xDisplay = wminfo.info.x11.display;
|
||||
this->xWindow = wminfo.info.x11.window;
|
||||
const char *client_exts = eglQueryString(NULL, EGL_EXTENSIONS);
|
||||
DEBUG_INFO("Supported extensions: %s", client_exts);
|
||||
|
||||
bool useNative = false;
|
||||
if (strstr(client_exts, "EGL_KHR_platform_base") != NULL)
|
||||
useNative = true;
|
||||
|
||||
DEBUG_INFO("use native: %s", useNative ? "true" : "false");
|
||||
|
||||
switch(wminfo.subsystem)
|
||||
{
|
||||
case SDL_SYSWM_X11:
|
||||
{
|
||||
if (!useNative)
|
||||
this->display = eglGetPlatformDisplay(EGL_PLATFORM_X11_KHR, wminfo.info.x11.display, NULL);
|
||||
else
|
||||
{
|
||||
EGLNativeDisplayType native = (EGLNativeDisplayType)wminfo.info.x11.display;
|
||||
this->display = eglGetDisplay(native);
|
||||
}
|
||||
this->nativeWind = (EGLNativeWindowType)wminfo.info.x11.window;
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(SDL_VIDEO_DRIVER_WAYLAND)
|
||||
case SDL_SYSWM_WAYLAND:
|
||||
{
|
||||
int width, height;
|
||||
SDL_GetWindowSize(window, &width, &height);
|
||||
if (!useNative)
|
||||
this->display = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wminfo.info.wl.display, NULL);
|
||||
else
|
||||
{
|
||||
EGLNativeDisplayType native = (EGLNativeDisplayType)wminfo.info.wl.display;
|
||||
this->display = eglGetDisplay(native);
|
||||
}
|
||||
this->nativeWind = (EGLNativeWindowType)wl_egl_window_create(wminfo.info.wl.surface, width, height);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Unsupported subsystem");
|
||||
return false;
|
||||
}
|
||||
|
||||
this->display = eglGetDisplay((EGLNativeDisplayType)this->xDisplay);
|
||||
if (this->display == EGL_NO_DISPLAY)
|
||||
{
|
||||
DEBUG_ERROR("eglGetDisplay failed");
|
||||
@@ -312,10 +461,10 @@ bool egl_render_startup(void * opaque, SDL_Window * window)
|
||||
|
||||
EGLint attr[] =
|
||||
{
|
||||
EGL_BUFFER_SIZE , 16,
|
||||
EGL_BUFFER_SIZE , 32,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_SAMPLE_BUFFERS , 1,
|
||||
EGL_SAMPLES , 8,
|
||||
EGL_SAMPLES , 4,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
@@ -326,7 +475,7 @@ bool egl_render_startup(void * opaque, SDL_Window * window)
|
||||
return false;
|
||||
}
|
||||
|
||||
this->surface = eglCreateWindowSurface(this->display, this->configs, this->xWindow, NULL);
|
||||
this->surface = eglCreateWindowSurface(this->display, this->configs, this->nativeWind, NULL);
|
||||
if (this->surface == EGL_NO_SURFACE)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create EGL surface (eglError: 0x%x)", eglGetError());
|
||||
@@ -354,7 +503,7 @@ bool egl_render_startup(void * opaque, SDL_Window * window)
|
||||
|
||||
eglSwapInterval(this->display, this->opt.vsync ? 1 : 0);
|
||||
|
||||
if (!egl_desktop_init(&this->desktop))
|
||||
if (!egl_desktop_init(this, &this->desktop))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the desktop");
|
||||
return false;
|
||||
@@ -394,8 +543,15 @@ bool egl_render(void * opaque, SDL_Window * window)
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
egl_desktop_render(this->desktop, this->translateX, this->translateY, this->scaleX, this->scaleY);
|
||||
if (this->start && egl_desktop_render(this->desktop,
|
||||
this->translateX, this->translateY,
|
||||
this->scaleX , this->scaleY ,
|
||||
this->useNearest))
|
||||
{
|
||||
if (!this->waitFadeTime)
|
||||
this->waitFadeTime = microtime() + SPLASH_FADE_TIME;
|
||||
egl_cursor_render(this->cursor);
|
||||
}
|
||||
|
||||
if (!this->waitDone)
|
||||
{
|
||||
@@ -413,8 +569,15 @@ bool egl_render(void * opaque, SDL_Window * window)
|
||||
a = 1.0f / SPLASH_FADE_TIME * delta;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->waitDone)
|
||||
egl_splash_render(this->splash, a, this->splashRatio);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!this->start)
|
||||
egl_splash_render(this->splash, 1.0f, this->splashRatio);
|
||||
}
|
||||
|
||||
if (this->showAlert)
|
||||
{
|
||||
@@ -432,15 +595,6 @@ bool egl_render(void * opaque, SDL_Window * window)
|
||||
|
||||
egl_fps_render(this->fps, this->screenScaleX, this->screenScaleY);
|
||||
eglSwapBuffers(this->display, this->surface);
|
||||
|
||||
// defer texture uploads until after the flip to avoid stalling
|
||||
if (!egl_desktop_perform_update(this->desktop, this->sourceChanged))
|
||||
{
|
||||
DEBUG_ERROR("Failed to perform the desktop update");
|
||||
return false;
|
||||
}
|
||||
this->sourceChanged = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -453,33 +607,14 @@ void egl_update_fps(void * opaque, const float avgUPS, const float avgFPS)
|
||||
egl_fps_update(this->fps, avgUPS, avgFPS);
|
||||
}
|
||||
|
||||
static void handle_opt_vsync(void * opaque, const char *value)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
this->opt.vsync = LG_RendererValueToBool(value);
|
||||
}
|
||||
|
||||
static LG_RendererOpt egl_options[] =
|
||||
{
|
||||
{
|
||||
.name = "vsync",
|
||||
.desc ="Enable or disable vsync [default: enabled]",
|
||||
.validator = LG_RendererValidatorBool,
|
||||
.handler = handle_opt_vsync
|
||||
}
|
||||
};
|
||||
|
||||
struct LG_Renderer LGR_EGL =
|
||||
{
|
||||
.create = egl_create,
|
||||
.get_name = egl_get_name,
|
||||
.options = egl_options,
|
||||
.option_count = LGR_OPTION_COUNT(egl_options),
|
||||
.setup = egl_setup,
|
||||
.create = egl_create,
|
||||
.initialize = egl_initialize,
|
||||
.deinitialize = egl_deinitialize,
|
||||
.on_restart = egl_on_restart,
|
||||
.on_resize = egl_on_resize,
|
||||
.on_mouse_shape = egl_on_mouse_shape,
|
||||
.on_mouse_event = egl_on_mouse_event,
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -18,7 +18,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "fps.h"
|
||||
#include "debug.h"
|
||||
#include "common/debug.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "texture.h"
|
||||
@@ -28,6 +28,11 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#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;
|
||||
@@ -39,6 +44,7 @@ struct EGL_FPS
|
||||
EGL_Model * model;
|
||||
|
||||
bool ready;
|
||||
int iwidth, iheight;
|
||||
float width, height;
|
||||
|
||||
// uniforms
|
||||
@@ -46,57 +52,6 @@ struct EGL_FPS
|
||||
GLint uScreenBG, uSizeBG;
|
||||
};
|
||||
|
||||
static const char vertex_shader[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
layout(location = 0) in vec3 vertexPosition_modelspace;\
|
||||
layout(location = 1) in vec2 vertexUV;\
|
||||
\
|
||||
uniform vec2 screen;\
|
||||
uniform vec2 size;\
|
||||
\
|
||||
out highp vec2 uv;\
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
gl_Position.xyz = vertexPosition_modelspace; \
|
||||
gl_Position.w = 1.0; \
|
||||
gl_Position.xy *= screen.xy * size.xy; \
|
||||
gl_Position.x -= 1.0 - (screen.x * size.x);\
|
||||
gl_Position.y += 1.0 - (screen.y * size.y);\
|
||||
gl_Position.x += screen.x * 10.0; \
|
||||
gl_Position.y -= screen.y * 10.0; \
|
||||
\
|
||||
uv = vertexUV;\
|
||||
}\
|
||||
";
|
||||
|
||||
static const char frag_shader[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
in highp vec2 uv;\
|
||||
out highp vec4 color;\
|
||||
\
|
||||
uniform sampler2D sampler1;\
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
color = texture(sampler1, uv);\
|
||||
}\
|
||||
";
|
||||
|
||||
static const char frag_shaderBG[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
out highp vec4 color;\
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
color = vec4(0.0, 0.0, 1.0, 0.5);\
|
||||
}\
|
||||
";
|
||||
|
||||
|
||||
bool egl_fps_init(EGL_FPS ** fps, const LG_Font * font, LG_FontObj fontObj)
|
||||
{
|
||||
*fps = (EGL_FPS *)malloc(sizeof(EGL_FPS));
|
||||
@@ -131,16 +86,16 @@ bool egl_fps_init(EGL_FPS ** fps, const LG_Font * font, LG_FontObj fontObj)
|
||||
|
||||
|
||||
if (!egl_shader_compile((*fps)->shader,
|
||||
vertex_shader, sizeof(vertex_shader),
|
||||
frag_shader , sizeof(frag_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,
|
||||
vertex_shader, sizeof(vertex_shader),
|
||||
frag_shaderBG, sizeof(frag_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;
|
||||
@@ -190,14 +145,22 @@ void egl_fps_update(EGL_FPS * fps, const float avgFPS, const float renderFPS)
|
||||
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->height * bmp->bpp,
|
||||
bmp->width * bmp->bpp,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
egl_texture_update
|
||||
(
|
||||
@@ -205,10 +168,7 @@ void egl_fps_update(EGL_FPS * fps, const float avgFPS, const float renderFPS)
|
||||
bmp->pixels
|
||||
);
|
||||
|
||||
fps->width = bmp->width;
|
||||
fps->height = bmp->height;
|
||||
fps->ready = true;
|
||||
|
||||
fps->font->release(fps->fontObj, bmp);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -21,7 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "lg-fonts.h"
|
||||
#include "interface/font.h"
|
||||
|
||||
typedef struct EGL_FPS EGL_FPS;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -21,7 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "shader.h"
|
||||
#include "texture.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "common/debug.h"
|
||||
#include "utils.h"
|
||||
#include "ll.h"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -18,7 +18,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "shader.h"
|
||||
#include "debug.h"
|
||||
#include "common/debug.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -20,6 +20,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <GL/gl.h>
|
||||
|
||||
12
client/renderers/EGL/shader/alert.frag
Normal file
12
client/renderers/EGL/shader/alert.frag
Normal file
@@ -0,0 +1,12 @@
|
||||
#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);
|
||||
}
|
||||
24
client/renderers/EGL/shader/alert.vert
Normal file
24
client/renderers/EGL/shader/alert.vert
Normal file
@@ -0,0 +1,24 @@
|
||||
#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;
|
||||
}
|
||||
9
client/renderers/EGL/shader/alert_bg.frag
Normal file
9
client/renderers/EGL/shader/alert_bg.frag
Normal file
@@ -0,0 +1,9 @@
|
||||
#version 300 es
|
||||
|
||||
in highp vec4 c;
|
||||
out highp vec4 color;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = c;
|
||||
}
|
||||
25
client/renderers/EGL/shader/cursor.vert
Normal file
25
client/renderers/EGL/shader/cursor.vert
Normal file
@@ -0,0 +1,25 @@
|
||||
#version 300 es
|
||||
|
||||
layout(location = 0) in vec3 vertexPosition_modelspace;
|
||||
layout(location = 1) in vec2 vertexUV;
|
||||
|
||||
uniform vec4 mouse;
|
||||
|
||||
out highp vec2 uv;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position.xyz = vertexPosition_modelspace;
|
||||
gl_Position.w = 1.0;
|
||||
|
||||
gl_Position.x += 1.0f;
|
||||
gl_Position.y -= 1.0f;
|
||||
|
||||
gl_Position.x *= mouse.z;
|
||||
gl_Position.y *= mouse.w;
|
||||
|
||||
gl_Position.x += mouse.x;
|
||||
gl_Position.y -= mouse.y;
|
||||
|
||||
uv = vertexUV;
|
||||
}
|
||||
14
client/renderers/EGL/shader/cursor_mono.frag
Normal file
14
client/renderers/EGL/shader/cursor_mono.frag
Normal file
@@ -0,0 +1,14 @@
|
||||
#version 300 es
|
||||
|
||||
in highp vec2 uv;
|
||||
out highp vec4 color;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp vec4 tmp = texture(sampler1, uv);
|
||||
if (tmp.rgb == vec3(0.0, 0.0, 0.0))
|
||||
discard;
|
||||
color = tmp;
|
||||
}
|
||||
11
client/renderers/EGL/shader/cursor_rgb.frag
Normal file
11
client/renderers/EGL/shader/cursor_rgb.frag
Normal file
@@ -0,0 +1,11 @@
|
||||
#version 300 es
|
||||
|
||||
in highp vec2 uv;
|
||||
out highp vec4 color;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = texture(sampler1, uv);
|
||||
}
|
||||
20
client/renderers/EGL/shader/desktop.vert
Normal file
20
client/renderers/EGL/shader/desktop.vert
Normal file
@@ -0,0 +1,20 @@
|
||||
#version 300 es
|
||||
|
||||
layout(location = 0) in vec3 vertexPosition_modelspace;
|
||||
layout(location = 1) in vec2 vertexUV;
|
||||
|
||||
uniform vec4 position;
|
||||
|
||||
out highp vec2 uv;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position.xyz = vertexPosition_modelspace;
|
||||
gl_Position.w = 1.0;
|
||||
gl_Position.x -= position.x;
|
||||
gl_Position.y -= position.y;
|
||||
gl_Position.x *= position.z;
|
||||
gl_Position.y *= position.w;
|
||||
|
||||
uv = vertexUV;
|
||||
}
|
||||
27
client/renderers/EGL/shader/desktop_rgb.frag
Normal file
27
client/renderers/EGL/shader/desktop_rgb.frag
Normal file
@@ -0,0 +1,27 @@
|
||||
#version 300 es
|
||||
|
||||
in highp vec2 uv;
|
||||
out highp vec4 color;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
|
||||
uniform int nearest;
|
||||
uniform highp vec2 size;
|
||||
|
||||
uniform int nv;
|
||||
uniform highp float nvGain;
|
||||
|
||||
void main()
|
||||
{
|
||||
if(nearest == 1)
|
||||
color = texture(sampler1, uv);
|
||||
else
|
||||
color = texelFetch(sampler1, ivec2(uv * size), 0);
|
||||
|
||||
if (nv == 1)
|
||||
{
|
||||
highp float lumi = 1.0 - (0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b);
|
||||
color *= 1.0 + lumi;
|
||||
color *= nvGain;
|
||||
}
|
||||
}
|
||||
53
client/renderers/EGL/shader/desktop_yuv.frag
Normal file
53
client/renderers/EGL/shader/desktop_yuv.frag
Normal file
@@ -0,0 +1,53 @@
|
||||
#version 300 es
|
||||
|
||||
in highp vec2 uv;
|
||||
out highp vec4 color;
|
||||
|
||||
uniform int nearest;
|
||||
uniform highp vec2 size;
|
||||
|
||||
uniform int nv;
|
||||
uniform highp float nvGain;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
uniform sampler2D sampler2;
|
||||
uniform sampler2D sampler3;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp vec4 yuv;
|
||||
if(nearest == 1)
|
||||
{
|
||||
yuv = vec4(
|
||||
texture(sampler1, uv).r,
|
||||
texture(sampler2, uv).r,
|
||||
texture(sampler3, uv).r,
|
||||
1.0
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
highp ivec2 px = ivec2(uv * size);
|
||||
yuv = vec4(
|
||||
texelFetch(sampler1, px, 0).r,
|
||||
texelFetch(sampler2, px, 0).r,
|
||||
texelFetch(sampler3, px, 0).r,
|
||||
1.0
|
||||
);
|
||||
}
|
||||
|
||||
highp mat4 yuv_to_rgb = mat4(
|
||||
1.0, 0.0 , 1.402, -0.701,
|
||||
1.0, -0.344, -0.714, 0.529,
|
||||
1.0, 1.772, 0.0 , -0.886,
|
||||
1.0, 1.0 , 1.0 , 1.0
|
||||
);
|
||||
|
||||
color = yuv * yuv_to_rgb;
|
||||
if (nv == 1)
|
||||
{
|
||||
highp float lumi = 1.0 - (0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b);
|
||||
color *= 1.0 + lumi;
|
||||
color *= nvGain;
|
||||
}
|
||||
}
|
||||
11
client/renderers/EGL/shader/fps.frag
Normal file
11
client/renderers/EGL/shader/fps.frag
Normal file
@@ -0,0 +1,11 @@
|
||||
#version 300 es
|
||||
|
||||
in highp vec2 uv;
|
||||
out highp vec4 color;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = texture(sampler1, uv);
|
||||
}
|
||||
22
client/renderers/EGL/shader/fps.vert
Normal file
22
client/renderers/EGL/shader/fps.vert
Normal file
@@ -0,0 +1,22 @@
|
||||
#version 300 es
|
||||
|
||||
layout(location = 0) in vec3 vertexPosition_modelspace;
|
||||
layout(location = 1) in vec2 vertexUV;
|
||||
|
||||
uniform vec2 screen;
|
||||
uniform vec2 size;
|
||||
|
||||
out highp vec2 uv;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position.xyz = vertexPosition_modelspace;
|
||||
gl_Position.w = 1.0;
|
||||
gl_Position.xy *= screen.xy * size.xy;
|
||||
gl_Position.x -= 1.0 - (screen.x * size.x);
|
||||
gl_Position.y += 1.0 - (screen.y * size.y);
|
||||
gl_Position.x += screen.x * 10.0;
|
||||
gl_Position.y -= screen.y * 10.0;
|
||||
|
||||
uv = vertexUV;
|
||||
}
|
||||
8
client/renderers/EGL/shader/fps_bg.frag
Normal file
8
client/renderers/EGL/shader/fps_bg.frag
Normal file
@@ -0,0 +1,8 @@
|
||||
#version 300 es
|
||||
|
||||
out highp vec4 color;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = vec4(0.0, 0.0, 1.0, 0.5);
|
||||
}
|
||||
13
client/renderers/EGL/shader/splash_bg.frag
Normal file
13
client/renderers/EGL/shader/splash_bg.frag
Normal file
@@ -0,0 +1,13 @@
|
||||
#version 300 es
|
||||
|
||||
in highp vec3 pos;
|
||||
in highp float a;
|
||||
out highp vec4 color;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
|
||||
void main()
|
||||
{
|
||||
highp float d = 1.0 - sqrt(pos.x * pos.x + pos.y * pos.y) / 2.0;
|
||||
color = vec4(0.234375 * d, 0.015625f * d, 0.425781f * d, a);
|
||||
}
|
||||
17
client/renderers/EGL/shader/splash_bg.vert
Normal file
17
client/renderers/EGL/shader/splash_bg.vert
Normal file
@@ -0,0 +1,17 @@
|
||||
#version 300 es
|
||||
|
||||
layout(location = 0) in vec3 vertexPosition_modelspace;
|
||||
|
||||
uniform float alpha;
|
||||
|
||||
out highp vec3 pos;
|
||||
out highp float a;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position.xyz = vertexPosition_modelspace;
|
||||
gl_Position.w = 1.0;
|
||||
|
||||
pos = vertexPosition_modelspace;
|
||||
a = alpha;
|
||||
}
|
||||
11
client/renderers/EGL/shader/splash_logo.frag
Normal file
11
client/renderers/EGL/shader/splash_logo.frag
Normal file
@@ -0,0 +1,11 @@
|
||||
#version 300 es
|
||||
|
||||
out highp vec4 color;
|
||||
in highp float a;
|
||||
|
||||
uniform sampler2D sampler1;
|
||||
|
||||
void main()
|
||||
{
|
||||
color = vec4(1.0, 1.0, 1.0, a);
|
||||
}
|
||||
16
client/renderers/EGL/shader/splash_logo.vert
Normal file
16
client/renderers/EGL/shader/splash_logo.vert
Normal file
@@ -0,0 +1,16 @@
|
||||
#version 300 es
|
||||
|
||||
layout(location = 0) in vec3 vertexPosition_modelspace;
|
||||
|
||||
uniform vec2 scale;
|
||||
|
||||
out highp float a;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position.xyz = vertexPosition_modelspace;
|
||||
gl_Position.y *= scale.y;
|
||||
gl_Position.w = 1.0;
|
||||
|
||||
a = scale.x;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -18,7 +18,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "splash.h"
|
||||
#include "debug.h"
|
||||
#include "common/debug.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "draw.h"
|
||||
@@ -31,6 +31,12 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
// these headers are auto generated by cmake
|
||||
#include "splash_bg.vert.h"
|
||||
#include "splash_bg.frag.h"
|
||||
#include "splash_logo.vert.h"
|
||||
#include "splash_logo.frag.h"
|
||||
|
||||
struct EGL_Splash
|
||||
{
|
||||
EGL_Shader * bgShader;
|
||||
@@ -44,75 +50,6 @@ struct EGL_Splash
|
||||
GLint uScale;
|
||||
};
|
||||
|
||||
static const char vertex_bgShader[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
layout(location = 0) in vec3 vertexPosition_modelspace;\
|
||||
\
|
||||
uniform float alpha;\
|
||||
\
|
||||
out highp vec3 pos; \
|
||||
out highp float a; \
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
gl_Position.xyz = vertexPosition_modelspace; \
|
||||
gl_Position.w = 1.0; \
|
||||
\
|
||||
pos = vertexPosition_modelspace; \
|
||||
a = alpha; \
|
||||
}\
|
||||
";
|
||||
|
||||
static const char frag_bgShader[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
in highp vec3 pos;\
|
||||
in highp float a;\
|
||||
out highp vec4 color;\
|
||||
\
|
||||
uniform sampler2D sampler1;\
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
highp float d = 1.0 - sqrt(pos.x * pos.x + pos.y * pos.y) / 2.0; \
|
||||
color = vec4(0.234375 * d, 0.015625f * d, 0.425781f * d, a); \
|
||||
}\
|
||||
";
|
||||
|
||||
static const char vertex_logoShader[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
layout(location = 0) in vec3 vertexPosition_modelspace;\
|
||||
\
|
||||
uniform vec2 scale;\
|
||||
\
|
||||
out highp float a; \
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
gl_Position.xyz = vertexPosition_modelspace; \
|
||||
gl_Position.y *= scale.y; \
|
||||
gl_Position.w = 1.0; \
|
||||
\
|
||||
a = scale.x; \
|
||||
}\
|
||||
";
|
||||
|
||||
static const char frag_logoShader[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
out highp vec4 color;\
|
||||
in highp float a;\
|
||||
\
|
||||
uniform sampler2D sampler1;\
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
color = vec4(1.0, 1.0, 1.0, a);\
|
||||
}\
|
||||
";
|
||||
|
||||
bool egl_splash_init(EGL_Splash ** splash)
|
||||
{
|
||||
*splash = (EGL_Splash *)malloc(sizeof(EGL_Splash));
|
||||
@@ -131,8 +68,8 @@ bool egl_splash_init(EGL_Splash ** splash)
|
||||
}
|
||||
|
||||
if (!egl_shader_compile((*splash)->bgShader,
|
||||
vertex_bgShader, sizeof(vertex_bgShader),
|
||||
frag_bgShader , sizeof(frag_bgShader )))
|
||||
b_shader_splash_bg_vert, b_shader_splash_bg_vert_size,
|
||||
b_shader_splash_bg_frag, b_shader_splash_bg_frag_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the splash bgShader");
|
||||
return false;
|
||||
@@ -155,8 +92,8 @@ bool egl_splash_init(EGL_Splash ** splash)
|
||||
}
|
||||
|
||||
if (!egl_shader_compile((*splash)->logoShader,
|
||||
vertex_logoShader, sizeof(vertex_logoShader),
|
||||
frag_logoShader , sizeof(frag_logoShader )))
|
||||
b_shader_splash_logo_vert, b_shader_splash_logo_vert_size,
|
||||
b_shader_splash_logo_frag, b_shader_splash_logo_frag_size))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the splash logoShader");
|
||||
return false;
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -21,8 +21,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "lg-fonts.h"
|
||||
|
||||
typedef struct EGL_Splash EGL_Splash;
|
||||
|
||||
bool egl_splash_init(EGL_Splash ** splash);
|
||||
479
client/renderers/EGL/texture.c
Normal file
479
client/renderers/EGL/texture.c
Normal file
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
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 "texture.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/framebuffer.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#include <SDL2/SDL_egl.h>
|
||||
|
||||
/* this must be a multiple of 2 */
|
||||
#define TEXTURE_COUNT 2
|
||||
|
||||
struct Tex
|
||||
{
|
||||
GLuint t[3];
|
||||
bool hasPBO;
|
||||
GLuint pbo;
|
||||
void * map;
|
||||
GLsync sync;
|
||||
};
|
||||
|
||||
struct TexState
|
||||
{
|
||||
_Atomic(uint8_t) w, u, s, d;
|
||||
};
|
||||
|
||||
struct EGL_Texture
|
||||
{
|
||||
enum EGL_PixelFormat pixFmt;
|
||||
size_t width, height, stride;
|
||||
size_t bpp;
|
||||
bool streaming;
|
||||
bool ready;
|
||||
|
||||
int planeCount;
|
||||
GLuint samplers[3];
|
||||
size_t planes [3][3];
|
||||
GLintptr offsets [3];
|
||||
GLenum intFormat;
|
||||
GLenum format;
|
||||
GLenum dataType;
|
||||
size_t pboBufferSize;
|
||||
|
||||
struct TexState state;
|
||||
int textureCount;
|
||||
struct Tex tex[TEXTURE_COUNT];
|
||||
};
|
||||
|
||||
bool egl_texture_init(EGL_Texture ** texture)
|
||||
{
|
||||
*texture = (EGL_Texture *)malloc(sizeof(EGL_Texture));
|
||||
if (!*texture)
|
||||
{
|
||||
DEBUG_ERROR("Failed to malloc EGL_Texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(*texture, 0, sizeof(EGL_Texture));
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_texture_free(EGL_Texture ** texture)
|
||||
{
|
||||
if (!*texture)
|
||||
return;
|
||||
|
||||
if ((*texture)->planeCount > 0)
|
||||
glDeleteSamplers((*texture)->planeCount, (*texture)->samplers);
|
||||
|
||||
for(int i = 0; i < (*texture)->textureCount; ++i)
|
||||
{
|
||||
struct Tex * t = &(*texture)->tex[i];
|
||||
if (t->hasPBO)
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, t->pbo);
|
||||
if ((*texture)->tex[i].map)
|
||||
{
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
(*texture)->tex[i].map = NULL;
|
||||
}
|
||||
glDeleteBuffers(1, &t->pbo);
|
||||
if (t->sync)
|
||||
glDeleteSync(t->sync);
|
||||
}
|
||||
|
||||
if ((*texture)->planeCount > 0)
|
||||
glDeleteTextures((*texture)->planeCount, t->t);
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
free(*texture);
|
||||
*texture = NULL;
|
||||
}
|
||||
|
||||
static bool egl_texture_map(EGL_Texture * texture, uint8_t i)
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->tex[i].pbo);
|
||||
texture->tex[i].map = glMapBufferRange(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
0,
|
||||
texture->pboBufferSize,
|
||||
GL_MAP_WRITE_BIT |
|
||||
GL_MAP_UNSYNCHRONIZED_BIT |
|
||||
GL_MAP_INVALIDATE_BUFFER_BIT
|
||||
);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
if (!texture->tex[i].map)
|
||||
{
|
||||
EGL_ERROR("glMapBufferRange failed for %d of %lu bytes", i, texture->pboBufferSize);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void egl_texture_unmap(EGL_Texture * texture, uint8_t i)
|
||||
{
|
||||
if (!texture->tex[i].map)
|
||||
return;
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->tex[i].pbo);
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
texture->tex[i].map = NULL;
|
||||
}
|
||||
|
||||
bool egl_texture_setup(EGL_Texture * texture, enum EGL_PixelFormat pixFmt, size_t width, size_t height, size_t stride, bool streaming)
|
||||
{
|
||||
int planeCount;
|
||||
|
||||
if (texture->streaming)
|
||||
{
|
||||
for(int i = 0; i < texture->textureCount; ++i)
|
||||
{
|
||||
egl_texture_unmap(texture, i);
|
||||
if (texture->tex[i].hasPBO)
|
||||
{
|
||||
glDeleteBuffers(1, &texture->tex[i].pbo);
|
||||
texture->tex[i].hasPBO = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
texture->pixFmt = pixFmt;
|
||||
texture->width = width;
|
||||
texture->height = height;
|
||||
texture->stride = stride;
|
||||
texture->streaming = streaming;
|
||||
texture->textureCount = streaming ? TEXTURE_COUNT : 1;
|
||||
texture->ready = false;
|
||||
|
||||
atomic_store_explicit(&texture->state.w, 0, memory_order_relaxed);
|
||||
atomic_store_explicit(&texture->state.u, 0, memory_order_relaxed);
|
||||
atomic_store_explicit(&texture->state.s, 0, memory_order_relaxed);
|
||||
atomic_store_explicit(&texture->state.d, 0, memory_order_relaxed);
|
||||
|
||||
switch(pixFmt)
|
||||
{
|
||||
case EGL_PF_BGRA:
|
||||
planeCount = 1;
|
||||
texture->bpp = 4;
|
||||
texture->format = GL_BGRA;
|
||||
texture->planes[0][0] = width;
|
||||
texture->planes[0][1] = height;
|
||||
texture->planes[0][2] = stride / 4;
|
||||
texture->offsets[0] = 0;
|
||||
texture->intFormat = GL_BGRA;
|
||||
texture->dataType = GL_UNSIGNED_BYTE;
|
||||
texture->pboBufferSize = height * stride;
|
||||
break;
|
||||
|
||||
case EGL_PF_RGBA:
|
||||
planeCount = 1;
|
||||
texture->bpp = 4;
|
||||
texture->format = GL_RGBA;
|
||||
texture->planes[0][0] = width;
|
||||
texture->planes[0][1] = height;
|
||||
texture->planes[0][2] = stride / 4;
|
||||
texture->offsets[0] = 0;
|
||||
texture->intFormat = GL_BGRA;
|
||||
texture->dataType = GL_UNSIGNED_BYTE;
|
||||
texture->pboBufferSize = height * stride;
|
||||
break;
|
||||
|
||||
case EGL_PF_RGBA10:
|
||||
planeCount = 1;
|
||||
texture->bpp = 4;
|
||||
texture->format = GL_RGBA;
|
||||
texture->planes[0][0] = width;
|
||||
texture->planes[0][1] = height;
|
||||
texture->planes[0][2] = stride / 4;
|
||||
texture->offsets[0] = 0;
|
||||
texture->intFormat = GL_RGB10_A2;
|
||||
texture->dataType = GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
texture->pboBufferSize = height * stride;
|
||||
break;
|
||||
|
||||
case EGL_PF_YUV420:
|
||||
planeCount = 3;
|
||||
texture->bpp = 4;
|
||||
texture->format = GL_RED;
|
||||
texture->planes[0][0] = width;
|
||||
texture->planes[0][1] = height;
|
||||
texture->planes[0][2] = stride;
|
||||
texture->planes[1][0] = width / 2;
|
||||
texture->planes[1][1] = height / 2;
|
||||
texture->planes[1][2] = stride / 2;
|
||||
texture->planes[2][0] = width / 2;
|
||||
texture->planes[2][1] = height / 2;
|
||||
texture->planes[2][2] = stride / 2;
|
||||
texture->offsets[0] = 0;
|
||||
texture->offsets[1] = stride * height;
|
||||
texture->offsets[2] = texture->offsets[1] + (texture->offsets[1] / 4);
|
||||
texture->dataType = GL_UNSIGNED_BYTE;
|
||||
texture->pboBufferSize = texture->offsets[2] + (texture->offsets[1] / 4);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Unsupported pixel format");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (planeCount > texture->planeCount)
|
||||
{
|
||||
if (texture->planeCount > 0)
|
||||
glDeleteSamplers(texture->planeCount, texture->samplers);
|
||||
|
||||
for(int i = 0; i < texture->textureCount; ++i)
|
||||
{
|
||||
if (texture->planeCount > 0)
|
||||
glDeleteTextures(texture->planeCount, texture->tex[i].t);
|
||||
glGenTextures(planeCount, texture->tex[i].t);
|
||||
}
|
||||
|
||||
glGenSamplers(planeCount, texture->samplers);
|
||||
for(int p = 0; p < planeCount; ++p)
|
||||
{
|
||||
glSamplerParameteri(texture->samplers[p], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(texture->samplers[p], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(texture->samplers[p], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(texture->samplers[p], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
texture->planeCount = planeCount;
|
||||
}
|
||||
|
||||
for(int i = 0; i < texture->textureCount; ++i)
|
||||
{
|
||||
for(int p = 0; p < planeCount; ++p)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, texture->tex[i].t[p]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, texture->intFormat, texture->planes[p][0],
|
||||
texture->planes[p][1], 0, texture->format, texture->dataType, NULL);
|
||||
}
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
if (!streaming)
|
||||
return true;
|
||||
|
||||
for(int i = 0; i < texture->textureCount; ++i)
|
||||
{
|
||||
glGenBuffers(1, &texture->tex[i].pbo);
|
||||
texture->tex[i].hasPBO = true;
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->tex[i].pbo);
|
||||
glBufferStorage(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
texture->pboBufferSize,
|
||||
NULL,
|
||||
GL_MAP_WRITE_BIT
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void egl_warn_slow()
|
||||
{
|
||||
static bool warnDone = false;
|
||||
if (!warnDone)
|
||||
{
|
||||
warnDone = true;
|
||||
DEBUG_BREAK();
|
||||
DEBUG_WARN("The guest is providing updates faster then your computer can display them");
|
||||
DEBUG_WARN("This is a hardware limitation, expect microstutters & frame skips");
|
||||
DEBUG_BREAK();
|
||||
}
|
||||
}
|
||||
|
||||
bool egl_texture_update(EGL_Texture * texture, const uint8_t * buffer)
|
||||
{
|
||||
if (texture->streaming)
|
||||
{
|
||||
const uint8_t sw =
|
||||
atomic_load_explicit(&texture->state.w, memory_order_acquire);
|
||||
|
||||
if (atomic_load_explicit(&texture->state.u, memory_order_acquire) == (uint8_t)(sw + 1))
|
||||
{
|
||||
egl_warn_slow();
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint8_t t = sw % TEXTURE_COUNT;
|
||||
if (!egl_texture_map(texture, t))
|
||||
return EGL_TEX_STATUS_ERROR;
|
||||
|
||||
memcpy(texture->tex[t].map, buffer, texture->pboBufferSize);
|
||||
atomic_fetch_add_explicit(&texture->state.w, 1, memory_order_release);
|
||||
egl_texture_unmap(texture, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int p = 0; p < texture->planeCount; ++p)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, texture->tex[0].t[p]);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->planes[p][0]);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->planes[p][0], texture->planes[p][1],
|
||||
texture->format, texture->dataType, buffer + texture->offsets[p]);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_texture_update_from_frame(EGL_Texture * texture, const FrameBuffer * frame)
|
||||
{
|
||||
if (!texture->streaming)
|
||||
return false;
|
||||
|
||||
const uint8_t sw =
|
||||
atomic_load_explicit(&texture->state.w, memory_order_acquire);
|
||||
|
||||
if (atomic_load_explicit(&texture->state.u, memory_order_acquire) == (uint8_t)(sw + 1))
|
||||
{
|
||||
egl_warn_slow();
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint8_t t = sw % TEXTURE_COUNT;
|
||||
if (!egl_texture_map(texture, t))
|
||||
return EGL_TEX_STATUS_ERROR;
|
||||
|
||||
framebuffer_read(
|
||||
frame,
|
||||
texture->tex[t].map,
|
||||
texture->stride,
|
||||
texture->height,
|
||||
texture->width,
|
||||
texture->bpp,
|
||||
texture->stride
|
||||
);
|
||||
|
||||
atomic_fetch_add_explicit(&texture->state.w, 1, memory_order_release);
|
||||
egl_texture_unmap(texture, t);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum EGL_TexStatus egl_texture_process(EGL_Texture * texture)
|
||||
{
|
||||
if (!texture->streaming)
|
||||
return EGL_TEX_STATUS_OK;
|
||||
|
||||
const uint8_t su =
|
||||
atomic_load_explicit(&texture->state.u, memory_order_acquire);
|
||||
|
||||
const uint8_t nextu = su + 1;
|
||||
if (
|
||||
su == atomic_load_explicit(&texture->state.w, memory_order_acquire) ||
|
||||
nextu == atomic_load_explicit(&texture->state.s, memory_order_acquire) ||
|
||||
nextu == atomic_load_explicit(&texture->state.d, memory_order_acquire))
|
||||
return texture->ready ? EGL_TEX_STATUS_OK : EGL_TEX_STATUS_NOTREADY;
|
||||
|
||||
/* update the texture */
|
||||
const uint8_t t = su % TEXTURE_COUNT;
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->tex[t].pbo);
|
||||
for(int p = 0; p < texture->planeCount; ++p)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, texture->tex[t].t[p]);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->planes[p][2]);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->planes[p][0], texture->planes[p][1],
|
||||
texture->format, texture->dataType, (const void *)texture->offsets[p]);
|
||||
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
/* create a fence to prevent usage before the update is complete */
|
||||
texture->tex[t].sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
|
||||
/* we must flush to ensure the sync is in the command buffer */
|
||||
glFlush();
|
||||
|
||||
texture->ready = true;
|
||||
atomic_fetch_add_explicit(&texture->state.u, 1, memory_order_release);
|
||||
|
||||
return EGL_TEX_STATUS_OK;
|
||||
}
|
||||
|
||||
enum EGL_TexStatus egl_texture_bind(EGL_Texture * texture)
|
||||
{
|
||||
uint8_t ss = atomic_load_explicit(&texture->state.s, memory_order_acquire);
|
||||
uint8_t sd = atomic_load_explicit(&texture->state.d, memory_order_acquire);
|
||||
|
||||
if (texture->streaming)
|
||||
{
|
||||
if (!texture->ready)
|
||||
return EGL_TEX_STATUS_NOTREADY;
|
||||
|
||||
const uint8_t t = ss % TEXTURE_COUNT;
|
||||
if (texture->tex[t].sync != 0)
|
||||
{
|
||||
switch(glClientWaitSync(texture->tex[t].sync, 0, 20000000)) // 20ms
|
||||
{
|
||||
case GL_ALREADY_SIGNALED:
|
||||
case GL_CONDITION_SATISFIED:
|
||||
glDeleteSync(texture->tex[t].sync);
|
||||
texture->tex[t].sync = 0;
|
||||
|
||||
ss = atomic_fetch_add_explicit(&texture->state.s, 1,
|
||||
memory_order_release) + 1;
|
||||
break;
|
||||
|
||||
case GL_TIMEOUT_EXPIRED:
|
||||
break;
|
||||
|
||||
case GL_WAIT_FAILED:
|
||||
case GL_INVALID_VALUE:
|
||||
glDeleteSync(texture->tex[t].sync);
|
||||
texture->tex[t].sync = 0;
|
||||
EGL_ERROR("glClientWaitSync failed");
|
||||
return EGL_TEX_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (ss != sd && ss != (uint8_t)(sd + 1))
|
||||
sd = atomic_fetch_add_explicit(&texture->state.d, 1,
|
||||
memory_order_release) + 1;
|
||||
}
|
||||
|
||||
const uint8_t t = sd % TEXTURE_COUNT;
|
||||
for(int i = 0; i < texture->planeCount; ++i)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
glBindTexture(GL_TEXTURE_2D, texture->tex[t].t[i]);
|
||||
glBindSampler(i, texture->samplers[i]);
|
||||
}
|
||||
|
||||
return EGL_TEX_STATUS_OK;
|
||||
}
|
||||
|
||||
int egl_texture_count(EGL_Texture * texture)
|
||||
{
|
||||
return texture->planeCount;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -21,6 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "shader.h"
|
||||
#include "common/framebuffer.h"
|
||||
|
||||
#include <GL/gl.h>
|
||||
|
||||
@@ -34,10 +35,19 @@ enum EGL_PixelFormat
|
||||
EGL_PF_YUV420
|
||||
};
|
||||
|
||||
enum EGL_TexStatus
|
||||
{
|
||||
EGL_TEX_STATUS_NOTREADY,
|
||||
EGL_TEX_STATUS_OK,
|
||||
EGL_TEX_STATUS_ERROR
|
||||
};
|
||||
|
||||
bool egl_texture_init(EGL_Texture ** tex);
|
||||
void egl_texture_free(EGL_Texture ** tex);
|
||||
|
||||
bool egl_texture_setup (EGL_Texture * texture, enum EGL_PixelFormat pixfmt, size_t width, size_t height, size_t bufferSize, bool streaming);
|
||||
bool egl_texture_setup (EGL_Texture * texture, enum EGL_PixelFormat pixfmt, size_t width, size_t height, size_t stride, bool streaming);
|
||||
bool egl_texture_update (EGL_Texture * texture, const uint8_t * buffer);
|
||||
void egl_texture_bind (EGL_Texture * texture);
|
||||
bool egl_texture_update_from_frame(EGL_Texture * texture, const FrameBuffer * frame);
|
||||
enum EGL_TexStatus egl_texture_process(EGL_Texture * texture);
|
||||
enum EGL_TexStatus egl_texture_bind (EGL_Texture * texture);
|
||||
int egl_texture_count (EGL_Texture * texture);
|
||||
25
client/renderers/OpenGL/CMakeLists.txt
Normal file
25
client/renderers/OpenGL/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(renderer_Opengl LANGUAGES C)
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(RENDERER_OPENGL_PKGCONFIG REQUIRED
|
||||
gl
|
||||
glu
|
||||
)
|
||||
|
||||
add_library(renderer_OpenGL STATIC
|
||||
opengl.c
|
||||
)
|
||||
|
||||
target_link_libraries(renderer_OpenGL
|
||||
${RENDERER_OPENGL_PKGCONFIG_LIBRARIES}
|
||||
lg_common
|
||||
decoders
|
||||
fonts
|
||||
)
|
||||
|
||||
target_include_directories(renderer_OpenGL
|
||||
PRIVATE
|
||||
src
|
||||
${RENDERER_OPENGL_PKGCONFIG_INCLUDE_DIRS}
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -17,7 +17,7 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lg-renderer.h"
|
||||
#include "interface/renderer.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
@@ -30,10 +30,11 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include <GL/glu.h>
|
||||
#include <GL/glx.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
#include "lg-decoders.h"
|
||||
#include "lg-fonts.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/option.h"
|
||||
#include "common/framebuffer.h"
|
||||
#include "common/locking.h"
|
||||
#include "dynamic/fonts.h"
|
||||
#include "ll.h"
|
||||
|
||||
#define BUFFER_COUNT 2
|
||||
@@ -47,7 +48,40 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#define FADE_TIME 1000000
|
||||
|
||||
struct Options
|
||||
static struct Option opengl_options[] =
|
||||
{
|
||||
{
|
||||
.module = "opengl",
|
||||
.name = "mipmap",
|
||||
.description = "Enable mipmapping",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
{
|
||||
.module = "opengl",
|
||||
.name = "vsync",
|
||||
.description = "Enable vsync",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
{
|
||||
.module = "opengl",
|
||||
.name = "preventBuffer",
|
||||
.description = "Prevent the driver from buffering frames",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
{
|
||||
.module = "opengl",
|
||||
.name = "amdPinnedMem",
|
||||
.description = "Use GL_AMD_pinned_memory if it is available",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
{0}
|
||||
};
|
||||
|
||||
struct OpenGL_Options
|
||||
{
|
||||
bool mipmap;
|
||||
bool vsync;
|
||||
@@ -55,14 +89,6 @@ struct Options
|
||||
bool amdPinnedMem;
|
||||
};
|
||||
|
||||
static struct Options defaultOptions =
|
||||
{
|
||||
.mipmap = true,
|
||||
.vsync = true,
|
||||
.preventBuffer = true,
|
||||
.amdPinnedMem = true,
|
||||
};
|
||||
|
||||
struct Alert
|
||||
{
|
||||
bool ready;
|
||||
@@ -77,7 +103,7 @@ struct Alert
|
||||
struct Inst
|
||||
{
|
||||
LG_RendererParams params;
|
||||
struct Options opt;
|
||||
struct OpenGL_Options opt;
|
||||
|
||||
bool amdPinnedMemSupport;
|
||||
bool renderStarted;
|
||||
@@ -97,8 +123,8 @@ struct Inst
|
||||
GLuint vboFormat;
|
||||
GLuint dataFormat;
|
||||
size_t texSize;
|
||||
const LG_Decoder* decoder;
|
||||
void * decoderData;
|
||||
size_t texPos;
|
||||
const FrameBuffer * frame;
|
||||
|
||||
uint64_t drawStart;
|
||||
bool hasBuffers;
|
||||
@@ -115,7 +141,6 @@ struct Inst
|
||||
bool hasTextures, hasFrames;
|
||||
GLuint frames[BUFFER_COUNT];
|
||||
GLsync fences[BUFFER_COUNT];
|
||||
void * decoderFrames[BUFFER_COUNT];
|
||||
GLuint textures[TEXTURE_COUNT];
|
||||
struct ll * alerts;
|
||||
int alertList;
|
||||
@@ -145,8 +170,15 @@ struct Inst
|
||||
static bool _check_gl_error(unsigned int line, const char * name);
|
||||
#define check_gl_error(name) _check_gl_error(__LINE__, name)
|
||||
|
||||
enum ConfigStatus
|
||||
{
|
||||
CONFIG_STATUS_OK,
|
||||
CONFIG_STATUS_ERROR,
|
||||
CONFIG_STATUS_NOOP
|
||||
};
|
||||
|
||||
static void deconfigure(struct Inst * this);
|
||||
static bool configure(struct Inst * this, SDL_Window *window);
|
||||
static enum ConfigStatus configure(struct Inst * this, SDL_Window *window);
|
||||
static void update_mouse_shape(struct Inst * this, bool * newShape);
|
||||
static bool draw_frame(struct Inst * this);
|
||||
static void draw_mouse(struct Inst * this);
|
||||
@@ -157,6 +189,11 @@ const char * opengl_get_name()
|
||||
return "OpenGL";
|
||||
}
|
||||
|
||||
static void opengl_setup()
|
||||
{
|
||||
option_register(opengl_options);
|
||||
}
|
||||
|
||||
bool opengl_create(void ** opaque, const LG_RendererParams params)
|
||||
{
|
||||
// create our local storage
|
||||
@@ -170,7 +207,12 @@ bool opengl_create(void ** opaque, const LG_RendererParams params)
|
||||
|
||||
struct Inst * this = (struct Inst *)*opaque;
|
||||
memcpy(&this->params, ¶ms, sizeof(LG_RendererParams));
|
||||
memcpy(&this->opt , &defaultOptions, sizeof(struct Options ));
|
||||
|
||||
this->opt.mipmap = option_get_bool("opengl", "mipmap" );
|
||||
this->opt.vsync = option_get_bool("opengl", "vsync" );
|
||||
this->opt.preventBuffer = option_get_bool("opengl", "preventBuffer");
|
||||
this->opt.amdPinnedMem = option_get_bool("opengl", "amdPinnedMem" );
|
||||
|
||||
|
||||
LG_LOCK_INIT(this->formatLock);
|
||||
LG_LOCK_INIT(this->syncLock );
|
||||
@@ -253,6 +295,12 @@ void opengl_deinitialize(void * opaque)
|
||||
free(this);
|
||||
}
|
||||
|
||||
void opengl_on_restart(void * opaque)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
this->waiting = true;
|
||||
}
|
||||
|
||||
void opengl_on_resize(void * opaque, const int width, const int height, const LG_RendererRect destRect)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
@@ -327,7 +375,7 @@ bool opengl_on_mouse_event(void * opaque, const bool visible, const int x, const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const uint8_t * data)
|
||||
bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const FrameBuffer * frame)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
@@ -359,12 +407,7 @@ bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const
|
||||
LG_UNLOCK(this->formatLock);
|
||||
|
||||
LG_LOCK(this->syncLock);
|
||||
if (!this->decoder->decode(this->decoderData, data, format.pitch))
|
||||
{
|
||||
DEBUG_ERROR("decode returned failure");
|
||||
LG_UNLOCK(this->syncLock);
|
||||
return false;
|
||||
}
|
||||
this->frame = frame;
|
||||
this->frameUpdate = true;
|
||||
LG_UNLOCK(this->syncLock);
|
||||
|
||||
@@ -377,7 +420,7 @@ bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const
|
||||
return true;
|
||||
}
|
||||
|
||||
void opengl_on_alert(void * opaque, const LG_RendererAlert alert, const char * message, bool ** closeFlag)
|
||||
void opengl_on_alert(void * opaque, const LG_MsgAlert alert, const char * message, bool ** closeFlag)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
struct Alert * a = malloc(sizeof(struct Alert));
|
||||
@@ -520,9 +563,17 @@ bool opengl_render(void * opaque, SDL_Window * window)
|
||||
if (!this)
|
||||
return false;
|
||||
|
||||
if (configure(this, window))
|
||||
switch(configure(this, window))
|
||||
{
|
||||
case CONFIG_STATUS_ERROR:
|
||||
DEBUG_ERROR("configure failed");
|
||||
return false;
|
||||
|
||||
case CONFIG_STATUS_NOOP :
|
||||
case CONFIG_STATUS_OK :
|
||||
if (!draw_frame(this))
|
||||
return false;
|
||||
}
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
@@ -770,79 +821,15 @@ static void render_wait(struct Inst * this)
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
static void handle_opt_mipmap(void * opaque, const char *value)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
this->opt.mipmap = LG_RendererValueToBool(value);
|
||||
}
|
||||
|
||||
static void handle_opt_vsync(void * opaque, const char *value)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
this->opt.vsync = LG_RendererValueToBool(value);
|
||||
}
|
||||
|
||||
static void handle_opt_prevent_buffer(void * opaque, const char *value)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
this->opt.preventBuffer = LG_RendererValueToBool(value);
|
||||
}
|
||||
|
||||
static void handle_opt_amd_pinned_mem(void * opaque, const char *value)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
this->opt.amdPinnedMem = LG_RendererValueToBool(value);
|
||||
}
|
||||
|
||||
|
||||
static LG_RendererOpt opengl_options[] =
|
||||
{
|
||||
{
|
||||
.name = "mipmap",
|
||||
.desc = "Enable or disable mipmapping [default: enabled]",
|
||||
.validator = LG_RendererValidatorBool,
|
||||
.handler = handle_opt_mipmap
|
||||
},
|
||||
{
|
||||
.name = "vsync",
|
||||
.desc ="Enable or disable vsync [default: enabled]",
|
||||
.validator = LG_RendererValidatorBool,
|
||||
.handler = handle_opt_vsync
|
||||
},
|
||||
{
|
||||
.name = "preventBuffer",
|
||||
.desc = "Prevent the driver from buffering frames [default: disabled]",
|
||||
.validator = LG_RendererValidatorBool,
|
||||
.handler = handle_opt_prevent_buffer
|
||||
},
|
||||
{
|
||||
.name = "amdPinnedMem",
|
||||
.desc = "Use GL_AMD_pinned_memory if it is available [default: enabled]",
|
||||
.validator = LG_RendererValidatorBool,
|
||||
.handler = handle_opt_amd_pinned_mem
|
||||
}
|
||||
};
|
||||
|
||||
const LG_Renderer LGR_OpenGL =
|
||||
{
|
||||
.get_name = opengl_get_name,
|
||||
.options = opengl_options,
|
||||
.option_count = LGR_OPTION_COUNT(opengl_options),
|
||||
.setup = opengl_setup,
|
||||
|
||||
.create = opengl_create,
|
||||
.initialize = opengl_initialize,
|
||||
.deinitialize = opengl_deinitialize,
|
||||
.on_restart = opengl_on_restart,
|
||||
.on_resize = opengl_on_resize,
|
||||
.on_mouse_shape = opengl_on_mouse_shape,
|
||||
.on_mouse_event = opengl_on_mouse_event,
|
||||
@@ -864,13 +851,13 @@ static bool _check_gl_error(unsigned int line, const char * name)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool configure(struct Inst * this, SDL_Window *window)
|
||||
static enum ConfigStatus configure(struct Inst * this, SDL_Window *window)
|
||||
{
|
||||
LG_LOCK(this->formatLock);
|
||||
if (!this->reconfigure)
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return this->configured;
|
||||
return CONFIG_STATUS_NOOP;
|
||||
}
|
||||
|
||||
if (this->configured)
|
||||
@@ -879,78 +866,32 @@ static bool configure(struct Inst * this, SDL_Window *window)
|
||||
switch(this->format.type)
|
||||
{
|
||||
case FRAME_TYPE_BGRA:
|
||||
case FRAME_TYPE_RGBA:
|
||||
case FRAME_TYPE_RGBA10:
|
||||
this->decoder = &LGD_NULL;
|
||||
break;
|
||||
|
||||
case FRAME_TYPE_YUV420:
|
||||
this->decoder = &LGD_YUV420;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Unknown/unsupported compression type");
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Using decoder: %s", this->decoder->name);
|
||||
|
||||
if (!this->decoder->create(&this->decoderData))
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the decoder");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->decoder->initialize(
|
||||
this->decoderData,
|
||||
this->format,
|
||||
window))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize decoder");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(this->decoder->get_out_format(this->decoderData))
|
||||
{
|
||||
case LG_OUTPUT_BGRA:
|
||||
this->intFormat = GL_RGBA8;
|
||||
this->vboFormat = GL_BGRA;
|
||||
this->dataFormat = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case LG_OUTPUT_RGBA:
|
||||
case FRAME_TYPE_RGBA:
|
||||
this->intFormat = GL_RGBA8;
|
||||
this->vboFormat = GL_RGBA;
|
||||
this->dataFormat = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case LG_OUTPUT_RGBA10:
|
||||
case FRAME_TYPE_RGBA10:
|
||||
this->intFormat = GL_RGB10_A2;
|
||||
this->vboFormat = GL_RGBA;
|
||||
this->dataFormat = GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
break;
|
||||
|
||||
case LG_OUTPUT_YUV420:
|
||||
// fixme
|
||||
this->intFormat = GL_RGBA8;
|
||||
this->vboFormat = GL_BGRA;
|
||||
this->dataFormat = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Format not supported");
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
DEBUG_ERROR("Unknown/unsupported compression type");
|
||||
return CONFIG_STATUS_ERROR;
|
||||
}
|
||||
|
||||
// calculate the texture size in bytes
|
||||
this->texSize =
|
||||
this->format.height *
|
||||
this->decoder->get_frame_pitch(this->decoderData);
|
||||
this->texSize = this->format.height * this->format.pitch;
|
||||
this->texPos = 0;
|
||||
|
||||
// generate the pixel unpack buffers if the decoder isn't going to do it for us
|
||||
if (!this->decoder->has_gl)
|
||||
{
|
||||
glGenBuffers(BUFFER_COUNT, this->vboID);
|
||||
if (check_gl_error("glGenBuffers"))
|
||||
{
|
||||
@@ -962,30 +903,36 @@ static bool configure(struct Inst * this, SDL_Window *window)
|
||||
if (this->amdPinnedMemSupport)
|
||||
{
|
||||
const int pagesize = getpagesize();
|
||||
this->texPixels[0] = memalign(pagesize, this->texSize * BUFFER_COUNT);
|
||||
memset(this->texPixels[0], 0, this->texSize * BUFFER_COUNT);
|
||||
for(int i = 1; i < BUFFER_COUNT; ++i)
|
||||
this->texPixels[i] = this->texPixels[0] + this->texSize;
|
||||
|
||||
for(int i = 0; i < BUFFER_COUNT; ++i)
|
||||
{
|
||||
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, this->vboID[i]);
|
||||
this->texPixels[i] = aligned_alloc(pagesize, this->texSize);
|
||||
if (!this->texPixels[i])
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate memory for texture");
|
||||
return CONFIG_STATUS_ERROR;
|
||||
}
|
||||
|
||||
memset(this->texPixels[i], 0, this->texSize);
|
||||
|
||||
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, this->vboID[i]);
|
||||
if (check_gl_error("glBindBuffer"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
return CONFIG_STATUS_ERROR;
|
||||
}
|
||||
|
||||
glBufferData(
|
||||
GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD,
|
||||
this->texSize,
|
||||
this->texPixels[i],
|
||||
GL_STREAM_DRAW);
|
||||
GL_STREAM_DRAW
|
||||
);
|
||||
|
||||
if (check_gl_error("glBufferData"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
return CONFIG_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0);
|
||||
@@ -998,7 +945,7 @@ static bool configure(struct Inst * this, SDL_Window *window)
|
||||
if (check_gl_error("glBindBuffer"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
return CONFIG_STATUS_ERROR;
|
||||
}
|
||||
|
||||
glBufferData(
|
||||
@@ -1010,19 +957,18 @@ static bool configure(struct Inst * this, SDL_Window *window)
|
||||
if (check_gl_error("glBufferData"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
return CONFIG_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// create the frame textures
|
||||
glGenTextures(BUFFER_COUNT, this->frames);
|
||||
if (check_gl_error("glGenTextures"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
return CONFIG_STATUS_ERROR;
|
||||
}
|
||||
this->hasFrames = true;
|
||||
|
||||
@@ -1033,7 +979,7 @@ static bool configure(struct Inst * this, SDL_Window *window)
|
||||
if (check_gl_error("glBindTexture"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
return CONFIG_STATUS_ERROR;
|
||||
}
|
||||
|
||||
glTexImage2D(
|
||||
@@ -1050,29 +996,14 @@ static bool configure(struct Inst * this, SDL_Window *window)
|
||||
if (check_gl_error("glTexImage2D"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
return CONFIG_STATUS_ERROR;
|
||||
}
|
||||
|
||||
if (this->decoder->has_gl)
|
||||
{
|
||||
if (!this->decoder->init_gl_texture(
|
||||
this->decoderData,
|
||||
GL_TEXTURE_2D,
|
||||
this->frames[i],
|
||||
&this->decoderFrames[i]))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// configure the texture
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
}
|
||||
|
||||
// create the display lists
|
||||
glNewList(this->texList + i, GL_COMPILE);
|
||||
@@ -1096,7 +1027,7 @@ static bool configure(struct Inst * this, SDL_Window *window)
|
||||
this->reconfigure = false;
|
||||
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return true;
|
||||
return CONFIG_STATUS_OK;
|
||||
}
|
||||
|
||||
static void deconfigure(struct Inst * this)
|
||||
@@ -1112,19 +1043,6 @@ static void deconfigure(struct Inst * this)
|
||||
|
||||
if (this->hasFrames)
|
||||
{
|
||||
if (this->decoder->has_gl)
|
||||
{
|
||||
for(int i = 0; i < BUFFER_COUNT; ++i)
|
||||
{
|
||||
if (this->decoderFrames[i])
|
||||
this->decoder->free_gl_texture(
|
||||
this->decoderData,
|
||||
this->decoderFrames[i]
|
||||
);
|
||||
this->decoderFrames[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
glDeleteTextures(BUFFER_COUNT, this->frames);
|
||||
this->hasFrames = false;
|
||||
}
|
||||
@@ -1137,9 +1055,6 @@ static void deconfigure(struct Inst * this)
|
||||
|
||||
if (this->amdPinnedMemSupport)
|
||||
{
|
||||
if (this->texPixels[0])
|
||||
free(this->texPixels[0]);
|
||||
|
||||
for(int i = 0; i < BUFFER_COUNT; ++i)
|
||||
{
|
||||
if (this->fences[i])
|
||||
@@ -1147,14 +1062,13 @@ static void deconfigure(struct Inst * this)
|
||||
glDeleteSync(this->fences[i]);
|
||||
this->fences[i] = NULL;
|
||||
}
|
||||
|
||||
if (this->texPixels[i])
|
||||
{
|
||||
free(this->texPixels[i]);
|
||||
this->texPixels[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->decoderData)
|
||||
{
|
||||
this->decoder->destroy(this->decoderData);
|
||||
this->decoderData = NULL;
|
||||
}
|
||||
|
||||
this->configured = false;
|
||||
@@ -1307,6 +1221,23 @@ static void update_mouse_shape(struct Inst * this, bool * newShape)
|
||||
LG_UNLOCK(this->mouseLock);
|
||||
}
|
||||
|
||||
static bool opengl_buffer_fn(void * opaque, const void * data, size_t size)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
|
||||
// update the buffer, this performs a DMA transfer if possible
|
||||
glBufferSubData(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
this->texPos,
|
||||
size,
|
||||
data
|
||||
);
|
||||
check_gl_error("glBufferSubData");
|
||||
|
||||
this->texPos += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool draw_frame(struct Inst * this)
|
||||
{
|
||||
LG_LOCK(this->syncLock);
|
||||
@@ -1323,20 +1254,6 @@ static bool draw_frame(struct Inst * this)
|
||||
LG_UNLOCK(this->syncLock);
|
||||
|
||||
LG_LOCK(this->formatLock);
|
||||
if (this->decoder->has_gl)
|
||||
{
|
||||
if (!this->decoder->update_gl_texture(
|
||||
this->decoderData,
|
||||
this->decoderFrames[this->texIndex]
|
||||
))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
DEBUG_ERROR("Failed to update the texture from the decoder");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (glIsSync(this->fences[this->texIndex]))
|
||||
{
|
||||
switch(glClientWaitSync(this->fences[this->texIndex], 0, GL_TIMEOUT_IGNORED))
|
||||
@@ -1361,30 +1278,24 @@ static bool draw_frame(struct Inst * this)
|
||||
this->fences[this->texIndex] = NULL;
|
||||
}
|
||||
|
||||
const uint8_t * data = this->decoder->get_buffer(this->decoderData);
|
||||
if (!data)
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
DEBUG_ERROR("Failed to get the buffer from the decoder");
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, this->frames[this->texIndex]);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[this->texIndex]);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT , 4);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH ,
|
||||
this->decoder->get_frame_stride(this->decoderData)
|
||||
);
|
||||
const int bpp = this->format.bpp / 8;
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT , bpp);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, this->format.width);
|
||||
|
||||
// update the buffer, this performs a DMA transfer if possible
|
||||
glBufferSubData(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
0,
|
||||
this->texSize,
|
||||
data
|
||||
this->texPos = 0;
|
||||
|
||||
framebuffer_read_fn(
|
||||
this->frame,
|
||||
this->format.height,
|
||||
this->format.width,
|
||||
bpp,
|
||||
this->format.pitch,
|
||||
opengl_buffer_fn,
|
||||
this
|
||||
);
|
||||
check_gl_error("glBufferSubData");
|
||||
|
||||
// update the texture
|
||||
glTexSubImage2D(
|
||||
@@ -1411,7 +1322,6 @@ static bool draw_frame(struct Inst * this)
|
||||
|
||||
// unbind the buffer
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
const bool mipmap = this->opt.mipmap && (
|
||||
(this->format.width > this->destRect.w) ||
|
||||
@@ -1,278 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 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 "desktop.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
#include "model.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct EGL_Desktop
|
||||
{
|
||||
EGL_Texture * texture;
|
||||
EGL_Shader * shader; // the active shader
|
||||
EGL_Model * model;
|
||||
|
||||
// shader instances
|
||||
EGL_Shader * shader_generic;
|
||||
EGL_Shader * shader_yuv;
|
||||
|
||||
// uniforms
|
||||
GLint uDesktopPos;
|
||||
|
||||
// internals
|
||||
enum EGL_PixelFormat pixFmt;
|
||||
unsigned int width, height;
|
||||
size_t frameSize;
|
||||
const uint8_t * data;
|
||||
bool update;
|
||||
};
|
||||
|
||||
static const char vertex_shader[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
layout(location = 0) in vec3 vertexPosition_modelspace;\
|
||||
layout(location = 1) in vec2 vertexUV;\
|
||||
\
|
||||
uniform vec4 position;\
|
||||
\
|
||||
out highp vec2 uv;\
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
gl_Position.xyz = vertexPosition_modelspace; \
|
||||
gl_Position.w = 1.0; \
|
||||
gl_Position.x -= position.x; \
|
||||
gl_Position.y -= position.y; \
|
||||
gl_Position.x *= position.z; \
|
||||
gl_Position.y *= position.w; \
|
||||
\
|
||||
uv = vertexUV;\
|
||||
}\
|
||||
";
|
||||
|
||||
|
||||
static const char frag_generic[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
in highp vec2 uv;\
|
||||
out highp vec4 color;\
|
||||
\
|
||||
uniform sampler2D sampler1;\
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
color = texture(sampler1, uv);\
|
||||
}\
|
||||
";
|
||||
|
||||
static const char frag_yuv[] = "\
|
||||
#version 300 es\n\
|
||||
\
|
||||
in highp vec2 uv;\
|
||||
out highp vec4 color;\
|
||||
\
|
||||
uniform sampler2D sampler1;\
|
||||
uniform sampler2D sampler2;\
|
||||
uniform sampler2D sampler3;\
|
||||
\
|
||||
void main()\
|
||||
{\
|
||||
highp vec4 yuv = vec4(\
|
||||
texture(sampler1, uv).r,\
|
||||
texture(sampler2, uv).r,\
|
||||
texture(sampler3, uv).r,\
|
||||
1.0\
|
||||
);\
|
||||
\
|
||||
highp mat4 yuv_to_rgb = mat4(\
|
||||
1.0, 0.0 , 1.402, -0.701,\
|
||||
1.0, -0.344, -0.714, 0.529,\
|
||||
1.0, 1.772, 0.0 , -0.886,\
|
||||
1.0, 1.0 , 1.0 , 1.0\
|
||||
);\
|
||||
\
|
||||
color = yuv * yuv_to_rgb;\
|
||||
}\
|
||||
";
|
||||
|
||||
bool egl_desktop_init(EGL_Desktop ** desktop)
|
||||
{
|
||||
*desktop = (EGL_Desktop *)malloc(sizeof(EGL_Desktop));
|
||||
if (!*desktop)
|
||||
{
|
||||
DEBUG_ERROR("Failed to malloc EGL_Desktop");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(*desktop, 0, sizeof(EGL_Desktop));
|
||||
|
||||
if (!egl_texture_init(&(*desktop)->texture))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the desktop texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shader_init(&(*desktop)->shader_generic))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the generic desktop shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shader_init(&(*desktop)->shader_yuv))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the yuv desktop shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shader_compile((*desktop)->shader_generic,
|
||||
vertex_shader, sizeof(vertex_shader),
|
||||
frag_generic , sizeof(frag_generic)))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the generic desktop shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_shader_compile((*desktop)->shader_yuv,
|
||||
vertex_shader, sizeof(vertex_shader),
|
||||
frag_yuv , sizeof(frag_yuv )))
|
||||
{
|
||||
DEBUG_ERROR("Failed to compile the yuv desktop shader");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!egl_model_init(&(*desktop)->model))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize the desktop model");
|
||||
return false;
|
||||
}
|
||||
|
||||
egl_model_set_default((*desktop)->model);
|
||||
egl_model_set_texture((*desktop)->model, (*desktop)->texture);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_desktop_free(EGL_Desktop ** desktop)
|
||||
{
|
||||
if (!*desktop)
|
||||
return;
|
||||
|
||||
egl_texture_free(&(*desktop)->texture );
|
||||
egl_shader_free (&(*desktop)->shader_generic);
|
||||
egl_shader_free (&(*desktop)->shader_yuv );
|
||||
egl_model_free (&(*desktop)->model );
|
||||
|
||||
free(*desktop);
|
||||
*desktop = NULL;
|
||||
}
|
||||
|
||||
bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const uint8_t * data)
|
||||
{
|
||||
if (sourceChanged)
|
||||
{
|
||||
switch(format.type)
|
||||
{
|
||||
case FRAME_TYPE_BGRA:
|
||||
desktop->pixFmt = EGL_PF_BGRA;
|
||||
desktop->shader = desktop->shader_generic;
|
||||
desktop->frameSize = format.height * format.pitch;
|
||||
break;
|
||||
|
||||
case FRAME_TYPE_RGBA:
|
||||
desktop->pixFmt = EGL_PF_RGBA;
|
||||
desktop->shader = desktop->shader_generic;
|
||||
desktop->frameSize = format.height * format.pitch;
|
||||
break;
|
||||
|
||||
case FRAME_TYPE_RGBA10:
|
||||
desktop->pixFmt = EGL_PF_RGBA10;
|
||||
desktop->shader = desktop->shader_generic;
|
||||
desktop->frameSize = format.height * format.pitch;
|
||||
break;
|
||||
|
||||
case FRAME_TYPE_YUV420:
|
||||
desktop->pixFmt = EGL_PF_YUV420;
|
||||
desktop->shader = desktop->shader_yuv;
|
||||
desktop->frameSize = format.width * format.height * 3 / 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Unsupported frame format");
|
||||
return false;
|
||||
}
|
||||
|
||||
desktop->width = format.width;
|
||||
desktop->height = format.height;
|
||||
}
|
||||
|
||||
desktop->data = data;
|
||||
desktop->update = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_desktop_perform_update(EGL_Desktop * desktop, const bool sourceChanged)
|
||||
{
|
||||
if (sourceChanged)
|
||||
{
|
||||
if (desktop->shader)
|
||||
desktop->uDesktopPos = egl_shader_get_uniform_location(desktop->shader, "position");
|
||||
|
||||
if (!egl_texture_setup(
|
||||
desktop->texture,
|
||||
desktop->pixFmt,
|
||||
desktop->width,
|
||||
desktop->height,
|
||||
desktop->frameSize,
|
||||
true // streaming texture
|
||||
))
|
||||
{
|
||||
DEBUG_ERROR("Failed to setup the desktop texture");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!desktop->update)
|
||||
return true;
|
||||
|
||||
if (!egl_texture_update(desktop->texture, desktop->data))
|
||||
{
|
||||
DEBUG_ERROR("Failed to update the desktop texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
desktop->update = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_desktop_render(EGL_Desktop * desktop, const float x, const float y, const float scaleX, const float scaleY)
|
||||
{
|
||||
if (!desktop->shader)
|
||||
return;
|
||||
|
||||
egl_shader_use(desktop->shader);
|
||||
glUniform4f(desktop->uDesktopPos, x, y, scaleX, scaleY);
|
||||
egl_model_render(desktop->model);
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 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 "texture.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <SDL2/SDL_egl.h>
|
||||
|
||||
struct EGL_Texture
|
||||
{
|
||||
enum EGL_PixelFormat pixFmt;
|
||||
size_t width, height;
|
||||
bool streaming;
|
||||
|
||||
int textureCount;
|
||||
GLuint textures[3];
|
||||
GLuint samplers[3];
|
||||
size_t planes[3][2];
|
||||
GLintptr offsets[3];
|
||||
GLenum intFormat;
|
||||
GLenum format;
|
||||
GLenum dataType;
|
||||
|
||||
bool hasPBO;
|
||||
GLuint pbo[2];
|
||||
int pboIndex;
|
||||
bool needsUpdate;
|
||||
size_t pboBufferSize;
|
||||
};
|
||||
|
||||
bool egl_texture_init(EGL_Texture ** texture)
|
||||
{
|
||||
*texture = (EGL_Texture *)malloc(sizeof(EGL_Texture));
|
||||
if (!*texture)
|
||||
{
|
||||
DEBUG_ERROR("Failed to malloc EGL_Texture");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(*texture, 0, sizeof(EGL_Texture));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_texture_free(EGL_Texture ** texture)
|
||||
{
|
||||
if (!*texture)
|
||||
return;
|
||||
|
||||
if ((*texture)->textureCount > 0)
|
||||
{
|
||||
glDeleteTextures((*texture)->textureCount, (*texture)->textures);
|
||||
glDeleteSamplers((*texture)->textureCount, (*texture)->samplers);
|
||||
}
|
||||
|
||||
if ((*texture)->hasPBO)
|
||||
glDeleteBuffers(2, (*texture)->pbo);
|
||||
|
||||
free(*texture);
|
||||
*texture = NULL;
|
||||
}
|
||||
|
||||
bool egl_texture_setup(EGL_Texture * texture, enum EGL_PixelFormat pixFmt, size_t width, size_t height, size_t bufferSize, bool streaming)
|
||||
{
|
||||
int textureCount;
|
||||
|
||||
texture->pixFmt = pixFmt;
|
||||
texture->width = width;
|
||||
texture->height = height;
|
||||
texture->pboBufferSize = bufferSize;
|
||||
texture->streaming = streaming;
|
||||
|
||||
switch(pixFmt)
|
||||
{
|
||||
case EGL_PF_BGRA:
|
||||
textureCount = 1;
|
||||
texture->format = GL_BGRA;
|
||||
texture->planes[0][0] = width;
|
||||
texture->planes[0][1] = height;
|
||||
texture->offsets[0] = 0;
|
||||
texture->intFormat = GL_BGRA;
|
||||
texture->dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case EGL_PF_RGBA:
|
||||
textureCount = 1;
|
||||
texture->format = GL_RGBA;
|
||||
texture->planes[0][0] = width;
|
||||
texture->planes[0][1] = height;
|
||||
texture->offsets[0] = 0;
|
||||
texture->intFormat = GL_BGRA;
|
||||
texture->dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case EGL_PF_RGBA10:
|
||||
textureCount = 1;
|
||||
texture->format = GL_RGBA;
|
||||
texture->planes[0][0] = width;
|
||||
texture->planes[0][1] = height;
|
||||
texture->offsets[0] = 0;
|
||||
texture->intFormat = GL_RGB10_A2;
|
||||
texture->dataType = GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
break;
|
||||
|
||||
case EGL_PF_YUV420:
|
||||
textureCount = 3;
|
||||
texture->format = GL_RED;
|
||||
texture->planes[0][0] = width;
|
||||
texture->planes[0][1] = height;
|
||||
texture->planes[1][0] = width / 2;
|
||||
texture->planes[1][1] = height / 2;
|
||||
texture->planes[2][0] = width / 2;
|
||||
texture->planes[2][1] = height / 2;
|
||||
texture->offsets[0] = 0;
|
||||
texture->offsets[1] = width * height;
|
||||
texture->offsets[2] = texture->offsets[1] + (texture->offsets[1] / 4);
|
||||
texture->dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Unsupported pixel format");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (textureCount > texture->textureCount)
|
||||
{
|
||||
if (texture->textureCount > 0)
|
||||
{
|
||||
glDeleteTextures(texture->textureCount, texture->textures);
|
||||
glDeleteSamplers(texture->textureCount, texture->samplers);
|
||||
}
|
||||
|
||||
texture->textureCount = textureCount;
|
||||
glGenTextures(texture->textureCount, texture->textures);
|
||||
glGenSamplers(texture->textureCount, texture->samplers);
|
||||
}
|
||||
|
||||
for(int i = 0; i < textureCount; ++i)
|
||||
{
|
||||
glSamplerParameteri(texture->samplers[i], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(texture->samplers[i], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glSamplerParameteri(texture->samplers[i], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
|
||||
glSamplerParameteri(texture->samplers[i], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture->textures[i]);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, texture->intFormat, texture->planes[i][0], texture->planes[i][1],
|
||||
0, texture->format, texture->dataType, NULL);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
if (streaming)
|
||||
{
|
||||
if (!texture->hasPBO)
|
||||
{
|
||||
glGenBuffers(2, texture->pbo);
|
||||
texture->hasPBO = true;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 2; ++i)
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->pbo[i]);
|
||||
glBufferData(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
bufferSize,
|
||||
NULL,
|
||||
GL_DYNAMIC_DRAW
|
||||
);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_texture_update(EGL_Texture * texture, const uint8_t * buffer)
|
||||
{
|
||||
if (texture->streaming)
|
||||
{
|
||||
if (texture->needsUpdate)
|
||||
{
|
||||
DEBUG_ERROR("Previous frame was not consumed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (++texture->pboIndex == 2)
|
||||
texture->pboIndex = 0;
|
||||
|
||||
/* initiate the data upload */
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->pbo[texture->pboIndex]);
|
||||
glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, texture->pboBufferSize, buffer);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
texture->needsUpdate = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < texture->textureCount; ++i)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, texture->textures[i]);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->planes[i][0], texture->planes[i][1],
|
||||
texture->format, texture->dataType, buffer + texture->offsets[i]);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void egl_texture_bind(EGL_Texture * texture)
|
||||
{
|
||||
if (texture->streaming && texture->needsUpdate)
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->pbo[texture->pboIndex]);
|
||||
for(int i = 0; i < texture->textureCount; ++i)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, texture->textures[i]);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->planes[i][0], texture->planes[i][1],
|
||||
texture->format, texture->dataType, (const void *)texture->offsets[i]);
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
texture->needsUpdate = false;
|
||||
}
|
||||
|
||||
for(int i = 0; i < texture->textureCount; ++i)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
glBindTexture(GL_TEXTURE_2D, texture->textures[i]);
|
||||
glBindSampler(i, texture->samplers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int egl_texture_count(EGL_Texture * texture)
|
||||
{
|
||||
return texture->textureCount;
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 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 <stdint.h>
|
||||
|
||||
#pragma pack(push,1)
|
||||
|
||||
typedef struct SpicePoint16
|
||||
{
|
||||
int16_t x, y;
|
||||
}
|
||||
SpicePoint16;
|
||||
|
||||
typedef struct SpiceMsgMainInit
|
||||
{
|
||||
uint32_t session_id;
|
||||
uint32_t display_channels_hint;
|
||||
uint32_t supported_mouse_modes;
|
||||
uint32_t current_mouse_mode;
|
||||
uint32_t agent_connected;
|
||||
uint32_t agent_tokens;
|
||||
uint32_t multi_media_time;
|
||||
uint32_t ram_hint;
|
||||
}
|
||||
SpiceMsgMainInit;
|
||||
|
||||
typedef struct SpiceMsgcMainMouseModeRequest
|
||||
{
|
||||
uint16_t mouse_mode;
|
||||
}
|
||||
SpiceMsgcMainMouseModeRequest;
|
||||
|
||||
typedef struct SpiceMsgPing
|
||||
{
|
||||
uint32_t id;
|
||||
uint64_t timestamp;
|
||||
}
|
||||
SpiceMsgPing,
|
||||
SpiceMsgcPong;
|
||||
|
||||
typedef struct SpiceMsgSetAck
|
||||
{
|
||||
uint32_t generation;
|
||||
uint32_t window;
|
||||
}
|
||||
SpiceMsgSetAck;
|
||||
|
||||
typedef struct SpiceMsgcAckSync
|
||||
{
|
||||
uint32_t generation;
|
||||
}
|
||||
SpiceMsgcAckSync;
|
||||
|
||||
typedef struct SpiceMsgNotify
|
||||
{
|
||||
uint64_t time_stamp;
|
||||
uint32_t severity;
|
||||
uint32_t visibility;
|
||||
uint32_t what;
|
||||
uint32_t message_len;
|
||||
//char message[message_len+1]
|
||||
}
|
||||
SpiceMsgNotify;
|
||||
|
||||
typedef struct SpiceMsgInputsInit
|
||||
{
|
||||
uint16_t modifiers;
|
||||
}
|
||||
SpiceMsgInputsInit,
|
||||
SpiceMsgInputsKeyModifiers,
|
||||
SpiceMsgcInputsKeyModifiers;
|
||||
|
||||
typedef struct SpiceMsgcKeyDown
|
||||
{
|
||||
uint32_t code;
|
||||
}
|
||||
SpiceMsgcKeyDown,
|
||||
SpiceMsgcKeyUp;
|
||||
|
||||
typedef struct SpiceMsgcMousePosition
|
||||
{
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
uint16_t button_state;
|
||||
uint8_t display_id;
|
||||
}
|
||||
SpiceMsgcMousePosition;
|
||||
|
||||
typedef struct SpiceMsgcMouseMotion
|
||||
{
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
uint16_t button_state;
|
||||
}
|
||||
SpiceMsgcMouseMotion;
|
||||
|
||||
typedef struct SpiceMsgcMousePress
|
||||
{
|
||||
uint8_t button;
|
||||
uint16_t button_state;
|
||||
}
|
||||
SpiceMsgcMousePress,
|
||||
SpiceMsgcMouseRelease;
|
||||
|
||||
#pragma pack(pop)
|
||||
@@ -1,227 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 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 "rsa.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <spice/protocol.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(USE_OPENSSL) && defined(USE_NETTLE)
|
||||
#error "USE_OPENSSL and USE_NETTLE are both defined"
|
||||
#elif !defined(USE_OPENSSL) && !defined(USE_NETTLE)
|
||||
#error "One of USE_OPENSSL or USE_NETTLE must be defined"
|
||||
#endif
|
||||
|
||||
#if defined(USE_OPENSSL)
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/x509.h>
|
||||
#endif
|
||||
|
||||
#if defined(USE_NETTLE)
|
||||
#include <stdlib.h>
|
||||
#include <nettle/asn1.h>
|
||||
#include <nettle/sha1.h>
|
||||
#include <nettle/rsa.h>
|
||||
#include <nettle/bignum.h>
|
||||
#include <gmp.h>
|
||||
|
||||
#define SHA1_HASH_LEN 20
|
||||
#endif
|
||||
|
||||
#if defined(USE_NETTLE)
|
||||
/* the below OAEP implementation is derived from the FreeTDS project */
|
||||
static void memxor(uint8_t * a, const uint8_t * b, const unsigned int len)
|
||||
{
|
||||
for(unsigned int i = 0; i < len; ++i)
|
||||
a[i] = a[i] ^ b[i];
|
||||
}
|
||||
|
||||
static void sha1(uint8_t * hash, const uint8_t *data, unsigned int len)
|
||||
{
|
||||
struct sha1_ctx ctx;
|
||||
|
||||
sha1_init(&ctx);
|
||||
sha1_update(&ctx, len, data);
|
||||
sha1_digest(&ctx, SHA1_HASH_LEN, hash);
|
||||
}
|
||||
|
||||
static void oaep_mask(uint8_t * dest, size_t dest_len, const uint8_t * mask, size_t mask_len)
|
||||
{
|
||||
uint8_t hash[SHA1_HASH_LEN];
|
||||
uint8_t seed[mask_len + 4 ];
|
||||
memcpy(seed, mask, mask_len);
|
||||
|
||||
for(unsigned int n = 0;; ++n)
|
||||
{
|
||||
(seed+mask_len)[0] = n >> 24;
|
||||
(seed+mask_len)[1] = n >> 16;
|
||||
(seed+mask_len)[2] = n >> 8;
|
||||
(seed+mask_len)[3] = n >> 0;
|
||||
|
||||
sha1(hash, seed, sizeof(seed));
|
||||
if (dest_len <= SHA1_HASH_LEN)
|
||||
{
|
||||
memxor(dest, hash, dest_len);
|
||||
break;
|
||||
}
|
||||
|
||||
memxor(dest, hash, SHA1_HASH_LEN);
|
||||
dest += SHA1_HASH_LEN;
|
||||
dest_len -= SHA1_HASH_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
static bool oaep_pad(mpz_t m, unsigned int key_size, const uint8_t * message, unsigned int len)
|
||||
{
|
||||
if (len + SHA1_HASH_LEN * 2 + 2 > key_size)
|
||||
{
|
||||
DEBUG_ERROR("Message too long");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct
|
||||
{
|
||||
uint8_t all[1];
|
||||
uint8_t ros[SHA1_HASH_LEN];
|
||||
uint8_t db [key_size - SHA1_HASH_LEN - 1];
|
||||
} em;
|
||||
|
||||
memset(&em, 0, sizeof(em));
|
||||
sha1(em.db, (uint8_t *)"", 0);
|
||||
em.all[key_size - len - 1] = 0x1;
|
||||
memcpy(em.all + (key_size - len), message, len);
|
||||
|
||||
/* we are not too worried about randomness since we are just making a local
|
||||
* connection, should anyone use this code outside of LookingGlass please be
|
||||
* sure to use something better such as `gnutls_rnd` */
|
||||
for(int i = 0; i < SHA1_HASH_LEN; ++i)
|
||||
em.ros[i] = rand() % 255;
|
||||
|
||||
const int db_len = key_size - SHA1_HASH_LEN - 1;
|
||||
oaep_mask(em.db , db_len , em.ros, SHA1_HASH_LEN);
|
||||
oaep_mask(em.ros, SHA1_HASH_LEN, em.db , db_len );
|
||||
|
||||
nettle_mpz_set_str_256_u(m, key_size, em.all);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool spice_rsa_encrypt_password(uint8_t * pub_key, char * password, struct spice_password * result)
|
||||
{
|
||||
result->size = 0;
|
||||
result->data = NULL;
|
||||
|
||||
#if defined(USE_OPENSSL)
|
||||
BIO *bioKey = BIO_new(BIO_s_mem());
|
||||
if (!bioKey)
|
||||
{
|
||||
DEBUG_ERROR("failed to allocate bioKey");
|
||||
return false;
|
||||
}
|
||||
|
||||
BIO_write(bioKey, pub_key, SPICE_TICKET_PUBKEY_BYTES);
|
||||
EVP_PKEY *rsaKey = d2i_PUBKEY_bio(bioKey, NULL);
|
||||
RSA *rsa = EVP_PKEY_get1_RSA(rsaKey);
|
||||
|
||||
result->size = RSA_size(rsa);
|
||||
result->data = (char *)malloc(result->size);
|
||||
|
||||
if (RSA_public_encrypt(
|
||||
strlen(password) + 1,
|
||||
(uint8_t*)password,
|
||||
(uint8_t*)result->data,
|
||||
rsa,
|
||||
RSA_PKCS1_OAEP_PADDING
|
||||
) <= 0)
|
||||
{
|
||||
free(result->data);
|
||||
result->size = 0;
|
||||
result->data = NULL;
|
||||
|
||||
DEBUG_ERROR("rsa public encrypt failed");
|
||||
EVP_PKEY_free(rsaKey);
|
||||
BIO_free(bioKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
EVP_PKEY_free(rsaKey);
|
||||
BIO_free(bioKey);
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#if defined(USE_NETTLE)
|
||||
struct asn1_der_iterator der;
|
||||
struct asn1_der_iterator j;
|
||||
struct rsa_public_key pub;
|
||||
|
||||
if (asn1_der_iterator_first(&der, SPICE_TICKET_PUBKEY_BYTES, pub_key) == ASN1_ITERATOR_CONSTRUCTED
|
||||
&& der.type == ASN1_SEQUENCE
|
||||
&& asn1_der_decode_constructed_last(&der) == ASN1_ITERATOR_CONSTRUCTED
|
||||
&& der.type == ASN1_SEQUENCE
|
||||
&& asn1_der_decode_constructed(&der, &j) == ASN1_ITERATOR_PRIMITIVE
|
||||
&& j.type == ASN1_IDENTIFIER
|
||||
&& asn1_der_iterator_next(&der) == ASN1_ITERATOR_PRIMITIVE
|
||||
&& der.type == ASN1_BITSTRING
|
||||
&& asn1_der_decode_bitstring_last(&der))
|
||||
{
|
||||
if (j.length != 9)
|
||||
{
|
||||
DEBUG_ERROR("Invalid key, not RSA");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (asn1_der_iterator_next(&j) == ASN1_ITERATOR_PRIMITIVE
|
||||
&& j.type == ASN1_NULL
|
||||
&& j.length == 0
|
||||
&& asn1_der_iterator_next(&j) == ASN1_ITERATOR_END)
|
||||
{
|
||||
rsa_public_key_init(&pub);
|
||||
if (!rsa_public_key_from_der_iterator(&pub, 0, &der))
|
||||
{
|
||||
DEBUG_ERROR("Unable to load public key from DER iterator");
|
||||
rsa_public_key_clear(&pub);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mpz_t p;
|
||||
mpz_init(p);
|
||||
oaep_pad(p, pub.size, (uint8_t *)password, strlen(password)+1);
|
||||
mpz_powm(p, p, pub.e, pub.n);
|
||||
|
||||
result->size = pub.size;
|
||||
result->data = malloc(pub.size);
|
||||
nettle_mpz_get_str_256(pub.size, (uint8_t *)result->data, p);
|
||||
|
||||
rsa_public_key_clear(&pub);
|
||||
mpz_clear(p);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void spice_rsa_free_password(struct spice_password * pass)
|
||||
{
|
||||
free(pass->data);
|
||||
pass->size = 0;
|
||||
pass->data = NULL;
|
||||
}
|
||||
@@ -1,952 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 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 "spice.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/select.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <spice/protocol.h>
|
||||
#include <spice/error_codes.h>
|
||||
|
||||
#include "messages.h"
|
||||
#include "rsa.h"
|
||||
|
||||
#ifdef DEBUG_SPICE_MOUSE
|
||||
#define DEBUG_MOUSE(fmt, args...) DEBUG_PRINT("[M]", fmt, ##args)
|
||||
#else
|
||||
#define DEBUG_MOUSE(fmt, args...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_SPICE_KEYBOARD
|
||||
#define DEBUG_KEYBOARD(fmt, args...) DEBUG_PRINT("[K]", fmt, ##args)
|
||||
#else
|
||||
#define DEBUG_KEYBOARD(fmt, args...) do {} while(0)
|
||||
#endif
|
||||
|
||||
|
||||
// ============================================================================
|
||||
|
||||
// internal structures
|
||||
struct SpiceChannel
|
||||
{
|
||||
bool connected;
|
||||
bool initDone;
|
||||
uint8_t channelType;
|
||||
int socket;
|
||||
uint32_t ackFrequency;
|
||||
uint32_t ackCount;
|
||||
uint32_t serial;
|
||||
LG_Lock lock;
|
||||
};
|
||||
|
||||
struct SpiceKeyboard
|
||||
{
|
||||
uint32_t modifiers;
|
||||
};
|
||||
|
||||
#define SPICE_MOUSE_QUEUE_SIZE 64
|
||||
|
||||
struct SpiceMouse
|
||||
{
|
||||
uint32_t buttonState;
|
||||
|
||||
int sentCount;
|
||||
SpiceMsgcMouseMotion queue[SPICE_MOUSE_QUEUE_SIZE];
|
||||
int rpos, wpos;
|
||||
int queueLen;
|
||||
LG_Lock lock;
|
||||
};
|
||||
|
||||
union SpiceAddr
|
||||
{
|
||||
struct sockaddr addr;
|
||||
struct sockaddr_in in;
|
||||
struct sockaddr_in6 in6;
|
||||
struct sockaddr_un un;
|
||||
};
|
||||
|
||||
struct Spice
|
||||
{
|
||||
char password[32];
|
||||
short family;
|
||||
union SpiceAddr addr;
|
||||
|
||||
uint32_t sessionID;
|
||||
uint32_t channelID;
|
||||
struct SpiceChannel scMain;
|
||||
struct SpiceChannel scInputs;
|
||||
|
||||
struct SpiceKeyboard kb;
|
||||
struct SpiceMouse mouse;
|
||||
};
|
||||
|
||||
// globals
|
||||
struct Spice spice =
|
||||
{
|
||||
.sessionID = 0,
|
||||
.scMain .connected = false,
|
||||
.scMain .channelType = SPICE_CHANNEL_MAIN,
|
||||
.scInputs.connected = false,
|
||||
.scInputs.channelType = SPICE_CHANNEL_INPUTS,
|
||||
};
|
||||
|
||||
// internal forward decls
|
||||
bool spice_connect_channel (struct SpiceChannel * channel);
|
||||
void spice_disconnect_channel(struct SpiceChannel * channel);
|
||||
|
||||
bool spice_process_ack(struct SpiceChannel * channel);
|
||||
|
||||
bool spice_on_common_read (struct SpiceChannel * channel, SpiceDataHeader * header, bool * handled);
|
||||
bool spice_on_main_channel_read ();
|
||||
bool spice_on_inputs_channel_read();
|
||||
|
||||
bool spice_read (const struct SpiceChannel * channel, void * buffer, const ssize_t size);
|
||||
ssize_t spice_write (const struct SpiceChannel * channel, const void * buffer, const ssize_t size);
|
||||
bool spice_write_msg(struct SpiceChannel * channel, uint32_t type, const void * buffer, const ssize_t size);
|
||||
bool spice_discard (const struct SpiceChannel * channel, ssize_t size);
|
||||
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_connect(const char * host, const short port, const char * password)
|
||||
{
|
||||
strncpy(spice.password, password, sizeof(spice.password) - 1);
|
||||
memset(&spice.addr, 0, sizeof(spice.addr));
|
||||
|
||||
if (port == 0)
|
||||
{
|
||||
spice.family = AF_UNIX;
|
||||
spice.addr.un.sun_family = spice.family;
|
||||
strncpy(spice.addr.un.sun_path, host, sizeof(spice.addr.un.sun_path) - 1);
|
||||
DEBUG_INFO("Remote: %s", host);
|
||||
}
|
||||
else
|
||||
{
|
||||
spice.family = AF_INET;
|
||||
inet_pton(spice.family, host, &spice.addr.in.sin_addr);
|
||||
spice.addr.in.sin_family = spice.family;
|
||||
spice.addr.in.sin_port = htons(port);
|
||||
DEBUG_INFO("Remote: %s:%d", host, port);
|
||||
}
|
||||
|
||||
LG_LOCK_INIT(spice.mouse.lock);
|
||||
|
||||
spice.channelID = 0;
|
||||
if (!spice_connect_channel(&spice.scMain))
|
||||
{
|
||||
DEBUG_ERROR("connect main channel failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
void spice_disconnect()
|
||||
{
|
||||
spice_disconnect_channel(&spice.scMain );
|
||||
spice_disconnect_channel(&spice.scInputs);
|
||||
|
||||
LG_LOCK_FREE(spice.mouse.lock);
|
||||
|
||||
spice.sessionID = 0;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_ready()
|
||||
{
|
||||
return spice.scMain.connected &&
|
||||
spice.scInputs.connected;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_process()
|
||||
{
|
||||
fd_set readSet;
|
||||
FD_ZERO(&readSet);
|
||||
FD_SET(spice.scMain.socket , &readSet);
|
||||
FD_SET(spice.scInputs.socket, &readSet);
|
||||
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 1;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
int rc = select(FD_SETSIZE, &readSet, NULL, NULL, &timeout);
|
||||
if (rc < 0)
|
||||
{
|
||||
DEBUG_ERROR("select failure");
|
||||
return false;
|
||||
}
|
||||
|
||||
for(int i = 0; i < FD_SETSIZE; ++i)
|
||||
if (FD_ISSET(i, &readSet))
|
||||
{
|
||||
if (i == spice.scMain.socket)
|
||||
{
|
||||
if (spice_on_main_channel_read())
|
||||
{
|
||||
if (spice.scMain.connected && !spice_process_ack(&spice.scMain))
|
||||
{
|
||||
DEBUG_ERROR("failed to process ack on main channel");
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_ERROR("failed to perform read on main channel");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (spice.scInputs.connected && i == spice.scInputs.socket)
|
||||
{
|
||||
if (!spice_process_ack(&spice.scInputs))
|
||||
{
|
||||
DEBUG_ERROR("failed to process ack on inputs channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spice_on_inputs_channel_read())
|
||||
continue;
|
||||
else
|
||||
{
|
||||
DEBUG_ERROR("failed to perform read on inputs channel");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_process_ack(struct SpiceChannel * channel)
|
||||
{
|
||||
if (channel->ackFrequency == 0)
|
||||
return true;
|
||||
|
||||
if (channel->ackCount++ != channel->ackFrequency)
|
||||
return true;
|
||||
|
||||
channel->ackCount = 0;
|
||||
return spice_write_msg(channel, SPICE_MSGC_ACK, "\0", 1);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_on_common_read(struct SpiceChannel * channel, SpiceDataHeader * header, bool * handled)
|
||||
{
|
||||
if (!spice_read(channel, header, sizeof(SpiceDataHeader)))
|
||||
{
|
||||
DEBUG_ERROR("read failure");
|
||||
*handled = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf("socket: %d, serial: %6u, type: %2u, size %6u, sub_list %4u\n",
|
||||
channel->socket,
|
||||
header->serial, header->type, header->size, header->sub_list);
|
||||
#endif
|
||||
|
||||
if (!channel->initDone)
|
||||
{
|
||||
*handled = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
switch(header->type)
|
||||
{
|
||||
case SPICE_MSG_MIGRATE:
|
||||
case SPICE_MSG_MIGRATE_DATA:
|
||||
{
|
||||
DEBUG_PROTO("SPICE_MSG_MIGRATE_DATA");
|
||||
|
||||
*handled = true;
|
||||
DEBUG_WARN("migration is not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
case SPICE_MSG_SET_ACK:
|
||||
{
|
||||
DEBUG_INFO("SPICE_MSG_SET_ACK");
|
||||
*handled = true;
|
||||
|
||||
SpiceMsgSetAck in;
|
||||
if (!spice_read(channel, &in, sizeof(in)))
|
||||
return false;
|
||||
|
||||
channel->ackFrequency = in.window;
|
||||
|
||||
SpiceMsgcAckSync out;
|
||||
out.generation = in.generation;
|
||||
if (!spice_write_msg(channel, SPICE_MSGC_ACK_SYNC, &out, sizeof(out)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case SPICE_MSG_PING:
|
||||
{
|
||||
DEBUG_PROTO("SPICE_MSG_PING");
|
||||
*handled = true;
|
||||
|
||||
SpiceMsgPing in;
|
||||
if (!spice_read(channel, &in, sizeof(in)))
|
||||
return false;
|
||||
|
||||
if (!spice_discard(channel, header->size - sizeof(in)))
|
||||
{
|
||||
DEBUG_ERROR("failed discarding enough bytes from the ping packet");
|
||||
return false;
|
||||
}
|
||||
|
||||
SpiceMsgcPong out;
|
||||
out.id = in.id;
|
||||
out.timestamp = in.timestamp;
|
||||
if (!spice_write_msg(channel, SPICE_MSGC_PONG, &out, sizeof(out)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case SPICE_MSG_WAIT_FOR_CHANNELS:
|
||||
case SPICE_MSG_DISCONNECTING :
|
||||
{
|
||||
*handled = true;
|
||||
DEBUG_FIXME("ignored wait-for-channels or disconnect message");
|
||||
return false;
|
||||
}
|
||||
|
||||
case SPICE_MSG_NOTIFY:
|
||||
{
|
||||
DEBUG_PROTO("SPICE_MSG_NOTIFY");
|
||||
SpiceMsgNotify in;
|
||||
if (!spice_read(channel, &in, sizeof(in)))
|
||||
return false;
|
||||
|
||||
char msg[in.message_len+1];
|
||||
if (!spice_read(channel, msg, in.message_len+1))
|
||||
return false;
|
||||
|
||||
DEBUG_INFO("notify message: %s", msg);
|
||||
*handled = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
*handled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_on_main_channel_read()
|
||||
{
|
||||
struct SpiceChannel *channel = &spice.scMain;
|
||||
|
||||
SpiceDataHeader header;
|
||||
bool handled;
|
||||
|
||||
if (!spice_on_common_read(channel, &header, &handled))
|
||||
{
|
||||
DEBUG_ERROR("read failure");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (handled)
|
||||
return true;
|
||||
|
||||
if (!channel->initDone)
|
||||
{
|
||||
if (header.type != SPICE_MSG_MAIN_INIT)
|
||||
{
|
||||
spice_disconnect();
|
||||
DEBUG_ERROR("expected main init message but got type %u", header.type);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_PROTO("SPICE_MSG_MAIN_INIT");
|
||||
|
||||
channel->initDone = true;
|
||||
SpiceMsgMainInit msg;
|
||||
if (!spice_read(channel, &msg, sizeof(msg)))
|
||||
{
|
||||
spice_disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
spice.sessionID = msg.session_id;
|
||||
if (!spice_connect_channel(&spice.scInputs))
|
||||
{
|
||||
DEBUG_ERROR("failed to connect inputs channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (msg.current_mouse_mode != SPICE_MOUSE_MODE_CLIENT && !spice_mouse_mode(false))
|
||||
{
|
||||
DEBUG_ERROR("failed to set mouse mode");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DEBUG_WARN("main channel unhandled message type %u", header.type);
|
||||
spice_discard(channel, header.size);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_on_inputs_channel_read()
|
||||
{
|
||||
struct SpiceChannel *channel = &spice.scInputs;
|
||||
|
||||
SpiceDataHeader header;
|
||||
bool handled;
|
||||
|
||||
if (!spice_on_common_read(channel, &header, &handled))
|
||||
{
|
||||
DEBUG_ERROR("read failure");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (handled)
|
||||
return true;
|
||||
|
||||
switch(header.type)
|
||||
{
|
||||
case SPICE_MSG_INPUTS_INIT:
|
||||
{
|
||||
DEBUG_PROTO("SPICE_MSG_INPUTS_INIT");
|
||||
|
||||
if (channel->initDone)
|
||||
{
|
||||
DEBUG_ERROR("input init message already done");
|
||||
return false;
|
||||
}
|
||||
|
||||
channel->initDone = true;
|
||||
|
||||
SpiceMsgInputsInit in;
|
||||
if (!spice_read(channel, &in, sizeof(in)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case SPICE_MSG_INPUTS_KEY_MODIFIERS:
|
||||
{
|
||||
DEBUG_PROTO("SPICE_MSG_INPUTS_KEY_MODIFIERS");
|
||||
SpiceMsgInputsInit in;
|
||||
if (!spice_read(channel, &in, sizeof(in)))
|
||||
return false;
|
||||
|
||||
spice.kb.modifiers = in.modifiers;
|
||||
return true;
|
||||
}
|
||||
|
||||
case SPICE_MSG_INPUTS_MOUSE_MOTION_ACK:
|
||||
{
|
||||
DEBUG_PROTO("SPICE_MSG_INPUTS_MOUSE_MOTION_ACK");
|
||||
int sent = 0;
|
||||
LG_LOCK(spice.mouse.lock);
|
||||
while(spice.mouse.queueLen && sent < 4)
|
||||
{
|
||||
SpiceMsgcMouseMotion *msg = &spice.mouse.queue[spice.mouse.rpos];
|
||||
if (!spice_write_msg(channel, SPICE_MSGC_INPUTS_MOUSE_MOTION, msg, sizeof(SpiceMsgcMouseMotion)))
|
||||
{
|
||||
DEBUG_ERROR("failed to send post ack");
|
||||
spice.mouse.sentCount = sent;
|
||||
LG_UNLOCK(spice.mouse.lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (++spice.mouse.rpos == SPICE_MOUSE_QUEUE_SIZE)
|
||||
spice.mouse.rpos = 0;
|
||||
|
||||
++sent;
|
||||
--spice.mouse.queueLen;
|
||||
}
|
||||
|
||||
spice.mouse.sentCount = sent;
|
||||
LG_UNLOCK(spice.mouse.lock);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_WARN("inputs channel unhandled message type %u", header.type);
|
||||
spice_discard(channel, header.size);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_connect_channel(struct SpiceChannel * channel)
|
||||
{
|
||||
channel->initDone = false;
|
||||
channel->ackFrequency = 0;
|
||||
channel->ackCount = 0;
|
||||
channel->serial = 0;
|
||||
|
||||
LG_LOCK_INIT(channel->lock);
|
||||
|
||||
size_t addrSize;
|
||||
switch(spice.family)
|
||||
{
|
||||
case AF_UNIX:
|
||||
addrSize = sizeof(spice.addr.un);
|
||||
break;
|
||||
|
||||
case AF_INET:
|
||||
addrSize = sizeof(spice.addr.in);
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
addrSize = sizeof(spice.addr.in6);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Unsupported socket family");
|
||||
return false;
|
||||
}
|
||||
|
||||
channel->socket = socket(spice.family, SOCK_STREAM, 0);
|
||||
if (channel->socket == -1)
|
||||
return false;
|
||||
|
||||
if (spice.family != AF_UNIX)
|
||||
{
|
||||
int flag = 1;
|
||||
setsockopt(channel->socket, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int));
|
||||
}
|
||||
|
||||
if (connect(channel->socket, &spice.addr.addr, addrSize) == -1)
|
||||
{
|
||||
DEBUG_ERROR("socket connect failure");
|
||||
close(channel->socket);
|
||||
return false;
|
||||
}
|
||||
channel->connected = true;
|
||||
|
||||
SpiceLinkHeader header =
|
||||
{
|
||||
.magic = SPICE_MAGIC ,
|
||||
.major_version = SPICE_VERSION_MAJOR,
|
||||
.minor_version = SPICE_VERSION_MINOR,
|
||||
.size = sizeof(SpiceLinkMess)
|
||||
};
|
||||
|
||||
SpiceLinkMess message =
|
||||
{
|
||||
.connection_id = spice.sessionID,
|
||||
.channel_type = channel->channelType,
|
||||
.channel_id = spice.channelID,
|
||||
.num_common_caps = 0,
|
||||
.num_channel_caps = 0,
|
||||
.caps_offset = sizeof(SpiceLinkMess)
|
||||
};
|
||||
|
||||
spice_write(channel, &header , sizeof(header ));
|
||||
spice_write(channel, &message, sizeof(message));
|
||||
|
||||
if (!spice_read(channel, &header, sizeof(header)))
|
||||
{
|
||||
DEBUG_ERROR("failed to read SpiceLinkHeader");
|
||||
spice_disconnect_channel(channel);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.magic != SPICE_MAGIC || header.major_version != SPICE_VERSION_MAJOR)
|
||||
{
|
||||
DEBUG_ERROR("invalid or unsupported protocol version");
|
||||
spice_disconnect_channel(channel);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.size < sizeof(SpiceLinkReply))
|
||||
{
|
||||
DEBUG_ERROR("reported data size too small");
|
||||
spice_disconnect_channel(channel);
|
||||
return false;
|
||||
}
|
||||
|
||||
SpiceLinkReply reply;
|
||||
if (!spice_read(channel, &reply, sizeof(reply)))
|
||||
{
|
||||
DEBUG_ERROR("failed to read SpiceLinkReply");
|
||||
spice_disconnect_channel(channel);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reply.error != SPICEC_ERROR_CODE_SUCCESS)
|
||||
{
|
||||
DEBUG_ERROR("server replied with error %u", reply.error);
|
||||
spice_disconnect_channel(channel);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t capsCommon [reply.num_common_caps ];
|
||||
uint32_t capsChannel[reply.num_channel_caps];
|
||||
spice_read(channel, &capsCommon , sizeof(capsCommon ));
|
||||
spice_read(channel, &capsChannel, sizeof(capsChannel));
|
||||
|
||||
struct spice_password pass;
|
||||
if (!spice_rsa_encrypt_password(reply.pub_key, spice.password, &pass))
|
||||
{
|
||||
spice_disconnect_channel(channel);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!spice_write(channel, pass.data, pass.size))
|
||||
{
|
||||
spice_rsa_free_password(&pass);
|
||||
DEBUG_ERROR("failed to write encrypted data");
|
||||
spice_disconnect_channel(channel);
|
||||
return false;
|
||||
}
|
||||
|
||||
spice_rsa_free_password(&pass);
|
||||
|
||||
uint32_t linkResult;
|
||||
if (!spice_read(channel, &linkResult, sizeof(linkResult)))
|
||||
{
|
||||
DEBUG_ERROR("failed to read SpiceLinkResult");
|
||||
spice_disconnect_channel(channel);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (linkResult != SPICE_LINK_ERR_OK)
|
||||
{
|
||||
DEBUG_ERROR("connect code error %u", linkResult);
|
||||
spice_disconnect_channel(channel);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
void spice_disconnect_channel(struct SpiceChannel * channel)
|
||||
{
|
||||
if (channel->connected)
|
||||
{
|
||||
shutdown(channel->socket, SHUT_WR);
|
||||
|
||||
char buffer[1024];
|
||||
ssize_t len = 0;
|
||||
do
|
||||
len = read(channel->socket, buffer, sizeof(buffer));
|
||||
while(len > 0);
|
||||
|
||||
close(channel->socket);
|
||||
}
|
||||
channel->connected = false;
|
||||
LG_LOCK_FREE(channel->lock);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
ssize_t spice_write(const struct SpiceChannel * channel, const void * buffer, const ssize_t size)
|
||||
{
|
||||
if (!channel->connected)
|
||||
{
|
||||
DEBUG_ERROR("not connected");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
DEBUG_ERROR("invalid buffer argument supplied");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t len = send(channel->socket, buffer, size, 0);
|
||||
if (len != size)
|
||||
DEBUG_WARN("incomplete write");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_write_msg(struct SpiceChannel * channel, uint32_t type, const void * buffer, const ssize_t size)
|
||||
{
|
||||
LG_LOCK(channel->lock);
|
||||
|
||||
SpiceDataHeader header;
|
||||
header.serial = channel->serial++;
|
||||
header.type = type;
|
||||
header.size = size;
|
||||
header.sub_list = 0;
|
||||
|
||||
if (spice_write(channel, &header, sizeof(header)) != sizeof(header))
|
||||
{
|
||||
DEBUG_ERROR("failed to write message header");
|
||||
LG_UNLOCK(channel->lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (spice_write(channel, buffer, size) != size)
|
||||
{
|
||||
DEBUG_ERROR("failed to write message body");
|
||||
LG_UNLOCK(channel->lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
LG_UNLOCK(channel->lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_read(const struct SpiceChannel * channel, void * buffer, const ssize_t size)
|
||||
{
|
||||
if (!channel->connected)
|
||||
{
|
||||
DEBUG_ERROR("not connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!buffer)
|
||||
{
|
||||
DEBUG_ERROR("invalid buffer argument supplied");
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t len = read(channel->socket, buffer, size);
|
||||
if (len != size)
|
||||
{
|
||||
DEBUG_ERROR("incomplete write");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_discard(const struct SpiceChannel * channel, ssize_t size)
|
||||
{
|
||||
while(size)
|
||||
{
|
||||
char c[8192];
|
||||
size_t len = read(channel->socket, c, size > sizeof(c) ? sizeof(c) : size);
|
||||
if (len <= 0)
|
||||
return false;
|
||||
|
||||
size -= len;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_key_down(uint32_t code)
|
||||
{
|
||||
DEBUG_KEYBOARD("%u", code);
|
||||
if (!spice.scInputs.connected)
|
||||
{
|
||||
DEBUG_ERROR("not connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (code > 0x100)
|
||||
code = 0xe0 | ((code - 0x100) << 8);
|
||||
|
||||
SpiceMsgcKeyDown msg;
|
||||
msg.code = code;
|
||||
|
||||
return spice_write_msg(&spice.scInputs, SPICE_MSGC_INPUTS_KEY_DOWN, &msg, sizeof(msg));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_key_up(uint32_t code)
|
||||
{
|
||||
DEBUG_KEYBOARD("%u", code);
|
||||
if (!spice.scInputs.connected)
|
||||
{
|
||||
DEBUG_ERROR("not connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (code < 0x100)
|
||||
code |= 0x80;
|
||||
else
|
||||
code = 0x80e0 | ((code - 0x100) << 8);
|
||||
|
||||
SpiceMsgcKeyDown msg;
|
||||
msg.code = code;
|
||||
|
||||
return spice_write_msg(&spice.scInputs, SPICE_MSGC_INPUTS_KEY_UP, &msg, sizeof(msg));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_mouse_mode(bool server)
|
||||
{
|
||||
DEBUG_MOUSE("%s", server ? "server" : "client");
|
||||
if (!spice.scInputs.connected)
|
||||
{
|
||||
DEBUG_ERROR("not connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
SpiceMsgcMainMouseModeRequest msg;
|
||||
msg.mouse_mode = server ? SPICE_MOUSE_MODE_SERVER : SPICE_MOUSE_MODE_CLIENT;
|
||||
|
||||
return spice_write_msg(&spice.scMain, SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST, &msg, sizeof(msg));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_mouse_position(uint32_t x, uint32_t y)
|
||||
{
|
||||
DEBUG_MOUSE("x=%u, y=%u", x, y);
|
||||
if (!spice.scInputs.connected)
|
||||
{
|
||||
DEBUG_ERROR("not connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
SpiceMsgcMousePosition msg;
|
||||
msg.x = x;
|
||||
msg.y = y;
|
||||
msg.button_state = spice.mouse.buttonState;
|
||||
msg.display_id = 0;
|
||||
|
||||
return spice_write_msg(&spice.scInputs, SPICE_MSGC_INPUTS_MOUSE_POSITION, &msg, sizeof(msg));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_mouse_motion(int32_t x, int32_t y)
|
||||
{
|
||||
DEBUG_MOUSE("x=%d, y=%d", x, y);
|
||||
if (!spice.scInputs.connected)
|
||||
{
|
||||
DEBUG_ERROR("not connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
LG_LOCK(spice.mouse.lock);
|
||||
if (spice.mouse.sentCount == 4)
|
||||
{
|
||||
if (spice.mouse.queueLen == SPICE_MOUSE_QUEUE_SIZE)
|
||||
{
|
||||
DEBUG_ERROR("mouse motion ringbuffer full!");
|
||||
LG_UNLOCK(spice.mouse.lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
SpiceMsgcMouseMotion *msg =
|
||||
&spice.mouse.queue[spice.mouse.wpos++];
|
||||
msg->x = x;
|
||||
msg->y = y;
|
||||
msg->button_state = spice.mouse.buttonState;
|
||||
|
||||
if (spice.mouse.wpos == SPICE_MOUSE_QUEUE_SIZE)
|
||||
spice.mouse.wpos = 0;
|
||||
|
||||
++spice.mouse.queueLen;
|
||||
LG_UNLOCK(spice.mouse.lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
SpiceMsgcMouseMotion msg;
|
||||
msg.x = x;
|
||||
msg.y = y;
|
||||
msg.button_state = spice.mouse.buttonState;
|
||||
|
||||
++spice.mouse.sentCount;
|
||||
LG_UNLOCK(spice.mouse.lock);
|
||||
return spice_write_msg(&spice.scInputs, SPICE_MSGC_INPUTS_MOUSE_MOTION, &msg, sizeof(msg));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_mouse_press(uint32_t button)
|
||||
{
|
||||
DEBUG_MOUSE("%u", button);
|
||||
if (!spice.scInputs.connected)
|
||||
{
|
||||
DEBUG_ERROR("not connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(button)
|
||||
{
|
||||
case SPICE_MOUSE_BUTTON_LEFT : spice.mouse.buttonState |= SPICE_MOUSE_BUTTON_MASK_LEFT ; break;
|
||||
case SPICE_MOUSE_BUTTON_MIDDLE: spice.mouse.buttonState |= SPICE_MOUSE_BUTTON_MASK_MIDDLE; break;
|
||||
case SPICE_MOUSE_BUTTON_RIGHT : spice.mouse.buttonState |= SPICE_MOUSE_BUTTON_MASK_RIGHT ; break;
|
||||
}
|
||||
|
||||
SpiceMsgcMousePress msg;
|
||||
msg.button = button;
|
||||
msg.button_state = spice.mouse.buttonState;
|
||||
|
||||
return spice_write_msg(&spice.scInputs, SPICE_MSGC_INPUTS_MOUSE_PRESS, &msg, sizeof(msg));
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
bool spice_mouse_release(uint32_t button)
|
||||
{
|
||||
DEBUG_MOUSE("%u", button);
|
||||
if (!spice.scInputs.connected)
|
||||
{
|
||||
DEBUG_ERROR("not connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(button)
|
||||
{
|
||||
case SPICE_MOUSE_BUTTON_LEFT : spice.mouse.buttonState &= ~SPICE_MOUSE_BUTTON_MASK_LEFT ; break;
|
||||
case SPICE_MOUSE_BUTTON_MIDDLE: spice.mouse.buttonState &= ~SPICE_MOUSE_BUTTON_MASK_MIDDLE; break;
|
||||
case SPICE_MOUSE_BUTTON_RIGHT : spice.mouse.buttonState &= ~SPICE_MOUSE_BUTTON_MASK_RIGHT ; break;
|
||||
}
|
||||
|
||||
SpiceMsgcMouseRelease msg;
|
||||
msg.button = button;
|
||||
msg.button_state = spice.mouse.buttonState;
|
||||
|
||||
return spice_write_msg(&spice.scInputs, SPICE_MSGC_INPUTS_MOUSE_RELEASE, &msg, sizeof(msg));
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 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 <sys/types.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
bool spice_connect(const char * host, const short port, const char * password);
|
||||
void spice_disconnect();
|
||||
bool spice_process();
|
||||
bool spice_ready();
|
||||
|
||||
bool spice_key_down (uint32_t code);
|
||||
bool spice_key_up (uint32_t code);
|
||||
bool spice_mouse_mode (bool server);
|
||||
bool spice_mouse_position(uint32_t x, uint32_t y);
|
||||
bool spice_mouse_motion ( int32_t x, int32_t y);
|
||||
bool spice_mouse_press (uint32_t button);
|
||||
bool spice_mouse_release (uint32_t button);
|
||||
75
client/src/app.c
Normal file
75
client/src/app.c
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
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 "main.h"
|
||||
#include "common/debug.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
void app_alert(LG_MsgAlert type, const char * fmt, ...)
|
||||
{
|
||||
if (!state.lgr || !params.showAlerts)
|
||||
return;
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
const int length = vsnprintf(NULL, 0, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
char *buffer = malloc(length + 1);
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buffer, length + 1, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
state.lgr->on_alert(
|
||||
state.lgrData,
|
||||
type,
|
||||
buffer,
|
||||
NULL
|
||||
);
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
KeybindHandle app_register_keybind(SDL_Scancode key, SuperEventFn callback, void * opaque)
|
||||
{
|
||||
// don't allow duplicate binds
|
||||
if (state.bindings[key])
|
||||
{
|
||||
DEBUG_INFO("Key already bound");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
KeybindHandle handle = (KeybindHandle)malloc(sizeof(struct KeybindHandle));
|
||||
handle->key = key;
|
||||
handle->callback = callback;
|
||||
handle->opaque = opaque;
|
||||
|
||||
state.bindings[key] = handle;
|
||||
return handle;
|
||||
}
|
||||
|
||||
void app_release_keybind(KeybindHandle * handle)
|
||||
{
|
||||
if (!*handle)
|
||||
return;
|
||||
|
||||
state.bindings[(*handle)->key] = NULL;
|
||||
free(*handle);
|
||||
*handle = NULL;
|
||||
}
|
||||
586
client/src/config.c
Normal file
586
client/src/config.c
Normal file
@@ -0,0 +1,586 @@
|
||||
/*
|
||||
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 "main.h"
|
||||
#include "config.h"
|
||||
#include "common/option.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/stringutils.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// forwards
|
||||
static bool optRendererParse (struct Option * opt, const char * str);
|
||||
static StringList optRendererValues (struct Option * opt);
|
||||
static char * optRendererToString(struct Option * opt);
|
||||
static bool optPosParse (struct Option * opt, const char * str);
|
||||
static StringList optPosValues (struct Option * opt);
|
||||
static char * optPosToString (struct Option * opt);
|
||||
static bool optSizeParse (struct Option * opt, const char * str);
|
||||
static StringList optSizeValues (struct Option * opt);
|
||||
static char * optSizeToString (struct Option * opt);
|
||||
static char * optScancodeToString(struct Option * opt);
|
||||
|
||||
static void doLicense();
|
||||
|
||||
static struct Option options[] =
|
||||
{
|
||||
// app options
|
||||
{
|
||||
.module = "app",
|
||||
.name = "configFile",
|
||||
.description = "A file to read additional configuration from",
|
||||
.shortopt = 'C',
|
||||
.type = OPTION_TYPE_STRING,
|
||||
.value.x_string = NULL,
|
||||
},
|
||||
{
|
||||
.module = "app",
|
||||
.name = "renderer",
|
||||
.description = "Specify the renderer to use",
|
||||
.shortopt = 'g',
|
||||
.type = OPTION_TYPE_CUSTOM,
|
||||
.parser = optRendererParse,
|
||||
.getValues = optRendererValues,
|
||||
.toString = optRendererToString
|
||||
},
|
||||
{
|
||||
.module = "app",
|
||||
.name = "license",
|
||||
.description = "Show the license for this application and then terminate",
|
||||
.shortopt = 'l',
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false,
|
||||
},
|
||||
{
|
||||
.module = "app",
|
||||
.name = "cursorPollInterval",
|
||||
.description = "How often to check for a cursor update in microseconds",
|
||||
.type = OPTION_TYPE_INT,
|
||||
.value.x_int = 1000
|
||||
},
|
||||
{
|
||||
.module = "app",
|
||||
.name = "framePollInterval",
|
||||
.description = "How often to check for a frame update in microseconds",
|
||||
.type = OPTION_TYPE_INT,
|
||||
.value.x_int = 1000
|
||||
},
|
||||
|
||||
// window options
|
||||
{
|
||||
.module = "win",
|
||||
.name = "title",
|
||||
.description = "The window title",
|
||||
.type = OPTION_TYPE_STRING,
|
||||
.value.x_string = "Looking Glass (client)"
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "position",
|
||||
.description = "Initial window position at startup",
|
||||
.type = OPTION_TYPE_CUSTOM,
|
||||
.parser = optPosParse,
|
||||
.getValues = optPosValues,
|
||||
.toString = optPosToString
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "size",
|
||||
.description = "Initial window size at startup",
|
||||
.type = OPTION_TYPE_CUSTOM,
|
||||
.parser = optSizeParse,
|
||||
.getValues = optSizeValues,
|
||||
.toString = optSizeToString
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "autoResize",
|
||||
.description = "Auto resize the window to the guest",
|
||||
.shortopt = 'a',
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false,
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "allowResize",
|
||||
.description = "Allow the window to be manually resized",
|
||||
.shortopt = 'n',
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true,
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "keepAspect",
|
||||
.description = "Maintain the correct aspect ratio",
|
||||
.shortopt = 'r',
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true,
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "forceAspect",
|
||||
.description = "Force the window to maintain the aspect ratio",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true,
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "borderless",
|
||||
.description = "Borderless mode",
|
||||
.shortopt = 'd',
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false,
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "fullScreen",
|
||||
.description = "Launch in fullscreen borderless mode",
|
||||
.shortopt = 'F',
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false,
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "maximize",
|
||||
.description = "Launch window maximized",
|
||||
.shortopt = 'T',
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false,
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "minimizeOnFocusLoss",
|
||||
.description = "Minimize window on focus loss",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true,
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "fpsMin",
|
||||
.description = "Frame rate minimum (0 = disable - not recommended, -1 = auto detect)",
|
||||
.shortopt = 'K',
|
||||
.type = OPTION_TYPE_INT,
|
||||
.value.x_int = -1,
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "showFPS",
|
||||
.description = "Enable the FPS & UPS display",
|
||||
.shortopt = 'k',
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false,
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "ignoreQuit",
|
||||
.description = "Ignore requests to quit (ie: Alt+F4)",
|
||||
.shortopt = 'Q',
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false,
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "noScreensaver",
|
||||
.description = "Prevent the screensaver from starting",
|
||||
.shortopt = 'S',
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false,
|
||||
},
|
||||
{
|
||||
.module = "win",
|
||||
.name = "alerts",
|
||||
.description = "Show on screen alert messages",
|
||||
.shortopt = 'q',
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true,
|
||||
},
|
||||
|
||||
// input options
|
||||
{
|
||||
.module = "input",
|
||||
.name = "grabKeyboard",
|
||||
.description = "Grab the keyboard in capture mode",
|
||||
.shortopt = 'G',
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true,
|
||||
},
|
||||
{
|
||||
.module = "input",
|
||||
.name = "escapeKey",
|
||||
.description = "Specify the escape key, see https://wiki.libsdl.org/SDLScancodeLookup for valid values",
|
||||
.shortopt = 'm',
|
||||
.type = OPTION_TYPE_INT,
|
||||
.value.x_int = SDL_SCANCODE_SCROLLLOCK,
|
||||
.toString = optScancodeToString
|
||||
},
|
||||
{
|
||||
.module = "input",
|
||||
.name = "hideCursor",
|
||||
.description = "Hide the local mouse cursor",
|
||||
.shortopt = 'M',
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true,
|
||||
},
|
||||
{
|
||||
.module = "input",
|
||||
.name = "mouseSens",
|
||||
.description = "Initial mouse sensitivity when in capture mode (-9 to 9)",
|
||||
.type = OPTION_TYPE_INT,
|
||||
.value.x_int = 0,
|
||||
},
|
||||
{
|
||||
.module = "input",
|
||||
.name = "mouseRedraw",
|
||||
.description = "Mouse movements trigger redraws (ignores FPS minimum)",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true,
|
||||
},
|
||||
|
||||
// spice options
|
||||
{
|
||||
.module = "spice",
|
||||
.name = "enable",
|
||||
.description = "Enable the built in SPICE client for input and/or clipboard support",
|
||||
.shortopt = 's',
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
{
|
||||
.module = "spice",
|
||||
.name = "host",
|
||||
.description = "The SPICE server host or UNIX socket",
|
||||
.shortopt = 'c',
|
||||
.type = OPTION_TYPE_STRING,
|
||||
.value.x_string = "127.0.0.1"
|
||||
},
|
||||
{
|
||||
.module = "spice",
|
||||
.name = "port",
|
||||
.description = "The SPICE server port (0 = unix socket)",
|
||||
.shortopt = 'p',
|
||||
.type = OPTION_TYPE_INT,
|
||||
.value.x_int = 5900
|
||||
},
|
||||
{
|
||||
.module = "spice",
|
||||
.name = "input",
|
||||
.description = "Use SPICE to send keyboard and mouse input events to the guest",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
{
|
||||
.module = "spice",
|
||||
.name = "clipboard",
|
||||
.description = "Use SPICE to syncronize the clipboard contents with the guest",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
{
|
||||
.module = "spice",
|
||||
.name = "clipboardToVM",
|
||||
.description = "Allow the clipboard to be syncronized TO the VM",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
{
|
||||
.module = "spice",
|
||||
.name = "clipboardToLocal",
|
||||
.description = "Allow the clipboard to be syncronized FROM the VM",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
{
|
||||
.module = "spice",
|
||||
.name = "scaleCursor",
|
||||
.description = "Scale cursor input position to screen size when up/down scaled",
|
||||
.shortopt = 'j',
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = true
|
||||
},
|
||||
{
|
||||
.module = "spice",
|
||||
.name = "captureOnStart",
|
||||
.description = "Capture mouse and keyboard on start",
|
||||
.type = OPTION_TYPE_BOOL,
|
||||
.value.x_bool = false
|
||||
},
|
||||
{0}
|
||||
};
|
||||
|
||||
void config_init()
|
||||
{
|
||||
params.center = true;
|
||||
params.w = 1024;
|
||||
params.h = 768;
|
||||
|
||||
option_register(options);
|
||||
}
|
||||
|
||||
bool config_load(int argc, char * argv[])
|
||||
{
|
||||
// load any global options first
|
||||
struct stat st;
|
||||
if (stat("/etc/looking-glass-client.ini", &st) >= 0)
|
||||
{
|
||||
DEBUG_INFO("Loading config from: /etc/looking-glass-client.ini");
|
||||
if (!option_load("/etc/looking-glass-client.ini"))
|
||||
return false;
|
||||
}
|
||||
|
||||
// load user's local options
|
||||
struct passwd * pw = getpwuid(getuid());
|
||||
char * localFile;
|
||||
alloc_sprintf(&localFile, "%s/.looking-glass-client.ini", pw->pw_dir);
|
||||
if (stat(localFile, &st) >= 0)
|
||||
{
|
||||
DEBUG_INFO("Loading config from: %s", localFile);
|
||||
if (!option_load(localFile))
|
||||
{
|
||||
free(localFile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
free(localFile);
|
||||
|
||||
// parse the command line arguments
|
||||
if (!option_parse(argc, argv))
|
||||
return false;
|
||||
|
||||
// if a file was specified to also load, do it
|
||||
const char * configFile = option_get_string("app", "configFile");
|
||||
if (configFile)
|
||||
{
|
||||
DEBUG_INFO("Loading config from: %s", configFile);
|
||||
if (!option_load(configFile))
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate the values are sane
|
||||
if (!option_validate())
|
||||
return false;
|
||||
|
||||
if (option_get_bool("app", "license"))
|
||||
{
|
||||
doLicense();
|
||||
return false;
|
||||
}
|
||||
|
||||
// setup the application params for the basic types
|
||||
params.cursorPollInterval = option_get_int ("app", "cursorPollInterval");
|
||||
params.framePollInterval = option_get_int ("app", "framePollInterval" );
|
||||
|
||||
params.windowTitle = option_get_string("win", "title" );
|
||||
params.autoResize = option_get_bool ("win", "autoResize" );
|
||||
params.allowResize = option_get_bool ("win", "allowResize" );
|
||||
params.keepAspect = option_get_bool ("win", "keepAspect" );
|
||||
params.forceAspect = option_get_bool ("win", "forceAspect" );
|
||||
params.borderless = option_get_bool ("win", "borderless" );
|
||||
params.fullscreen = option_get_bool ("win", "fullScreen" );
|
||||
params.maximize = option_get_bool ("win", "maximize" );
|
||||
params.fpsMin = option_get_int ("win", "fpsMin" );
|
||||
params.showFPS = option_get_bool ("win", "showFPS" );
|
||||
params.ignoreQuit = option_get_bool ("win", "ignoreQuit" );
|
||||
params.noScreensaver = option_get_bool ("win", "noScreensaver");
|
||||
params.showAlerts = option_get_bool ("win", "alerts" );
|
||||
|
||||
params.grabKeyboard = option_get_bool ("input", "grabKeyboard");
|
||||
params.escapeKey = option_get_int ("input", "escapeKey" );
|
||||
params.hideMouse = option_get_bool ("input", "hideCursor" );
|
||||
params.mouseSens = option_get_int ("input", "mouseSens" );
|
||||
params.mouseRedraw = option_get_bool ("input", "mouseRedraw" );
|
||||
|
||||
params.minimizeOnFocusLoss = option_get_bool("win", "minimizeOnFocusLoss");
|
||||
|
||||
if (option_get_bool("spice", "enable"))
|
||||
{
|
||||
params.spiceHost = option_get_string("spice", "host");
|
||||
params.spicePort = option_get_int ("spice", "port");
|
||||
|
||||
params.useSpiceInput = option_get_bool("spice", "input" );
|
||||
params.useSpiceClipboard = option_get_bool("spice", "clipboard");
|
||||
|
||||
if (params.useSpiceClipboard)
|
||||
{
|
||||
params.clipboardToVM = option_get_bool("spice", "clipboardToVM" );
|
||||
params.clipboardToLocal = option_get_bool("spice", "clipboardToLocal");
|
||||
|
||||
if (!params.clipboardToVM && !params.clipboardToLocal)
|
||||
params.useSpiceClipboard = false;
|
||||
}
|
||||
|
||||
params.scaleMouseInput = option_get_bool("spice", "scaleCursor");
|
||||
params.captureOnStart = option_get_bool("spice", "captureOnStart");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void config_free()
|
||||
{
|
||||
option_free();
|
||||
}
|
||||
|
||||
static void doLicense()
|
||||
{
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
"Looking Glass - KVM FrameRelay (KVMFR) Client\n"
|
||||
"Copyright(C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>\n"
|
||||
"https://looking-glass.hostfission.com\n"
|
||||
"\n"
|
||||
"This program is free software; you can redistribute it and / or modify it under\n"
|
||||
"the terms of the GNU General Public License as published by the Free Software\n"
|
||||
"Foundation; either version 2 of the License, or (at your option) any later\n"
|
||||
"version.\n"
|
||||
"\n"
|
||||
"This program is distributed in the hope that it will be useful, but WITHOUT ANY\n"
|
||||
"WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A\n"
|
||||
"PARTICULAR PURPOSE.See the GNU General Public License for more details.\n"
|
||||
"\n"
|
||||
"You should have received a copy of the GNU General Public License along with\n"
|
||||
"this program; if not, write to the Free Software Foundation, Inc., 59 Temple\n"
|
||||
"Place, Suite 330, Boston, MA 02111 - 1307 USA\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
static bool optRendererParse(struct Option * opt, const char * str)
|
||||
{
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
if (strcasecmp(str, "auto") == 0)
|
||||
{
|
||||
params.forceRenderer = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i)
|
||||
if (strcasecmp(str, LG_Renderers[i]->get_name()) == 0)
|
||||
{
|
||||
params.forceRenderer = true;
|
||||
params.forceRendererIndex = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static StringList optRendererValues(struct Option * opt)
|
||||
{
|
||||
StringList sl = stringlist_new(false);
|
||||
|
||||
// this typecast is safe as the stringlist doesn't own the values
|
||||
for(unsigned int i = 0; i < LG_RENDERER_COUNT; ++i)
|
||||
stringlist_push(sl, (char *)LG_Renderers[i]->get_name());
|
||||
|
||||
return sl;
|
||||
}
|
||||
|
||||
static char * optRendererToString(struct Option * opt)
|
||||
{
|
||||
if (!params.forceRenderer)
|
||||
return strdup("auto");
|
||||
|
||||
if (params.forceRendererIndex >= LG_RENDERER_COUNT)
|
||||
return NULL;
|
||||
|
||||
return strdup(LG_Renderers[params.forceRendererIndex]->get_name());
|
||||
}
|
||||
|
||||
static bool optPosParse(struct Option * opt, const char * str)
|
||||
{
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
if (strcmp(str, "center") == 0)
|
||||
{
|
||||
params.center = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sscanf(str, "%dx%d", ¶ms.x, ¶ms.y) == 2)
|
||||
{
|
||||
params.center = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static StringList optPosValues(struct Option * opt)
|
||||
{
|
||||
StringList sl = stringlist_new(false);
|
||||
stringlist_push(sl, "center");
|
||||
stringlist_push(sl, "<left>x<top>, ie: 100x100");
|
||||
return sl;
|
||||
}
|
||||
|
||||
static char * optPosToString(struct Option * opt)
|
||||
{
|
||||
if (params.center)
|
||||
return strdup("center");
|
||||
|
||||
int len = snprintf(NULL, 0, "%dx%d", params.x, params.y);
|
||||
char * str = malloc(len + 1);
|
||||
sprintf(str, "%dx%d", params.x, params.y);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static bool optSizeParse(struct Option * opt, const char * str)
|
||||
{
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
if (sscanf(str, "%dx%d", ¶ms.w, ¶ms.h) == 2)
|
||||
{
|
||||
if (params.w < 1 || params.h < 1)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static StringList optSizeValues(struct Option * opt)
|
||||
{
|
||||
StringList sl = stringlist_new(false);
|
||||
stringlist_push(sl, "<left>x<top>, ie: 100x100");
|
||||
return sl;
|
||||
}
|
||||
|
||||
static char * optSizeToString(struct Option * opt)
|
||||
{
|
||||
int len = snprintf(NULL, 0, "%dx%d", params.w, params.h);
|
||||
char * str = malloc(len + 1);
|
||||
sprintf(str, "%dx%d", params.w, params.h);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static char * optScancodeToString(struct Option * opt)
|
||||
{
|
||||
char * str;
|
||||
alloc_sprintf(&str, "%d = %s", opt->value.x_int, SDL_GetScancodeName(opt->value.x_int));
|
||||
return str;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -17,7 +17,8 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "lg-font.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
extern const LG_Font * LG_Fonts[];
|
||||
void config_init();
|
||||
bool config_load(int argc, char * argv[]);
|
||||
void config_free();
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -17,8 +17,7 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "lg-renderer.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
bool LG_RendererValidatorBool(const char * value)
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KVMGFX Client - A KVM Client for VGA Passthrough
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -19,7 +19,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include "ll.h"
|
||||
|
||||
#include "utils.h"
|
||||
#include "common/locking.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
1700
client/src/main.c
Normal file
1700
client/src/main.c
Normal file
File diff suppressed because it is too large
Load Diff
182
client/src/main.h
Normal file
182
client/src/main.h
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
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 <stdbool.h>
|
||||
#include <stdatomic.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "interface/app.h"
|
||||
#include "dynamic/renderers.h"
|
||||
#include "dynamic/clipboards.h"
|
||||
#include "common/ivshmem.h"
|
||||
|
||||
#include "spice/spice.h"
|
||||
#include <lgmp/client.h>
|
||||
|
||||
enum RunState
|
||||
{
|
||||
APP_STATE_RUNNING,
|
||||
APP_STATE_RESTART,
|
||||
APP_STATE_SHUTDOWN
|
||||
};
|
||||
|
||||
struct CursorInfo
|
||||
{
|
||||
int x , y;
|
||||
int hx, hy;
|
||||
};
|
||||
|
||||
enum WarpState
|
||||
{
|
||||
WARP_STATE_ARMED,
|
||||
WARP_STATE_ON,
|
||||
WARP_STATE_ACTIVE,
|
||||
WARP_STATE_OFF
|
||||
};
|
||||
|
||||
struct AppState
|
||||
{
|
||||
enum RunState state;
|
||||
bool ignoreInput;
|
||||
bool escapeActive;
|
||||
SDL_Scancode escapeAction;
|
||||
KeybindHandle bindings[SDL_NUM_SCANCODES];
|
||||
bool keyDown[SDL_NUM_SCANCODES];
|
||||
|
||||
bool haveSrcSize;
|
||||
int windowW, windowH;
|
||||
SDL_Point srcSize;
|
||||
LG_RendererRect dstRect;
|
||||
struct CursorInfo cursor;
|
||||
bool cursorVisible;
|
||||
|
||||
bool serverMode;
|
||||
bool haveCursorPos;
|
||||
bool drawCursor;
|
||||
bool cursorInView;
|
||||
bool updateCursor;
|
||||
bool initialCursorSync;
|
||||
float scaleX, scaleY;
|
||||
float accX, accY;
|
||||
int curLastX;
|
||||
int curLastY;
|
||||
bool haveCurLocal;
|
||||
int curLocalX;
|
||||
int curLocalY;
|
||||
bool haveAligned;
|
||||
|
||||
enum WarpState warpState;
|
||||
int warpFromX, warpFromY;
|
||||
int warpToX , warpToY;
|
||||
|
||||
const LG_Renderer * lgr;
|
||||
void * lgrData;
|
||||
bool lgrResize;
|
||||
|
||||
const LG_Clipboard * lgc;
|
||||
SpiceDataType cbType;
|
||||
struct ll * cbRequestList;
|
||||
|
||||
SDL_SysWMinfo wminfo;
|
||||
SDL_Window * window;
|
||||
|
||||
struct IVSHMEM shm;
|
||||
PLGMPClient lgmp;
|
||||
PLGMPClientQueue frameQueue;
|
||||
PLGMPClientQueue pointerQueue;
|
||||
|
||||
atomic_uint_least64_t frameTime;
|
||||
uint64_t lastFrameTime;
|
||||
uint64_t renderTime;
|
||||
uint64_t frameCount;
|
||||
uint64_t renderCount;
|
||||
|
||||
|
||||
uint64_t resizeTimeout;
|
||||
bool resizeDone;
|
||||
|
||||
KeybindHandle kbFS;
|
||||
KeybindHandle kbInput;
|
||||
KeybindHandle kbQuit;
|
||||
KeybindHandle kbMouseSensInc;
|
||||
KeybindHandle kbMouseSensDec;
|
||||
KeybindHandle kbCtrlAltFn[12];
|
||||
|
||||
int mouseSens;
|
||||
float sensX, sensY;
|
||||
};
|
||||
|
||||
struct AppParams
|
||||
{
|
||||
bool autoResize;
|
||||
bool allowResize;
|
||||
bool keepAspect;
|
||||
bool forceAspect;
|
||||
bool borderless;
|
||||
bool fullscreen;
|
||||
bool maximize;
|
||||
bool minimizeOnFocusLoss;
|
||||
bool center;
|
||||
int x, y;
|
||||
unsigned int w, h;
|
||||
unsigned int fpsMin;
|
||||
bool showFPS;
|
||||
bool useSpiceInput;
|
||||
bool useSpiceClipboard;
|
||||
const char * spiceHost;
|
||||
unsigned int spicePort;
|
||||
bool clipboardToVM;
|
||||
bool clipboardToLocal;
|
||||
bool scaleMouseInput;
|
||||
bool hideMouse;
|
||||
bool ignoreQuit;
|
||||
bool noScreensaver;
|
||||
bool grabKeyboard;
|
||||
SDL_Scancode escapeKey;
|
||||
bool showAlerts;
|
||||
bool captureOnStart;
|
||||
|
||||
unsigned int cursorPollInterval;
|
||||
unsigned int framePollInterval;
|
||||
|
||||
bool forceRenderer;
|
||||
unsigned int forceRendererIndex;
|
||||
|
||||
const char * windowTitle;
|
||||
int mouseSens;
|
||||
bool mouseRedraw;
|
||||
};
|
||||
|
||||
struct CBRequest
|
||||
{
|
||||
SpiceDataType type;
|
||||
LG_ClipboardReplyFn replyFn;
|
||||
void * opaque;
|
||||
};
|
||||
|
||||
struct KeybindHandle
|
||||
{
|
||||
SDL_Scancode key;
|
||||
SuperEventFn callback;
|
||||
void * opaque;
|
||||
};
|
||||
|
||||
// forwards
|
||||
extern struct AppState state;
|
||||
extern struct AppParams params;
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -18,7 +18,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 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 <time.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static inline uint64_t microtime()
|
||||
{
|
||||
struct timespec time;
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &time);
|
||||
return ((uint64_t)time.tv_sec * 1000000) + (time.tv_nsec / 1000);
|
||||
}
|
||||
|
||||
static inline uint64_t nanotime()
|
||||
{
|
||||
struct timespec time;
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &time);
|
||||
return ((uint64_t)time.tv_sec * 1e9) + time.tv_nsec;
|
||||
}
|
||||
|
||||
static inline void nsleep(uint64_t ns)
|
||||
{
|
||||
const struct timespec ts =
|
||||
{
|
||||
.tv_sec = ns / 1e9,
|
||||
.tv_nsec = ns - ((ns / 1e9) * 1e9)
|
||||
};
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
#ifdef ATOMIC_LOCKING
|
||||
#define LG_LOCK_MODE "Atomic"
|
||||
typedef volatile int LG_Lock;
|
||||
#define LG_LOCK_INIT(x) (x) = 0
|
||||
#define LG_LOCK(x) while(__sync_lock_test_and_set(&(x), 1)) {nsleep(100);}
|
||||
#define LG_UNLOCK(x) __sync_lock_release(&x)
|
||||
#define LG_LOCK_FREE(x)
|
||||
#else
|
||||
#include <SDL2/SDL.h>
|
||||
#define LG_LOCK_MODE "Mutex"
|
||||
typedef SDL_mutex * LG_Lock;
|
||||
#define LG_LOCK_INIT(x) (x = SDL_CreateMutex())
|
||||
#define LG_LOCK(x) SDL_LockMutex(x)
|
||||
#define LG_UNLOCK(x) SDL_UnlockMutex(x)
|
||||
#define LG_LOCK_FREE(x) SDL_DestroyMutex(x)
|
||||
#endif
|
||||
|
||||
static inline uint32_t get_bit(const uint8_t * const base, size_t * const offset)
|
||||
{
|
||||
uint32_t out = ((*(base + (*offset >> 0x3))) >> (0x7 - (*offset & 0x7))) & 0x1;
|
||||
++*offset;
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline uint32_t get_bits(const uint8_t * const base, size_t * const offset, const uint8_t bits)
|
||||
{
|
||||
uint32_t value = 0;
|
||||
for (int i = 0; i < bits; ++i)
|
||||
value |= (get_bit(base, offset) ? 1 : 0) << (bits - i - 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline uint32_t decode_u_golomb(const uint8_t * const base, size_t * const offset)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
while(get_bit(base, offset) == 0)
|
||||
++i;
|
||||
|
||||
return ((1 << i) - 1 + get_bits(base, offset, i));
|
||||
}
|
||||
|
||||
static inline int32_t decode_s_golomb(const uint8_t * const base, size_t * const offset)
|
||||
{
|
||||
const uint32_t g = decode_u_golomb(base, offset);
|
||||
return (g & 0x1) ? (g + 1) / 2 : -(g / 2);
|
||||
}
|
||||
|
||||
// reads the specified file into a new buffer
|
||||
// the callee must free the buffer
|
||||
bool file_get_contents(const char * filename, char ** buffer, size_t * length);
|
||||
31
common/CMakeLists.txt
Normal file
31
common/CMakeLists.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(lg_common LANGUAGES C)
|
||||
|
||||
include_directories(
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
add_definitions(-D_GNU_SOURCE)
|
||||
|
||||
if(ENABLE_BACKTRACE)
|
||||
add_definitions(-DENABLE_BACKTRACE)
|
||||
endif()
|
||||
|
||||
add_subdirectory(src/platform)
|
||||
|
||||
set(COMMON_SOURCES
|
||||
src/stringutils.c
|
||||
src/stringlist.c
|
||||
src/option.c
|
||||
src/framebuffer.c
|
||||
)
|
||||
|
||||
add_library(lg_common STATIC ${COMMON_SOURCES})
|
||||
target_link_libraries(lg_common lg_common_platform)
|
||||
|
||||
target_include_directories(lg_common
|
||||
INTERFACE
|
||||
include
|
||||
PRIVATE
|
||||
src
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR)
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -20,8 +20,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define KVMFR_HEADER_MAGIC "[[KVMFR]]"
|
||||
#define KVMFR_HEADER_VERSION 8
|
||||
#define LGMP_Q_POINTER 1
|
||||
#define LGMP_Q_FRAME 2
|
||||
|
||||
typedef enum FrameType
|
||||
{
|
||||
@@ -34,6 +34,14 @@ typedef enum FrameType
|
||||
}
|
||||
FrameType;
|
||||
|
||||
enum
|
||||
{
|
||||
CURSOR_FLAG_POSITION = 0x1,
|
||||
CURSOR_FLAG_VISIBLE = 0x2,
|
||||
CURSOR_FLAG_SHAPE = 0x4
|
||||
};
|
||||
typedef uint32_t KVMFRCursorFlags;
|
||||
|
||||
typedef enum CursorType
|
||||
{
|
||||
CURSOR_TYPE_COLOR ,
|
||||
@@ -42,49 +50,35 @@ typedef enum CursorType
|
||||
}
|
||||
CursorType;
|
||||
|
||||
#define KVMFR_CURSOR_FLAG_UPDATE 1 // cursor update available
|
||||
#define KVMFR_CURSOR_FLAG_VISIBLE 2 // cursor is visible
|
||||
#define KVMFR_CURSOR_FLAG_SHAPE 4 // shape updated
|
||||
#define KVMFR_CURSOR_FLAG_POS 8 // position updated
|
||||
#define KVMFR_MAGIC "KVMFR---"
|
||||
#define KVMFR_VERSION 3
|
||||
|
||||
typedef struct KVMFR
|
||||
{
|
||||
char magic[8];
|
||||
uint32_t version;
|
||||
char hostver[32];
|
||||
}
|
||||
KVMFR;
|
||||
|
||||
typedef struct KVMFRCursor
|
||||
{
|
||||
uint8_t flags; // KVMFR_CURSOR_FLAGS
|
||||
int16_t x, y; // cursor x & y position
|
||||
|
||||
uint32_t version; // shape version
|
||||
CursorType type; // shape buffer data type
|
||||
int8_t hx, hy; // shape hotspot x & y
|
||||
uint32_t width; // width of the shape
|
||||
uint32_t height; // height of the shape
|
||||
uint32_t pitch; // row length in bytes of the shape
|
||||
uint64_t dataPos; // offset to the shape data
|
||||
}
|
||||
KVMFRCursor;
|
||||
|
||||
#define KVMFR_FRAME_FLAG_UPDATE 1 // frame update available
|
||||
|
||||
typedef struct KVMFRFrame
|
||||
{
|
||||
uint8_t flags; // KVMFR_FRAME_FLAGS
|
||||
FrameType type; // the frame data type
|
||||
uint32_t width; // the width
|
||||
uint32_t height; // the height
|
||||
uint32_t stride; // the row stride (zero if compressed data)
|
||||
uint32_t pitch; // the row pitch (stride in bytes or the compressed frame size)
|
||||
uint64_t dataPos; // offset to the frame
|
||||
uint32_t offset; // offset from the start of this header to the FrameBuffer header
|
||||
}
|
||||
KVMFRFrame;
|
||||
|
||||
#define KVMFR_HEADER_FLAG_RESTART 1 // restart signal from client
|
||||
#define KVMFR_HEADER_FLAG_READY 2 // ready signal from client
|
||||
#define KVMFR_HEADER_FLAG_PAUSED 4 // capture has been paused by the host
|
||||
|
||||
typedef struct KVMFRHeader
|
||||
{
|
||||
char magic[sizeof(KVMFR_HEADER_MAGIC)];
|
||||
uint32_t version; // version of this structure
|
||||
uint8_t flags; // KVMFR_HEADER_FLAGS
|
||||
KVMFRFrame frame; // the frame information
|
||||
KVMFRCursor cursor; // the cursor information
|
||||
}
|
||||
KVMFRHeader;
|
||||
22
common/include/common/crash.h
Normal file
22
common/include/common/crash.h
Normal file
@@ -0,0 +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
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool installCrashHandler(const char * exe);
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KVMGFX Client - A KVM Client for VGA Passthrough
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
@@ -17,9 +17,15 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#if _WIN32
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "time.h"
|
||||
|
||||
#if defined(_WIN32) && !defined(__GNUC__)
|
||||
#define DIRECTORY_SEPARATOR '\\'
|
||||
#else
|
||||
#define DIRECTORY_SEPARATOR '/'
|
||||
@@ -47,8 +53,9 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
sizeof(s) > 20 && (s)[sizeof(s)-21] == DIRECTORY_SEPARATOR ? (s) + sizeof(s) - 20 : \
|
||||
sizeof(s) > 21 && (s)[sizeof(s)-22] == DIRECTORY_SEPARATOR ? (s) + sizeof(s) - 21 : (s))
|
||||
|
||||
#define DEBUG_PRINT(type, fmt, ...) do {fprintf(stderr, type " %20s:%-4u | %-30s | " fmt "\n", STRIPPATH(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__);} while (0)
|
||||
#define DEBUG_PRINT(type, fmt, ...) do {fprintf(stderr, "%12" PRId64 " " type " %20s:%-4u | %-30s | " fmt "\n", microtime(), STRIPPATH(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__);} while (0)
|
||||
|
||||
#define DEBUG_BREAK() DEBUG_PRINT("[ ]", "%s", "================================================================================")
|
||||
#define DEBUG_INFO(fmt, ...) DEBUG_PRINT("[I]", fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_WARN(fmt, ...) DEBUG_PRINT("[W]", fmt, ##__VA_ARGS__)
|
||||
#define DEBUG_ERROR(fmt, ...) DEBUG_PRINT("[E]", fmt, ##__VA_ARGS__)
|
||||
@@ -59,8 +66,3 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#else
|
||||
#define DEBUG_PROTO(fmt, ...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Util.h"
|
||||
#define DEBUG_WINERROR(x, y) Util::DebugWinError(STRIPPATH(__FILE__), __LINE__, __FUNCTION__, x, y)
|
||||
#endif
|
||||
42
common/include/common/event.h
Normal file
42
common/include/common/event.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017-2020 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 <time.h>
|
||||
|
||||
#define TIMEOUT_INFINITE ((unsigned int)~0)
|
||||
|
||||
typedef struct LGEvent LGEvent;
|
||||
|
||||
LGEvent * lgCreateEvent(bool autoReset, unsigned int msSpinTime);
|
||||
void lgFreeEvent (LGEvent * handle);
|
||||
bool lgWaitEvent (LGEvent * handle, unsigned int timeout);
|
||||
bool lgWaitEvents (LGEvent * handles[], int count, bool waitAll, unsigned int timeout);
|
||||
bool lgSignalEvent(LGEvent * handle);
|
||||
bool lgResetEvent (LGEvent * handle);
|
||||
|
||||
// os specific method to wrap/convert a native event into a LGEvent
|
||||
// for windows this is an event HANDLE
|
||||
LGEvent * lgWrapEvent(void * handle);
|
||||
|
||||
// Posix specific, not implmented/possible in windows
|
||||
bool lgWaitEventAbs(LGEvent * handle, struct timespec * ts);
|
||||
bool lgWaitEventNS (LGEvent * handle, unsigned int timeout);
|
||||
60
common/include/common/framebuffer.h
Normal file
60
common/include/common/framebuffer.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct stFrameBuffer FrameBuffer;
|
||||
|
||||
typedef bool (*FrameBufferReadFn)(void * opaque, const void * src, size_t size);
|
||||
|
||||
/**
|
||||
* The size of the FrameBuffer struct
|
||||
*/
|
||||
extern const size_t FrameBufferStructSize;
|
||||
|
||||
/**
|
||||
* Wait for the framebuffer to fill to the specified size
|
||||
*/
|
||||
void framebuffer_wait(const FrameBuffer * frame, size_t size);
|
||||
|
||||
/**
|
||||
* Read data from the KVMFRFrame into the dst buffer
|
||||
*/
|
||||
bool framebuffer_read(const FrameBuffer * frame, void * dst, size_t dstpitch,
|
||||
size_t height, size_t width, size_t bpp, size_t pitch);
|
||||
|
||||
/**
|
||||
* Read data from the KVMFRFrame using a callback
|
||||
*/
|
||||
bool framebuffer_read_fn(const FrameBuffer * frame, size_t height, size_t width,
|
||||
size_t bpp, size_t pitch, FrameBufferReadFn fn, void * opaque);
|
||||
|
||||
/**
|
||||
* Prepare the framebuffer for writing
|
||||
*/
|
||||
void framebuffer_prepare(FrameBuffer * frame);
|
||||
|
||||
/**
|
||||
* Write data from the src buffer into the KVMFRFrame
|
||||
*/
|
||||
bool framebuffer_write(FrameBuffer * frame, const void * src, size_t size);
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Looking Glass - KVM FrameRelay (KVMFR) Client
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
Copyright (C) 2017-2020 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under
|
||||
@@ -17,14 +17,20 @@ 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 <stdint.h>
|
||||
#pragma once
|
||||
|
||||
struct spice_password
|
||||
#include <stdbool.h>
|
||||
|
||||
struct IVSHMEM
|
||||
{
|
||||
char * data;
|
||||
unsigned int size;
|
||||
void * mem;
|
||||
|
||||
// internal use
|
||||
void * opaque;
|
||||
};
|
||||
|
||||
bool spice_rsa_encrypt_password(uint8_t * pub_key, char * password, struct spice_password * result);
|
||||
void spice_rsa_free_password(struct spice_password * pass);
|
||||
void ivshmemOptionsInit();
|
||||
bool ivshmemOpen(struct IVSHMEM * dev);
|
||||
bool ivshmemOpenDev(struct IVSHMEM * dev, const char * shmDev);
|
||||
void ivshmemClose(struct IVSHMEM * dev);
|
||||
40
common/include/common/locking.h
Normal file
40
common/include/common/locking.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
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 "time.h"
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
#define LG_LOCK_MODE "Atomic"
|
||||
typedef atomic_flag LG_Lock;
|
||||
#define LG_LOCK_INIT(x) atomic_flag_clear(&(x))
|
||||
#define LG_LOCK(x) \
|
||||
while(atomic_flag_test_and_set_explicit(&(x), memory_order_acquire)) { ; }
|
||||
#define LG_UNLOCK(x) \
|
||||
atomic_flag_clear_explicit(&(x), memory_order_release);
|
||||
#define LG_LOCK_FREE(x)
|
||||
|
||||
#define INTERLOCKED_INC(x) atomic_fetch_add((x), 1)
|
||||
#define INTERLOCKED_DEC(x) atomic_fetch_sub((x), 1)
|
||||
|
||||
#define INTERLOCKED_SECTION(lock, x) \
|
||||
LG_LOCK(lock) \
|
||||
x \
|
||||
LG_UNLOCK(lock)
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
KVMGFX Client - A KVM Client for VGA Passthrough
|
||||
Copyright (C) 2017 Geoffrey McRae <geoff@hostfission.com>
|
||||
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
|
||||
84
common/include/common/option.h
Normal file
84
common/include/common/option.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "common/stringlist.h"
|
||||
|
||||
enum OptionType
|
||||
{
|
||||
OPTION_TYPE_NONE = 0,
|
||||
OPTION_TYPE_INT,
|
||||
OPTION_TYPE_STRING,
|
||||
OPTION_TYPE_BOOL,
|
||||
OPTION_TYPE_CUSTOM
|
||||
};
|
||||
|
||||
struct Option;
|
||||
|
||||
struct Option
|
||||
{
|
||||
char * module;
|
||||
char * name;
|
||||
char * description;
|
||||
const char shortopt;
|
||||
|
||||
enum OptionType type;
|
||||
union
|
||||
{
|
||||
int x_int;
|
||||
char * x_string;
|
||||
bool x_bool;
|
||||
void * x_custom;
|
||||
}
|
||||
value;
|
||||
|
||||
bool (*parser )(struct Option * opt, const char * str);
|
||||
bool (*validator)(struct Option * opt, const char ** error);
|
||||
char * (*toString )(struct Option * opt);
|
||||
StringList (*getValues)(struct Option * opt);
|
||||
|
||||
void (*printHelp)();
|
||||
|
||||
// internal use only
|
||||
bool failed_set;
|
||||
};
|
||||
|
||||
// register an NULL terminated array of options
|
||||
bool option_register(struct Option options[]);
|
||||
|
||||
// lookup the value of an option
|
||||
struct Option * option_get (const char * module, const char * name);
|
||||
int option_get_int (const char * module, const char * name);
|
||||
const char * option_get_string(const char * module, const char * name);
|
||||
bool option_get_bool (const char * module, const char * name);
|
||||
|
||||
// called by the main application to parse the command line arguments
|
||||
bool option_parse(int argc, char * argv[]);
|
||||
|
||||
// called by the main application to load configuration from a file
|
||||
bool option_load(const char * filename);
|
||||
|
||||
// called by the main application to validate the option values
|
||||
bool option_validate();
|
||||
|
||||
// print out the options, help, and their current values
|
||||
void option_print();
|
||||
|
||||
// final cleanup
|
||||
void option_free();
|
||||
28
common/include/common/stringlist.h
Normal file
28
common/include/common/stringlist.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct StringList * StringList;
|
||||
|
||||
StringList stringlist_new (bool owns_strings);
|
||||
void stringlist_free (StringList * sl);
|
||||
int stringlist_push (StringList sl, char * str);
|
||||
unsigned int stringlist_count(StringList sl);
|
||||
char * stringlist_at (StringList sl, unsigned int index);
|
||||
22
common/include/common/stringutils.h
Normal file
22
common/include/common/stringutils.h
Normal file
@@ -0,0 +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
|
||||
*/
|
||||
|
||||
// sprintf but with buffer allocation
|
||||
int alloc_sprintf(char ** str, const char * format, ...)
|
||||
__attribute__ ((format (printf, 2, 3)));
|
||||
24
common/include/common/sysinfo.h
Normal file
24
common/include/common/sysinfo.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
// returns the maximum number of multisamples supported by the system
|
||||
int sysinfo_gfx_max_multisample();
|
||||
|
||||
// returns the page size
|
||||
long sysinfo_getPageSize();
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user