mirror of
https://github.com/gnif/LookingGlass.git
synced 2025-06-11 04:35:37 +00:00
Compare commits
432 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f15d72cdfe | ||
![]() |
54d811e098 | ||
![]() |
9593301511 | ||
![]() |
c2a19f5e76 | ||
![]() |
d8baa62c6a | ||
![]() |
81162b460e | ||
![]() |
852eb6bf69 | ||
![]() |
4b11743f18 | ||
![]() |
656d01a694 | ||
![]() |
a4406ac867 | ||
![]() |
57a2f68931 | ||
![]() |
f4df3f0ec7 | ||
![]() |
4e951184f1 | ||
![]() |
be4782b062 | ||
![]() |
b17c66d6bb | ||
![]() |
94fbbad21c | ||
![]() |
63a9365377 | ||
![]() |
cadcfe4b39 | ||
![]() |
3b883bf9fe | ||
![]() |
b58171c3e1 | ||
![]() |
7afb9b93eb | ||
![]() |
6396ff1e9c | ||
![]() |
db1dda00c8 | ||
![]() |
5b07286c65 | ||
![]() |
6dad0de8b8 | ||
![]() |
35c975d334 | ||
![]() |
530e83e7bf | ||
![]() |
40a4debfda | ||
![]() |
75f07cb28c | ||
![]() |
f26fa17bc1 | ||
![]() |
648fca7caa | ||
![]() |
868504d22d | ||
![]() |
cb423730e4 | ||
![]() |
86de1c9ac6 | ||
![]() |
d839a45d0b | ||
![]() |
9ffb800e93 | ||
![]() |
91e8440c9d | ||
![]() |
15eff234ec | ||
![]() |
6a4edfc6b6 | ||
![]() |
bf59e45118 | ||
![]() |
9cc99e7db9 | ||
![]() |
af5a48deca | ||
![]() |
b1b6da32ce | ||
![]() |
17b24cc8ff | ||
![]() |
7248b666ea | ||
![]() |
2084a9fee3 | ||
![]() |
a2a771f94e | ||
![]() |
b526eb3da0 | ||
![]() |
7a88a49f1c | ||
![]() |
ce23cff12e | ||
![]() |
09df8c41aa | ||
![]() |
0db9d3a27b | ||
![]() |
4f2eb984d3 | ||
![]() |
8b198091ce | ||
![]() |
62c075cfb5 | ||
![]() |
bea198735a | ||
![]() |
a421329d9a | ||
![]() |
5382a94945 | ||
![]() |
4278a10fe1 | ||
![]() |
35e6a6e81a | ||
![]() |
afbd844be8 | ||
![]() |
7c285a45fb | ||
![]() |
968fd42d46 | ||
![]() |
66ac453c98 | ||
![]() |
27fe47cbe2 | ||
![]() |
aefbebff9c | ||
![]() |
9174b1ae0f | ||
![]() |
f6b7ea11c8 | ||
![]() |
2c50ce4dbd | ||
![]() |
f6b0752e45 | ||
![]() |
50fee59b29 | ||
![]() |
554f5bf75d | ||
![]() |
b43f572af0 | ||
![]() |
d9f2df361d | ||
![]() |
6dcf178879 | ||
![]() |
ea00b623ed | ||
![]() |
711c932380 | ||
![]() |
be52a86a9a | ||
![]() |
420eaebb71 | ||
![]() |
551298ed5b | ||
![]() |
41008add12 | ||
![]() |
32d1b8063e | ||
![]() |
b0227a8ff8 | ||
![]() |
a1c713556d | ||
![]() |
a0cf34df73 | ||
![]() |
fbb489b9b6 | ||
![]() |
03ca20d3e4 | ||
![]() |
7e9e38faa5 | ||
![]() |
0fd6f59bbb | ||
![]() |
661efd107e | ||
![]() |
f6094de919 | ||
![]() |
77f6054f0a | ||
![]() |
c169d4ab23 | ||
![]() |
e25492a3a3 | ||
![]() |
4e8201da07 | ||
![]() |
6a0a635781 | ||
![]() |
3ea37b86e3 | ||
![]() |
d060e375ea | ||
![]() |
7305ce36af | ||
![]() |
c04f84b85c | ||
![]() |
d21bdebc52 | ||
![]() |
695cbc2d61 | ||
![]() |
2067b21d47 | ||
![]() |
9bc82ab1b4 | ||
![]() |
82c9df54c5 | ||
![]() |
c48bd35b3a | ||
![]() |
ecd3692e1e | ||
![]() |
23b773ad80 | ||
![]() |
a626a1142d | ||
![]() |
e70718600c | ||
![]() |
0990c59eff | ||
![]() |
fb1d96e147 | ||
![]() |
7f515c54b3 | ||
![]() |
20972cfd9b | ||
![]() |
dc9065b62f | ||
![]() |
13b9756e80 | ||
![]() |
d902afa3dc | ||
![]() |
6e37305765 | ||
![]() |
dd6c79594b | ||
![]() |
ce9ed5da5d | ||
![]() |
fdad5daff8 | ||
![]() |
8d25469d27 | ||
![]() |
8c5d1d47ee | ||
![]() |
90398bc04f | ||
![]() |
989fe2bb0b | ||
![]() |
7a41169104 | ||
![]() |
0b4322d921 | ||
![]() |
b251b22a64 | ||
![]() |
90b27ae1f7 | ||
![]() |
4d388d6b9b | ||
![]() |
a88783e5b7 | ||
![]() |
da820769e8 | ||
![]() |
d5b32225f4 | ||
![]() |
778c21070c | ||
![]() |
cae410d4de | ||
![]() |
064a605208 | ||
![]() |
279357e205 | ||
![]() |
98aade2ec9 | ||
![]() |
4acea9fa25 | ||
![]() |
01efbc62c4 | ||
![]() |
ed512f5943 | ||
![]() |
4a4f72ba38 | ||
![]() |
eb31815b46 | ||
![]() |
545e736389 | ||
![]() |
6a72633674 | ||
![]() |
9123984ecc | ||
![]() |
d81395b672 | ||
![]() |
bfadf0a427 | ||
![]() |
03662f45a7 | ||
![]() |
a4e761bedc | ||
![]() |
beb8de922d | ||
![]() |
e247f1fc7b | ||
![]() |
4463ca15f6 | ||
![]() |
97d91a32c8 | ||
![]() |
2d41cda640 | ||
![]() |
a894348530 | ||
![]() |
ad7ac6540f | ||
![]() |
0184ddeedd | ||
![]() |
b87d8d2f33 | ||
![]() |
5c4540ed8b | ||
![]() |
c7f1aadb9e | ||
![]() |
57ac020c8c | ||
![]() |
71b826458d | ||
![]() |
4408359597 | ||
![]() |
2f3ca443cf | ||
![]() |
66049cf763 | ||
![]() |
a6dc8a9db3 | ||
![]() |
dc4d93f50a | ||
![]() |
3b43dcb80d | ||
![]() |
9de047d9cb | ||
![]() |
1098b7e6bd | ||
![]() |
055d5527ef | ||
![]() |
b1313980fb | ||
![]() |
319241b597 | ||
![]() |
b0b851dd4b | ||
![]() |
60b01566e1 | ||
![]() |
4076377820 | ||
![]() |
0b210a280d | ||
![]() |
a4fede01f3 | ||
![]() |
071e4323fa | ||
![]() |
be82b7e578 | ||
![]() |
c07b72883a | ||
![]() |
462d8187b6 | ||
![]() |
4523b9ba00 | ||
![]() |
cae4b2f4f9 | ||
![]() |
72b25b99bc | ||
![]() |
e376e6fb53 | ||
![]() |
34e8a2255e | ||
![]() |
adaf40e2bf | ||
![]() |
84dd68dd2b | ||
![]() |
738a04d0bc | ||
![]() |
2316a5e64d | ||
![]() |
47ad93f48d | ||
![]() |
c5cbb8aa46 | ||
![]() |
ad00aaacd0 | ||
![]() |
1b75ae0762 | ||
![]() |
5b3cc4cd48 | ||
![]() |
7247fadad8 | ||
![]() |
c2237f29ae | ||
![]() |
e5a9c0242f | ||
![]() |
537218d6ae | ||
![]() |
f05151c9a6 | ||
![]() |
b776b00a67 | ||
![]() |
4b4e07875d | ||
![]() |
6104956a27 | ||
![]() |
3668040892 | ||
![]() |
8cd002f1b2 | ||
![]() |
22d949c411 | ||
![]() |
43a3fb0db3 | ||
![]() |
8d27d9e2e2 | ||
![]() |
660b4b8ec8 | ||
![]() |
4911e129f8 | ||
![]() |
d3ee5bddde | ||
![]() |
0ce4c34c37 | ||
![]() |
584de4133f | ||
![]() |
3330f83af6 | ||
![]() |
5d4c1d348c | ||
![]() |
750cab83a3 | ||
![]() |
e61678ef1b | ||
![]() |
6357df1a7a | ||
![]() |
1f4395570c | ||
![]() |
accf300c6c | ||
![]() |
a0fd03d328 | ||
![]() |
929e88b9d3 | ||
![]() |
7bea919352 | ||
![]() |
96b5892c31 | ||
![]() |
b14aad7118 | ||
![]() |
7321ca6768 | ||
![]() |
cd6485f2ed | ||
![]() |
7bcad37568 | ||
![]() |
ba8075a9fd | ||
![]() |
3bad3837b4 | ||
![]() |
084ebe5035 | ||
![]() |
d480b674ca | ||
![]() |
10e30eec57 | ||
![]() |
38b6b0ac40 | ||
![]() |
503efdd0d8 | ||
![]() |
75e10688d4 | ||
![]() |
ec88a52fe2 | ||
![]() |
a28deae569 | ||
![]() |
cf51503a54 | ||
![]() |
aa42751743 | ||
![]() |
9a53880b9b | ||
![]() |
a3b51220ed | ||
![]() |
17fce1cf78 | ||
![]() |
9f3f8cc5bd | ||
![]() |
0524980cb4 | ||
![]() |
a0f5907cb6 | ||
![]() |
0a9784d09d | ||
![]() |
120e063a10 | ||
![]() |
f59ef4422a | ||
![]() |
417c9cf092 | ||
![]() |
852825a97e | ||
![]() |
cce12508cc | ||
![]() |
43f9a4c0e1 | ||
![]() |
ee5c02f72a | ||
![]() |
55fa5cc851 | ||
![]() |
b70811dcb9 | ||
![]() |
3c1405719c | ||
![]() |
f6befb4567 | ||
![]() |
62aef5a240 | ||
![]() |
4f508d320a | ||
![]() |
29f1434270 | ||
![]() |
27f3af8221 | ||
![]() |
cb849b287c | ||
![]() |
8630fd20ad | ||
![]() |
0057cf5377 | ||
![]() |
e31874b809 | ||
![]() |
a2443cf926 | ||
![]() |
eaaef65791 | ||
![]() |
11542d7ace | ||
![]() |
138a0aee53 | ||
![]() |
7a30736ac4 | ||
![]() |
174b51b144 | ||
![]() |
aa9dbe654d | ||
![]() |
d592f13f88 | ||
![]() |
905fea57f0 | ||
![]() |
3a6afd04d2 | ||
![]() |
c0e09e13a5 | ||
![]() |
3843afa927 | ||
![]() |
49bdf046fe | ||
![]() |
8605df8c8d | ||
![]() |
d847c2c144 | ||
![]() |
6492c47e1e | ||
![]() |
86e8e99107 | ||
![]() |
dcde981a17 | ||
![]() |
c54a09ca25 | ||
![]() |
5bba4dfab5 | ||
![]() |
3af2cf54d6 | ||
![]() |
4aba15f31c | ||
![]() |
a455078e0f | ||
![]() |
f8586fd063 | ||
![]() |
ad13928c73 | ||
![]() |
f991f994f0 | ||
![]() |
772e0e3b4a | ||
![]() |
fd79bb1333 | ||
![]() |
d6519c4486 | ||
![]() |
9fefbae749 | ||
![]() |
fa561c121e | ||
![]() |
c2e3c37bab | ||
![]() |
54bd08c3cb | ||
![]() |
eb2796d40b | ||
![]() |
748c9c177e | ||
![]() |
cc48257aeb | ||
![]() |
3838e1f996 | ||
![]() |
881aa9e179 | ||
![]() |
9a2638bfa0 | ||
![]() |
8d7d5ba8fd | ||
![]() |
09b6fee360 | ||
![]() |
561c45bcb9 | ||
![]() |
5f613b09d6 | ||
![]() |
30c577beeb | ||
![]() |
6c7f3c4197 | ||
![]() |
139e98ac3b | ||
![]() |
d02e3730b2 | ||
![]() |
ea5b6b4026 | ||
![]() |
6329779893 | ||
![]() |
1da50d220e | ||
![]() |
3106d0e3e2 | ||
![]() |
d44fc36fc4 | ||
![]() |
c29404eea6 | ||
![]() |
c665044bfa | ||
![]() |
578d98fd22 | ||
![]() |
b3879ff1d7 | ||
![]() |
f6b2cec841 | ||
![]() |
626f5eb32e | ||
![]() |
29c797d7b6 | ||
![]() |
3625207801 | ||
![]() |
25d6dd3ba2 | ||
![]() |
1e30539fb2 | ||
![]() |
52410beea7 | ||
![]() |
c591f7a8ae | ||
![]() |
21cd380cad | ||
![]() |
e225f66cee | ||
![]() |
2206752b66 | ||
![]() |
0510d06c4b | ||
![]() |
699d95818d | ||
![]() |
fffac35300 | ||
![]() |
35b0f8edf3 | ||
![]() |
544164f637 | ||
![]() |
b94166177f | ||
![]() |
69b984aa2c | ||
![]() |
c100df4037 | ||
![]() |
47329ebd89 | ||
![]() |
5d7469d23e | ||
![]() |
6625cd733a | ||
![]() |
b41840b010 | ||
![]() |
2f36aaff5c | ||
![]() |
200b7b732c | ||
![]() |
eeea24ebfb | ||
![]() |
54066094bd | ||
![]() |
c5923b9b4d | ||
![]() |
bde2eef175 | ||
![]() |
e0bdd869d6 | ||
![]() |
ccd0a0bcf9 | ||
![]() |
742e41c2c3 | ||
![]() |
3ed71a09f4 | ||
![]() |
12d051d8c0 | ||
![]() |
a6a6b8779a | ||
![]() |
ad65561511 | ||
![]() |
6b65c7e339 | ||
![]() |
646f5b1be8 | ||
![]() |
128a8938c6 | ||
![]() |
2e515657dd | ||
![]() |
1cf8e8c846 | ||
![]() |
df5c648377 | ||
![]() |
78df2073ff | ||
![]() |
844a37a276 | ||
![]() |
e658c2e0a2 | ||
![]() |
b2ec60d2dc | ||
![]() |
e6aa2b85a9 | ||
![]() |
a3045e0b4a | ||
![]() |
9cadb64942 | ||
![]() |
82607a7d6f | ||
![]() |
8d90c9c2a5 | ||
![]() |
24d4fce17c | ||
![]() |
8dba4b6c0b | ||
![]() |
193977895b | ||
![]() |
219c73edbe | ||
![]() |
6522920ea1 | ||
![]() |
e32b292cc1 | ||
![]() |
53525847fd | ||
![]() |
9d66a68403 | ||
![]() |
07bcc54732 | ||
![]() |
dab5618a6d | ||
![]() |
ad43969c1a | ||
![]() |
99333a03c1 | ||
![]() |
45318aa653 | ||
![]() |
f84165ac66 | ||
![]() |
e4a8424fad | ||
![]() |
880d8042a4 | ||
![]() |
a629d24dc3 | ||
![]() |
7c1bb13d70 | ||
![]() |
9d5c543a53 | ||
![]() |
063a859de1 | ||
![]() |
4f4cf2be7d | ||
![]() |
9759b5aa8f | ||
![]() |
bbd0c7a99b | ||
![]() |
c11748a76f | ||
![]() |
d6b26b0eb1 | ||
![]() |
fd0cc6aa10 | ||
![]() |
80b9bda59d | ||
![]() |
0c176acf94 | ||
![]() |
0c3dce3ca6 | ||
![]() |
3c85957b99 | ||
![]() |
77ddcfe489 | ||
![]() |
d228ef135e | ||
![]() |
0afcf2c2ce | ||
![]() |
75da66a090 | ||
![]() |
11676d3d56 | ||
![]() |
b13682a9ef | ||
![]() |
642634293d | ||
![]() |
01f9c2bfb5 | ||
![]() |
9385b2de7a | ||
![]() |
d2f7667fae | ||
![]() |
cfef966603 | ||
![]() |
4f09d5b771 | ||
![]() |
b7b302334c | ||
![]() |
28e74a73a4 | ||
![]() |
996e1b2f7a | ||
![]() |
0ee5751b3a | ||
![]() |
e067db7bb4 | ||
![]() |
b4b4a37b2b | ||
![]() |
bbc9204bfe | ||
![]() |
894b8b0c22 | ||
![]() |
91b0cba145 | ||
![]() |
2f48798db9 | ||
![]() |
254b370bbf | ||
![]() |
86efc47505 | ||
![]() |
b559d6b9bc | ||
![]() |
48cd0c3789 | ||
![]() |
6f8e6f9a19 |
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@ -1,4 +1,4 @@
|
|||||||
# Jonathan Rubenstein (JJRcop)
|
# Jonathan Rubenstein (JJRcop)
|
||||||
# - Primary documentation manager. Does not require direct approval for every
|
# - Primary documentation manager. Does not require direct approval for every
|
||||||
# - change, but should be consulted for large additions and changes.
|
# - change, but should be consulted for large additions and changes.
|
||||||
docs/ jrubcop@gmail.com
|
/doc/ jrubcop@gmail.com
|
||||||
|
22
.github/workflows/build.yml
vendored
22
.github/workflows/build.yml
vendored
@ -2,7 +2,7 @@ name: build
|
|||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
jobs:
|
jobs:
|
||||||
client:
|
client:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -15,12 +15,6 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Install libdecor PPA
|
|
||||||
run: sudo add-apt-repository ppa:christianrauch/libdecoration
|
|
||||||
if: ${{ matrix.wayland_shell == 'libdecor' }}
|
|
||||||
- name: Install PipeWire repository
|
|
||||||
run: |
|
|
||||||
echo 'deb [trusted=yes] https://pipewire-ubuntu.quantum5.workers.dev ./' | sudo tee /etc/apt/sources.list.d/pipewire.list
|
|
||||||
- name: Update apt
|
- name: Update apt
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
@ -32,6 +26,7 @@ jobs:
|
|||||||
libgl-dev libgles-dev \
|
libgl-dev libgles-dev \
|
||||||
libx11-dev libxss-dev libxi-dev libxinerama-dev libxcursor-dev libxpresent-dev \
|
libx11-dev libxss-dev libxi-dev libxinerama-dev libxcursor-dev libxpresent-dev \
|
||||||
libwayland-dev libxkbcommon-dev \
|
libwayland-dev libxkbcommon-dev \
|
||||||
|
libfontconfig-dev \
|
||||||
libsamplerate0-dev libpipewire-0.3-dev libpulse-dev \
|
libsamplerate0-dev libpipewire-0.3-dev libpulse-dev \
|
||||||
$([ '${{ matrix.wayland_shell }}' = libdecor ] && echo 'libdecor-0-dev libdbus-1-dev') \
|
$([ '${{ matrix.wayland_shell }}' = libdecor ] && echo 'libdecor-0-dev libdbus-1-dev') \
|
||||||
$([ '${{ matrix.compiler.cc }}' = clang ] && echo 'clang-tools')
|
$([ '${{ matrix.compiler.cc }}' = clang ] && echo 'clang-tools')
|
||||||
@ -76,16 +71,13 @@ jobs:
|
|||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Install PipeWire repository
|
|
||||||
run: |
|
|
||||||
echo 'deb [trusted=yes] https://pipewire-ubuntu.quantum5.workers.dev ./' | sudo tee /etc/apt/sources.list.d/pipewire.list
|
|
||||||
- name: Update apt
|
- name: Update apt
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
- name: Install Linux host dependencies
|
- name: Install Linux host dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install binutils-dev libxcb-xfixes0-dev \
|
sudo apt-get install binutils-dev libglib2.0-dev libxcb-xfixes0-dev \
|
||||||
libpipewire-0.3-dev
|
libpipewire-0.3-dev libxcb-shm0-dev
|
||||||
- name: Configure Linux host
|
- name: Configure Linux host
|
||||||
run: |
|
run: |
|
||||||
mkdir host/build
|
mkdir host/build
|
||||||
@ -97,7 +89,7 @@ jobs:
|
|||||||
make -j$(nproc)
|
make -j$(nproc)
|
||||||
|
|
||||||
host-windows-cross:
|
host-windows-cross:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
@ -183,8 +175,8 @@ jobs:
|
|||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
- name: Install docs dependencies
|
- name: Install docs dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install python3-sphinx python3-sphinx-rtd-theme
|
sudo apt-get install python3-sphinx python3-sphinx-rtd-theme \
|
||||||
sudo pip3 install sphinxcontrib-spelling
|
python3-sphinxcontrib.spelling
|
||||||
- name: Build docs
|
- name: Build docs
|
||||||
run: |
|
run: |
|
||||||
cd doc
|
cd doc
|
||||||
|
22
.github/workflows/pr-check.yml
vendored
22
.github/workflows/pr-check.yml
vendored
@ -3,16 +3,36 @@ on: pull_request
|
|||||||
jobs:
|
jobs:
|
||||||
authors:
|
authors:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: Check AUTHORS file
|
- name: Check AUTHORS file
|
||||||
|
id: check-authors
|
||||||
run: |
|
run: |
|
||||||
user="$(curl -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' -s https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }} | jq -r .user.login)"
|
user="$(curl -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' -s https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }} | jq -r .user.login)"
|
||||||
|
echo "user=$user" >> "$GITHUB_OUTPUT"
|
||||||
echo "Checking if GitHub user $user is in AUTHORS file..."
|
echo "Checking if GitHub user $user is in AUTHORS file..."
|
||||||
if grep -q -E '> \('"$user"'\)' AUTHORS; then
|
if grep -q -E '> \('"$user"'\)' AUTHORS; then
|
||||||
echo "$user found in AUTHORS file, all good!"
|
echo "$user found in AUTHORS file, all good!"
|
||||||
else
|
else
|
||||||
echo "$user not found in AUTHORS file."
|
echo "$user not found in AUTHORS file."
|
||||||
echo "Please add yourself to the AUTHORS file and try again."
|
echo "Please add yourself to the AUTHORS file and try again."
|
||||||
exit 1
|
echo "not-found=yes" >> "$GITHUB_OUTPUT"
|
||||||
fi
|
fi
|
||||||
|
- name: 'Not found: Create review requesting changes'
|
||||||
|
if: ${{ steps.check-authors.outputs.not-found }}
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
github.rest.pulls.createReview({
|
||||||
|
owner: context.issue.owner,
|
||||||
|
repo: context.issue.repo,
|
||||||
|
pull_number: context.issue.number,
|
||||||
|
event: "REQUEST_CHANGES",
|
||||||
|
body: "@${{ steps.check-authors.outputs.user }} not found in AUTHORS file.\n" +
|
||||||
|
"Please add yourself to the AUTHORS file and try again."
|
||||||
|
});
|
||||||
|
- name: 'Not found: Fail job'
|
||||||
|
if: ${{ steps.check-authors.outputs.not-found }}
|
||||||
|
run: exit 1
|
||||||
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -10,3 +10,11 @@ module/modules.order
|
|||||||
*/build
|
*/build
|
||||||
__pycache__
|
__pycache__
|
||||||
*.py[co]
|
*.py[co]
|
||||||
|
*/.vs
|
||||||
|
*.user
|
||||||
|
idd/Debug
|
||||||
|
idd/x64
|
||||||
|
idd/*/x64
|
||||||
|
idd/*/Debug
|
||||||
|
idd/*/VersionInfo.h
|
||||||
|
idd/packages
|
||||||
|
32
.woodpecker/idd.yaml
Normal file
32
.woodpecker/idd.yaml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
labels:
|
||||||
|
platform: windows/amd64
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
BUILD_TYPE:
|
||||||
|
- Debug
|
||||||
|
- Release
|
||||||
|
|
||||||
|
clone:
|
||||||
|
- name: clone
|
||||||
|
image: woodpeckerci/plugin-git
|
||||||
|
pull: false
|
||||||
|
settings:
|
||||||
|
tags: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: idd
|
||||||
|
when:
|
||||||
|
- branch: master
|
||||||
|
event: [push, manual]
|
||||||
|
image: lg-vs2022:latest
|
||||||
|
pull: true
|
||||||
|
environment:
|
||||||
|
VS_PATH: "\"C:\\\\Program Files (x86)\\\\Microsoft Visual Studio\\\\2022\\\\\""
|
||||||
|
entrypoint:
|
||||||
|
- cmd
|
||||||
|
- /C
|
||||||
|
- >
|
||||||
|
%VS_PATH%\BuildTools\Common7\Tools\VsDevCmd.bat -arch=amd64 &&
|
||||||
|
msbuild /restore idd\LGIdd.sln /p:Configuration=${BUILD_TYPE} /p:RestorePackagesConfig=true /p:Platform=x64 /p:SignMode=Off /m &&
|
||||||
|
IF EXIST C:\artifacts\build.cmd (cmd /C C:\artifacts\build.cmd)
|
7
AUTHORS
7
AUTHORS
@ -65,3 +65,10 @@ Leonard Fricke <leonard.fricke98@gmail.com> (Leo1998)
|
|||||||
David Meier <meier_david_91@hotmail.com> (Kenny.ch)
|
David Meier <meier_david_91@hotmail.com> (Kenny.ch)
|
||||||
Daniel Cordero <looking-glass@0xdc.io> (0xdc)
|
Daniel Cordero <looking-glass@0xdc.io> (0xdc)
|
||||||
esi <git@esibun.net> (esibun)
|
esi <git@esibun.net> (esibun)
|
||||||
|
MakiseKurisu <saberconer@gmail.com> (MakiseKurisu)
|
||||||
|
Zenithal <i@zenithal.me> (ZenithalHourlyRate)
|
||||||
|
Kamplom <6284968128@protonmail.ch> (kamplom)
|
||||||
|
Jacob McNamee <jacob@jacobmcnamee.com> (jacobmcnamee)
|
||||||
|
Marco Antonio J. Costa <marco.antonio.costa@gmail.com> (majcosta)
|
||||||
|
rs189 <35667100+rs189@users.noreply.github.com> (rs189)
|
||||||
|
Jérôme Poulin <jeromepoulin@gmail.com> (ticpu)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(looking-glass-client C CXX)
|
project(looking-glass-client C CXX)
|
||||||
|
|
||||||
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
|
get_filename_component(PROJECT_TOP "${PROJECT_SOURCE_DIR}/.." ABSOLUTE)
|
||||||
@ -42,25 +42,12 @@ add_feature_info(ENABLE_ASAN ENABLE_ASAN "AddressSanitizer support.")
|
|||||||
option(ENABLE_UBSAN "Build with UndefinedBehaviorSanitizer" OFF)
|
option(ENABLE_UBSAN "Build with UndefinedBehaviorSanitizer" OFF)
|
||||||
add_feature_info(ENABLE_UBSAN ENABLE_UBSAN "UndefinedBehaviorSanitizer support.")
|
add_feature_info(ENABLE_UBSAN ENABLE_UBSAN "UndefinedBehaviorSanitizer support.")
|
||||||
|
|
||||||
option(ENABLE_X11 "Build with X11 support" ON)
|
|
||||||
add_feature_info(ENABLE_X11 ENABLE_X11 "X11 support.")
|
|
||||||
|
|
||||||
option(ENABLE_WAYLAND "Build with Wayland support" ON)
|
|
||||||
add_feature_info(ENABLE_WAYLAND ENABLE_WAYLAND "Wayland support.")
|
|
||||||
|
|
||||||
option(ENABLE_LIBDECOR "Build with libdecor support" OFF)
|
|
||||||
add_feature_info(ENABLE_LIBDECOR ENABLE_LIBDECOR "libdecor support.")
|
|
||||||
|
|
||||||
option(ENABLE_PIPEWIRE "Build with PipeWire audio output support" ON)
|
option(ENABLE_PIPEWIRE "Build with PipeWire audio output support" ON)
|
||||||
add_feature_info(ENABLE_PIPEWIRE ENABLE_PIPEWIRE "PipeWire audio support.")
|
add_feature_info(ENABLE_PIPEWIRE ENABLE_PIPEWIRE "PipeWire audio support.")
|
||||||
|
|
||||||
option(ENABLE_PULSEAUDIO "Build with PulseAudio audio output support" ON)
|
option(ENABLE_PULSEAUDIO "Build with PulseAudio audio output support" ON)
|
||||||
add_feature_info(ENABLE_PULSEAUDIO ENABLE_PULSEAUDIO "PulseAudio audio support.")
|
add_feature_info(ENABLE_PULSEAUDIO ENABLE_PULSEAUDIO "PulseAudio audio support.")
|
||||||
|
|
||||||
if (NOT ENABLE_X11 AND NOT ENABLE_WAYLAND)
|
|
||||||
message(FATAL_ERROR "Either ENABLE_X11 or ENABLE_WAYLAND must be on")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_compile_options(
|
add_compile_options(
|
||||||
"-Wall"
|
"-Wall"
|
||||||
"-Wextra"
|
"-Wextra"
|
||||||
@ -126,6 +113,7 @@ set(SOURCES
|
|||||||
src/main.c
|
src/main.c
|
||||||
src/core.c
|
src/core.c
|
||||||
src/app.c
|
src/app.c
|
||||||
|
src/message.c
|
||||||
src/audio.c
|
src/audio.c
|
||||||
src/config.c
|
src/config.c
|
||||||
src/keybind.c
|
src/keybind.c
|
||||||
@ -137,6 +125,7 @@ set(SOURCES
|
|||||||
src/eglutil.c
|
src/eglutil.c
|
||||||
src/overlay_utils.c
|
src/overlay_utils.c
|
||||||
src/render_queue.c
|
src/render_queue.c
|
||||||
|
src/evdev.c
|
||||||
|
|
||||||
src/overlay/splash.c
|
src/overlay/splash.c
|
||||||
src/overlay/alert.c
|
src/overlay/alert.c
|
||||||
@ -150,6 +139,8 @@ set(SOURCES
|
|||||||
|
|
||||||
# Force cimgui to build as a static library.
|
# Force cimgui to build as a static library.
|
||||||
set(IMGUI_STATIC "yes" CACHE STRING "Build as a static library")
|
set(IMGUI_STATIC "yes" CACHE STRING "Build as a static library")
|
||||||
|
add_definitions("-DCIMGUI_USE_OPENGL2=1")
|
||||||
|
add_definitions("-DCIMGUI_USE_OPENGL3=1")
|
||||||
|
|
||||||
add_subdirectory("${PROJECT_TOP}/resources" "${CMAKE_BINARY_DIR}/resources")
|
add_subdirectory("${PROJECT_TOP}/resources" "${CMAKE_BINARY_DIR}/resources")
|
||||||
add_subdirectory("${PROJECT_TOP}/common" "${CMAKE_BINARY_DIR}/common" )
|
add_subdirectory("${PROJECT_TOP}/common" "${CMAKE_BINARY_DIR}/common" )
|
||||||
@ -160,6 +151,7 @@ add_subdirectory("${PROJECT_TOP}/repos/cimgui" "${CMAKE_BINARY_DIR}/cimgui" E
|
|||||||
add_subdirectory(displayservers)
|
add_subdirectory(displayservers)
|
||||||
add_subdirectory(renderers)
|
add_subdirectory(renderers)
|
||||||
|
|
||||||
|
configure_file("${PROJECT_TOP}/resources/looking-glass-client.desktop.in" "${CMAKE_BINARY_DIR}/resources/looking-glass-client.desktop" @ONLY)
|
||||||
add_executable(looking-glass-client ${SOURCES})
|
add_executable(looking-glass-client ${SOURCES})
|
||||||
|
|
||||||
target_compile_definitions(looking-glass-client PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1)
|
target_compile_definitions(looking-glass-client PRIVATE CIMGUI_DEFINE_ENUMS_AND_STRUCTS=1)
|
||||||
@ -190,4 +182,10 @@ install(TARGETS looking-glass-client
|
|||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
COMPONENT binary)
|
COMPONENT binary)
|
||||||
|
|
||||||
|
install(FILES "${CMAKE_BINARY_DIR}/resources/looking-glass-client.desktop"
|
||||||
|
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications")
|
||||||
|
install(FILES "${PROJECT_TOP}/resources/lg-logo.svg"
|
||||||
|
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps"
|
||||||
|
RENAME "looking-glass.svg")
|
||||||
|
|
||||||
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)
|
feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(audiodevs LANGUAGES C)
|
project(audiodevs LANGUAGES C)
|
||||||
|
|
||||||
set(AUDIODEV_H "${CMAKE_BINARY_DIR}/include/dynamic/audiodev.h")
|
set(AUDIODEV_H "${CMAKE_BINARY_DIR}/include/dynamic/audiodev.h")
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(audiodev_PipeWire LANGUAGES C)
|
project(audiodev_PipeWire LANGUAGES C)
|
||||||
|
|
||||||
find_package(PkgConfig)
|
find_package(PkgConfig)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -28,6 +28,7 @@
|
|||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
#include "common/stringutils.h"
|
#include "common/stringutils.h"
|
||||||
#include "common/util.h"
|
#include "common/util.h"
|
||||||
|
#include "common/option.h"
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
@ -47,6 +48,7 @@ struct PipeWire
|
|||||||
{
|
{
|
||||||
struct pw_stream * stream;
|
struct pw_stream * stream;
|
||||||
struct spa_io_rate_match * rateMatch;
|
struct spa_io_rate_match * rateMatch;
|
||||||
|
struct pw_time time;
|
||||||
|
|
||||||
int channels;
|
int channels;
|
||||||
int sampleRate;
|
int sampleRate;
|
||||||
@ -90,6 +92,14 @@ static void pipewire_onPlaybackProcess(void * userdata)
|
|||||||
{
|
{
|
||||||
struct pw_buffer * pbuf;
|
struct pw_buffer * pbuf;
|
||||||
|
|
||||||
|
#if PW_CHECK_VERSION(0, 3, 50)
|
||||||
|
if (pw_stream_get_time_n(pw.playback.stream, &pw.playback.time,
|
||||||
|
sizeof(pw.playback.time)) < 0)
|
||||||
|
#else
|
||||||
|
if (pw_stream_get_time(pw.playback.stream, &pw.playback.time) < 0)
|
||||||
|
#endif
|
||||||
|
DEBUG_ERROR("pw_stream_get_time failed");
|
||||||
|
|
||||||
if (!(pbuf = pw_stream_dequeue_buffer(pw.playback.stream)))
|
if (!(pbuf = pw_stream_dequeue_buffer(pw.playback.stream)))
|
||||||
{
|
{
|
||||||
DEBUG_WARN("out of buffers");
|
DEBUG_WARN("out of buffers");
|
||||||
@ -114,6 +124,7 @@ static void pipewire_onPlaybackProcess(void * userdata)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pbuf->size = frames;
|
||||||
sbuf->datas[0].chunk->offset = 0;
|
sbuf->datas[0].chunk->offset = 0;
|
||||||
sbuf->datas[0].chunk->stride = pw.playback.stride;
|
sbuf->datas[0].chunk->stride = pw.playback.stride;
|
||||||
sbuf->datas[0].chunk->size = frames * pw.playback.stride;
|
sbuf->datas[0].chunk->size = frames * pw.playback.stride;
|
||||||
@ -129,6 +140,29 @@ static void pipewire_onPlaybackDrained(void * userdata)
|
|||||||
pw_thread_loop_unlock(pw.thread);
|
pw_thread_loop_unlock(pw.thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct Option pipewire_options[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
.module = "pipewire",
|
||||||
|
.name = "outDevice",
|
||||||
|
.description = "The default playback device to use",
|
||||||
|
.type = OPTION_TYPE_STRING
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.module = "pipewire",
|
||||||
|
.name = "recDevice",
|
||||||
|
.description = "The default record device to use",
|
||||||
|
.type = OPTION_TYPE_STRING
|
||||||
|
},
|
||||||
|
|
||||||
|
{0}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pipewire_earlyInit(void)
|
||||||
|
{
|
||||||
|
option_register(pipewire_options);
|
||||||
|
}
|
||||||
|
|
||||||
static bool pipewire_init(void)
|
static bool pipewire_init(void)
|
||||||
{
|
{
|
||||||
pw_init(NULL, NULL);
|
pw_init(NULL, NULL);
|
||||||
@ -221,17 +255,32 @@ static void pipewire_playbackSetup(int channels, int sampleRate,
|
|||||||
pw.playback.pullFn = pullFn;
|
pw.playback.pullFn = pullFn;
|
||||||
|
|
||||||
pw_thread_loop_lock(pw.thread);
|
pw_thread_loop_lock(pw.thread);
|
||||||
pw.playback.stream = pw_stream_new_simple(
|
|
||||||
pw.loop,
|
struct pw_properties * props =
|
||||||
"Looking Glass",
|
|
||||||
pw_properties_new(
|
pw_properties_new(
|
||||||
|
PW_KEY_APP_NAME , "Looking Glass",
|
||||||
PW_KEY_NODE_NAME , "Looking Glass",
|
PW_KEY_NODE_NAME , "Looking Glass",
|
||||||
PW_KEY_MEDIA_TYPE , "Audio",
|
PW_KEY_MEDIA_TYPE , "Audio",
|
||||||
PW_KEY_MEDIA_CATEGORY, "Playback",
|
PW_KEY_MEDIA_CATEGORY, "Playback",
|
||||||
PW_KEY_MEDIA_ROLE , "Music",
|
PW_KEY_MEDIA_ROLE , "Music",
|
||||||
PW_KEY_NODE_LATENCY , requestedNodeLatency,
|
PW_KEY_NODE_LATENCY , requestedNodeLatency,
|
||||||
NULL
|
NULL
|
||||||
),
|
);
|
||||||
|
|
||||||
|
const char * device = option_get_string("pipewire", "outDevice");
|
||||||
|
if (device)
|
||||||
|
{
|
||||||
|
#ifdef PW_KEY_TARGET_OBJECT
|
||||||
|
pw_properties_set(props, PW_KEY_TARGET_OBJECT, device);
|
||||||
|
#else
|
||||||
|
pw_properties_set(props, PW_KEY_NODE_TARGET, device);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
pw.playback.stream = pw_stream_new_simple(
|
||||||
|
pw.loop,
|
||||||
|
"Looking Glass",
|
||||||
|
props,
|
||||||
&events,
|
&events,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
@ -363,20 +412,32 @@ static void pipewire_playbackMute(bool mute)
|
|||||||
pw_thread_loop_unlock(pw.thread);
|
pw_thread_loop_unlock(pw.thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t pipewire_playbackLatency(void)
|
static uint64_t pipewire_playbackLatency(void)
|
||||||
{
|
{
|
||||||
struct pw_time time = { 0 };
|
|
||||||
|
|
||||||
pw_thread_loop_lock(pw.thread);
|
|
||||||
#if PW_CHECK_VERSION(0, 3, 50)
|
#if PW_CHECK_VERSION(0, 3, 50)
|
||||||
if (pw_stream_get_time_n(pw.playback.stream, &time, sizeof(time)) < 0)
|
if (pw.playback.time.rate.num == 0)
|
||||||
#else
|
return 0;
|
||||||
if (pw_stream_get_time(pw.playback.stream, &time) < 0)
|
|
||||||
#endif
|
|
||||||
DEBUG_ERROR("pw_stream_get_time failed");
|
|
||||||
pw_thread_loop_unlock(pw.thread);
|
|
||||||
|
|
||||||
return time.delay + time.queued / pw.playback.stride;
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
|
||||||
|
// diff in ns
|
||||||
|
int64_t diff = SPA_TIMESPEC_TO_NSEC(&ts) - pw.playback.time.now;
|
||||||
|
|
||||||
|
// elapsed frames
|
||||||
|
int64_t elapsed =
|
||||||
|
(pw.playback.time.rate.denom * diff) /
|
||||||
|
(pw.playback.time.rate.num * SPA_NSEC_PER_SEC);
|
||||||
|
|
||||||
|
const int64_t buffered = pw.playback.time.buffered + pw.playback.time.queued;
|
||||||
|
int64_t latency = (buffered * 1000 / pw.playback.sampleRate) +
|
||||||
|
((pw.playback.time.delay - elapsed) * 1000 *
|
||||||
|
pw.playback.time.rate.num / pw.playback.time.rate.denom);
|
||||||
|
|
||||||
|
return max(0, -latency);
|
||||||
|
#else
|
||||||
|
return pw.playback.time.delay + pw.playback.time.queued / pw.playback.stride;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pipewire_recordStopStream(void)
|
static void pipewire_recordStopStream(void)
|
||||||
@ -449,17 +510,30 @@ static void pipewire_recordStart(int channels, int sampleRate,
|
|||||||
pw.record.stride = sizeof(uint16_t) * channels;
|
pw.record.stride = sizeof(uint16_t) * channels;
|
||||||
pw.record.pushFn = pushFn;
|
pw.record.pushFn = pushFn;
|
||||||
|
|
||||||
pw_thread_loop_lock(pw.thread);
|
struct pw_properties * props =
|
||||||
pw.record.stream = pw_stream_new_simple(
|
|
||||||
pw.loop,
|
|
||||||
"Looking Glass",
|
|
||||||
pw_properties_new(
|
pw_properties_new(
|
||||||
PW_KEY_NODE_NAME , "Looking Glass",
|
PW_KEY_NODE_NAME , "Looking Glass",
|
||||||
PW_KEY_MEDIA_TYPE , "Audio",
|
PW_KEY_MEDIA_TYPE , "Audio",
|
||||||
PW_KEY_MEDIA_CATEGORY, "Capture",
|
PW_KEY_MEDIA_CATEGORY, "Capture",
|
||||||
PW_KEY_MEDIA_ROLE , "Music",
|
PW_KEY_MEDIA_ROLE , "Music",
|
||||||
NULL
|
NULL
|
||||||
),
|
);
|
||||||
|
|
||||||
|
const char * device = option_get_string("pipewire", "recDevice");
|
||||||
|
if (device)
|
||||||
|
{
|
||||||
|
#ifdef PW_KEY_TARGET_OBJECT
|
||||||
|
pw_properties_set(props, PW_KEY_TARGET_OBJECT, device);
|
||||||
|
#else
|
||||||
|
pw_properties_set(props, PW_KEY_NODE_TARGET, device);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
pw_thread_loop_lock(pw.thread);
|
||||||
|
pw.record.stream = pw_stream_new_simple(
|
||||||
|
pw.loop,
|
||||||
|
"Looking Glass",
|
||||||
|
props,
|
||||||
&events,
|
&events,
|
||||||
NULL
|
NULL
|
||||||
);
|
);
|
||||||
@ -543,9 +617,10 @@ static void pipewire_free(void)
|
|||||||
|
|
||||||
struct LG_AudioDevOps LGAD_PipeWire =
|
struct LG_AudioDevOps LGAD_PipeWire =
|
||||||
{
|
{
|
||||||
.name = "PipeWire",
|
.name = "PipeWire",
|
||||||
.init = pipewire_init,
|
.earlyInit = pipewire_earlyInit,
|
||||||
.free = pipewire_free,
|
.init = pipewire_init,
|
||||||
|
.free = pipewire_free,
|
||||||
.playback =
|
.playback =
|
||||||
{
|
{
|
||||||
.setup = pipewire_playbackSetup,
|
.setup = pipewire_playbackSetup,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(audiodev_PulseAudio LANGUAGES C)
|
project(audiodev_PulseAudio LANGUAGES C)
|
||||||
|
|
||||||
find_package(PkgConfig)
|
find_package(PkgConfig)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(displayservers LANGUAGES C)
|
project(displayservers LANGUAGES C)
|
||||||
|
|
||||||
set(DISPLAYSERVER_H "${CMAKE_BINARY_DIR}/include/dynamic/displayservers.h")
|
set(DISPLAYSERVER_H "${CMAKE_BINARY_DIR}/include/dynamic/displayservers.h")
|
||||||
@ -18,6 +18,16 @@ function(add_displayserver name)
|
|||||||
add_subdirectory(${name})
|
add_subdirectory(${name})
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
option(ENABLE_X11 "Build with X11 support" ON)
|
||||||
|
add_feature_info(ENABLE_X11 ENABLE_X11 "X11 support.")
|
||||||
|
|
||||||
|
option(ENABLE_WAYLAND "Build with Wayland support" ON)
|
||||||
|
add_feature_info(ENABLE_WAYLAND ENABLE_WAYLAND "Wayland support.")
|
||||||
|
|
||||||
|
if (NOT ENABLE_X11 AND NOT ENABLE_WAYLAND)
|
||||||
|
message(FATAL_ERROR "Either ENABLE_X11 or ENABLE_WAYLAND must be on")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Add/remove displayservers here!
|
# Add/remove displayservers here!
|
||||||
if (ENABLE_WAYLAND)
|
if (ENABLE_WAYLAND)
|
||||||
add_displayserver(Wayland)
|
add_displayserver(Wayland)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(displayserver_Wayland LANGUAGES C)
|
project(displayserver_Wayland LANGUAGES C)
|
||||||
|
|
||||||
find_package(PkgConfig)
|
find_package(PkgConfig)
|
||||||
@ -8,19 +8,7 @@ pkg_check_modules(DISPLAYSERVER_Wayland REQUIRED IMPORTED_TARGET
|
|||||||
xkbcommon
|
xkbcommon
|
||||||
)
|
)
|
||||||
|
|
||||||
set(DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES "")
|
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
|
||||||
set(displayserver_Wayland_SHELL_SRC "")
|
|
||||||
|
|
||||||
if (ENABLE_LIBDECOR)
|
|
||||||
pkg_check_modules(DISPLAYSERVER_Wayland_LIBDECOR REQUIRED IMPORTED_TARGET
|
|
||||||
libdecor-0
|
|
||||||
)
|
|
||||||
list(APPEND DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES PkgConfig::DISPLAYSERVER_Wayland_LIBDECOR)
|
|
||||||
list(APPEND displayserver_Wayland_SHELL_SRC shell_libdecor.c)
|
|
||||||
add_compile_definitions(ENABLE_LIBDECOR)
|
|
||||||
else()
|
|
||||||
list(APPEND displayserver_Wayland_SHELL_SRC shell_xdg.c)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(displayserver_Wayland STATIC
|
add_library(displayserver_Wayland STATIC
|
||||||
activation.c
|
activation.c
|
||||||
@ -36,66 +24,19 @@ add_library(displayserver_Wayland STATIC
|
|||||||
registry.c
|
registry.c
|
||||||
wayland.c
|
wayland.c
|
||||||
window.c
|
window.c
|
||||||
${displayserver_Wayland_SHELL_SRC}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_subdirectory(protocol)
|
||||||
|
add_subdirectory(desktops)
|
||||||
|
|
||||||
target_link_libraries(displayserver_Wayland
|
target_link_libraries(displayserver_Wayland
|
||||||
PkgConfig::DISPLAYSERVER_Wayland
|
PkgConfig::DISPLAYSERVER_Wayland
|
||||||
${DISPLAYSERVER_Wayland_OPT_PKGCONFIG_LIBRARIES}
|
|
||||||
lg_common
|
lg_common
|
||||||
|
wayland_protocol
|
||||||
|
wayland_desktops
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(displayserver_Wayland
|
target_include_directories(displayserver_Wayland
|
||||||
PRIVATE
|
PRIVATE
|
||||||
src
|
.
|
||||||
)
|
)
|
||||||
|
|
||||||
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
|
|
||||||
|
|
||||||
macro(wayland_generate protocol_file output_file)
|
|
||||||
add_custom_command(OUTPUT "${output_file}.h"
|
|
||||||
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" client-header "${protocol_file}" "${output_file}.h"
|
|
||||||
DEPENDS "${protocol_file}"
|
|
||||||
VERBATIM)
|
|
||||||
|
|
||||||
add_custom_command(OUTPUT "${output_file}.c"
|
|
||||||
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${protocol_file}" "${output_file}.c"
|
|
||||||
DEPENDS "${protocol_file}"
|
|
||||||
VERBATIM)
|
|
||||||
|
|
||||||
target_sources(displayserver_Wayland PRIVATE "${output_file}.h" "${output_file}.c")
|
|
||||||
endmacro()
|
|
||||||
|
|
||||||
set(WAYLAND_PROTOCOLS_BASE "${PROJECT_TOP}/repos/wayland-protocols")
|
|
||||||
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/wayland")
|
|
||||||
include_directories("${CMAKE_BINARY_DIR}/wayland")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-shell-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/stable/presentation-time/presentation-time.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-presentation-time-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/stable/viewporter/viewporter.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-viewporter-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-decoration-unstable-v1-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/relative-pointer/relative-pointer-unstable-v1.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-relative-pointer-unstable-v1-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-pointer-constraints-unstable-v1-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-idle-inhibit-unstable-v1-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-output/xdg-output-unstable-v1.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-output-unstable-v1-client-protocol")
|
|
||||||
wayland_generate(
|
|
||||||
"${WAYLAND_PROTOCOLS_BASE}/staging/xdg-activation/xdg-activation-v1.xml"
|
|
||||||
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-activation-v1-client-protocol")
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -475,6 +475,7 @@ void waylandCBRequest(LG_ClipboardData type)
|
|||||||
close(data->fd);
|
close(data->fd);
|
||||||
free(data->buf);
|
free(data->buf);
|
||||||
free(data);
|
free(data);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlCb.currentRead = data;
|
wlCb.currentRead = data;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
58
client/displayservers/Wayland/desktops/CMakeLists.txt
Normal file
58
client/displayservers/Wayland/desktops/CMakeLists.txt
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
project(wayland_desktops LANGUAGES C)
|
||||||
|
|
||||||
|
set(DESKTOP_H "${CMAKE_BINARY_DIR}/include/dynamic/wayland_desktops.h")
|
||||||
|
set(DESKTOP_C "${CMAKE_BINARY_DIR}/src/wayland_desktops.c")
|
||||||
|
|
||||||
|
file(WRITE ${DESKTOP_H} "#include \"interface/desktop.h\"\n\n")
|
||||||
|
file(APPEND ${DESKTOP_H} "extern struct WL_DesktopOps * WL_Desktops[];\n\n")
|
||||||
|
|
||||||
|
file(WRITE ${DESKTOP_C} "#include \"interface/desktop.h\"\n\n")
|
||||||
|
file(APPEND ${DESKTOP_C} "#include <stddef.h>\n\n")
|
||||||
|
|
||||||
|
set(DESKTOPS "_")
|
||||||
|
set(DESKTOPS_LINK "_")
|
||||||
|
function(add_desktop name)
|
||||||
|
set(DESKTOPS "${DESKTOPS};${name}" PARENT_SCOPE)
|
||||||
|
set(DESKTOPS_LINK "${DESKTOPS_LINK};wayland_desktop_${name}" PARENT_SCOPE)
|
||||||
|
add_subdirectory(${name})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Add/remove desktops here!
|
||||||
|
|
||||||
|
# the first entry here is the default
|
||||||
|
add_desktop(xdg)
|
||||||
|
|
||||||
|
pkg_check_modules(LIBDECOR IMPORTED_TARGET libdecor-0)
|
||||||
|
if(LIBDECOR_FOUND)
|
||||||
|
option(ENABLE_LIBDECOR "Build with libdecor support" ON)
|
||||||
|
else()
|
||||||
|
option(ENABLE_LIBDECOR "Build with libdecor support" OFF)
|
||||||
|
endif()
|
||||||
|
add_feature_info(ENABLE_LIBDECOR ENABLE_LIBDECOR "libdecor support.")
|
||||||
|
if (ENABLE_LIBDECOR)
|
||||||
|
add_desktop(libdecor)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(REMOVE_AT DESKTOPS 0)
|
||||||
|
list(REMOVE_AT DESKTOPS_LINK 0)
|
||||||
|
|
||||||
|
list(LENGTH DESKTOPS DESKTOP_COUNT)
|
||||||
|
file(APPEND ${DESKTOP_H} "#define WL_DESKTOP_COUNT ${DESKTOP_COUNT}\n")
|
||||||
|
|
||||||
|
foreach(desktop ${DESKTOPS})
|
||||||
|
file(APPEND ${DESKTOP_C} "extern struct WL_DesktopOps WLD_${desktop};\n")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
file(APPEND ${DESKTOP_C} "\nconst struct WL_DesktopOps * WL_Desktops[] =\n{\n")
|
||||||
|
foreach(desktop ${DESKTOPS})
|
||||||
|
file(APPEND ${DESKTOP_C} " &WLD_${desktop},\n")
|
||||||
|
endforeach()
|
||||||
|
file(APPEND ${DESKTOP_C} " NULL\n};")
|
||||||
|
|
||||||
|
add_library(wayland_desktops STATIC ${DESKTOP_C})
|
||||||
|
target_link_libraries(wayland_desktops ${DESKTOPS_LINK})
|
||||||
|
target_include_directories(wayland_desktops
|
||||||
|
PRIVATE
|
||||||
|
../
|
||||||
|
)
|
@ -0,0 +1,16 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
project(wayland_desktop_libdecor LANGUAGES C)
|
||||||
|
|
||||||
|
add_library(wayland_desktop_libdecor STATIC
|
||||||
|
libdecor.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(wayland_desktop_libdecor
|
||||||
|
lg_common
|
||||||
|
wayland_protocol
|
||||||
|
PkgConfig::LIBDECOR
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
"../../"
|
||||||
|
)
|
275
client/displayservers/Wayland/desktops/libdecor/libdecor.c
Normal file
275
client/displayservers/Wayland/desktops/libdecor/libdecor.c
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "interface/desktop.h"
|
||||||
|
#include "wayland-xdg-shell-client-protocol.h"
|
||||||
|
|
||||||
|
#include "wayland.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
|
||||||
|
#include <libdecor.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include "app.h"
|
||||||
|
#include "common/debug.h"
|
||||||
|
|
||||||
|
// Maximum number of fds we can process at once in waylandWait
|
||||||
|
#define MAX_EPOLL_EVENTS 10
|
||||||
|
|
||||||
|
typedef struct LibDecorState
|
||||||
|
{
|
||||||
|
bool configured;
|
||||||
|
struct libdecor * libdecor;
|
||||||
|
struct libdecor_frame * libdecorFrame;
|
||||||
|
|
||||||
|
int32_t width, height;
|
||||||
|
bool needsResize;
|
||||||
|
bool fullscreen;
|
||||||
|
uint32_t resizeSerial;
|
||||||
|
}
|
||||||
|
LibDecorState;
|
||||||
|
|
||||||
|
static LibDecorState state = {0};
|
||||||
|
|
||||||
|
struct libdecor_configuration
|
||||||
|
{
|
||||||
|
uint32_t serial;
|
||||||
|
|
||||||
|
bool has_window_state;
|
||||||
|
enum libdecor_window_state window_state;
|
||||||
|
|
||||||
|
bool has_size;
|
||||||
|
int window_width;
|
||||||
|
int window_height;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void libdecorHandleError(struct libdecor * context, enum libdecor_error error,
|
||||||
|
const char *message)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Got libdecor error (%d): %s", error, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecorFrameConfigure(struct libdecor_frame * frame,
|
||||||
|
struct libdecor_configuration * configuration, void * opaque)
|
||||||
|
{
|
||||||
|
if (!state.configured)
|
||||||
|
{
|
||||||
|
xdg_surface_ack_configure(libdecor_frame_get_xdg_surface(frame), configuration->serial);
|
||||||
|
state.configured = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
if (libdecor_configuration_get_content_size(configuration, frame, &width, &height))
|
||||||
|
{
|
||||||
|
state.width = width;
|
||||||
|
state.height = height;
|
||||||
|
|
||||||
|
struct libdecor_state * s = libdecor_state_new(width, height);
|
||||||
|
libdecor_frame_commit(state.libdecorFrame, s, NULL);
|
||||||
|
libdecor_state_free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum libdecor_window_state windowState;
|
||||||
|
if (libdecor_configuration_get_window_state(configuration, &windowState))
|
||||||
|
state.fullscreen = windowState & LIBDECOR_WINDOW_STATE_FULLSCREEN;
|
||||||
|
|
||||||
|
state.resizeSerial = configuration->serial;
|
||||||
|
waylandNeedsResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecorFrameClose(struct libdecor_frame * frame, void * opaque)
|
||||||
|
{
|
||||||
|
app_handleCloseEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecorFrameCommit(struct libdecor_frame * frame, void * opaque)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||||
|
static struct libdecor_interface libdecorListener =
|
||||||
|
{
|
||||||
|
libdecorHandleError,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct libdecor_frame_interface libdecorFrameListener =
|
||||||
|
{
|
||||||
|
libdecorFrameConfigure,
|
||||||
|
libdecorFrameClose,
|
||||||
|
libdecorFrameCommit,
|
||||||
|
};
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
static void libdecorCallback(uint32_t events, void * opaque)
|
||||||
|
{
|
||||||
|
libdecor_dispatch(state.libdecor, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool libdecor_shellInit(
|
||||||
|
struct wl_display * display, struct wl_surface * surface,
|
||||||
|
const char * title, const char * appId, bool fullscreen,
|
||||||
|
bool maximize, bool borderless, bool resizable)
|
||||||
|
{
|
||||||
|
state.libdecor = libdecor_new(display, &libdecorListener);
|
||||||
|
state.libdecorFrame = libdecor_decorate(state.libdecor, surface,
|
||||||
|
&libdecorFrameListener, NULL);
|
||||||
|
|
||||||
|
libdecor_frame_set_app_id(state.libdecorFrame, appId);
|
||||||
|
libdecor_frame_set_title(state.libdecorFrame, title);
|
||||||
|
libdecor_frame_map(state.libdecorFrame);
|
||||||
|
|
||||||
|
if (fullscreen)
|
||||||
|
libdecor_frame_set_fullscreen(state.libdecorFrame, NULL);
|
||||||
|
|
||||||
|
if (maximize)
|
||||||
|
libdecor_frame_set_maximized(state.libdecorFrame);
|
||||||
|
|
||||||
|
if (resizable)
|
||||||
|
libdecor_frame_set_capabilities(state.libdecorFrame,
|
||||||
|
LIBDECOR_ACTION_RESIZE);
|
||||||
|
else
|
||||||
|
libdecor_frame_unset_capabilities(state.libdecorFrame,
|
||||||
|
LIBDECOR_ACTION_RESIZE);
|
||||||
|
|
||||||
|
while (!state.configured)
|
||||||
|
libdecor_dispatch(state.libdecor, 0);
|
||||||
|
|
||||||
|
if (!waylandPollRegister(libdecor_get_fd(state.libdecor),
|
||||||
|
libdecorCallback, NULL, EPOLLIN))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecor_shellAckConfigureIfNeeded(void)
|
||||||
|
{
|
||||||
|
if (state.resizeSerial)
|
||||||
|
{
|
||||||
|
xdg_surface_ack_configure(
|
||||||
|
libdecor_frame_get_xdg_surface(state.libdecorFrame), state.resizeSerial);
|
||||||
|
state.resizeSerial = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecor_setFullscreen(bool fs)
|
||||||
|
{
|
||||||
|
if (fs)
|
||||||
|
libdecor_frame_set_fullscreen(state.libdecorFrame, NULL);
|
||||||
|
else
|
||||||
|
libdecor_frame_unset_fullscreen(state.libdecorFrame);
|
||||||
|
|
||||||
|
libdecor_frame_set_visibility(state.libdecorFrame, !fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool libdecor_getFullscreen(void)
|
||||||
|
{
|
||||||
|
return state.fullscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecor_minimize(void)
|
||||||
|
{
|
||||||
|
libdecor_frame_set_minimized(state.libdecorFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecor_shellResize(int w, int h)
|
||||||
|
{
|
||||||
|
if (!libdecor_frame_is_floating(state.libdecorFrame))
|
||||||
|
return;
|
||||||
|
|
||||||
|
state.width = w;
|
||||||
|
state.height = h;
|
||||||
|
|
||||||
|
struct libdecor_state * s = libdecor_state_new(w, h);
|
||||||
|
libdecor_frame_commit(state.libdecorFrame, s, NULL);
|
||||||
|
libdecor_state_free(s);
|
||||||
|
|
||||||
|
waylandNeedsResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecor_setSize(int w, int h)
|
||||||
|
{
|
||||||
|
state.width = w;
|
||||||
|
state.height = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void libdecor_getSize(int * w, int * h)
|
||||||
|
{
|
||||||
|
*w = state.width;
|
||||||
|
*h = state.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool libdecor_registryGlobalHandler(void * data,
|
||||||
|
struct wl_registry * registry, uint32_t name, const char * interface,
|
||||||
|
uint32_t version)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool libdecor_pollInit(struct wl_display * display)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void libdecor_pollWait(struct wl_display * display, int epollFd,
|
||||||
|
unsigned int time)
|
||||||
|
{
|
||||||
|
libdecor_dispatch(state.libdecor, 0);
|
||||||
|
|
||||||
|
struct epoll_event events[MAX_EPOLL_EVENTS];
|
||||||
|
int count;
|
||||||
|
if ((count = epoll_wait(epollFd, events, MAX_EPOLL_EVENTS, time)) < 0)
|
||||||
|
{
|
||||||
|
if (errno != EINTR)
|
||||||
|
DEBUG_INFO("epoll failed: %s", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
struct WaylandPoll * poll = events[i].data.ptr;
|
||||||
|
if (!poll->removed)
|
||||||
|
poll->callback(events[i].events, poll->opaque);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WL_DesktopOps WLD_libdecor =
|
||||||
|
{
|
||||||
|
.name = "libdecor",
|
||||||
|
.compositor = "gnome-shell",
|
||||||
|
.shellInit = libdecor_shellInit,
|
||||||
|
.shellAckConfigureIfNeeded = libdecor_shellAckConfigureIfNeeded,
|
||||||
|
.setFullscreen = libdecor_setFullscreen,
|
||||||
|
.getFullscreen = libdecor_getFullscreen,
|
||||||
|
.minimize = libdecor_minimize,
|
||||||
|
.shellResize = libdecor_shellResize,
|
||||||
|
.setSize = libdecor_setSize,
|
||||||
|
.getSize = libdecor_getSize,
|
||||||
|
.registryGlobalHandler = libdecor_registryGlobalHandler,
|
||||||
|
.pollInit = libdecor_pollInit,
|
||||||
|
.pollWait = libdecor_pollWait
|
||||||
|
};
|
15
client/displayservers/Wayland/desktops/xdg/CMakeLists.txt
Normal file
15
client/displayservers/Wayland/desktops/xdg/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
project(wayland_desktop_xdg LANGUAGES C)
|
||||||
|
|
||||||
|
add_library(wayland_desktop_xdg STATIC
|
||||||
|
xdg.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(wayland_desktop_xdg
|
||||||
|
lg_common
|
||||||
|
wayland_protocol
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
"../../"
|
||||||
|
)
|
314
client/displayservers/Wayland/desktops/xdg/xdg.c
Normal file
314
client/displayservers/Wayland/desktops/xdg/xdg.c
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "wayland.h"
|
||||||
|
#include "wayland-xdg-shell-client-protocol.h"
|
||||||
|
#include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include "app.h"
|
||||||
|
#include "common/debug.h"
|
||||||
|
|
||||||
|
// Maximum number of fds we can process at once in waylandWait
|
||||||
|
#define MAX_EPOLL_EVENTS 10
|
||||||
|
|
||||||
|
typedef struct XDGState
|
||||||
|
{
|
||||||
|
bool configured;
|
||||||
|
|
||||||
|
struct xdg_wm_base * wmBase;
|
||||||
|
struct xdg_surface * surface;
|
||||||
|
struct xdg_toplevel * toplevel;
|
||||||
|
struct zxdg_decoration_manager_v1 * decorationManager;
|
||||||
|
struct zxdg_toplevel_decoration_v1 * toplevelDecoration;
|
||||||
|
|
||||||
|
int32_t width, height;
|
||||||
|
uint32_t resizeSerial;
|
||||||
|
bool fullscreen;
|
||||||
|
bool floating;
|
||||||
|
int displayFd;
|
||||||
|
}
|
||||||
|
XDGState;
|
||||||
|
|
||||||
|
static XDGState state = {0};
|
||||||
|
|
||||||
|
// XDG WM base listeners.
|
||||||
|
|
||||||
|
static void xdgWmBasePing(void * data, struct xdg_wm_base * xdgWmBase, uint32_t serial)
|
||||||
|
{
|
||||||
|
xdg_wm_base_pong(xdgWmBase, serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct xdg_wm_base_listener xdgWmBaseListener = {
|
||||||
|
.ping = xdgWmBasePing,
|
||||||
|
};
|
||||||
|
|
||||||
|
// XDG Surface listeners.
|
||||||
|
|
||||||
|
static void xdgSurfaceConfigure(void * data, struct xdg_surface * xdgSurface,
|
||||||
|
uint32_t serial)
|
||||||
|
{
|
||||||
|
if (state.configured)
|
||||||
|
{
|
||||||
|
state.resizeSerial = serial;
|
||||||
|
waylandNeedsResize();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
xdg_surface_ack_configure(xdgSurface, serial);
|
||||||
|
state.configured = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct xdg_surface_listener xdgSurfaceListener = {
|
||||||
|
.configure = xdgSurfaceConfigure,
|
||||||
|
};
|
||||||
|
|
||||||
|
// XDG Toplevel listeners.
|
||||||
|
|
||||||
|
static void xdgToplevelConfigure(void * data, struct xdg_toplevel * xdgToplevel,
|
||||||
|
int32_t width, int32_t height, struct wl_array * states)
|
||||||
|
{
|
||||||
|
state.width = width;
|
||||||
|
state.height = height;
|
||||||
|
state.fullscreen = false;
|
||||||
|
state.floating = true;
|
||||||
|
|
||||||
|
enum xdg_toplevel_state * s;
|
||||||
|
wl_array_for_each(s, states)
|
||||||
|
{
|
||||||
|
switch (*s)
|
||||||
|
{
|
||||||
|
case XDG_TOPLEVEL_STATE_FULLSCREEN:
|
||||||
|
state.fullscreen = true;
|
||||||
|
// fallthrough
|
||||||
|
case XDG_TOPLEVEL_STATE_MAXIMIZED:
|
||||||
|
case XDG_TOPLEVEL_STATE_TILED_LEFT:
|
||||||
|
case XDG_TOPLEVEL_STATE_TILED_RIGHT:
|
||||||
|
case XDG_TOPLEVEL_STATE_TILED_TOP:
|
||||||
|
case XDG_TOPLEVEL_STATE_TILED_BOTTOM:
|
||||||
|
state.floating = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdgToplevelClose(void * data, struct xdg_toplevel * xdgToplevel)
|
||||||
|
{
|
||||||
|
app_handleCloseEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct xdg_toplevel_listener xdgToplevelListener = {
|
||||||
|
.configure = xdgToplevelConfigure,
|
||||||
|
.close = xdgToplevelClose,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool xdg_shellInit(struct wl_display * display, struct wl_surface * surface,
|
||||||
|
const char * title, const char * appId, bool fullscreen, bool maximize, bool borderless,
|
||||||
|
bool resizable)
|
||||||
|
{
|
||||||
|
if (!state.wmBase)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Compositor missing xdg_wm_base, will not proceed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
xdg_wm_base_add_listener(state.wmBase, &xdgWmBaseListener, NULL);
|
||||||
|
|
||||||
|
state.surface = xdg_wm_base_get_xdg_surface(state.wmBase, surface);
|
||||||
|
xdg_surface_add_listener(state.surface, &xdgSurfaceListener, NULL);
|
||||||
|
|
||||||
|
state.toplevel = xdg_surface_get_toplevel(state.surface);
|
||||||
|
xdg_toplevel_add_listener(state.toplevel, &xdgToplevelListener, NULL);
|
||||||
|
xdg_toplevel_set_title(state.toplevel, title);
|
||||||
|
xdg_toplevel_set_app_id(state.toplevel, appId);
|
||||||
|
|
||||||
|
if (fullscreen)
|
||||||
|
xdg_toplevel_set_fullscreen(state.toplevel, NULL);
|
||||||
|
|
||||||
|
if (maximize)
|
||||||
|
xdg_toplevel_set_maximized(state.toplevel);
|
||||||
|
|
||||||
|
if (state.decorationManager)
|
||||||
|
{
|
||||||
|
state.toplevelDecoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
|
||||||
|
state.decorationManager, state.toplevel);
|
||||||
|
if (state.toplevelDecoration)
|
||||||
|
{
|
||||||
|
zxdg_toplevel_decoration_v1_set_mode(state.toplevelDecoration,
|
||||||
|
borderless ?
|
||||||
|
ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE :
|
||||||
|
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_shellAckConfigureIfNeeded(void)
|
||||||
|
{
|
||||||
|
if (state.resizeSerial)
|
||||||
|
{
|
||||||
|
xdg_surface_ack_configure(state.surface, state.resizeSerial);
|
||||||
|
state.resizeSerial = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_setFullscreen(bool fs)
|
||||||
|
{
|
||||||
|
if (fs)
|
||||||
|
xdg_toplevel_set_fullscreen(state.toplevel, NULL);
|
||||||
|
else
|
||||||
|
xdg_toplevel_unset_fullscreen(state.toplevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool xdg_getFullscreen(void)
|
||||||
|
{
|
||||||
|
return state.fullscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_minimize(void)
|
||||||
|
{
|
||||||
|
xdg_toplevel_set_minimized(state.toplevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_shellResize(int w, int h)
|
||||||
|
{
|
||||||
|
if (!state.floating)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state.width = w;
|
||||||
|
state.height = h;
|
||||||
|
xdg_surface_set_window_geometry(state.surface, 0, 0, w, h);
|
||||||
|
|
||||||
|
waylandNeedsResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_setSize(int w, int h)
|
||||||
|
{
|
||||||
|
state.width = w;
|
||||||
|
state.height = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_getSize(int * w, int * h)
|
||||||
|
{
|
||||||
|
*w = state.width;
|
||||||
|
*h = state.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool xdg_registryGlobalHandler(void * data,
|
||||||
|
struct wl_registry * registry, uint32_t name, const char * interface,
|
||||||
|
uint32_t version)
|
||||||
|
{
|
||||||
|
if (!strcmp(interface, xdg_wm_base_interface.name))
|
||||||
|
{
|
||||||
|
state.wmBase = wl_registry_bind(registry, name,
|
||||||
|
&xdg_wm_base_interface, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name))
|
||||||
|
{
|
||||||
|
state.decorationManager = wl_registry_bind(registry, name,
|
||||||
|
&zxdg_decoration_manager_v1_interface, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void waylandDisplayCallback(uint32_t events, void * opaque)
|
||||||
|
{
|
||||||
|
struct wl_display * display = (struct wl_display *)opaque;
|
||||||
|
if (events & EPOLLERR)
|
||||||
|
wl_display_cancel_read(display);
|
||||||
|
else
|
||||||
|
wl_display_read_events(display);
|
||||||
|
wl_display_dispatch_pending(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool xdg_pollInit(struct wl_display * display)
|
||||||
|
{
|
||||||
|
state.displayFd = wl_display_get_fd(display);
|
||||||
|
if (!waylandPollRegister(state.displayFd, waylandDisplayCallback,
|
||||||
|
display, EPOLLIN))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xdg_pollWait(struct wl_display * display, int epollFd,
|
||||||
|
unsigned int time)
|
||||||
|
{
|
||||||
|
while (wl_display_prepare_read(display))
|
||||||
|
wl_display_dispatch_pending(display);
|
||||||
|
wl_display_flush(display);
|
||||||
|
|
||||||
|
struct epoll_event events[MAX_EPOLL_EVENTS];
|
||||||
|
int count;
|
||||||
|
if ((count = epoll_wait(epollFd, events, MAX_EPOLL_EVENTS, time)) < 0)
|
||||||
|
{
|
||||||
|
if (errno != EINTR)
|
||||||
|
DEBUG_INFO("epoll failed: %s", strerror(errno));
|
||||||
|
wl_display_cancel_read(display);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sawDisplay = false;
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
struct WaylandPoll * poll = events[i].data.ptr;
|
||||||
|
if (!poll->removed)
|
||||||
|
poll->callback(events[i].events, poll->opaque);
|
||||||
|
if (poll->fd == state.displayFd)
|
||||||
|
sawDisplay = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sawDisplay)
|
||||||
|
wl_display_cancel_read(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
WL_DesktopOps WLD_xdg =
|
||||||
|
{
|
||||||
|
.name = "xdg",
|
||||||
|
.compositor = "",
|
||||||
|
.shellInit = xdg_shellInit,
|
||||||
|
.shellAckConfigureIfNeeded = xdg_shellAckConfigureIfNeeded,
|
||||||
|
.setFullscreen = xdg_setFullscreen,
|
||||||
|
.getFullscreen = xdg_getFullscreen,
|
||||||
|
.minimize = xdg_minimize,
|
||||||
|
.shellResize = xdg_shellResize,
|
||||||
|
.setSize = xdg_setSize,
|
||||||
|
.getSize = xdg_getSize,
|
||||||
|
.registryGlobalHandler = xdg_registryGlobalHandler,
|
||||||
|
.pollInit = xdg_pollInit,
|
||||||
|
.pollWait = xdg_pollWait
|
||||||
|
};
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -89,18 +89,25 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
|
|||||||
if (wlWm.needsResize)
|
if (wlWm.needsResize)
|
||||||
{
|
{
|
||||||
bool skipResize = false;
|
bool skipResize = false;
|
||||||
wl_egl_window_resize(wlWm.eglWindow, wl_fixed_to_int(wlWm.width * wlWm.scale),
|
|
||||||
wl_fixed_to_int(wlWm.height * wlWm.scale), 0, 0);
|
|
||||||
|
|
||||||
if (wlWm.width == 0 || wlWm.height == 0)
|
int width, height;
|
||||||
|
wlWm.desktop->getSize(&width, &height);
|
||||||
|
wl_egl_window_resize(wlWm.eglWindow, wl_fixed_to_int(width * wlWm.scale),
|
||||||
|
wl_fixed_to_int(height * wlWm.scale), 0, 0);
|
||||||
|
|
||||||
|
if (width == 0 || height == 0)
|
||||||
skipResize = true;
|
skipResize = true;
|
||||||
else if (wlWm.fractionalScale)
|
else if (wlWm.fractionalScale)
|
||||||
{
|
{
|
||||||
wl_surface_set_buffer_scale(wlWm.surface, 1);
|
wl_surface_set_buffer_scale(wlWm.surface, 1);
|
||||||
if (!wlWm.viewport)
|
if (!wlWm.viewport)
|
||||||
wlWm.viewport = wp_viewporter_get_viewport(wlWm.viewporter, wlWm.surface);
|
wlWm.viewport = wp_viewporter_get_viewport(wlWm.viewporter, wlWm.surface);
|
||||||
wp_viewport_set_source(wlWm.viewport, 0, 0, wlWm.width * wlWm.scale, wlWm.height * wlWm.scale);
|
wp_viewport_set_source(
|
||||||
wp_viewport_set_destination(wlWm.viewport, wlWm.width, wlWm.height);
|
wlWm.viewport,
|
||||||
|
wl_fixed_from_int(-1), wl_fixed_from_int(-1),
|
||||||
|
wl_fixed_from_int(-1), wl_fixed_from_int(-1)
|
||||||
|
);
|
||||||
|
wp_viewport_set_destination(wlWm.viewport, width, height);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -120,18 +127,18 @@ void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
|
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
|
||||||
wl_region_add(region, 0, 0, wlWm.width, wlWm.height);
|
wl_region_add(region, 0, 0, width, height);
|
||||||
wl_surface_set_opaque_region(wlWm.surface, region);
|
wl_surface_set_opaque_region(wlWm.surface, region);
|
||||||
wl_region_destroy(region);
|
wl_region_destroy(region);
|
||||||
|
|
||||||
app_handleResizeEvent(wlWm.width, wlWm.height, wl_fixed_to_double(wlWm.scale),
|
app_handleResizeEvent(width, height, wl_fixed_to_double(wlWm.scale),
|
||||||
(struct Border) {0, 0, 0, 0});
|
(struct Border) {0, 0, 0, 0});
|
||||||
app_invalidateWindow(true);
|
app_invalidateWindow(true);
|
||||||
waylandStopWaitFrame();
|
waylandStopWaitFrame();
|
||||||
wlWm.needsResize = skipResize;
|
wlWm.needsResize = skipResize;
|
||||||
}
|
}
|
||||||
|
|
||||||
waylandShellAckConfigureIfNeeded();
|
wlWm.desktop->shellAckConfigureIfNeeded();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -209,7 +209,7 @@ done:
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getCharcode(uint32_t key)
|
int waylandGetCharCode(int key)
|
||||||
{
|
{
|
||||||
key += 8; // xkb scancode is evdev scancode + 8
|
key += 8; // xkb scancode is evdev scancode + 8
|
||||||
xkb_keysym_t sym = xkb_state_key_get_one_sym(wlWm.xkbState, key);
|
xkb_keysym_t sym = xkb_state_key_get_one_sym(wlWm.xkbState, key);
|
||||||
@ -232,7 +232,7 @@ static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
|
|||||||
|
|
||||||
uint32_t * key;
|
uint32_t * key;
|
||||||
wl_array_for_each(key, keys)
|
wl_array_for_each(key, keys)
|
||||||
app_handleKeyPress(*key, getCharcode(*key));
|
app_handleKeyPress(*key);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboardLeaveHandler(void * data, struct wl_keyboard * keyboard,
|
static void keyboardLeaveHandler(void * data, struct wl_keyboard * keyboard,
|
||||||
@ -253,9 +253,9 @@ static void keyboardKeyHandler(void * data, struct wl_keyboard * keyboard,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
|
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||||
app_handleKeyPress(key, getCharcode(key));
|
app_handleKeyPress(key);
|
||||||
else
|
else
|
||||||
app_handleKeyRelease(key, getCharcode(key));
|
app_handleKeyRelease(key);
|
||||||
|
|
||||||
if (!wlWm.xkbState || !app_isOverlayMode() || state != WL_KEYBOARD_KEY_STATE_PRESSED)
|
if (!wlWm.xkbState || !app_isOverlayMode() || state != WL_KEYBOARD_KEY_STATE_PRESSED)
|
||||||
return;
|
return;
|
||||||
@ -590,10 +590,13 @@ void waylandWarpPointer(int x, int y, bool exiting)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
wlWm.desktop->getSize(&width, &height);
|
||||||
|
|
||||||
if (x < 0) x = 0;
|
if (x < 0) x = 0;
|
||||||
else if (x >= wlWm.width) x = wlWm.width - 1;
|
else if (x >= width) x = width - 1;
|
||||||
if (y < 0) y = 0;
|
if (y < 0) y = 0;
|
||||||
else if (y >= wlWm.height) y = wlWm.height - 1;
|
else if (y >= height) y = height - 1;
|
||||||
|
|
||||||
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
|
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
|
||||||
wl_region_add(region, x, y, 1, 1);
|
wl_region_add(region, x, y, 1, 1);
|
||||||
|
64
client/displayservers/Wayland/interface/desktop.h
Normal file
64
client/displayservers/Wayland/interface/desktop.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _H_WAYLAND_DESKTOP_H_
|
||||||
|
#define _H_WAYLAND_DESKTOP_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
typedef struct WL_DesktopOps
|
||||||
|
{
|
||||||
|
// the friendly name
|
||||||
|
const char * name;
|
||||||
|
|
||||||
|
// the compositor process name to match
|
||||||
|
const char * compositor;
|
||||||
|
|
||||||
|
bool (*shellInit)(
|
||||||
|
struct wl_display * display, struct wl_surface * surface,
|
||||||
|
const char * title, const char * appId, bool fullscreen, bool maximize,
|
||||||
|
bool borderless, bool resizable);
|
||||||
|
|
||||||
|
void (*shellAckConfigureIfNeeded)(void);
|
||||||
|
|
||||||
|
void (*setFullscreen)(bool fs);
|
||||||
|
|
||||||
|
bool (*getFullscreen)(void);
|
||||||
|
|
||||||
|
void (*minimize)(void);
|
||||||
|
|
||||||
|
void (*shellResize)(int w, int h);
|
||||||
|
|
||||||
|
void (*setSize)(int w, int h);
|
||||||
|
|
||||||
|
void (*getSize)(int * w, int * h);
|
||||||
|
|
||||||
|
bool (*registryGlobalHandler)(
|
||||||
|
void * data, struct wl_registry * registry,
|
||||||
|
uint32_t name, const char * interface, uint32_t version);
|
||||||
|
|
||||||
|
bool (*pollInit)(struct wl_display * display);
|
||||||
|
|
||||||
|
void (*pollWait)(struct wl_display * display, int epollFd, unsigned int time);
|
||||||
|
}
|
||||||
|
WL_DesktopOps;
|
||||||
|
|
||||||
|
#endif
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -30,23 +30,6 @@
|
|||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
#include "common/locking.h"
|
#include "common/locking.h"
|
||||||
|
|
||||||
#ifdef ENABLE_LIBDECOR
|
|
||||||
#include <libdecor.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define EPOLL_EVENTS 10 // Maximum number of fds we can process at once in waylandWait
|
|
||||||
|
|
||||||
#ifndef ENABLE_LIBDECOR
|
|
||||||
static void waylandDisplayCallback(uint32_t events, void * opaque)
|
|
||||||
{
|
|
||||||
if (events & EPOLLERR)
|
|
||||||
wl_display_cancel_read(wlWm.display);
|
|
||||||
else
|
|
||||||
wl_display_read_events(wlWm.display);
|
|
||||||
wl_display_dispatch_pending(wlWm.display);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool waylandPollInit(void)
|
bool waylandPollInit(void)
|
||||||
{
|
{
|
||||||
wlWm.epollFd = epoll_create1(EPOLL_CLOEXEC);
|
wlWm.epollFd = epoll_create1(EPOLL_CLOEXEC);
|
||||||
@ -61,58 +44,12 @@ bool waylandPollInit(void)
|
|||||||
LG_LOCK_INIT(wlWm.pollLock);
|
LG_LOCK_INIT(wlWm.pollLock);
|
||||||
LG_LOCK_INIT(wlWm.pollFreeLock);
|
LG_LOCK_INIT(wlWm.pollFreeLock);
|
||||||
|
|
||||||
#ifndef ENABLE_LIBDECOR
|
return wlWm.desktop->pollInit(wlWm.display);
|
||||||
wlWm.displayFd = wl_display_get_fd(wlWm.display);
|
|
||||||
if (!waylandPollRegister(wlWm.displayFd, waylandDisplayCallback, NULL, EPOLLIN))
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void waylandWait(unsigned int time)
|
void waylandWait(unsigned int time)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_LIBDECOR
|
wlWm.desktop->pollWait(wlWm.display, wlWm.epollFd, time);
|
||||||
libdecor_dispatch(wlWm.libdecor, 0);
|
|
||||||
#else
|
|
||||||
while (wl_display_prepare_read(wlWm.display))
|
|
||||||
wl_display_dispatch_pending(wlWm.display);
|
|
||||||
wl_display_flush(wlWm.display);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct epoll_event events[EPOLL_EVENTS];
|
|
||||||
int count;
|
|
||||||
if ((count = epoll_wait(wlWm.epollFd, events, EPOLL_EVENTS, time)) < 0)
|
|
||||||
{
|
|
||||||
if (errno != EINTR)
|
|
||||||
DEBUG_INFO("epoll failed: %s", strerror(errno));
|
|
||||||
#ifndef ENABLE_LIBDECOR
|
|
||||||
wl_display_cancel_read(wlWm.display);
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef ENABLE_LIBDECOR
|
|
||||||
bool sawDisplay = false;
|
|
||||||
#endif
|
|
||||||
for (int i = 0; i < count; ++i) {
|
|
||||||
struct WaylandPoll * poll = events[i].data.ptr;
|
|
||||||
if (!poll->removed)
|
|
||||||
poll->callback(events[i].events, poll->opaque);
|
|
||||||
#ifndef ENABLE_LIBDECOR
|
|
||||||
if (poll->fd == wlWm.displayFd)
|
|
||||||
sawDisplay = true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef ENABLE_LIBDECOR
|
|
||||||
if (!sawDisplay)
|
|
||||||
wl_display_cancel_read(wlWm.display);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
INTERLOCKED_SECTION(wlWm.pollFreeLock,
|
INTERLOCKED_SECTION(wlWm.pollFreeLock,
|
||||||
{
|
{
|
||||||
struct WaylandPoll * node;
|
struct WaylandPoll * node;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -109,10 +109,17 @@ void waylandPresentationFrame(void)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
struct FrameData * data = malloc(sizeof(*data));
|
struct FrameData * data = malloc(sizeof(*data));
|
||||||
|
if (!data)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("out of memory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (clock_gettime(wlWm.clkId, &data->sent))
|
if (clock_gettime(wlWm.clkId, &data->sent))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("clock_gettime failed: %s\n", strerror(errno));
|
DEBUG_ERROR("clock_gettime failed: %s\n", strerror(errno));
|
||||||
free(data);
|
free(data);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wp_presentation_feedback * feedback = wp_presentation_feedback(wlWm.presentation, wlWm.surface);
|
struct wp_presentation_feedback * feedback = wp_presentation_feedback(wlWm.presentation, wlWm.surface);
|
||||||
|
71
client/displayservers/Wayland/protocol/CMakeLists.txt
Normal file
71
client/displayservers/Wayland/protocol/CMakeLists.txt
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
project(wayland_protocol LANGUAGES C)
|
||||||
|
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules(WAYLAND REQUIRED IMPORTED_TARGET
|
||||||
|
wayland-client
|
||||||
|
wayland-cursor
|
||||||
|
xkbcommon
|
||||||
|
)
|
||||||
|
|
||||||
|
find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
|
||||||
|
|
||||||
|
add_library(wayland_protocol STATIC
|
||||||
|
)
|
||||||
|
|
||||||
|
macro(wayland_generate protocol_file output_file)
|
||||||
|
add_custom_command(OUTPUT "${output_file}.h"
|
||||||
|
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" client-header "${protocol_file}" "${output_file}.h"
|
||||||
|
DEPENDS "${protocol_file}"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
add_custom_command(OUTPUT "${output_file}.c"
|
||||||
|
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${protocol_file}" "${output_file}.c"
|
||||||
|
DEPENDS "${protocol_file}"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
target_sources(wayland_protocol PRIVATE "${output_file}.h" "${output_file}.c")
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
set(WAYLAND_PROTOCOLS_BASE "${PROJECT_TOP}/repos/wayland-protocols")
|
||||||
|
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/wayland")
|
||||||
|
include_directories("${CMAKE_BINARY_DIR}/wayland")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/stable/xdg-shell/xdg-shell.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-shell-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/stable/presentation-time/presentation-time.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-presentation-time-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/stable/viewporter/viewporter.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-viewporter-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-decoration-unstable-v1-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/unstable/relative-pointer/relative-pointer-unstable-v1.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-relative-pointer-unstable-v1-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-pointer-constraints-unstable-v1-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-idle-inhibit-unstable-v1-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/unstable/xdg-output/xdg-output-unstable-v1.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-output-unstable-v1-client-protocol")
|
||||||
|
wayland_generate(
|
||||||
|
"${WAYLAND_PROTOCOLS_BASE}/staging/xdg-activation/xdg-activation-v1.xml"
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland/wayland-xdg-activation-v1-client-protocol")
|
||||||
|
|
||||||
|
target_link_libraries(wayland_protocol
|
||||||
|
PkgConfig::WAYLAND
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(wayland_protocol
|
||||||
|
PUBLIC
|
||||||
|
"${CMAKE_BINARY_DIR}/wayland"
|
||||||
|
)
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -40,13 +40,6 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
|
|||||||
wlWm.compositor = wl_registry_bind(wlWm.registry, name,
|
wlWm.compositor = wl_registry_bind(wlWm.registry, name,
|
||||||
// we only need v3 to run, but v4 can use eglSwapBuffersWithDamageKHR
|
// we only need v3 to run, but v4 can use eglSwapBuffersWithDamageKHR
|
||||||
&wl_compositor_interface, version > 4 ? 4 : version);
|
&wl_compositor_interface, version > 4 ? 4 : version);
|
||||||
#ifndef ENABLE_LIBDECOR
|
|
||||||
else if (!strcmp(interface, xdg_wm_base_interface.name))
|
|
||||||
wlWm.xdgWmBase = wl_registry_bind(wlWm.registry, name, &xdg_wm_base_interface, 1);
|
|
||||||
else if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name))
|
|
||||||
wlWm.xdgDecorationManager = wl_registry_bind(wlWm.registry, name,
|
|
||||||
&zxdg_decoration_manager_v1_interface, 1);
|
|
||||||
#endif
|
|
||||||
else if (!strcmp(interface, wp_presentation_interface.name))
|
else if (!strcmp(interface, wp_presentation_interface.name))
|
||||||
wlWm.presentation = wl_registry_bind(wlWm.registry, name,
|
wlWm.presentation = wl_registry_bind(wlWm.registry, name,
|
||||||
&wp_presentation_interface, 1);
|
&wp_presentation_interface, 1);
|
||||||
@ -75,6 +68,9 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry,
|
|||||||
else if (!strcmp(interface, xdg_activation_v1_interface.name))
|
else if (!strcmp(interface, xdg_activation_v1_interface.name))
|
||||||
wlWm.xdgActivation = wl_registry_bind(wlWm.registry, name,
|
wlWm.xdgActivation = wl_registry_bind(wlWm.registry, name,
|
||||||
&xdg_activation_v1_interface, 1);
|
&xdg_activation_v1_interface, 1);
|
||||||
|
else if (wlWm.desktop->registryGlobalHandler(
|
||||||
|
data, registry, name, interface, version))
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void registryGlobalRemoveHandler(void * data,
|
static void registryGlobalRemoveHandler(void * data,
|
||||||
|
@ -1,178 +0,0 @@
|
|||||||
/**
|
|
||||||
* Looking Glass
|
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
|
||||||
* https://looking-glass.io
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
|
||||||
* Software Foundation; either version 2 of the License, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
|
||||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "wayland.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/epoll.h>
|
|
||||||
|
|
||||||
#include <libdecor.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
|
|
||||||
#include "app.h"
|
|
||||||
#include "common/debug.h"
|
|
||||||
|
|
||||||
struct libdecor_configuration {
|
|
||||||
uint32_t serial;
|
|
||||||
|
|
||||||
bool has_window_state;
|
|
||||||
enum libdecor_window_state window_state;
|
|
||||||
|
|
||||||
bool has_size;
|
|
||||||
int window_width;
|
|
||||||
int window_height;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void libdecorHandleError(struct libdecor * context, enum libdecor_error error,
|
|
||||||
const char *message)
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Got libdecor error (%d): %s", error, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void libdecorFrameConfigure(struct libdecor_frame * frame,
|
|
||||||
struct libdecor_configuration * configuration, void * opaque)
|
|
||||||
{
|
|
||||||
if (!wlWm.configured)
|
|
||||||
{
|
|
||||||
xdg_surface_ack_configure(libdecor_frame_get_xdg_surface(frame), configuration->serial);
|
|
||||||
wlWm.configured = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int width, height;
|
|
||||||
if (libdecor_configuration_get_content_size(configuration, frame, &width, &height))
|
|
||||||
{
|
|
||||||
wlWm.width = width;
|
|
||||||
wlWm.height = height;
|
|
||||||
|
|
||||||
struct libdecor_state * state = libdecor_state_new(wlWm.width, wlWm.height);
|
|
||||||
libdecor_frame_commit(wlWm.libdecorFrame, state, NULL);
|
|
||||||
libdecor_state_free(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum libdecor_window_state windowState;
|
|
||||||
if (libdecor_configuration_get_window_state(configuration, &windowState))
|
|
||||||
wlWm.fullscreen = windowState & LIBDECOR_WINDOW_STATE_FULLSCREEN;
|
|
||||||
|
|
||||||
wlWm.needsResize = true;
|
|
||||||
wlWm.resizeSerial = configuration->serial;
|
|
||||||
app_invalidateWindow(true);
|
|
||||||
waylandStopWaitFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void libdecorFrameClose(struct libdecor_frame * frame, void * opaque)
|
|
||||||
{
|
|
||||||
app_handleCloseEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void libdecorFrameCommit(struct libdecor_frame * frame, void * opaque)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
|
||||||
static struct libdecor_interface libdecorListener = {
|
|
||||||
libdecorHandleError,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct libdecor_frame_interface libdecorFrameListener = {
|
|
||||||
libdecorFrameConfigure,
|
|
||||||
libdecorFrameClose,
|
|
||||||
libdecorFrameCommit,
|
|
||||||
};
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
|
|
||||||
static void libdecorCallback(uint32_t events, void * opaque)
|
|
||||||
{
|
|
||||||
libdecor_dispatch(wlWm.libdecor, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
|
|
||||||
{
|
|
||||||
wlWm.libdecor = libdecor_new(wlWm.display, &libdecorListener);
|
|
||||||
wlWm.libdecorFrame = libdecor_decorate(wlWm.libdecor, wlWm.surface, &libdecorFrameListener, NULL);
|
|
||||||
|
|
||||||
libdecor_frame_set_app_id(wlWm.libdecorFrame, "looking-glass-client");
|
|
||||||
libdecor_frame_set_title(wlWm.libdecorFrame, title);
|
|
||||||
libdecor_frame_map(wlWm.libdecorFrame);
|
|
||||||
|
|
||||||
if (resizable)
|
|
||||||
libdecor_frame_set_capabilities(wlWm.libdecorFrame, LIBDECOR_ACTION_RESIZE);
|
|
||||||
else
|
|
||||||
libdecor_frame_unset_capabilities(wlWm.libdecorFrame, LIBDECOR_ACTION_RESIZE);
|
|
||||||
|
|
||||||
while (!wlWm.configured)
|
|
||||||
libdecor_dispatch(wlWm.libdecor, 0);
|
|
||||||
|
|
||||||
if (!waylandPollRegister(libdecor_get_fd(wlWm.libdecor), libdecorCallback, NULL, EPOLLIN))
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Failed register display to epoll: %s", strerror(errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandShellAckConfigureIfNeeded(void)
|
|
||||||
{
|
|
||||||
if (wlWm.resizeSerial)
|
|
||||||
{
|
|
||||||
xdg_surface_ack_configure(libdecor_frame_get_xdg_surface(wlWm.libdecorFrame), wlWm.resizeSerial);
|
|
||||||
wlWm.resizeSerial = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandSetFullscreen(bool fs)
|
|
||||||
{
|
|
||||||
if (fs)
|
|
||||||
libdecor_frame_set_fullscreen(wlWm.libdecorFrame, NULL);
|
|
||||||
else
|
|
||||||
libdecor_frame_unset_fullscreen(wlWm.libdecorFrame);
|
|
||||||
|
|
||||||
libdecor_frame_set_visibility(wlWm.libdecorFrame, !fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool waylandGetFullscreen(void)
|
|
||||||
{
|
|
||||||
return wlWm.fullscreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandMinimize(void)
|
|
||||||
{
|
|
||||||
libdecor_frame_set_minimized(wlWm.libdecorFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandShellResize(int w, int h)
|
|
||||||
{
|
|
||||||
if (!libdecor_frame_is_floating(wlWm.libdecorFrame))
|
|
||||||
return;
|
|
||||||
|
|
||||||
wlWm.width = w;
|
|
||||||
wlWm.height = h;
|
|
||||||
|
|
||||||
struct libdecor_state * state = libdecor_state_new(w, h);
|
|
||||||
libdecor_frame_commit(wlWm.libdecorFrame, state, NULL);
|
|
||||||
libdecor_state_free(state);
|
|
||||||
|
|
||||||
wlWm.needsResize = true;
|
|
||||||
app_invalidateWindow(true);
|
|
||||||
waylandStopWaitFrame();
|
|
||||||
}
|
|
@ -1,184 +0,0 @@
|
|||||||
/**
|
|
||||||
* Looking Glass
|
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
|
||||||
* https://looking-glass.io
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by the Free
|
|
||||||
* Software Foundation; either version 2 of the License, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
|
||||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "wayland.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
|
|
||||||
#include "app.h"
|
|
||||||
#include "common/debug.h"
|
|
||||||
|
|
||||||
// XDG WM base listeners.
|
|
||||||
|
|
||||||
static void xdgWmBasePing(void * data, struct xdg_wm_base * xdgWmBase, uint32_t serial)
|
|
||||||
{
|
|
||||||
xdg_wm_base_pong(xdgWmBase, serial);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_wm_base_listener xdgWmBaseListener = {
|
|
||||||
.ping = xdgWmBasePing,
|
|
||||||
};
|
|
||||||
|
|
||||||
// XDG Surface listeners.
|
|
||||||
|
|
||||||
static void xdgSurfaceConfigure(void * data, struct xdg_surface * xdgSurface,
|
|
||||||
uint32_t serial)
|
|
||||||
{
|
|
||||||
if (wlWm.configured)
|
|
||||||
{
|
|
||||||
wlWm.needsResize = true;
|
|
||||||
wlWm.resizeSerial = serial;
|
|
||||||
app_invalidateWindow(true);
|
|
||||||
waylandStopWaitFrame();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xdg_surface_ack_configure(xdgSurface, serial);
|
|
||||||
wlWm.configured = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_surface_listener xdgSurfaceListener = {
|
|
||||||
.configure = xdgSurfaceConfigure,
|
|
||||||
};
|
|
||||||
|
|
||||||
// XDG Toplevel listeners.
|
|
||||||
|
|
||||||
static void xdgToplevelConfigure(void * data, struct xdg_toplevel * xdgToplevel,
|
|
||||||
int32_t width, int32_t height, struct wl_array * states)
|
|
||||||
{
|
|
||||||
wlWm.width = width;
|
|
||||||
wlWm.height = height;
|
|
||||||
wlWm.fullscreen = false;
|
|
||||||
wlWm.floating = true;
|
|
||||||
|
|
||||||
enum xdg_toplevel_state * state;
|
|
||||||
wl_array_for_each(state, states)
|
|
||||||
{
|
|
||||||
switch (*state)
|
|
||||||
{
|
|
||||||
case XDG_TOPLEVEL_STATE_FULLSCREEN:
|
|
||||||
wlWm.fullscreen = true;
|
|
||||||
// fallthrough
|
|
||||||
case XDG_TOPLEVEL_STATE_MAXIMIZED:
|
|
||||||
case XDG_TOPLEVEL_STATE_TILED_LEFT:
|
|
||||||
case XDG_TOPLEVEL_STATE_TILED_RIGHT:
|
|
||||||
case XDG_TOPLEVEL_STATE_TILED_TOP:
|
|
||||||
case XDG_TOPLEVEL_STATE_TILED_BOTTOM:
|
|
||||||
wlWm.floating = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdgToplevelClose(void * data, struct xdg_toplevel * xdgToplevel)
|
|
||||||
{
|
|
||||||
app_handleCloseEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_toplevel_listener xdgToplevelListener = {
|
|
||||||
.configure = xdgToplevelConfigure,
|
|
||||||
.close = xdgToplevelClose,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
|
|
||||||
{
|
|
||||||
if (!wlWm.xdgWmBase)
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Compositor missing xdg_wm_base, will not proceed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
xdg_wm_base_add_listener(wlWm.xdgWmBase, &xdgWmBaseListener, NULL);
|
|
||||||
|
|
||||||
wlWm.xdgSurface = xdg_wm_base_get_xdg_surface(wlWm.xdgWmBase, wlWm.surface);
|
|
||||||
xdg_surface_add_listener(wlWm.xdgSurface, &xdgSurfaceListener, NULL);
|
|
||||||
|
|
||||||
wlWm.xdgToplevel = xdg_surface_get_toplevel(wlWm.xdgSurface);
|
|
||||||
xdg_toplevel_add_listener(wlWm.xdgToplevel, &xdgToplevelListener, NULL);
|
|
||||||
xdg_toplevel_set_title(wlWm.xdgToplevel, title);
|
|
||||||
xdg_toplevel_set_app_id(wlWm.xdgToplevel, "looking-glass-client");
|
|
||||||
|
|
||||||
if (fullscreen)
|
|
||||||
xdg_toplevel_set_fullscreen(wlWm.xdgToplevel, NULL);
|
|
||||||
|
|
||||||
if (maximize)
|
|
||||||
xdg_toplevel_set_maximized(wlWm.xdgToplevel);
|
|
||||||
|
|
||||||
if (wlWm.xdgDecorationManager)
|
|
||||||
{
|
|
||||||
wlWm.xdgToplevelDecoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
|
|
||||||
wlWm.xdgDecorationManager, wlWm.xdgToplevel);
|
|
||||||
if (wlWm.xdgToplevelDecoration)
|
|
||||||
{
|
|
||||||
zxdg_toplevel_decoration_v1_set_mode(wlWm.xdgToplevelDecoration,
|
|
||||||
borderless ?
|
|
||||||
ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE :
|
|
||||||
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandShellAckConfigureIfNeeded(void)
|
|
||||||
{
|
|
||||||
if (wlWm.resizeSerial)
|
|
||||||
{
|
|
||||||
xdg_surface_ack_configure(wlWm.xdgSurface, wlWm.resizeSerial);
|
|
||||||
wlWm.resizeSerial = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandSetFullscreen(bool fs)
|
|
||||||
{
|
|
||||||
if (fs)
|
|
||||||
xdg_toplevel_set_fullscreen(wlWm.xdgToplevel, NULL);
|
|
||||||
else
|
|
||||||
xdg_toplevel_unset_fullscreen(wlWm.xdgToplevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool waylandGetFullscreen(void)
|
|
||||||
{
|
|
||||||
return wlWm.fullscreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandMinimize(void)
|
|
||||||
{
|
|
||||||
xdg_toplevel_set_minimized(wlWm.xdgToplevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
void waylandShellResize(int w, int h)
|
|
||||||
{
|
|
||||||
if (!wlWm.floating)
|
|
||||||
return;
|
|
||||||
|
|
||||||
wlWm.width = w;
|
|
||||||
wlWm.height = h;
|
|
||||||
xdg_surface_set_window_geometry(wlWm.xdgSurface, 0, 0, w, h);
|
|
||||||
|
|
||||||
wlWm.needsResize = true;
|
|
||||||
app_invalidateWindow(true);
|
|
||||||
waylandStopWaitFrame();
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -24,10 +24,13 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
#include "common/option.h"
|
#include "common/option.h"
|
||||||
|
|
||||||
|
#include "dynamic/wayland_desktops.h"
|
||||||
|
|
||||||
static struct Option waylandOptions[] =
|
static struct Option waylandOptions[] =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@ -68,18 +71,69 @@ static bool waylandProbe(void)
|
|||||||
return getenv("WAYLAND_DISPLAY") != NULL;
|
return getenv("WAYLAND_DISPLAY") != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool getCompositor(char * dst, size_t size)
|
||||||
|
{
|
||||||
|
int fd = wl_display_get_fd(wlWm.display);
|
||||||
|
struct ucred ucred;
|
||||||
|
socklen_t len = sizeof(struct ucred);
|
||||||
|
|
||||||
|
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to get the pid of the socket");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char path[64];
|
||||||
|
snprintf(path, sizeof(path), "/proc/%d/comm", ucred.pid);
|
||||||
|
FILE *fp = fopen(path, "r");
|
||||||
|
if (!fp)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to open %s", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fgets(dst, size, fp))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to read %s", path);
|
||||||
|
fclose(fp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
dst[strlen(dst) - 1] = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool waylandInit(const LG_DSInitParams params)
|
static bool waylandInit(const LG_DSInitParams params)
|
||||||
{
|
{
|
||||||
memset(&wlWm, 0, sizeof(wlWm));
|
memset(&wlWm, 0, sizeof(wlWm));
|
||||||
|
wlWm.desktop = WL_Desktops[0];
|
||||||
|
|
||||||
|
wlWm.display = wl_display_connect(NULL);
|
||||||
|
if (!wlWm.display)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// select the desktop interface based on the compositor process name
|
||||||
|
char compositor[1024];
|
||||||
|
if (getCompositor(compositor, sizeof(compositor)))
|
||||||
|
{
|
||||||
|
DEBUG_INFO("Compositor: %s", compositor);
|
||||||
|
for(int i = 0; i < WL_DESKTOP_COUNT; ++i)
|
||||||
|
if (strcmp(WL_Desktops[i]->compositor, compositor) == 0)
|
||||||
|
{
|
||||||
|
wlWm.desktop = WL_Desktops[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DEBUG_WARN("Compositor: UNKNOWN");
|
||||||
|
DEBUG_INFO("Selected : %s", wlWm.desktop->name);
|
||||||
|
|
||||||
wl_list_init(&wlWm.surfaceOutputs);
|
wl_list_init(&wlWm.surfaceOutputs);
|
||||||
|
|
||||||
wlWm.warpSupport = option_get_bool("wayland", "warpSupport");
|
wlWm.warpSupport = option_get_bool("wayland", "warpSupport");
|
||||||
wlWm.useFractionalScale = option_get_bool("wayland", "fractionScale");
|
wlWm.useFractionalScale = option_get_bool("wayland", "fractionScale");
|
||||||
|
|
||||||
wlWm.display = wl_display_connect(NULL);
|
|
||||||
wlWm.width = params.w;
|
|
||||||
wlWm.height = params.h;
|
|
||||||
|
|
||||||
if (!waylandPollInit())
|
if (!waylandPollInit())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -104,7 +158,9 @@ static bool waylandInit(const LG_DSInitParams params)
|
|||||||
if (!waylandInputInit())
|
if (!waylandInputInit())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!waylandWindowInit(params.title, params.fullscreen, params.maximize, params.borderless, params.resizable))
|
wlWm.desktop->setSize(params.w, params.h);
|
||||||
|
if (!waylandWindowInit(params.title, params.appId, params.fullscreen, params.maximize,
|
||||||
|
params.borderless, params.resizable))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!waylandEGLInit(params.w, params.h))
|
if (!waylandEGLInit(params.w, params.h))
|
||||||
@ -115,9 +171,6 @@ static bool waylandInit(const LG_DSInitParams params)
|
|||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
wlWm.width = params.w;
|
|
||||||
wlWm.height = params.h;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,6 +205,28 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void waylandNeedsResize(void)
|
||||||
|
{
|
||||||
|
wlWm.needsResize = true;
|
||||||
|
app_invalidateWindow(true);
|
||||||
|
waylandStopWaitFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void waylandSetFullscreen(bool fs)
|
||||||
|
{
|
||||||
|
wlWm.desktop->setFullscreen(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool waylandGetFullscreen(void)
|
||||||
|
{
|
||||||
|
return wlWm.desktop->getFullscreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void waylandMinimize(void)
|
||||||
|
{
|
||||||
|
wlWm.desktop->minimize();
|
||||||
|
}
|
||||||
|
|
||||||
struct LG_DisplayServerOps LGDS_Wayland =
|
struct LG_DisplayServerOps LGDS_Wayland =
|
||||||
{
|
{
|
||||||
.name = "Wayland",
|
.name = "Wayland",
|
||||||
@ -188,6 +263,7 @@ struct LG_DisplayServerOps LGDS_Wayland =
|
|||||||
.uncapturePointer = waylandUncapturePointer,
|
.uncapturePointer = waylandUncapturePointer,
|
||||||
.grabKeyboard = waylandGrabKeyboard,
|
.grabKeyboard = waylandGrabKeyboard,
|
||||||
.ungrabKeyboard = waylandUngrabKeyboard,
|
.ungrabKeyboard = waylandUngrabKeyboard,
|
||||||
|
.getCharCode = waylandGetCharCode,
|
||||||
.warpPointer = waylandWarpPointer,
|
.warpPointer = waylandWarpPointer,
|
||||||
.realignPointer = waylandRealignPointer,
|
.realignPointer = waylandRealignPointer,
|
||||||
.isValidPointerPos = waylandIsValidPointerPos,
|
.isValidPointerPos = waylandIsValidPointerPos,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -37,11 +37,10 @@
|
|||||||
#include "common/countedbuffer.h"
|
#include "common/countedbuffer.h"
|
||||||
#include "common/ringbuffer.h"
|
#include "common/ringbuffer.h"
|
||||||
#include "interface/displayserver.h"
|
#include "interface/displayserver.h"
|
||||||
|
#include "interface/desktop.h"
|
||||||
|
|
||||||
#include "wayland-xdg-shell-client-protocol.h"
|
|
||||||
#include "wayland-presentation-time-client-protocol.h"
|
#include "wayland-presentation-time-client-protocol.h"
|
||||||
#include "wayland-viewporter-client-protocol.h"
|
#include "wayland-viewporter-client-protocol.h"
|
||||||
#include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
|
||||||
#include "wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
|
#include "wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
|
||||||
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
|
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
|
||||||
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
|
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
|
||||||
@ -100,6 +99,8 @@ struct WaylandDSState
|
|||||||
bool pointerInSurface;
|
bool pointerInSurface;
|
||||||
bool focusedOnSurface;
|
bool focusedOnSurface;
|
||||||
|
|
||||||
|
WL_DesktopOps * desktop;
|
||||||
|
|
||||||
struct wl_display * display;
|
struct wl_display * display;
|
||||||
struct wl_surface * surface;
|
struct wl_surface * surface;
|
||||||
struct wl_registry * registry;
|
struct wl_registry * registry;
|
||||||
@ -107,13 +108,9 @@ struct WaylandDSState
|
|||||||
struct wl_shm * shm;
|
struct wl_shm * shm;
|
||||||
struct wl_compositor * compositor;
|
struct wl_compositor * compositor;
|
||||||
|
|
||||||
int32_t width, height;
|
|
||||||
wl_fixed_t scale;
|
wl_fixed_t scale;
|
||||||
bool fractionalScale;
|
bool fractionalScale;
|
||||||
bool needsResize;
|
bool needsResize;
|
||||||
bool fullscreen;
|
|
||||||
bool floating;
|
|
||||||
uint32_t resizeSerial;
|
|
||||||
bool configured;
|
bool configured;
|
||||||
bool warpSupport;
|
bool warpSupport;
|
||||||
double cursorX, cursorY;
|
double cursorX, cursorY;
|
||||||
@ -134,17 +131,6 @@ struct WaylandDSState
|
|||||||
RingBuffer photonTimings;
|
RingBuffer photonTimings;
|
||||||
GraphHandle photonGraph;
|
GraphHandle photonGraph;
|
||||||
|
|
||||||
#ifdef ENABLE_LIBDECOR
|
|
||||||
struct libdecor * libdecor;
|
|
||||||
struct libdecor_frame * libdecorFrame;
|
|
||||||
#else
|
|
||||||
struct xdg_wm_base * xdgWmBase;
|
|
||||||
struct xdg_surface * xdgSurface;
|
|
||||||
struct xdg_toplevel * xdgToplevel;
|
|
||||||
struct zxdg_decoration_manager_v1 * xdgDecorationManager;
|
|
||||||
struct zxdg_toplevel_decoration_v1 * xdgToplevelDecoration;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char * cursorThemeName;
|
const char * cursorThemeName;
|
||||||
int cursorSize;
|
int cursorSize;
|
||||||
int cursorScale;
|
int cursorScale;
|
||||||
@ -291,6 +277,7 @@ void waylandUncapturePointer(void);
|
|||||||
void waylandRealignPointer(void);
|
void waylandRealignPointer(void);
|
||||||
void waylandWarpPointer(int x, int y, bool exiting);
|
void waylandWarpPointer(int x, int y, bool exiting);
|
||||||
void waylandGuestPointerUpdated(double x, double y, double localX, double localY);
|
void waylandGuestPointerUpdated(double x, double y, double localX, double localY);
|
||||||
|
int waylandGetCharCode(int key);
|
||||||
|
|
||||||
// output module
|
// output module
|
||||||
bool waylandOutputInit(void);
|
bool waylandOutputInit(void);
|
||||||
@ -314,16 +301,8 @@ void waylandPresentationFree(void);
|
|||||||
bool waylandRegistryInit(void);
|
bool waylandRegistryInit(void);
|
||||||
void waylandRegistryFree(void);
|
void waylandRegistryFree(void);
|
||||||
|
|
||||||
// shell module
|
|
||||||
bool waylandShellInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable);
|
|
||||||
void waylandShellAckConfigureIfNeeded(void);
|
|
||||||
void waylandSetFullscreen(bool fs);
|
|
||||||
bool waylandGetFullscreen(void);
|
|
||||||
void waylandMinimize(void);
|
|
||||||
void waylandShellResize(int w, int h);
|
|
||||||
|
|
||||||
// window module
|
// window module
|
||||||
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable);
|
bool waylandWindowInit(const char * title, const char * appId, bool fullscreen, bool maximize, bool borderless, bool resizable);
|
||||||
void waylandWindowFree(void);
|
void waylandWindowFree(void);
|
||||||
void waylandWindowUpdateScale(void);
|
void waylandWindowUpdateScale(void);
|
||||||
void waylandSetWindowSize(int x, int y);
|
void waylandSetWindowSize(int x, int y);
|
||||||
@ -331,3 +310,4 @@ bool waylandIsValidPointerPos(int x, int y);
|
|||||||
bool waylandWaitFrame(void);
|
bool waylandWaitFrame(void);
|
||||||
void waylandSkipFrame(void);
|
void waylandSkipFrame(void);
|
||||||
void waylandStopWaitFrame(void);
|
void waylandStopWaitFrame(void);
|
||||||
|
void waylandNeedsResize(void);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -85,7 +85,7 @@ static const struct wl_surface_listener wlSurfaceListener = {
|
|||||||
.leave = wlSurfaceLeaveHandler,
|
.leave = wlSurfaceLeaveHandler,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool borderless, bool resizable)
|
bool waylandWindowInit(const char * title, const char * appId, bool fullscreen, bool maximize, bool borderless, bool resizable)
|
||||||
{
|
{
|
||||||
wlWm.scale = wl_fixed_from_int(1);
|
wlWm.scale = wl_fixed_from_int(1);
|
||||||
|
|
||||||
@ -112,7 +112,8 @@ bool waylandWindowInit(const char * title, bool fullscreen, bool maximize, bool
|
|||||||
|
|
||||||
wl_surface_add_listener(wlWm.surface, &wlSurfaceListener, NULL);
|
wl_surface_add_listener(wlWm.surface, &wlSurfaceListener, NULL);
|
||||||
|
|
||||||
if (!waylandShellInit(title, fullscreen, maximize, borderless, resizable))
|
if (!wlWm.desktop->shellInit(wlWm.display, wlWm.surface,
|
||||||
|
title, appId, fullscreen, maximize, borderless, resizable))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
wl_surface_commit(wlWm.surface);
|
wl_surface_commit(wlWm.surface);
|
||||||
@ -127,12 +128,14 @@ void waylandWindowFree(void)
|
|||||||
|
|
||||||
void waylandSetWindowSize(int x, int y)
|
void waylandSetWindowSize(int x, int y)
|
||||||
{
|
{
|
||||||
waylandShellResize(x, y);
|
wlWm.desktop->shellResize(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waylandIsValidPointerPos(int x, int y)
|
bool waylandIsValidPointerPos(int x, int y)
|
||||||
{
|
{
|
||||||
return x >= 0 && x < wlWm.width && y >= 0 && y < wlWm.height;
|
int width, height;
|
||||||
|
wlWm.desktop->getSize(&width, &height);
|
||||||
|
return x >= 0 && x < width && y >= 0 && y < height;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void frameHandler(void * opaque, struct wl_callback * callback, unsigned int data)
|
static void frameHandler(void * opaque, struct wl_callback * callback, unsigned int data)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(displayserver_X11 LANGUAGES C)
|
project(displayserver_X11 LANGUAGES C)
|
||||||
|
|
||||||
find_package(PkgConfig)
|
find_package(PkgConfig)
|
||||||
@ -17,6 +17,10 @@ add_library(displayserver_X11 STATIC
|
|||||||
x11.c
|
x11.c
|
||||||
atoms.c
|
atoms.c
|
||||||
clipboard.c
|
clipboard.c
|
||||||
|
cursor.c
|
||||||
|
|
||||||
|
wm/default.c
|
||||||
|
wm/i3.c
|
||||||
)
|
)
|
||||||
|
|
||||||
add_definitions(-D GLX_GLXEXT_PROTOTYPES)
|
add_definitions(-D GLX_GLXEXT_PROTOTYPES)
|
||||||
@ -24,9 +28,10 @@ add_definitions(-D GLX_GLXEXT_PROTOTYPES)
|
|||||||
target_link_libraries(displayserver_X11
|
target_link_libraries(displayserver_X11
|
||||||
PkgConfig::DISPLAYSERVER_X11
|
PkgConfig::DISPLAYSERVER_X11
|
||||||
lg_common
|
lg_common
|
||||||
|
lg_resources
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(displayserver_X11
|
target_include_directories(displayserver_X11
|
||||||
PRIVATE
|
PRIVATE
|
||||||
src
|
.
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -60,40 +60,40 @@ static void x11CBSelectionIncr(const XPropertyEvent e);
|
|||||||
static void x11CBSelectionNotify(const XSelectionEvent e);
|
static void x11CBSelectionNotify(const XSelectionEvent e);
|
||||||
static void x11CBXFixesSelectionNotify(const XFixesSelectionNotifyEvent e);
|
static void x11CBXFixesSelectionNotify(const XFixesSelectionNotifyEvent e);
|
||||||
|
|
||||||
bool x11CBEventThread(const XEvent xe)
|
bool x11CBEventThread(const XEvent * xe)
|
||||||
{
|
{
|
||||||
switch(xe.type)
|
switch(xe->type)
|
||||||
{
|
{
|
||||||
case SelectionRequest:
|
case SelectionRequest:
|
||||||
x11CBSelectionRequest(xe.xselectionrequest);
|
x11CBSelectionRequest(xe->xselectionrequest);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case SelectionClear:
|
case SelectionClear:
|
||||||
x11CBSelectionClear(xe.xselectionclear);
|
x11CBSelectionClear(xe->xselectionclear);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case SelectionNotify:
|
case SelectionNotify:
|
||||||
x11CBSelectionNotify(xe.xselection);
|
x11CBSelectionNotify(xe->xselection);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case PropertyNotify:
|
case PropertyNotify:
|
||||||
if (xe.xproperty.state != PropertyNewValue)
|
if (xe->xproperty.state != PropertyNewValue)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (xe.xproperty.atom == x11atoms.SEL_DATA)
|
if (xe->xproperty.atom == x11atoms.SEL_DATA)
|
||||||
{
|
{
|
||||||
if (x11cb.lowerBound == 0)
|
if (x11cb.lowerBound == 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
x11CBSelectionIncr(xe.xproperty);
|
x11CBSelectionIncr(xe->xproperty);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (xe.type == x11.eventBase + XFixesSelectionNotify)
|
if (xe->type == x11.eventBase + XFixesSelectionNotify)
|
||||||
{
|
{
|
||||||
XFixesSelectionNotifyEvent * sne = (XFixesSelectionNotifyEvent *)&xe;
|
XFixesSelectionNotifyEvent * sne = (XFixesSelectionNotifyEvent *)xe;
|
||||||
x11CBXFixesSelectionNotify(*sne);
|
x11CBXFixesSelectionNotify(*sne);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
#include "interface/displayserver.h"
|
#include "interface/displayserver.h"
|
||||||
|
|
||||||
bool x11CBEventThread(const XEvent xe);
|
bool x11CBEventThread(const XEvent * xe);
|
||||||
|
|
||||||
bool x11CBInit(void);
|
bool x11CBInit(void);
|
||||||
void x11CBNotice(LG_ClipboardData type);
|
void x11CBNotice(LG_ClipboardData type);
|
||||||
|
105
client/displayservers/X11/cursor.c
Normal file
105
client/displayservers/X11/cursor.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cursor.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
struct MemFile
|
||||||
|
{
|
||||||
|
const char * data;
|
||||||
|
int size;
|
||||||
|
int pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int x11cursor_read(XcursorFile *file, unsigned char * buf, int len)
|
||||||
|
{
|
||||||
|
struct MemFile * f = (struct MemFile *)file->closure;
|
||||||
|
|
||||||
|
if (f->pos == f->size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
len = min(f->size - f->pos, len);
|
||||||
|
memcpy(buf, f->data + f->pos, len);
|
||||||
|
f->pos += len;
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int x11cursor_write(XcursorFile *file, unsigned char * buf, int len)
|
||||||
|
{
|
||||||
|
errno = -EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int x11cursor_seek(XcursorFile *file, long offset, int whence)
|
||||||
|
{
|
||||||
|
struct MemFile * f = (struct MemFile *)file->closure;
|
||||||
|
long target;
|
||||||
|
|
||||||
|
switch(whence)
|
||||||
|
{
|
||||||
|
case SEEK_SET:
|
||||||
|
target = offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SEEK_CUR:
|
||||||
|
target = f->pos + offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SEEK_END:
|
||||||
|
target = f->size + offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
errno = -EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target < 0 || target > f->size)
|
||||||
|
{
|
||||||
|
errno = -EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
f->pos = target;
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
XcursorImages * x11cursor_load(const char * cursor, int size)
|
||||||
|
{
|
||||||
|
struct MemFile closure =
|
||||||
|
{
|
||||||
|
.data = cursor,
|
||||||
|
.size = size,
|
||||||
|
.pos = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
XcursorFile f =
|
||||||
|
{
|
||||||
|
.closure = &closure,
|
||||||
|
.read = x11cursor_read,
|
||||||
|
.write = x11cursor_write,
|
||||||
|
.seek = x11cursor_seek
|
||||||
|
};
|
||||||
|
|
||||||
|
return XcursorXcFileLoadAllImages(&f);
|
||||||
|
}
|
29
client/displayservers/X11/cursor.h
Normal file
29
client/displayservers/X11/cursor.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _H_X11DS_CURSOR_
|
||||||
|
#define _H_X11DS_CURSOR_
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xcursor/Xcursor.h>
|
||||||
|
|
||||||
|
XcursorImages * x11cursor_load(const char * cursor, int size);
|
||||||
|
|
||||||
|
#endif
|
39
client/displayservers/X11/wm.h
Normal file
39
client/displayservers/X11/wm.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _H_X11DS_WM_
|
||||||
|
#define _H_X11DS_WM_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct X11WM
|
||||||
|
{
|
||||||
|
void (*setup)(void);
|
||||||
|
bool (*init)(void);
|
||||||
|
void (*deinit)(void);
|
||||||
|
void (*setFullscreen)(bool enable);
|
||||||
|
}
|
||||||
|
X11WM;
|
||||||
|
|
||||||
|
extern X11WM X11WM_Default;
|
||||||
|
extern X11WM X11WM_i3;
|
||||||
|
|
||||||
|
#endif
|
71
client/displayservers/X11/wm/default.c
Normal file
71
client/displayservers/X11/wm/default.c
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _H_X11DS_WM_DEFAULT_
|
||||||
|
#define _H_X11DS_WM_DEFAULT_
|
||||||
|
|
||||||
|
#include "wm.h"
|
||||||
|
#include "x11.h"
|
||||||
|
#include "atoms.h"
|
||||||
|
|
||||||
|
static void wm_default_setup(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool wm_default_init(void)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wm_default_deinit(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wm_default_setFullscreen(bool enable)
|
||||||
|
{
|
||||||
|
XEvent e =
|
||||||
|
{
|
||||||
|
.xclient = {
|
||||||
|
.type = ClientMessage,
|
||||||
|
.send_event = true,
|
||||||
|
.message_type = x11atoms._NET_WM_STATE,
|
||||||
|
.format = 32,
|
||||||
|
.window = x11.window,
|
||||||
|
.data.l = {
|
||||||
|
enable ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,
|
||||||
|
x11atoms._NET_WM_STATE_FULLSCREEN,
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
XSendEvent(x11.display, DefaultRootWindow(x11.display), False,
|
||||||
|
SubstructureNotifyMask | SubstructureRedirectMask, &e);
|
||||||
|
};
|
||||||
|
|
||||||
|
X11WM X11WM_Default =
|
||||||
|
{
|
||||||
|
.setup = wm_default_setup,
|
||||||
|
.init = wm_default_init,
|
||||||
|
.deinit = wm_default_deinit,
|
||||||
|
.setFullscreen = wm_default_setFullscreen
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
193
client/displayservers/X11/wm/i3.c
Normal file
193
client/displayservers/X11/wm/i3.c
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _H_X11DS_WM_DEFAULT_
|
||||||
|
#define _H_X11DS_WM_DEFAULT_
|
||||||
|
|
||||||
|
#include "wm.h"
|
||||||
|
#include "x11.h"
|
||||||
|
#include "atoms.h"
|
||||||
|
#include "common/debug.h"
|
||||||
|
#include "common/option.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
|
||||||
|
static struct Option options[] =
|
||||||
|
{
|
||||||
|
// app options
|
||||||
|
{
|
||||||
|
.module = "i3",
|
||||||
|
.name = "globalFullScreen",
|
||||||
|
.description = "Use i3's global full screen feature (spans all monitors)",
|
||||||
|
.type = OPTION_TYPE_BOOL,
|
||||||
|
.value.x_bool = false,
|
||||||
|
},
|
||||||
|
{0}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct i3state
|
||||||
|
{
|
||||||
|
int sock;
|
||||||
|
bool globalFullScreen;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct i3state i3;
|
||||||
|
|
||||||
|
static void wm_i3_setup(void)
|
||||||
|
{
|
||||||
|
option_register(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool wm_i3_init(void)
|
||||||
|
{
|
||||||
|
memset(&i3, 0, sizeof(i3));
|
||||||
|
|
||||||
|
i3.globalFullScreen = option_get_bool("i3", "globalFullScreen");
|
||||||
|
|
||||||
|
FILE * fd = popen("i3 --get-socketpath", "r");
|
||||||
|
if (!fd)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
struct sockaddr_un addr = { .sun_family = AF_UNIX };
|
||||||
|
char * path = (char *)&addr.sun_path;
|
||||||
|
int pathLen;
|
||||||
|
if ((pathLen = fread(path, 1, sizeof(addr.sun_path), fd)) <= 0)
|
||||||
|
{
|
||||||
|
pclose(fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pclose(fd);
|
||||||
|
|
||||||
|
if(path[pathLen-1] == '\n')
|
||||||
|
--pathLen;
|
||||||
|
path[pathLen] = '\0';
|
||||||
|
|
||||||
|
i3.sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (i3.sock < 0)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to create socket for i3 IPC");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connect(i3.sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to connect to the i3 IPC socket");
|
||||||
|
perror("connect");
|
||||||
|
goto err_socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_INFO("i3 IPC Connected");
|
||||||
|
return true;
|
||||||
|
|
||||||
|
err_socket:
|
||||||
|
close(i3.sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wm_i3_deinit(void)
|
||||||
|
{
|
||||||
|
close(i3.sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wm_i3_setFullscreen(bool enable)
|
||||||
|
{
|
||||||
|
if (!i3.globalFullScreen)
|
||||||
|
{
|
||||||
|
X11WM_Default.setFullscreen(enable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct i3Msg
|
||||||
|
{
|
||||||
|
char magic[6];
|
||||||
|
uint32_t length;
|
||||||
|
uint32_t type;
|
||||||
|
char payload[0];
|
||||||
|
}
|
||||||
|
__attribute__((packed));
|
||||||
|
|
||||||
|
#define I3_IPC_TYPE_RUN_COMMAND 0
|
||||||
|
|
||||||
|
char cmd[128];
|
||||||
|
int cmdLen = snprintf(cmd, sizeof(cmd),
|
||||||
|
"[id=%lu] fullscreen toggle global",
|
||||||
|
x11.window);
|
||||||
|
|
||||||
|
struct i3Msg *msg = alloca(sizeof(struct i3Msg) + cmdLen);
|
||||||
|
memcpy(msg->magic, "i3-ipc", 6);
|
||||||
|
msg->length = cmdLen;
|
||||||
|
msg->type = I3_IPC_TYPE_RUN_COMMAND;
|
||||||
|
memcpy(msg->payload, cmd, cmdLen);
|
||||||
|
|
||||||
|
int msgSize = sizeof(*msg) + msg->length;
|
||||||
|
char * buf = (char *)msg;
|
||||||
|
while(msgSize)
|
||||||
|
{
|
||||||
|
int wrote = write(i3.sock, buf, msgSize);
|
||||||
|
if (wrote <= 0)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("i3 IPC communication failure");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf += wrote;
|
||||||
|
msgSize -= wrote;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((msgSize = read(i3.sock, msg, sizeof(*msg))) < 0)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("i3 IPC read failure");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(msg->magic, "i3-ipc", 6) != 0 ||
|
||||||
|
msg->type != I3_IPC_TYPE_RUN_COMMAND)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("i3 IPC unexpected reply");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read and discard the payload
|
||||||
|
while(msg->length)
|
||||||
|
{
|
||||||
|
int len = read(i3.sock, cmd, min(msg->length, sizeof(cmd)));
|
||||||
|
if (len <= 0)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("i3 IPC failed to read payload");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
msg->length -= len;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
X11WM X11WM_i3 =
|
||||||
|
{
|
||||||
|
.setup = wm_i3_setup,
|
||||||
|
.init = wm_i3_init,
|
||||||
|
.deinit = wm_i3_deinit,
|
||||||
|
.setFullscreen = wm_i3_setFullscreen
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -23,7 +23,11 @@
|
|||||||
#include "x11.h"
|
#include "x11.h"
|
||||||
#include "atoms.h"
|
#include "atoms.h"
|
||||||
#include "clipboard.h"
|
#include "clipboard.h"
|
||||||
|
#include "cursor.h"
|
||||||
|
|
||||||
#include "resources/icondata.h"
|
#include "resources/icondata.h"
|
||||||
|
#include "resources/no-input-cursor/16.xcur.h"
|
||||||
|
#include "resources/no-input-cursor/32.xcur.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -53,10 +57,6 @@
|
|||||||
#include "common/event.h"
|
#include "common/event.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#define _NET_WM_STATE_REMOVE 0
|
|
||||||
#define _NET_WM_STATE_ADD 1
|
|
||||||
#define _NET_WM_STATE_TOGGLE 2
|
|
||||||
|
|
||||||
struct X11DSState x11;
|
struct X11DSState x11;
|
||||||
|
|
||||||
struct MwmHints
|
struct MwmHints
|
||||||
@ -166,6 +166,8 @@ static void x11DoPresent(uint64_t msc)
|
|||||||
|
|
||||||
static void x11Setup(void)
|
static void x11Setup(void)
|
||||||
{
|
{
|
||||||
|
X11WM_Default.setup();
|
||||||
|
X11WM_i3 .setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool x11Probe(void)
|
static bool x11Probe(void)
|
||||||
@ -227,9 +229,11 @@ static void x11CheckEWMHSupport(void)
|
|||||||
x11.ewmhHasFocusEvent = true;
|
x11.ewmhHasFocusEvent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_INFO("EWMH-complient window manager detected: %s", wmName);
|
DEBUG_INFO("EWMH-compliant window manager detected: %s", wmName);
|
||||||
x11.ewmhSupport = true;
|
x11.ewmhSupport = true;
|
||||||
|
|
||||||
|
if (strcmp(wmName, "i3") == 0)
|
||||||
|
x11.wm = &X11WM_i3;
|
||||||
|
|
||||||
XFree(wmName);
|
XFree(wmName);
|
||||||
out_supported:
|
out_supported:
|
||||||
@ -242,17 +246,37 @@ out:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int x11ErrorHandler(Display * display, XErrorEvent * error)
|
||||||
|
{
|
||||||
|
char errorText[1024];
|
||||||
|
XGetErrorText(display, error->error_code, errorText, sizeof(errorText));
|
||||||
|
|
||||||
|
DEBUG_ERROR("X11 Error: %s", errorText);
|
||||||
|
DEBUG_PRINT_BACKTRACE();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int x11IOErrorHandler(Display * display)
|
||||||
|
{
|
||||||
|
DEBUG_FATAL("Fatal X11 IO Error");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static bool x11Init(const LG_DSInitParams params)
|
static bool x11Init(const LG_DSInitParams params)
|
||||||
{
|
{
|
||||||
XIDeviceInfo *devinfo;
|
XIDeviceInfo *devinfo;
|
||||||
int count;
|
int count;
|
||||||
int event, error;
|
int event, error;
|
||||||
|
|
||||||
|
XSetErrorHandler(x11ErrorHandler);
|
||||||
|
XSetIOErrorHandler(x11IOErrorHandler);
|
||||||
|
|
||||||
memset(&x11, 0, sizeof(x11));
|
memset(&x11, 0, sizeof(x11));
|
||||||
x11.xValuator = -1;
|
x11.xValuator = -1;
|
||||||
x11.yValuator = -1;
|
x11.yValuator = -1;
|
||||||
x11.display = XOpenDisplay(NULL);
|
x11.display = XOpenDisplay(NULL);
|
||||||
x11.jitRender = params.jitRender;
|
x11.jitRender = params.jitRender;
|
||||||
|
x11.wm = &X11WM_Default;
|
||||||
|
|
||||||
XSetWindowAttributes swa =
|
XSetWindowAttributes swa =
|
||||||
{
|
{
|
||||||
@ -319,7 +343,7 @@ static bool x11Init(const LG_DSInitParams params)
|
|||||||
XClassHint hint =
|
XClassHint hint =
|
||||||
{
|
{
|
||||||
.res_name = strdup(params.title),
|
.res_name = strdup(params.title),
|
||||||
.res_class = strdup("looking-glass-client")
|
.res_class = strdup(params.appId)
|
||||||
};
|
};
|
||||||
XSetClassHint(x11.display, x11.window, &hint);
|
XSetClassHint(x11.display, x11.window, &hint);
|
||||||
free(hint.res_name);
|
free(hint.res_name);
|
||||||
@ -350,6 +374,16 @@ static bool x11Init(const LG_DSInitParams params)
|
|||||||
// check for Extended Window Manager Hints support
|
// check for Extended Window Manager Hints support
|
||||||
x11CheckEWMHSupport();
|
x11CheckEWMHSupport();
|
||||||
|
|
||||||
|
if (!x11.wm->init())
|
||||||
|
{
|
||||||
|
x11.wm = &X11WM_Default;
|
||||||
|
if (!x11.wm->init())
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to initialize the X11 window manager subsystem");
|
||||||
|
goto fail_window;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (x11atoms._NET_WM_PID)
|
if (x11atoms._NET_WM_PID)
|
||||||
{
|
{
|
||||||
pid_t pid = getpid();
|
pid_t pid = getpid();
|
||||||
@ -457,7 +491,7 @@ static bool x11Init(const LG_DSInitParams params)
|
|||||||
if (XIQueryVersion(x11.display, &major, &minor) != Success)
|
if (XIQueryVersion(x11.display, &major, &minor) != Success)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to query the XInput version");
|
DEBUG_ERROR("Failed to query the XInput version");
|
||||||
return false;
|
goto fail_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_INFO("X11 XInput %d.%d in use", major, minor);
|
DEBUG_INFO("X11 XInput %d.%d in use", major, minor);
|
||||||
@ -596,29 +630,17 @@ static bool x11Init(const LG_DSInitParams params)
|
|||||||
XFreePixmap(x11.display, temp);
|
XFreePixmap(x11.display, temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create the square cursor */
|
XcursorImages * images;
|
||||||
{
|
if (params.largeCursorDot)
|
||||||
static char data[] = { 0x07, 0x05, 0x07 };
|
images = x11cursor_load(b_no_input_cursor_32_xcur,
|
||||||
static char mask[] = { 0xff, 0xff, 0xff };
|
b_no_input_cursor_32_xcur_size);
|
||||||
|
else
|
||||||
|
images = x11cursor_load(b_no_input_cursor_16_xcur,
|
||||||
|
b_no_input_cursor_16_xcur_size);
|
||||||
|
|
||||||
Colormap cmap = DefaultColormap(x11.display, DefaultScreen(x11.display));
|
x11.cursors[LG_POINTER_SQUARE] =
|
||||||
XColor colors[2] =
|
XcursorImagesLoadCursor(x11.display, images);
|
||||||
{
|
XcursorImagesDestroy(images);
|
||||||
{ .pixel = BlackPixelOfScreen(DefaultScreenOfDisplay(x11.display)) },
|
|
||||||
{ .pixel = WhitePixelOfScreen(DefaultScreenOfDisplay(x11.display)) }
|
|
||||||
};
|
|
||||||
|
|
||||||
XQueryColors(x11.display, cmap, colors, 2);
|
|
||||||
|
|
||||||
Pixmap img = XCreateBitmapFromData(x11.display, x11.window, data, 3, 3);
|
|
||||||
Pixmap msk = XCreateBitmapFromData(x11.display, x11.window, mask, 3, 3);
|
|
||||||
|
|
||||||
x11.cursors[LG_POINTER_SQUARE] = XCreatePixmapCursor(x11.display, img, msk,
|
|
||||||
&colors[0], &colors[1], 1, 1);
|
|
||||||
|
|
||||||
XFreePixmap(x11.display, img);
|
|
||||||
XFreePixmap(x11.display, msk);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initialize the rest of the cursors */
|
/* initialize the rest of the cursors */
|
||||||
const char * cursorLookup[LG_POINTER_COUNT] = {
|
const char * cursorLookup[LG_POINTER_COUNT] = {
|
||||||
@ -677,7 +699,7 @@ static bool x11Init(const LG_DSInitParams params)
|
|||||||
XMoveWindow(x11.display, x11.window, params.x, params.y);
|
XMoveWindow(x11.display, x11.window, params.x, params.y);
|
||||||
|
|
||||||
if (params.fullscreen)
|
if (params.fullscreen)
|
||||||
x11SetFullscreen(true);
|
x11.doFullscreenOnExpose = true;
|
||||||
|
|
||||||
XSetLocaleModifiers(""); // Load XMODIFIERS
|
XSetLocaleModifiers(""); // Load XMODIFIERS
|
||||||
x11.xim = XOpenIM(x11.display, 0, 0, 0);
|
x11.xim = XOpenIM(x11.display, 0, 0, 0);
|
||||||
@ -762,6 +784,7 @@ static void x11Free(void)
|
|||||||
if (x11.keysyms)
|
if (x11.keysyms)
|
||||||
XFree(x11.keysyms);
|
XFree(x11.keysyms);
|
||||||
|
|
||||||
|
x11.wm->deinit();
|
||||||
XCloseDisplay(x11.display);
|
XCloseDisplay(x11.display);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -874,7 +897,7 @@ static int x11EventThread(void * unused)
|
|||||||
XNextEvent(x11.display, &xe);
|
XNextEvent(x11.display, &xe);
|
||||||
|
|
||||||
// call the clipboard handling code
|
// call the clipboard handling code
|
||||||
if (x11CBEventThread(xe))
|
if (x11CBEventThread(&xe))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
switch(xe.type)
|
switch(xe.type)
|
||||||
@ -923,6 +946,11 @@ static int x11EventThread(void * unused)
|
|||||||
{
|
{
|
||||||
atomic_store(&x11.lastWMEvent, microtime());
|
atomic_store(&x11.lastWMEvent, microtime());
|
||||||
x11.invalidateAll = true;
|
x11.invalidateAll = true;
|
||||||
|
if (x11.doFullscreenOnExpose)
|
||||||
|
{
|
||||||
|
x11SetFullscreen(true);
|
||||||
|
x11.doFullscreenOnExpose = false;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1059,8 +1087,9 @@ static void setFocus(bool focused, double x, double y)
|
|||||||
app_handleFocusEvent(focused);
|
app_handleFocusEvent(focused);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getCharcode(int detail)
|
static int x11GetCharCode(int detail)
|
||||||
{
|
{
|
||||||
|
detail += x11.minKeycode;
|
||||||
if (detail < x11.minKeycode || detail > x11.maxKeycode)
|
if (detail < x11.minKeycode || detail > x11.maxKeycode)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -1152,6 +1181,46 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
|||||||
app_updateCursorPos(xie->event_x, xie->event_y);
|
app_updateCursorPos(xie->event_x, xie->event_y);
|
||||||
app_handleEnterEvent(false);
|
app_handleEnterEvent(false);
|
||||||
x11.entered = false;
|
x11.entered = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Because there is a race with the pointer ungrab the enter event for the
|
||||||
|
* next window is sometimes sent with the mode NotifyUngrab, unfortunatly
|
||||||
|
* some window managers such as i3 will ignore these which breaks focus
|
||||||
|
* follows mouse mode. To correct this we generate and send a normal
|
||||||
|
* EnterNotify event.
|
||||||
|
*/
|
||||||
|
int root_x, root_y, win_x, win_y;
|
||||||
|
Window root_win, child_win;
|
||||||
|
unsigned int mask;
|
||||||
|
XQueryPointer(x11.display, DefaultRootWindow(x11.display), &root_win,
|
||||||
|
&child_win, &root_x, &root_y, &win_x, &win_y, &mask);
|
||||||
|
|
||||||
|
int target_x, target_y;
|
||||||
|
Window target_root;
|
||||||
|
XTranslateCoordinates(x11.display, DefaultRootWindow(x11.display),
|
||||||
|
child_win, root_x, root_y, &target_x, &target_y, &target_root);
|
||||||
|
|
||||||
|
XEvent event;
|
||||||
|
memset(&event, 0, sizeof(event));
|
||||||
|
event.type = EnterNotify;
|
||||||
|
event.xcrossing.serial = 0;
|
||||||
|
event.xcrossing.send_event = True;
|
||||||
|
event.xcrossing.display = x11.display;
|
||||||
|
event.xcrossing.window = child_win;
|
||||||
|
event.xcrossing.root = root_win;
|
||||||
|
event.xcrossing.subwindow = child_win;
|
||||||
|
event.xcrossing.time = CurrentTime;
|
||||||
|
event.xcrossing.mode = NotifyNormal;
|
||||||
|
event.xcrossing.detail = NotifyNonlinear;
|
||||||
|
event.xcrossing.same_screen = True;
|
||||||
|
event.xcrossing.focus = False;
|
||||||
|
event.xcrossing.x = target_x;
|
||||||
|
event.xcrossing.y = target_y;
|
||||||
|
event.xcrossing.x_root = root_x;
|
||||||
|
event.xcrossing.y_root = root_y;
|
||||||
|
|
||||||
|
XSendEvent(x11.display, child_win, True, EnterWindowMask, &event);
|
||||||
|
XFlush(x11.display);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1161,8 +1230,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
XIDeviceEvent *device = cookie->data;
|
XIDeviceEvent *device = cookie->data;
|
||||||
app_handleKeyPress(device->detail - x11.minKeycode,
|
app_handleKeyPress(device->detail - x11.minKeycode);
|
||||||
getCharcode(device->detail));
|
|
||||||
|
|
||||||
if (!x11.xic || !app_isOverlayMode())
|
if (!x11.xic || !app_isOverlayMode())
|
||||||
return;
|
return;
|
||||||
@ -1212,8 +1280,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
XIDeviceEvent *device = cookie->data;
|
XIDeviceEvent *device = cookie->data;
|
||||||
app_handleKeyRelease(device->detail - x11.minKeycode,
|
app_handleKeyRelease(device->detail - x11.minKeycode);
|
||||||
getCharcode(device->detail));
|
|
||||||
|
|
||||||
if (!x11.xic || !app_isOverlayMode())
|
if (!x11.xic || !app_isOverlayMode())
|
||||||
return;
|
return;
|
||||||
@ -1242,8 +1309,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
XIRawEvent *raw = cookie->data;
|
XIRawEvent *raw = cookie->data;
|
||||||
app_handleKeyPress(raw->detail - x11.minKeycode,
|
app_handleKeyPress(raw->detail - x11.minKeycode);
|
||||||
getCharcode(raw->detail));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1253,8 +1319,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
XIRawEvent *raw = cookie->data;
|
XIRawEvent *raw = cookie->data;
|
||||||
app_handleKeyRelease(raw->detail - x11.minKeycode,
|
app_handleKeyRelease(raw->detail - x11.minKeycode);
|
||||||
getCharcode(raw->detail));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1905,24 +1970,7 @@ static void x11SetFullscreen(bool fs)
|
|||||||
if (x11.fullscreen == fs)
|
if (x11.fullscreen == fs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
XEvent e =
|
x11.wm->setFullscreen(fs);
|
||||||
{
|
|
||||||
.xclient = {
|
|
||||||
.type = ClientMessage,
|
|
||||||
.send_event = true,
|
|
||||||
.message_type = x11atoms._NET_WM_STATE,
|
|
||||||
.format = 32,
|
|
||||||
.window = x11.window,
|
|
||||||
.data.l = {
|
|
||||||
fs ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,
|
|
||||||
x11atoms._NET_WM_STATE_FULLSCREEN,
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
XSendEvent(x11.display, DefaultRootWindow(x11.display), False,
|
|
||||||
SubstructureNotifyMask | SubstructureRedirectMask, &e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool x11GetFullscreen(void)
|
static bool x11GetFullscreen(void)
|
||||||
@ -1966,6 +2014,7 @@ struct LG_DisplayServerOps LGDS_X11 =
|
|||||||
.ungrabPointer = x11UngrabPointer,
|
.ungrabPointer = x11UngrabPointer,
|
||||||
.capturePointer = x11CapturePointer,
|
.capturePointer = x11CapturePointer,
|
||||||
.uncapturePointer = x11UncapturePointer,
|
.uncapturePointer = x11UncapturePointer,
|
||||||
|
.getCharCode = x11GetCharCode,
|
||||||
.grabKeyboard = x11GrabKeyboard,
|
.grabKeyboard = x11GrabKeyboard,
|
||||||
.ungrabKeyboard = x11UngrabKeyboard,
|
.ungrabKeyboard = x11UngrabKeyboard,
|
||||||
.warpPointer = x11WarpPointer,
|
.warpPointer = x11WarpPointer,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -33,6 +33,7 @@
|
|||||||
#include "interface/displayserver.h"
|
#include "interface/displayserver.h"
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
#include "wm.h"
|
||||||
|
|
||||||
enum Modifiers
|
enum Modifiers
|
||||||
{
|
{
|
||||||
@ -48,11 +49,16 @@ enum Modifiers
|
|||||||
|
|
||||||
#define MOD_COUNT (MOD_SUPER_RIGHT + 1)
|
#define MOD_COUNT (MOD_SUPER_RIGHT + 1)
|
||||||
|
|
||||||
|
#define _NET_WM_STATE_REMOVE 0
|
||||||
|
#define _NET_WM_STATE_ADD 1
|
||||||
|
#define _NET_WM_STATE_TOGGLE 2
|
||||||
|
|
||||||
struct X11DSState
|
struct X11DSState
|
||||||
{
|
{
|
||||||
Display * display;
|
Display * display;
|
||||||
Window window;
|
Window window;
|
||||||
XVisualInfo * visual;
|
XVisualInfo * visual;
|
||||||
|
X11WM * wm;
|
||||||
|
|
||||||
int minKeycode, maxKeycode;
|
int minKeycode, maxKeycode;
|
||||||
int symsPerKeycode;
|
int symsPerKeycode;
|
||||||
@ -65,6 +71,7 @@ struct X11DSState
|
|||||||
|
|
||||||
_Atomic(uint64_t) lastWMEvent;
|
_Atomic(uint64_t) lastWMEvent;
|
||||||
bool invalidateAll;
|
bool invalidateAll;
|
||||||
|
bool doFullscreenOnExpose;
|
||||||
|
|
||||||
int xpresentOp;
|
int xpresentOp;
|
||||||
bool jitRender;
|
bool jitRender;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -59,8 +59,8 @@ void app_handleButtonPress(int button);
|
|||||||
void app_handleButtonRelease(int button);
|
void app_handleButtonRelease(int button);
|
||||||
void app_handleWheelMotion(double motion);
|
void app_handleWheelMotion(double motion);
|
||||||
void app_handleKeyboardTyped(const char * typed);
|
void app_handleKeyboardTyped(const char * typed);
|
||||||
void app_handleKeyPress(int scancode, int charcode);
|
void app_handleKeyPress(int scancode);
|
||||||
void app_handleKeyRelease(int scancode, int charcode);
|
void app_handleKeyRelease(int scancode);
|
||||||
void app_handleKeyboardModifiers(bool ctrl, bool shift, bool alt, bool super);
|
void app_handleKeyboardModifiers(bool ctrl, bool shift, bool alt, bool super);
|
||||||
void app_handleKeyboardLEDs(bool numLock, bool capsLock, bool scrollLock);
|
void app_handleKeyboardLEDs(bool numLock, bool capsLock, bool scrollLock);
|
||||||
void app_handleEnterEvent(bool entered);
|
void app_handleEnterEvent(bool entered);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
51
client/include/evdev.h
Normal file
51
client/include/evdev.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* initialize configuration options
|
||||||
|
*/
|
||||||
|
void evdev_earlyInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* start the evdev layer
|
||||||
|
*/
|
||||||
|
bool evdev_start(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stop the evdev layer
|
||||||
|
*/
|
||||||
|
void evdev_stop(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* grab the keyboard for exclusive access
|
||||||
|
*/
|
||||||
|
void evdev_grabKeyboard(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ungrab the keyboard
|
||||||
|
*/
|
||||||
|
void evdev_ungrabKeyboard(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns true if input should only be processed by evdev
|
||||||
|
*/
|
||||||
|
bool evdev_isExclusive(void);
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -84,12 +84,14 @@ LG_DSPointer;
|
|||||||
typedef struct LG_DSInitParams
|
typedef struct LG_DSInitParams
|
||||||
{
|
{
|
||||||
const char * title;
|
const char * title;
|
||||||
|
const char * appId;
|
||||||
int x, y, w, h;
|
int x, y, w, h;
|
||||||
bool center;
|
bool center;
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
bool resizable;
|
bool resizable;
|
||||||
bool borderless;
|
bool borderless;
|
||||||
bool maximize;
|
bool maximize;
|
||||||
|
bool largeCursorDot;
|
||||||
|
|
||||||
// if true the renderer requires an OpenGL context
|
// if true the renderer requires an OpenGL context
|
||||||
bool opengl;
|
bool opengl;
|
||||||
@ -181,6 +183,9 @@ struct LG_DisplayServerOps
|
|||||||
void (*capturePointer)(void);
|
void (*capturePointer)(void);
|
||||||
void (*uncapturePointer)(void);
|
void (*uncapturePointer)(void);
|
||||||
|
|
||||||
|
/* get the character code for the provided scancode */
|
||||||
|
int (*getCharCode)(int sc);
|
||||||
|
|
||||||
/* exiting = true if the warp is to leave the window */
|
/* exiting = true if the warp is to leave the window */
|
||||||
void (*warpPointer)(int x, int y, bool exiting);
|
void (*warpPointer)(int x, int y, bool exiting);
|
||||||
|
|
||||||
@ -251,6 +256,7 @@ struct LG_DisplayServerOps
|
|||||||
DEBUG_ASSERT((x)->ungrabPointer ); \
|
DEBUG_ASSERT((x)->ungrabPointer ); \
|
||||||
DEBUG_ASSERT((x)->capturePointer ); \
|
DEBUG_ASSERT((x)->capturePointer ); \
|
||||||
DEBUG_ASSERT((x)->uncapturePointer ); \
|
DEBUG_ASSERT((x)->uncapturePointer ); \
|
||||||
|
DEBUG_ASSERT((x)->getCharCode ); \
|
||||||
DEBUG_ASSERT((x)->warpPointer ); \
|
DEBUG_ASSERT((x)->warpPointer ); \
|
||||||
DEBUG_ASSERT((x)->realignPointer ); \
|
DEBUG_ASSERT((x)->realignPointer ); \
|
||||||
DEBUG_ASSERT((x)->isValidPointerPos ); \
|
DEBUG_ASSERT((x)->isValidPointerPos ); \
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -72,8 +72,12 @@ LG_RendererRotate;
|
|||||||
typedef struct LG_RendererFormat
|
typedef struct LG_RendererFormat
|
||||||
{
|
{
|
||||||
FrameType type; // frame type
|
FrameType type; // frame type
|
||||||
|
bool hdr; // if the frame is HDR or not
|
||||||
|
bool hdrPQ; // if the HDR content is PQ mapped
|
||||||
unsigned int screenWidth; // actual width of the host
|
unsigned int screenWidth; // actual width of the host
|
||||||
unsigned int screenHeight; // actual height of the host
|
unsigned int screenHeight; // actual height of the host
|
||||||
|
unsigned int dataWidth; // the width of the packed data
|
||||||
|
unsigned int dataHeight; // the height of the packed data
|
||||||
unsigned int frameWidth; // width of frame transmitted
|
unsigned int frameWidth; // width of frame transmitted
|
||||||
unsigned int frameHeight; // height of frame transmitted
|
unsigned int frameHeight; // height of frame transmitted
|
||||||
unsigned int stride; // scanline width (zero if compresed)
|
unsigned int stride; // scanline width (zero if compresed)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(renderers LANGUAGES C)
|
project(renderers LANGUAGES C)
|
||||||
|
|
||||||
set(RENDERER_H "${CMAKE_BINARY_DIR}/include/dynamic/renderers.h")
|
set(RENDERER_H "${CMAKE_BINARY_DIR}/include/dynamic/renderers.h")
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(renderer_EGL LANGUAGES C CXX)
|
project(renderer_EGL LANGUAGES C CXX)
|
||||||
|
|
||||||
find_package(PkgConfig)
|
find_package(PkgConfig)
|
||||||
@ -56,6 +56,7 @@ build_shaders(
|
|||||||
shader/damage.vert
|
shader/damage.vert
|
||||||
shader/damage.frag
|
shader/damage.frag
|
||||||
shader/basic.vert
|
shader/basic.vert
|
||||||
|
shader/convert_24bit.frag
|
||||||
shader/ffx_cas.frag
|
shader/ffx_cas.frag
|
||||||
shader/ffx_fsr1_easu.frag
|
shader/ffx_fsr1_easu.frag
|
||||||
shader/ffx_fsr1_rcas.frag
|
shader/ffx_fsr1_rcas.frag
|
||||||
@ -87,6 +88,7 @@ add_library(renderer_EGL STATIC
|
|||||||
postprocess.c
|
postprocess.c
|
||||||
ffx.c
|
ffx.c
|
||||||
filter.c
|
filter.c
|
||||||
|
filter_24bit.c
|
||||||
filter_ffx_cas.c
|
filter_ffx_cas.c
|
||||||
filter_ffx_fsr1.c
|
filter_ffx_fsr1.c
|
||||||
filter_downscale.c
|
filter_downscale.c
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -100,7 +100,7 @@ static bool cursorTexInit(struct CursorTex * t,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!egl_shaderCompile(t->shader,
|
if (!egl_shaderCompile(t->shader,
|
||||||
vertex_code, vertex_size, fragment_code, fragment_size))
|
vertex_code, vertex_size, fragment_code, fragment_size, false, NULL))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to compile the cursor shader");
|
DEBUG_ERROR("Failed to compile the cursor shader");
|
||||||
return false;
|
return false;
|
||||||
@ -277,7 +277,7 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
|||||||
}
|
}
|
||||||
|
|
||||||
egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA,
|
egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA,
|
||||||
cursor->width, cursor->height, sizeof(xor[0]));
|
cursor->width, cursor->height, cursor->width, sizeof(xor[0]));
|
||||||
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor, true);
|
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor, true);
|
||||||
}
|
}
|
||||||
// fall through
|
// fall through
|
||||||
@ -285,7 +285,7 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
|||||||
case LG_CURSOR_COLOR:
|
case LG_CURSOR_COLOR:
|
||||||
{
|
{
|
||||||
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA,
|
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA,
|
||||||
cursor->width, cursor->height, cursor->stride);
|
cursor->width, cursor->height, cursor->width, cursor->stride);
|
||||||
egl_textureUpdate(cursor->norm.texture, data, true);
|
egl_textureUpdate(cursor->norm.texture, data, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -311,9 +311,9 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor,
|
|||||||
}
|
}
|
||||||
|
|
||||||
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA,
|
egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA,
|
||||||
cursor->width, cursor->height, sizeof(and[0]));
|
cursor->width, cursor->height, cursor->width, sizeof(and[0]));
|
||||||
egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA,
|
egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA,
|
||||||
cursor->width, cursor->height, sizeof(xor[0]));
|
cursor->width, cursor->height, cursor->width, sizeof(xor[0]));
|
||||||
egl_textureUpdate(cursor->norm.texture, (uint8_t *)and, true);
|
egl_textureUpdate(cursor->norm.texture, (uint8_t *)and, true);
|
||||||
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor, true);
|
egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor, true);
|
||||||
break;
|
break;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -76,7 +76,8 @@ bool egl_damageInit(EGL_Damage ** damage)
|
|||||||
|
|
||||||
if (!egl_shaderCompile((*damage)->shader,
|
if (!egl_shaderCompile((*damage)->shader,
|
||||||
b_shader_damage_vert, b_shader_damage_vert_size,
|
b_shader_damage_vert, b_shader_damage_vert_size,
|
||||||
b_shader_damage_frag, b_shader_damage_frag_size))
|
b_shader_damage_frag, b_shader_damage_frag_size,
|
||||||
|
false, NULL))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to compile the damage shader");
|
DEBUG_ERROR("Failed to compile the damage shader");
|
||||||
return false;
|
return false;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -46,9 +46,14 @@ struct DesktopShader
|
|||||||
EGL_Shader * shader;
|
EGL_Shader * shader;
|
||||||
GLint uTransform;
|
GLint uTransform;
|
||||||
GLint uDesktopSize;
|
GLint uDesktopSize;
|
||||||
|
GLint uSamplerType;
|
||||||
GLint uScaleAlgo;
|
GLint uScaleAlgo;
|
||||||
GLint uNVGain;
|
GLint uNVGain;
|
||||||
GLint uCBMode;
|
GLint uCBMode;
|
||||||
|
GLint uIsHDR;
|
||||||
|
GLint uMapHDRtoSDR;
|
||||||
|
GLint uMapHDRGain;
|
||||||
|
GLint uMapHDRPQ;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EGL_Desktop
|
struct EGL_Desktop
|
||||||
@ -57,12 +62,14 @@ struct EGL_Desktop
|
|||||||
EGLDisplay * display;
|
EGLDisplay * display;
|
||||||
|
|
||||||
EGL_Texture * texture;
|
EGL_Texture * texture;
|
||||||
struct DesktopShader shader;
|
struct DesktopShader dmaShader, shader;
|
||||||
EGL_DesktopRects * mesh;
|
EGL_DesktopRects * mesh;
|
||||||
CountedBuffer * matrix;
|
CountedBuffer * matrix;
|
||||||
|
|
||||||
// internals
|
// internals
|
||||||
int width, height;
|
int width, height;
|
||||||
|
bool hdr;
|
||||||
|
bool hdrPQ;
|
||||||
LG_RendererRotate rotate;
|
LG_RendererRotate rotate;
|
||||||
|
|
||||||
bool useSpice;
|
bool useSpice;
|
||||||
@ -82,6 +89,11 @@ struct EGL_Desktop
|
|||||||
bool useDMA;
|
bool useDMA;
|
||||||
LG_RendererFormat format;
|
LG_RendererFormat format;
|
||||||
|
|
||||||
|
// map HDR content to SDR
|
||||||
|
bool mapHDRtoSDR;
|
||||||
|
int peakLuminance;
|
||||||
|
int maxCLL;
|
||||||
|
|
||||||
EGL_PostProcess * pp;
|
EGL_PostProcess * pp;
|
||||||
_Atomic(bool) processFrame;
|
_Atomic(bool) processFrame;
|
||||||
};
|
};
|
||||||
@ -92,7 +104,8 @@ void toggleNV(int key, void * opaque);
|
|||||||
static bool egl_initDesktopShader(
|
static bool egl_initDesktopShader(
|
||||||
struct DesktopShader * shader,
|
struct DesktopShader * shader,
|
||||||
const char * vertex_code , size_t vertex_size,
|
const char * vertex_code , size_t vertex_size,
|
||||||
const char * fragment_code, size_t fragment_size
|
const char * fragment_code, size_t fragment_size,
|
||||||
|
bool useDMA
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (!egl_shaderInit(&shader->shader))
|
if (!egl_shaderInit(&shader->shader))
|
||||||
@ -100,16 +113,21 @@ static bool egl_initDesktopShader(
|
|||||||
|
|
||||||
if (!egl_shaderCompile(shader->shader,
|
if (!egl_shaderCompile(shader->shader,
|
||||||
vertex_code , vertex_size,
|
vertex_code , vertex_size,
|
||||||
fragment_code, fragment_size))
|
fragment_code, fragment_size,
|
||||||
|
useDMA, NULL))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
shader->uTransform = egl_shaderGetUniform(shader->shader, "transform" );
|
shader->uDesktopSize = egl_shaderGetUniform(shader->shader, "desktopSize" );
|
||||||
shader->uDesktopSize = egl_shaderGetUniform(shader->shader, "desktopSize");
|
shader->uTransform = egl_shaderGetUniform(shader->shader, "transform" );
|
||||||
shader->uScaleAlgo = egl_shaderGetUniform(shader->shader, "scaleAlgo" );
|
shader->uScaleAlgo = egl_shaderGetUniform(shader->shader, "scaleAlgo" );
|
||||||
shader->uNVGain = egl_shaderGetUniform(shader->shader, "nvGain" );
|
shader->uNVGain = egl_shaderGetUniform(shader->shader, "nvGain" );
|
||||||
shader->uCBMode = egl_shaderGetUniform(shader->shader, "cbMode" );
|
shader->uCBMode = egl_shaderGetUniform(shader->shader, "cbMode" );
|
||||||
|
shader->uIsHDR = egl_shaderGetUniform(shader->shader, "isHDR" );
|
||||||
|
shader->uMapHDRtoSDR = egl_shaderGetUniform(shader->shader, "mapHDRtoSDR" );
|
||||||
|
shader->uMapHDRGain = egl_shaderGetUniform(shader->shader, "mapHDRGain" );
|
||||||
|
shader->uMapHDRPQ = egl_shaderGetUniform(shader->shader, "mapHDRPQ" );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -135,15 +153,6 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!egl_initDesktopShader(
|
|
||||||
&desktop->shader,
|
|
||||||
b_shader_desktop_vert , b_shader_desktop_vert_size,
|
|
||||||
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size))
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Failed to initialize the generic desktop shader");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!egl_desktopRectsInit(&desktop->mesh, maxRects))
|
if (!egl_desktopRectsInit(&desktop->mesh, maxRects))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to initialize the desktop mesh");
|
DEBUG_ERROR("Failed to initialize the desktop mesh");
|
||||||
@ -157,6 +166,27 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!egl_initDesktopShader(
|
||||||
|
&desktop->shader,
|
||||||
|
b_shader_desktop_vert , b_shader_desktop_vert_size,
|
||||||
|
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size,
|
||||||
|
false))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to initialize the desktop shader");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useDMA)
|
||||||
|
if (!egl_initDesktopShader(
|
||||||
|
&desktop->dmaShader,
|
||||||
|
b_shader_desktop_vert , b_shader_desktop_vert_size,
|
||||||
|
b_shader_desktop_rgb_frag, b_shader_desktop_rgb_frag_size,
|
||||||
|
true))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to initialize the desktop DMA shader");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
app_registerKeybind(0, 'N', toggleNV, desktop,
|
app_registerKeybind(0, 'N', toggleNV, desktop,
|
||||||
"Toggle night vision mode");
|
"Toggle night vision mode");
|
||||||
|
|
||||||
@ -166,12 +196,19 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
|
|||||||
desktop->scaleAlgo = option_get_int("egl", "scale" );
|
desktop->scaleAlgo = option_get_int("egl", "scale" );
|
||||||
desktop->useDMA = useDMA;
|
desktop->useDMA = useDMA;
|
||||||
|
|
||||||
|
desktop->mapHDRtoSDR = option_get_bool("egl", "mapHDRtoSDR" );
|
||||||
|
desktop->peakLuminance = option_get_int ("egl", "peakLuminance");
|
||||||
|
desktop->maxCLL = option_get_int ("egl", "maxCLL" );
|
||||||
|
|
||||||
if (!egl_postProcessInit(&desktop->pp))
|
if (!egl_postProcessInit(&desktop->pp))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to initialize the post process manager");
|
DEBUG_ERROR("Failed to initialize the post process manager");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this MUST be first
|
||||||
|
egl_postProcessAdd(desktop->pp, &egl_filter24bitOps);
|
||||||
|
|
||||||
egl_postProcessAdd(desktop->pp, &egl_filterDownscaleOps);
|
egl_postProcessAdd(desktop->pp, &egl_filterDownscaleOps);
|
||||||
egl_postProcessAdd(desktop->pp, &egl_filterFFXCASOps );
|
egl_postProcessAdd(desktop->pp, &egl_filterFFXCASOps );
|
||||||
egl_postProcessAdd(desktop->pp, &egl_filterFFXFSR1Ops );
|
egl_postProcessAdd(desktop->pp, &egl_filterFFXFSR1Ops );
|
||||||
@ -205,11 +242,12 @@ void egl_desktopFree(EGL_Desktop ** desktop)
|
|||||||
if (!*desktop)
|
if (!*desktop)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
egl_textureFree (&(*desktop)->texture );
|
egl_textureFree (&(*desktop)->texture );
|
||||||
egl_textureFree (&(*desktop)->spiceTexture );
|
egl_textureFree (&(*desktop)->spiceTexture );
|
||||||
egl_shaderFree (&(*desktop)->shader.shader);
|
egl_shaderFree (&(*desktop)->shader .shader);
|
||||||
egl_desktopRectsFree(&(*desktop)->mesh );
|
egl_shaderFree (&(*desktop)->dmaShader.shader);
|
||||||
countedBufferRelease(&(*desktop)->matrix );
|
egl_desktopRectsFree(&(*desktop)->mesh );
|
||||||
|
countedBufferRelease(&(*desktop)->matrix );
|
||||||
|
|
||||||
egl_postProcessFree(&(*desktop)->pp);
|
egl_postProcessFree(&(*desktop)->pp);
|
||||||
|
|
||||||
@ -255,6 +293,28 @@ void egl_desktopConfigUI(EGL_Desktop * desktop)
|
|||||||
}
|
}
|
||||||
igSliderInt("##nvgain", &desktop->nvGain, 0, desktop->nvMax, format, 0);
|
igSliderInt("##nvgain", &desktop->nvGain, 0, desktop->nvMax, format, 0);
|
||||||
igPopItemWidth();
|
igPopItemWidth();
|
||||||
|
|
||||||
|
bool mapHDRtoSDR = desktop->mapHDRtoSDR;
|
||||||
|
int peakLuminance = desktop->peakLuminance;
|
||||||
|
int maxCLL = desktop->maxCLL;
|
||||||
|
|
||||||
|
igSeparator();
|
||||||
|
igCheckbox("Map HDR content to SDR", &mapHDRtoSDR);
|
||||||
|
igSliderInt("Peak Luminance", &peakLuminance, 1, 10000,
|
||||||
|
"%d nits",
|
||||||
|
ImGuiInputTextFlags_CharsDecimal);
|
||||||
|
igSliderInt("Max content light level", &maxCLL, 1, 10000,
|
||||||
|
"%d nits", ImGuiInputTextFlags_CharsDecimal);
|
||||||
|
|
||||||
|
if (mapHDRtoSDR != desktop->mapHDRtoSDR ||
|
||||||
|
peakLuminance != desktop->peakLuminance ||
|
||||||
|
maxCLL != desktop->maxCLL)
|
||||||
|
{
|
||||||
|
desktop->mapHDRtoSDR = mapHDRtoSDR;
|
||||||
|
desktop->peakLuminance = max(1, peakLuminance);
|
||||||
|
desktop->maxCLL = max(1, maxCLL);
|
||||||
|
app_invalidateWindow(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
|
bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
|
||||||
@ -280,6 +340,14 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
|
|||||||
pixFmt = EGL_PF_RGBA16F;
|
pixFmt = EGL_PF_RGBA16F;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FRAME_TYPE_BGR_32:
|
||||||
|
pixFmt = EGL_PF_BGR_32;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FRAME_TYPE_RGB_24:
|
||||||
|
pixFmt = EGL_PF_RGB_24;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DEBUG_ERROR("Unsupported frame format");
|
DEBUG_ERROR("Unsupported frame format");
|
||||||
return false;
|
return false;
|
||||||
@ -287,13 +355,16 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
|
|||||||
|
|
||||||
desktop->width = format.frameWidth;
|
desktop->width = format.frameWidth;
|
||||||
desktop->height = format.frameHeight;
|
desktop->height = format.frameHeight;
|
||||||
|
desktop->hdr = format.hdr;
|
||||||
|
desktop->hdrPQ = format.hdrPQ;
|
||||||
|
|
||||||
if (!egl_textureSetup(
|
if (!egl_textureSetup(
|
||||||
desktop->texture,
|
desktop->texture,
|
||||||
pixFmt,
|
pixFmt,
|
||||||
format.frameWidth,
|
desktop->format.dataWidth,
|
||||||
format.frameHeight,
|
desktop->format.dataHeight,
|
||||||
format.pitch
|
desktop->format.stride,
|
||||||
|
desktop->format.pitch
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to setup the desktop texture");
|
DEBUG_ERROR("Failed to setup the desktop texture");
|
||||||
@ -306,9 +377,9 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
|
|||||||
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd,
|
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd,
|
||||||
const FrameDamageRect * damageRects, int damageRectsCount)
|
const FrameDamageRect * damageRects, int damageRectsCount)
|
||||||
{
|
{
|
||||||
if (desktop->useDMA && dmaFd >= 0)
|
if (likely(desktop->useDMA && dmaFd >= 0))
|
||||||
{
|
{
|
||||||
if (egl_textureUpdateFromDMA(desktop->texture, frame, dmaFd))
|
if (likely(egl_textureUpdateFromDMA(desktop->texture, frame, dmaFd)))
|
||||||
{
|
{
|
||||||
atomic_store(&desktop->processFrame, true);
|
atomic_store(&desktop->processFrame, true);
|
||||||
return true;
|
return true;
|
||||||
@ -344,8 +415,8 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dma
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (egl_textureUpdateFromFrame(desktop->texture, frame,
|
if (likely(egl_textureUpdateFromFrame(desktop->texture, frame,
|
||||||
damageRects, damageRectsCount))
|
damageRects, damageRectsCount)))
|
||||||
{
|
{
|
||||||
atomic_store(&desktop->processFrame, true);
|
atomic_store(&desktop->processFrame, true);
|
||||||
return true;
|
return true;
|
||||||
@ -366,25 +437,28 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
|||||||
{
|
{
|
||||||
EGL_Texture * tex;
|
EGL_Texture * tex;
|
||||||
int width, height;
|
int width, height;
|
||||||
|
bool dma;
|
||||||
|
|
||||||
if (desktop->useSpice)
|
if (unlikely(desktop->useSpice))
|
||||||
{
|
{
|
||||||
tex = desktop->spiceTexture;
|
tex = desktop->spiceTexture;
|
||||||
width = desktop->spiceWidth;
|
width = desktop->spiceWidth;
|
||||||
height = desktop->spiceHeight;
|
height = desktop->spiceHeight;
|
||||||
|
dma = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tex = desktop->texture;
|
tex = desktop->texture;
|
||||||
width = desktop->width;
|
width = desktop->width;
|
||||||
height = desktop->height;
|
height = desktop->height;
|
||||||
|
dma = desktop->useDMA;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outputWidth == 0 && outputHeight == 0)
|
if (unlikely(outputWidth == 0 || outputHeight == 0))
|
||||||
DEBUG_FATAL("outputWidth || outputHeight == 0");
|
DEBUG_FATAL("outputWidth || outputHeight == 0");
|
||||||
|
|
||||||
enum EGL_TexStatus status;
|
enum EGL_TexStatus status;
|
||||||
if ((status = egl_textureProcess(tex)) != EGL_TEX_STATUS_OK)
|
if (unlikely((status = egl_textureProcess(tex)) != EGL_TEX_STATUS_OK))
|
||||||
{
|
{
|
||||||
if (status != EGL_TEX_STATUS_NOTREADY)
|
if (status != EGL_TEX_STATUS_NOTREADY)
|
||||||
DEBUG_ERROR("Failed to process the desktop texture");
|
DEBUG_ERROR("Failed to process the desktop texture");
|
||||||
@ -399,18 +473,17 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
|||||||
if (atomic_exchange(&desktop->processFrame, false) ||
|
if (atomic_exchange(&desktop->processFrame, false) ||
|
||||||
egl_postProcessConfigModified(desktop->pp))
|
egl_postProcessConfigModified(desktop->pp))
|
||||||
egl_postProcessRun(desktop->pp, tex, desktop->mesh,
|
egl_postProcessRun(desktop->pp, tex, desktop->mesh,
|
||||||
width, height, outputWidth, outputHeight);
|
width, height, outputWidth, outputHeight, dma);
|
||||||
|
|
||||||
unsigned int finalSizeX, finalSizeY;
|
unsigned int finalSizeX, finalSizeY;
|
||||||
GLuint texture = egl_postProcessGetOutput(desktop->pp,
|
EGL_Texture * texture = egl_postProcessGetOutput(desktop->pp,
|
||||||
&finalSizeX, &finalSizeY);
|
&finalSizeX, &finalSizeY);
|
||||||
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
egl_resetViewport(desktop->egl);
|
egl_resetViewport(desktop->egl);
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
egl_textureBind(texture);
|
||||||
glBindSampler(0, tex->sampler);
|
|
||||||
|
|
||||||
if (finalSizeX > width || finalSizeY > height)
|
if (finalSizeX > width || finalSizeY > height)
|
||||||
scaleType = EGL_DESKTOP_DOWNSCALE;
|
scaleType = EGL_DESKTOP_DOWNSCALE;
|
||||||
@ -435,7 +508,13 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
|||||||
scaleAlgo = desktop->scaleAlgo;
|
scaleAlgo = desktop->scaleAlgo;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct DesktopShader * shader = &desktop->shader;
|
const struct DesktopShader * shader =
|
||||||
|
desktop->useDMA && texture == desktop->texture ?
|
||||||
|
&desktop->dmaShader : &desktop->shader;
|
||||||
|
|
||||||
|
const float mapHDRGain =
|
||||||
|
(float)desktop->maxCLL / desktop->peakLuminance;
|
||||||
|
|
||||||
EGL_Uniform uniforms[] =
|
EGL_Uniform uniforms[] =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@ -463,6 +542,26 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
|
|||||||
.type = EGL_UNIFORM_TYPE_1I,
|
.type = EGL_UNIFORM_TYPE_1I,
|
||||||
.location = shader->uCBMode,
|
.location = shader->uCBMode,
|
||||||
.f = { desktop->cbMode }
|
.f = { desktop->cbMode }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = EGL_UNIFORM_TYPE_1I,
|
||||||
|
.location = shader->uIsHDR,
|
||||||
|
.i = { desktop->hdr }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = EGL_UNIFORM_TYPE_1I,
|
||||||
|
.location = shader->uMapHDRtoSDR,
|
||||||
|
.i = { desktop->mapHDRtoSDR }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = EGL_UNIFORM_TYPE_1F,
|
||||||
|
.location = shader->uMapHDRGain,
|
||||||
|
.f = { mapHDRGain }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = EGL_UNIFORM_TYPE_1I,
|
||||||
|
.location = shader->uMapHDRPQ,
|
||||||
|
.f = { desktop->hdrPQ }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -488,6 +587,7 @@ void egl_desktopSpiceConfigure(EGL_Desktop * desktop, int width, int height)
|
|||||||
EGL_PF_BGRA,
|
EGL_PF_BGRA,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
width,
|
||||||
width * 4
|
width * 4
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
@ -509,9 +609,9 @@ void egl_desktopSpiceDrawFill(EGL_Desktop * desktop, int x, int y, int width,
|
|||||||
for(int x = 0; x < width; ++x)
|
for(int x = 0; x < width; ++x)
|
||||||
line[x] = color;
|
line[x] = color;
|
||||||
|
|
||||||
for(; y < height; ++y)
|
for(int dy = 0; dy < height; ++dy)
|
||||||
egl_textureUpdateRect(desktop->spiceTexture,
|
egl_textureUpdateRect(desktop->spiceTexture,
|
||||||
x, y, width, 1, sizeof(line), (uint8_t *)line, false);
|
x, y + dy, width, 1, width, sizeof(line), (uint8_t *)line, false);
|
||||||
|
|
||||||
atomic_store(&desktop->processFrame, true);
|
atomic_store(&desktop->processFrame, true);
|
||||||
}
|
}
|
||||||
@ -520,11 +620,12 @@ void egl_desktopSpiceDrawBitmap(EGL_Desktop * desktop, int x, int y, int width,
|
|||||||
int height, int stride, uint8_t * data, bool topDown)
|
int height, int stride, uint8_t * data, bool topDown)
|
||||||
{
|
{
|
||||||
egl_textureUpdateRect(desktop->spiceTexture,
|
egl_textureUpdateRect(desktop->spiceTexture,
|
||||||
x, y, width, height, stride, data, topDown);
|
x, y, width, height, width, stride, data, topDown);
|
||||||
atomic_store(&desktop->processFrame, true);
|
atomic_store(&desktop->processFrame, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void egl_desktopSpiceShow(EGL_Desktop * desktop, bool show)
|
void egl_desktopSpiceShow(EGL_Desktop * desktop, bool show)
|
||||||
{
|
{
|
||||||
desktop->useSpice = show;
|
desktop->useSpice = show;
|
||||||
|
atomic_store(&desktop->processFrame, true);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -296,7 +296,7 @@ bool egl_screenToDesktop(struct FrameDamageRect * output, const double matrix[6]
|
|||||||
|
|
||||||
void egl_desktopRectsRender(EGL_DesktopRects * rects)
|
void egl_desktopRectsRender(EGL_DesktopRects * rects)
|
||||||
{
|
{
|
||||||
if (!rects->count)
|
if (unlikely(!rects->count))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
glBindVertexArray(rects->vao);
|
glBindVertexArray(rects->vao);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include "interface/renderer.h"
|
#include "interface/renderer.h"
|
||||||
|
|
||||||
|
#include "common/util.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
#include "common/KVMFR.h"
|
#include "common/KVMFR.h"
|
||||||
#include "common/option.h"
|
#include "common/option.h"
|
||||||
@ -202,6 +203,27 @@ static struct Option egl_options[] =
|
|||||||
.type = OPTION_TYPE_BOOL,
|
.type = OPTION_TYPE_BOOL,
|
||||||
.value.x_bool = true
|
.value.x_bool = true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.module = "egl",
|
||||||
|
.name = "mapHDRtoSDR",
|
||||||
|
.description = "Map HDR content to the SDR color space",
|
||||||
|
.type = OPTION_TYPE_BOOL,
|
||||||
|
.value.x_bool = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.module = "egl",
|
||||||
|
.name = "peakLuminance",
|
||||||
|
.description = "The peak luminance level in nits for HDR to SDR mapping",
|
||||||
|
.type = OPTION_TYPE_INT,
|
||||||
|
.value.x_int = 250,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.module = "egl",
|
||||||
|
.name = "maxCLL",
|
||||||
|
.description = "Maximum content light level in nits for HDR to SDR mapping",
|
||||||
|
.type = OPTION_TYPE_INT,
|
||||||
|
.value.x_int = 10000,
|
||||||
|
},
|
||||||
|
|
||||||
{0}
|
{0}
|
||||||
};
|
};
|
||||||
@ -559,7 +581,7 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo
|
|||||||
this->formatValid = true;
|
this->formatValid = true;
|
||||||
|
|
||||||
/* this event runs in a second thread so we need to init it here */
|
/* this event runs in a second thread so we need to init it here */
|
||||||
if (!this->frameContext)
|
if (unlikely(!this->frameContext))
|
||||||
{
|
{
|
||||||
static EGLint attrs[] = {
|
static EGLint attrs[] = {
|
||||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||||
@ -579,7 +601,7 @@ static bool egl_onFrameFormat(LG_Renderer * renderer, const LG_RendererFormat fo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->scalePointer)
|
if (likely(this->scalePointer))
|
||||||
{
|
{
|
||||||
float scale = max(1.0f, (float)format.screenWidth / this->width);
|
float scale = max(1.0f, (float)format.screenWidth / this->width);
|
||||||
egl_cursorSetScale(this->cursor, scale);
|
egl_cursorSetScale(this->cursor, scale);
|
||||||
@ -602,7 +624,8 @@ static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int d
|
|||||||
struct Inst * this = UPCAST(struct Inst, renderer);
|
struct Inst * this = UPCAST(struct Inst, renderer);
|
||||||
|
|
||||||
uint64_t start = nanotime();
|
uint64_t start = nanotime();
|
||||||
if (!egl_desktopUpdate(this->desktop, frame, dmaFd, damageRects, damageRectsCount))
|
if (unlikely(!egl_desktopUpdate(
|
||||||
|
this->desktop, frame, dmaFd, damageRects, damageRectsCount)))
|
||||||
{
|
{
|
||||||
DEBUG_INFO("Failed to to update the desktop");
|
DEBUG_INFO("Failed to to update the desktop");
|
||||||
return false;
|
return false;
|
||||||
@ -611,12 +634,17 @@ static bool egl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int d
|
|||||||
|
|
||||||
INTERLOCKED_SECTION(this->desktopDamageLock, {
|
INTERLOCKED_SECTION(this->desktopDamageLock, {
|
||||||
struct DesktopDamage * damage = this->desktopDamage + this->desktopDamageIdx;
|
struct DesktopDamage * damage = this->desktopDamage + this->desktopDamageIdx;
|
||||||
if (damage->count == -1 || damageRectsCount == 0 ||
|
if (unlikely(
|
||||||
damage->count + damageRectsCount >= KVMFR_MAX_DAMAGE_RECTS)
|
damage->count == -1 ||
|
||||||
|
damageRectsCount == 0 ||
|
||||||
|
damage->count + damageRectsCount >= KVMFR_MAX_DAMAGE_RECTS))
|
||||||
|
{
|
||||||
damage->count = -1;
|
damage->count = -1;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memcpy(damage->rects + damage->count, damageRects, damageRectsCount * sizeof(FrameDamageRect));
|
memcpy(damage->rects + damage->count, damageRects,
|
||||||
|
damageRectsCount * sizeof(FrameDamageRect));
|
||||||
damage->count += damageRectsCount;
|
damage->count += damageRectsCount;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -748,7 +776,7 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
|
|||||||
|
|
||||||
EGLint attr[] =
|
EGLint attr[] =
|
||||||
{
|
{
|
||||||
EGL_BUFFER_SIZE , 24,
|
EGL_BUFFER_SIZE , 30,
|
||||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||||
EGL_SAMPLE_BUFFERS , maxSamples > 0 ? 1 : 0,
|
EGL_SAMPLE_BUFFERS , maxSamples > 0 ? 1 : 0,
|
||||||
EGL_SAMPLES , maxSamples,
|
EGL_SAMPLES , maxSamples,
|
||||||
@ -1035,14 +1063,14 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
|||||||
accumulated->count = 0;
|
accumulated->count = 0;
|
||||||
|
|
||||||
INTERLOCKED_SECTION(this->desktopDamageLock, {
|
INTERLOCKED_SECTION(this->desktopDamageLock, {
|
||||||
if (!renderAll)
|
if (likely(!renderAll))
|
||||||
{
|
{
|
||||||
for (int i = 0; i < bufferAge; ++i)
|
for (int i = 0; i < bufferAge; ++i)
|
||||||
{
|
{
|
||||||
struct DesktopDamage * damage = this->desktopDamage +
|
struct DesktopDamage * damage = this->desktopDamage +
|
||||||
IDX_AGO(this->desktopDamageIdx, i, DESKTOP_DAMAGE_COUNT);
|
IDX_AGO(this->desktopDamageIdx, i, DESKTOP_DAMAGE_COUNT);
|
||||||
|
|
||||||
if (damage->count < 0)
|
if (unlikely(damage->count < 0))
|
||||||
{
|
{
|
||||||
renderAll = true;
|
renderAll = true;
|
||||||
break;
|
break;
|
||||||
@ -1066,7 +1094,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
|||||||
this->desktopDamage[this->desktopDamageIdx].count = 0;
|
this->desktopDamage[this->desktopDamageIdx].count = 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!renderAll)
|
if (likely(!renderAll))
|
||||||
{
|
{
|
||||||
double matrix[6];
|
double matrix[6];
|
||||||
egl_screenToDesktopMatrix(matrix,
|
egl_screenToDesktopMatrix(matrix,
|
||||||
@ -1080,7 +1108,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
|||||||
int count = this->overlayHistoryCount[idx];
|
int count = this->overlayHistoryCount[idx];
|
||||||
struct Rect * damage = this->overlayHistory[idx];
|
struct Rect * damage = this->overlayHistory[idx];
|
||||||
|
|
||||||
if (count < 0)
|
if (unlikely(count < 0))
|
||||||
{
|
{
|
||||||
renderAll = true;
|
renderAll = true;
|
||||||
break;
|
break;
|
||||||
@ -1093,11 +1121,12 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
accumulated->count = rectsMergeOverlapping(accumulated->rects, accumulated->count);
|
accumulated->count = rectsMergeOverlapping(accumulated->rects,
|
||||||
|
accumulated->count);
|
||||||
}
|
}
|
||||||
++this->overlayHistoryIdx;
|
++this->overlayHistoryIdx;
|
||||||
|
|
||||||
if (this->destRect.w > 0 && this->destRect.h > 0)
|
if (likely(this->destRect.w > 0 && this->destRect.h > 0))
|
||||||
{
|
{
|
||||||
if (egl_desktopRender(this->desktop,
|
if (egl_desktopRender(this->desktop,
|
||||||
this->destRect.w, this->destRect.h,
|
this->destRect.w, this->destRect.h,
|
||||||
@ -1115,41 +1144,39 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
|||||||
|
|
||||||
renderLetterBox(this);
|
renderLetterBox(this);
|
||||||
|
|
||||||
hasOverlay |= egl_damageRender(this->damage, rotate, newFrame ? desktopDamage : NULL);
|
hasOverlay |=
|
||||||
hasOverlay |= invalidateWindow;
|
egl_damageRender(this->damage, rotate, newFrame ? desktopDamage : NULL) |
|
||||||
|
invalidateWindow;
|
||||||
|
|
||||||
struct Rect damage[KVMFR_MAX_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2];
|
struct Rect damage[KVMFR_MAX_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2];
|
||||||
int damageIdx = app_renderOverlay(damage, MAX_OVERLAY_RECTS);
|
int damageIdx = app_renderOverlay(damage, MAX_OVERLAY_RECTS);
|
||||||
|
if (unlikely(damageIdx != 0))
|
||||||
switch (damageIdx)
|
|
||||||
{
|
{
|
||||||
case 0: // no overlay
|
if (damageIdx == -1)
|
||||||
break;
|
|
||||||
case -1: // full damage
|
|
||||||
hasOverlay = true;
|
hasOverlay = true;
|
||||||
// fallthrough
|
|
||||||
default:
|
|
||||||
ImGui_ImplOpenGL3_NewFrame();
|
|
||||||
ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData());
|
|
||||||
|
|
||||||
for (int i = 0; i < damageIdx; ++i)
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
damage[i].y = this->height - damage[i].y - damage[i].h;
|
ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData());
|
||||||
|
|
||||||
|
for (int i = 0; i < damageIdx; ++i)
|
||||||
|
damage[i].y = this->height - damage[i].y - damage[i].h;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (damageIdx >= 0 && cursorState.visible)
|
if (likely(damageIdx >= 0 && cursorState.visible))
|
||||||
damage[damageIdx++] = cursorState.rect;
|
damage[damageIdx++] = cursorState.rect;
|
||||||
|
|
||||||
int overlayHistoryIdx = this->overlayHistoryIdx % DESKTOP_DAMAGE_COUNT;
|
int overlayHistoryIdx = this->overlayHistoryIdx % DESKTOP_DAMAGE_COUNT;
|
||||||
if (hasOverlay)
|
if (unlikely(hasOverlay))
|
||||||
this->overlayHistoryCount[overlayHistoryIdx] = -1;
|
this->overlayHistoryCount[overlayHistoryIdx] = -1;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (damageIdx > 0)
|
if (unlikely(damageIdx > 0))
|
||||||
memcpy(this->overlayHistory[overlayHistoryIdx], damage, damageIdx * sizeof(struct Rect));
|
memcpy(this->overlayHistory[overlayHistoryIdx],
|
||||||
|
damage, damageIdx * sizeof(struct Rect));
|
||||||
this->overlayHistoryCount[overlayHistoryIdx] = damageIdx;
|
this->overlayHistoryCount[overlayHistoryIdx] = damageIdx;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasOverlay && !this->hadOverlay)
|
if (unlikely(!hasOverlay && !this->hadOverlay))
|
||||||
{
|
{
|
||||||
if (this->cursorLast.visible)
|
if (this->cursorLast.visible)
|
||||||
damage[damageIdx++] = this->cursorLast.rect;
|
damage[damageIdx++] = this->cursorLast.rect;
|
||||||
@ -1176,7 +1203,9 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
|
|||||||
this->cursorLast = cursorState;
|
this->cursorLast = cursorState;
|
||||||
|
|
||||||
preSwap(udata);
|
preSwap(udata);
|
||||||
app_eglSwapBuffers(this->display, this->surface, damage, this->noSwapDamage ? 0 : damageIdx);
|
app_eglSwapBuffers(this->display, this->surface, damage,
|
||||||
|
this->noSwapDamage ? 0 : damageIdx);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -37,7 +37,10 @@ typedef enum EGL_PixelFormat
|
|||||||
EGL_PF_RGBA,
|
EGL_PF_RGBA,
|
||||||
EGL_PF_BGRA,
|
EGL_PF_BGRA,
|
||||||
EGL_PF_RGBA10,
|
EGL_PF_RGBA10,
|
||||||
EGL_PF_RGBA16F
|
EGL_PF_RGBA16F,
|
||||||
|
EGL_PF_BGR_32,
|
||||||
|
EGL_PF_RGB_24,
|
||||||
|
EGL_PF_RGB_24_32
|
||||||
}
|
}
|
||||||
EGL_PixelFormat;
|
EGL_PixelFormat;
|
||||||
|
|
||||||
@ -60,13 +63,17 @@ typedef struct EGL_TexSetup
|
|||||||
/* the height of the texture in pixels */
|
/* the height of the texture in pixels */
|
||||||
size_t height;
|
size_t height;
|
||||||
|
|
||||||
/* the stide of the texture in bytes */
|
/* the row length of the texture in pixels */
|
||||||
size_t stride;
|
size_t stride;
|
||||||
|
|
||||||
|
/* the row length of the texture in bytes */
|
||||||
|
size_t pitch;
|
||||||
}
|
}
|
||||||
EGL_TexSetup;
|
EGL_TexSetup;
|
||||||
|
|
||||||
typedef enum EGL_FilterType
|
typedef enum EGL_FilterType
|
||||||
{
|
{
|
||||||
|
EGL_FILTER_TYPE_INTERNAL,
|
||||||
EGL_FILTER_TYPE_EFFECT,
|
EGL_FILTER_TYPE_EFFECT,
|
||||||
EGL_FILTER_TYPE_UPSCALE,
|
EGL_FILTER_TYPE_UPSCALE,
|
||||||
EGL_FILTER_TYPE_DOWNSCALE
|
EGL_FILTER_TYPE_DOWNSCALE
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -68,18 +68,21 @@ typedef struct EGL_FilterOps
|
|||||||
/* reads filter state from options */
|
/* reads filter state from options */
|
||||||
void (*loadState)(EGL_Filter * filter);
|
void (*loadState)(EGL_Filter * filter);
|
||||||
|
|
||||||
/* set the input format of the filter */
|
/* set the input format of the filter
|
||||||
|
* useDMA will be true if the texture provided needs to use samplerExternalOES
|
||||||
|
*/
|
||||||
bool (*setup)(EGL_Filter * filter, enum EGL_PixelFormat pixFmt,
|
bool (*setup)(EGL_Filter * filter, enum EGL_PixelFormat pixFmt,
|
||||||
unsigned int width, unsigned int height);
|
unsigned int width, unsigned int height,
|
||||||
|
unsigned int desktopWidth, unsigned int desktopHeight, bool useDMA);
|
||||||
|
|
||||||
/* set the output resolution hint for the filter
|
/* set the output resolution hint for the filter
|
||||||
* this is optional and only a hint */
|
* this is optional and only a hint */
|
||||||
void (*setOutputResHint)(EGL_Filter * filter,
|
void (*setOutputResHint)(EGL_Filter * filter,
|
||||||
unsigned int x, unsigned int y);
|
unsigned int x, unsigned int y);
|
||||||
|
|
||||||
/* returns the output resolution of the filter */
|
/* returns the output resolution and pixel format of the filter */
|
||||||
void (*getOutputRes)(EGL_Filter * filter,
|
void (*getOutputRes)(EGL_Filter * filter,
|
||||||
unsigned int *x, unsigned int *y);
|
unsigned int *x, unsigned int *y, enum EGL_PixelFormat *pixFmt);
|
||||||
|
|
||||||
/* prepare the shader for use
|
/* prepare the shader for use
|
||||||
* A filter can return false to bypass it */
|
* A filter can return false to bypass it */
|
||||||
@ -87,8 +90,8 @@ typedef struct EGL_FilterOps
|
|||||||
|
|
||||||
/* runs the filter on the provided texture
|
/* runs the filter on the provided texture
|
||||||
* returns the processed texture as the output */
|
* returns the processed texture as the output */
|
||||||
GLuint (*run)(EGL_Filter * filter, EGL_FilterRects * rects,
|
EGL_Texture * (*run)(EGL_Filter * filter, EGL_FilterRects * rects,
|
||||||
GLuint texture);
|
EGL_Texture * texture);
|
||||||
|
|
||||||
/* called when the filter output is no loger needed so it can release memory
|
/* called when the filter output is no loger needed so it can release memory
|
||||||
* this is optional */
|
* this is optional */
|
||||||
@ -102,6 +105,12 @@ typedef struct EGL_Filter
|
|||||||
}
|
}
|
||||||
EGL_Filter;
|
EGL_Filter;
|
||||||
|
|
||||||
|
static inline void egl_filterEarlyInit(const EGL_FilterOps * ops)
|
||||||
|
{
|
||||||
|
if (ops->earlyInit)
|
||||||
|
ops->earlyInit();
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool egl_filterInit(const EGL_FilterOps * ops, EGL_Filter ** filter)
|
static inline bool egl_filterInit(const EGL_FilterOps * ops, EGL_Filter ** filter)
|
||||||
{
|
{
|
||||||
if (!ops->init(filter))
|
if (!ops->init(filter))
|
||||||
@ -119,23 +128,30 @@ static inline void egl_filterFree(EGL_Filter ** filter)
|
|||||||
|
|
||||||
static inline bool egl_filterImguiConfig(EGL_Filter * filter)
|
static inline bool egl_filterImguiConfig(EGL_Filter * filter)
|
||||||
{
|
{
|
||||||
return filter->ops.imguiConfig(filter);
|
if (filter->ops.imguiConfig)
|
||||||
|
return filter->ops.imguiConfig(filter);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void egl_filterSaveState(EGL_Filter * filter)
|
static inline void egl_filterSaveState(EGL_Filter * filter)
|
||||||
{
|
{
|
||||||
filter->ops.saveState(filter);
|
if (filter->ops.saveState)
|
||||||
|
filter->ops.saveState(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void egl_filterLoadState(EGL_Filter * filter)
|
static inline void egl_filterLoadState(EGL_Filter * filter)
|
||||||
{
|
{
|
||||||
filter->ops.loadState(filter);
|
if (filter->ops.loadState)
|
||||||
|
filter->ops.loadState(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool egl_filterSetup(EGL_Filter * filter,
|
static inline bool egl_filterSetup(EGL_Filter * filter,
|
||||||
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
|
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
|
||||||
|
unsigned int desktopWidth, unsigned int desktopHeight,
|
||||||
|
bool useDMA)
|
||||||
{
|
{
|
||||||
return filter->ops.setup(filter, pixFmt, width, height);
|
return filter->ops.setup(filter, pixFmt, width, height,
|
||||||
|
desktopWidth, desktopHeight, useDMA);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void egl_filterSetOutputResHint(EGL_Filter * filter,
|
static inline void egl_filterSetOutputResHint(EGL_Filter * filter,
|
||||||
@ -146,9 +162,9 @@ static inline void egl_filterSetOutputResHint(EGL_Filter * filter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline void egl_filterGetOutputRes(EGL_Filter * filter,
|
static inline void egl_filterGetOutputRes(EGL_Filter * filter,
|
||||||
unsigned int *x, unsigned int *y)
|
unsigned int *x, unsigned int *y, enum EGL_PixelFormat *pixFmt)
|
||||||
{
|
{
|
||||||
return filter->ops.getOutputRes(filter, x, y);
|
return filter->ops.getOutputRes(filter, x, y, pixFmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool egl_filterPrepare(EGL_Filter * filter)
|
static inline bool egl_filterPrepare(EGL_Filter * filter)
|
||||||
@ -156,8 +172,8 @@ static inline bool egl_filterPrepare(EGL_Filter * filter)
|
|||||||
return filter->ops.prepare(filter);
|
return filter->ops.prepare(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline GLuint egl_filterRun(EGL_Filter * filter,
|
static inline EGL_Texture * egl_filterRun(EGL_Filter * filter,
|
||||||
EGL_FilterRects * rects, GLuint texture)
|
EGL_FilterRects * rects, EGL_Texture * texture)
|
||||||
{
|
{
|
||||||
return filter->ops.run(filter, rects, texture);
|
return filter->ops.run(filter, rects, texture);
|
||||||
}
|
}
|
||||||
|
221
client/renderers/EGL/filter_24bit.c
Normal file
221
client/renderers/EGL/filter_24bit.c
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "filter.h"
|
||||||
|
#include "framebuffer.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "common/array.h"
|
||||||
|
#include "common/debug.h"
|
||||||
|
#include "common/option.h"
|
||||||
|
#include "cimgui.h"
|
||||||
|
|
||||||
|
#include "basic.vert.h"
|
||||||
|
#include "convert_24bit.frag.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct EGL_Filter24bit
|
||||||
|
{
|
||||||
|
EGL_Filter base;
|
||||||
|
|
||||||
|
bool enable;
|
||||||
|
EGL_PixelFormat format;
|
||||||
|
int useDMA;
|
||||||
|
unsigned int width, height;
|
||||||
|
unsigned int desktopWidth, desktopHeight;
|
||||||
|
bool prepared;
|
||||||
|
|
||||||
|
EGL_Uniform uOutputSize;
|
||||||
|
|
||||||
|
EGL_Shader * shader;
|
||||||
|
EGL_Framebuffer * fb;
|
||||||
|
GLuint sampler[2];
|
||||||
|
}
|
||||||
|
EGL_Filter24bit;
|
||||||
|
|
||||||
|
static bool egl_filter24bitInit(EGL_Filter ** filter)
|
||||||
|
{
|
||||||
|
EGL_Filter24bit * this = calloc(1, sizeof(*this));
|
||||||
|
if (!this)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to allocate ram");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->useDMA = -1;
|
||||||
|
|
||||||
|
if (!egl_shaderInit(&this->shader))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to initialize the shader");
|
||||||
|
goto error_this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!egl_framebufferInit(&this->fb))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to initialize the framebuffer");
|
||||||
|
goto error_shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
glGenSamplers(ARRAY_LENGTH(this->sampler), this->sampler);
|
||||||
|
glSamplerParameteri(this->sampler[0], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glSamplerParameteri(this->sampler[0], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glSamplerParameteri(this->sampler[0], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
|
||||||
|
glSamplerParameteri(this->sampler[0], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
|
||||||
|
glSamplerParameteri(this->sampler[1], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glSamplerParameteri(this->sampler[1], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glSamplerParameteri(this->sampler[1], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
|
||||||
|
glSamplerParameteri(this->sampler[1], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
|
||||||
|
|
||||||
|
*filter = &this->base;
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error_shader:
|
||||||
|
egl_shaderFree(&this->shader);
|
||||||
|
|
||||||
|
error_this:
|
||||||
|
free(this);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void egl_filter24bitFree(EGL_Filter * filter)
|
||||||
|
{
|
||||||
|
EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter);
|
||||||
|
|
||||||
|
egl_shaderFree(&this->shader);
|
||||||
|
egl_framebufferFree(&this->fb);
|
||||||
|
glDeleteSamplers(ARRAY_LENGTH(this->sampler), this->sampler);
|
||||||
|
free(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool egl_filter24bitSetup(EGL_Filter * filter,
|
||||||
|
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
|
||||||
|
unsigned int desktopWidth, unsigned int desktopHeight,
|
||||||
|
bool useDMA)
|
||||||
|
{
|
||||||
|
EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter);
|
||||||
|
|
||||||
|
if (pixFmt != EGL_PF_BGR_32 && pixFmt != EGL_PF_RGB_24_32)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (this->useDMA != useDMA || this->format != pixFmt)
|
||||||
|
{
|
||||||
|
EGL_ShaderDefine defines[] =
|
||||||
|
{
|
||||||
|
{"OUTPUT", pixFmt == EGL_PF_BGR_32 ? "fragColor.bgra" : "fragColor.rgba" },
|
||||||
|
{0}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!egl_shaderCompile(this->shader,
|
||||||
|
b_shader_basic_vert , b_shader_basic_vert_size,
|
||||||
|
b_shader_convert_24bit_frag, b_shader_convert_24bit_frag_size,
|
||||||
|
useDMA, defines)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to compile the shader");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->uOutputSize.type = EGL_UNIFORM_TYPE_2F;
|
||||||
|
this->uOutputSize.location =
|
||||||
|
egl_shaderGetUniform(this->shader, "outputSize");
|
||||||
|
|
||||||
|
this->useDMA = useDMA;
|
||||||
|
this->prepared = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->prepared &&
|
||||||
|
this->width == width &&
|
||||||
|
this->height == height &&
|
||||||
|
this->desktopWidth == desktopWidth &&
|
||||||
|
this->desktopHeight == desktopHeight)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!egl_framebufferSetup(this->fb, EGL_PF_BGRA, desktopWidth, desktopHeight))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
this->format = pixFmt;
|
||||||
|
this->width = width;
|
||||||
|
this->height = height;
|
||||||
|
this->desktopWidth = desktopWidth;
|
||||||
|
this->desktopHeight = desktopHeight;
|
||||||
|
this->prepared = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void egl_filter24bitGetOutputRes(EGL_Filter * filter,
|
||||||
|
unsigned int *width, unsigned int *height, enum EGL_PixelFormat *pixFmt)
|
||||||
|
{
|
||||||
|
EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter);
|
||||||
|
*width = this->desktopWidth;
|
||||||
|
*height = this->desktopHeight;
|
||||||
|
*pixFmt = EGL_PF_BGRA;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool egl_filter24bitPrepare(EGL_Filter * filter)
|
||||||
|
{
|
||||||
|
EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter);
|
||||||
|
|
||||||
|
if (this->prepared)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
this->uOutputSize.f[0] = this->desktopWidth;
|
||||||
|
this->uOutputSize.f[1] = this->desktopHeight;
|
||||||
|
egl_shaderSetUniforms(this->shader, &this->uOutputSize, 1);
|
||||||
|
|
||||||
|
this->prepared = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EGL_Texture * egl_filter24bitRun(EGL_Filter * filter,
|
||||||
|
EGL_FilterRects * rects, EGL_Texture * texture)
|
||||||
|
{
|
||||||
|
EGL_Filter24bit * this = UPCAST(EGL_Filter24bit, filter);
|
||||||
|
|
||||||
|
egl_framebufferBind(this->fb);
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
egl_textureBind(texture);
|
||||||
|
|
||||||
|
glBindSampler(0, this->sampler[0]);
|
||||||
|
|
||||||
|
egl_shaderUse(this->shader);
|
||||||
|
egl_filterRectsRender(this->shader, rects);
|
||||||
|
|
||||||
|
return egl_framebufferGetTexture(this->fb);
|
||||||
|
}
|
||||||
|
|
||||||
|
EGL_FilterOps egl_filter24bitOps =
|
||||||
|
{
|
||||||
|
.id = "24bit",
|
||||||
|
.name = "24bit",
|
||||||
|
.type = EGL_FILTER_TYPE_INTERNAL,
|
||||||
|
.earlyInit = NULL,
|
||||||
|
.init = egl_filter24bitInit,
|
||||||
|
.free = egl_filter24bitFree,
|
||||||
|
.imguiConfig = NULL,
|
||||||
|
.saveState = NULL,
|
||||||
|
.loadState = NULL,
|
||||||
|
.setup = egl_filter24bitSetup,
|
||||||
|
.getOutputRes = egl_filter24bitGetOutputRes,
|
||||||
|
.prepare = egl_filter24bitPrepare,
|
||||||
|
.run = egl_filter24bitRun
|
||||||
|
};
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -60,6 +60,7 @@ typedef struct EGL_FilterDownscale
|
|||||||
EGL_Shader * lanczos2;
|
EGL_Shader * lanczos2;
|
||||||
|
|
||||||
DownscaleFilter filter;
|
DownscaleFilter filter;
|
||||||
|
int useDMA;
|
||||||
enum EGL_PixelFormat pixFmt;
|
enum EGL_PixelFormat pixFmt;
|
||||||
unsigned int width, height;
|
unsigned int width, height;
|
||||||
float pixelSize;
|
float pixelSize;
|
||||||
@ -157,55 +158,26 @@ static bool egl_filterDownscaleInit(EGL_Filter ** filter)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->useDMA = -1;
|
||||||
|
|
||||||
if (!egl_shaderInit(&this->nearest))
|
if (!egl_shaderInit(&this->nearest))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to initialize the shader");
|
DEBUG_ERROR("Failed to initialize the shader");
|
||||||
goto error_this;
|
goto error_this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!egl_shaderCompile(this->nearest,
|
|
||||||
b_shader_basic_vert , b_shader_basic_vert_size,
|
|
||||||
b_shader_downscale_frag, b_shader_downscale_frag_size)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Failed to compile the shader");
|
|
||||||
goto error_shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!egl_shaderInit(&this->linear))
|
if (!egl_shaderInit(&this->linear))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to initialize the shader");
|
DEBUG_ERROR("Failed to initialize the shader");
|
||||||
goto error_this;
|
goto error_this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!egl_shaderCompile(this->linear,
|
|
||||||
b_shader_basic_vert, b_shader_basic_vert_size,
|
|
||||||
b_shader_downscale_linear_frag, b_shader_downscale_linear_frag_size)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Failed to compile the shader");
|
|
||||||
goto error_shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!egl_shaderInit(&this->lanczos2))
|
if (!egl_shaderInit(&this->lanczos2))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to initialize the shader");
|
DEBUG_ERROR("Failed to initialize the shader");
|
||||||
goto error_this;
|
goto error_this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!egl_shaderCompile(this->lanczos2,
|
|
||||||
b_shader_basic_vert, b_shader_basic_vert_size,
|
|
||||||
b_shader_downscale_lanczos2_frag, b_shader_downscale_lanczos2_frag_size)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Failed to compile the shader");
|
|
||||||
goto error_shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->uNearest.type = EGL_UNIFORM_TYPE_3F;
|
|
||||||
this->uNearest.location =
|
|
||||||
egl_shaderGetUniform(this->nearest, "uConfig");
|
|
||||||
|
|
||||||
if (!egl_framebufferInit(&this->fb))
|
if (!egl_framebufferInit(&this->fb))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to initialize the framebuffer");
|
DEBUG_ERROR("Failed to initialize the framebuffer");
|
||||||
@ -326,7 +298,9 @@ static bool egl_filterDownscaleImguiConfig(EGL_Filter * filter)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool egl_filterDownscaleSetup(EGL_Filter * filter,
|
static bool egl_filterDownscaleSetup(EGL_Filter * filter,
|
||||||
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
|
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
|
||||||
|
unsigned int desktopWidth, unsigned int desktopHeight,
|
||||||
|
bool useDMA)
|
||||||
{
|
{
|
||||||
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
|
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
|
||||||
|
|
||||||
@ -336,6 +310,48 @@ static bool egl_filterDownscaleSetup(EGL_Filter * filter,
|
|||||||
if (!this->enable)
|
if (!this->enable)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (this->useDMA != useDMA)
|
||||||
|
{
|
||||||
|
if (!egl_shaderCompile(this->nearest,
|
||||||
|
b_shader_basic_vert , b_shader_basic_vert_size,
|
||||||
|
b_shader_downscale_frag,
|
||||||
|
b_shader_downscale_frag_size,
|
||||||
|
useDMA, NULL)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to compile the shader");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!egl_shaderCompile(this->linear,
|
||||||
|
b_shader_basic_vert, b_shader_basic_vert_size,
|
||||||
|
b_shader_downscale_linear_frag,
|
||||||
|
b_shader_downscale_linear_frag_size,
|
||||||
|
useDMA, NULL)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to compile the shader");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!egl_shaderCompile(this->lanczos2,
|
||||||
|
b_shader_basic_vert, b_shader_basic_vert_size,
|
||||||
|
b_shader_downscale_lanczos2_frag,
|
||||||
|
b_shader_downscale_lanczos2_frag_size,
|
||||||
|
useDMA, NULL)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to compile the shader");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->uNearest.type = EGL_UNIFORM_TYPE_3F;
|
||||||
|
this->uNearest.location =
|
||||||
|
egl_shaderGetUniform(this->nearest, "uConfig");
|
||||||
|
|
||||||
|
this->useDMA = useDMA;
|
||||||
|
}
|
||||||
|
|
||||||
if (this->prepared &&
|
if (this->prepared &&
|
||||||
pixFmt == this->pixFmt &&
|
pixFmt == this->pixFmt &&
|
||||||
this->width == width &&
|
this->width == width &&
|
||||||
@ -354,11 +370,12 @@ static bool egl_filterDownscaleSetup(EGL_Filter * filter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void egl_filterDownscaleGetOutputRes(EGL_Filter * filter,
|
static void egl_filterDownscaleGetOutputRes(EGL_Filter * filter,
|
||||||
unsigned int *width, unsigned int *height)
|
unsigned int *width, unsigned int *height, enum EGL_PixelFormat *pixFmt)
|
||||||
{
|
{
|
||||||
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
|
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
|
||||||
*width = this->width;
|
*width = this->width;
|
||||||
*height = this->height;
|
*height = this->height;
|
||||||
|
*pixFmt = this->pixFmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool egl_filterDownscalePrepare(EGL_Filter * filter)
|
static bool egl_filterDownscalePrepare(EGL_Filter * filter)
|
||||||
@ -385,15 +402,15 @@ static bool egl_filterDownscalePrepare(EGL_Filter * filter)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GLuint egl_filterDownscaleRun(EGL_Filter * filter,
|
static EGL_Texture * egl_filterDownscaleRun(EGL_Filter * filter,
|
||||||
EGL_FilterRects * rects, GLuint texture)
|
EGL_FilterRects * rects, EGL_Texture * texture)
|
||||||
{
|
{
|
||||||
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
|
EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter);
|
||||||
|
|
||||||
egl_framebufferBind(this->fb);
|
egl_framebufferBind(this->fb);
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
egl_textureBind(texture);
|
||||||
|
|
||||||
EGL_Shader * shader;
|
EGL_Shader * shader;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -37,6 +37,7 @@ typedef struct EGL_FilterFFXCAS
|
|||||||
EGL_Shader * shader;
|
EGL_Shader * shader;
|
||||||
bool enable;
|
bool enable;
|
||||||
|
|
||||||
|
int useDMA;
|
||||||
enum EGL_PixelFormat pixFmt;
|
enum EGL_PixelFormat pixFmt;
|
||||||
unsigned int width, height;
|
unsigned int width, height;
|
||||||
float sharpness;
|
float sharpness;
|
||||||
@ -106,21 +107,14 @@ static bool egl_filterFFXCASInit(EGL_Filter ** filter)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->useDMA = -1;
|
||||||
|
|
||||||
if (!egl_shaderInit(&this->shader))
|
if (!egl_shaderInit(&this->shader))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to initialize the shader");
|
DEBUG_ERROR("Failed to initialize the shader");
|
||||||
goto error_this;
|
goto error_this;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!egl_shaderCompile(this->shader,
|
|
||||||
b_shader_basic_vert , b_shader_basic_vert_size,
|
|
||||||
b_shader_ffx_cas_frag, b_shader_ffx_cas_frag_size)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Failed to compile the shader");
|
|
||||||
goto error_shader;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->consts = countedBufferNew(8 * sizeof(GLuint));
|
this->consts = countedBufferNew(8 * sizeof(GLuint));
|
||||||
if (!this->consts)
|
if (!this->consts)
|
||||||
{
|
{
|
||||||
@ -128,12 +122,6 @@ static bool egl_filterFFXCASInit(EGL_Filter ** filter)
|
|||||||
goto error_shader;
|
goto error_shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
egl_shaderSetUniforms(this->shader, &(EGL_Uniform) {
|
|
||||||
.type = EGL_UNIFORM_TYPE_4UIV,
|
|
||||||
.location = egl_shaderGetUniform(this->shader, "uConsts"),
|
|
||||||
.v = this->consts,
|
|
||||||
}, 1);
|
|
||||||
|
|
||||||
egl_filterFFXCASLoadState(&this->base);
|
egl_filterFFXCASLoadState(&this->base);
|
||||||
|
|
||||||
if (!egl_framebufferInit(&this->fb))
|
if (!egl_framebufferInit(&this->fb))
|
||||||
@ -220,13 +208,36 @@ static bool egl_filterFFXCASImguiConfig(EGL_Filter * filter)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool egl_filterFFXCASSetup(EGL_Filter * filter,
|
static bool egl_filterFFXCASSetup(EGL_Filter * filter,
|
||||||
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
|
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
|
||||||
|
unsigned int desktopWidth, unsigned int desktopHeight,
|
||||||
|
bool useDMA)
|
||||||
{
|
{
|
||||||
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
|
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
|
||||||
|
|
||||||
if (!this->enable)
|
if (!this->enable)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (this->useDMA != useDMA)
|
||||||
|
{
|
||||||
|
if (!egl_shaderCompile(this->shader,
|
||||||
|
b_shader_basic_vert , b_shader_basic_vert_size,
|
||||||
|
b_shader_ffx_cas_frag, b_shader_ffx_cas_frag_size,
|
||||||
|
useDMA, NULL)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to compile the shader");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
egl_shaderSetUniforms(this->shader, &(EGL_Uniform) {
|
||||||
|
.type = EGL_UNIFORM_TYPE_4UIV,
|
||||||
|
.location = egl_shaderGetUniform(this->shader, "uConsts"),
|
||||||
|
.v = this->consts,
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
this->useDMA = useDMA;
|
||||||
|
}
|
||||||
|
|
||||||
if (pixFmt == this->pixFmt && this->width == width && this->height == height)
|
if (pixFmt == this->pixFmt && this->width == width && this->height == height)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -243,11 +254,12 @@ static bool egl_filterFFXCASSetup(EGL_Filter * filter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void egl_filterFFXCASGetOutputRes(EGL_Filter * filter,
|
static void egl_filterFFXCASGetOutputRes(EGL_Filter * filter,
|
||||||
unsigned int *width, unsigned int *height)
|
unsigned int *width, unsigned int *height, enum EGL_PixelFormat *pixFmt)
|
||||||
{
|
{
|
||||||
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
|
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
|
||||||
*width = this->width;
|
*width = this->width;
|
||||||
*height = this->height;
|
*height = this->height;
|
||||||
|
*pixFmt = this->pixFmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool egl_filterFFXCASPrepare(EGL_Filter * filter)
|
static bool egl_filterFFXCASPrepare(EGL_Filter * filter)
|
||||||
@ -262,15 +274,15 @@ static bool egl_filterFFXCASPrepare(EGL_Filter * filter)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GLuint egl_filterFFXCASRun(EGL_Filter * filter,
|
static EGL_Texture * egl_filterFFXCASRun(EGL_Filter * filter,
|
||||||
EGL_FilterRects * rects, GLuint texture)
|
EGL_FilterRects * rects, EGL_Texture * texture)
|
||||||
{
|
{
|
||||||
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
|
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
|
||||||
|
|
||||||
egl_framebufferBind(this->fb);
|
egl_framebufferBind(this->fb);
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
egl_textureBind(texture);
|
||||||
glBindSampler(0, this->sampler);
|
glBindSampler(0, this->sampler);
|
||||||
|
|
||||||
egl_shaderUse(this->shader);
|
egl_shaderUse(this->shader);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -42,6 +42,7 @@ typedef struct EGL_FilterFFXFSR1
|
|||||||
CountedBuffer * consts;
|
CountedBuffer * consts;
|
||||||
EGL_Uniform easuUniform[2], rcasUniform;
|
EGL_Uniform easuUniform[2], rcasUniform;
|
||||||
|
|
||||||
|
int useDMA;
|
||||||
enum EGL_PixelFormat pixFmt;
|
enum EGL_PixelFormat pixFmt;
|
||||||
unsigned int width, height;
|
unsigned int width, height;
|
||||||
unsigned int inWidth, inHeight;
|
unsigned int inWidth, inHeight;
|
||||||
@ -109,6 +110,8 @@ static bool egl_filterFFXFSR1Init(EGL_Filter ** filter)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->useDMA = -1;
|
||||||
|
|
||||||
if (!egl_shaderInit(&this->easu))
|
if (!egl_shaderInit(&this->easu))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to initialize the Easu shader");
|
DEBUG_ERROR("Failed to initialize the Easu shader");
|
||||||
@ -121,18 +124,10 @@ static bool egl_filterFFXFSR1Init(EGL_Filter ** filter)
|
|||||||
goto error_esau;
|
goto error_esau;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!egl_shaderCompile(this->easu,
|
|
||||||
b_shader_basic_vert , b_shader_basic_vert_size,
|
|
||||||
b_shader_ffx_fsr1_easu_frag, b_shader_ffx_fsr1_easu_frag_size)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DEBUG_ERROR("Failed to compile the Easu shader");
|
|
||||||
goto error_rcas;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!egl_shaderCompile(this->rcas,
|
if (!egl_shaderCompile(this->rcas,
|
||||||
b_shader_basic_vert , b_shader_basic_vert_size,
|
b_shader_basic_vert , b_shader_basic_vert_size,
|
||||||
b_shader_ffx_fsr1_rcas_frag, b_shader_ffx_fsr1_rcas_frag_size)
|
b_shader_ffx_fsr1_rcas_frag, b_shader_ffx_fsr1_rcas_frag_size,
|
||||||
|
false, NULL)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to compile the Rcas shader");
|
DEBUG_ERROR("Failed to compile the Rcas shader");
|
||||||
@ -148,14 +143,6 @@ static bool egl_filterFFXFSR1Init(EGL_Filter ** filter)
|
|||||||
|
|
||||||
egl_filterFFXFSR1LoadState(&this->base);
|
egl_filterFFXFSR1LoadState(&this->base);
|
||||||
|
|
||||||
this->easuUniform[0].type = EGL_UNIFORM_TYPE_4UIV;
|
|
||||||
this->easuUniform[0].location =
|
|
||||||
egl_shaderGetUniform(this->easu, "uConsts");
|
|
||||||
this->easuUniform[0].v = this->consts;
|
|
||||||
this->easuUniform[1].type = EGL_UNIFORM_TYPE_2F;
|
|
||||||
this->easuUniform[1].location =
|
|
||||||
egl_shaderGetUniform(this->easu, "uOutRes");
|
|
||||||
|
|
||||||
this->rcasUniform.type = EGL_UNIFORM_TYPE_4UI;
|
this->rcasUniform.type = EGL_UNIFORM_TYPE_4UI;
|
||||||
this->rcasUniform.location = egl_shaderGetUniform(this->rcas, "uConsts");
|
this->rcasUniform.location = egl_shaderGetUniform(this->rcas, "uConsts");
|
||||||
rcasUpdateUniform(this);
|
rcasUpdateUniform(this);
|
||||||
@ -335,13 +322,38 @@ static void egl_filterFFXFSR1SetOutputResHint(EGL_Filter * filter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool egl_filterFFXFSR1Setup(EGL_Filter * filter,
|
static bool egl_filterFFXFSR1Setup(EGL_Filter * filter,
|
||||||
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
|
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height,
|
||||||
|
unsigned int desktopWidth, unsigned int desktopHeight,
|
||||||
|
bool useDMA)
|
||||||
{
|
{
|
||||||
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
|
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
|
||||||
|
|
||||||
if (!this->enable)
|
if (!this->enable)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (this->useDMA != useDMA)
|
||||||
|
{
|
||||||
|
if (!egl_shaderCompile(this->easu,
|
||||||
|
b_shader_basic_vert , b_shader_basic_vert_size,
|
||||||
|
b_shader_ffx_fsr1_easu_frag, b_shader_ffx_fsr1_easu_frag_size,
|
||||||
|
useDMA, NULL)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to compile the Easu shader");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->easuUniform[0].type = EGL_UNIFORM_TYPE_4UIV;
|
||||||
|
this->easuUniform[0].location =
|
||||||
|
egl_shaderGetUniform(this->easu, "uConsts");
|
||||||
|
this->easuUniform[0].v = this->consts;
|
||||||
|
this->easuUniform[1].type = EGL_UNIFORM_TYPE_2F;
|
||||||
|
this->easuUniform[1].location =
|
||||||
|
egl_shaderGetUniform(this->easu, "uOutRes");
|
||||||
|
|
||||||
|
this->useDMA = useDMA;
|
||||||
|
}
|
||||||
|
|
||||||
this->active = this->width > width && this->height > height;
|
this->active = this->width > width && this->height > height;
|
||||||
if (!this->active)
|
if (!this->active)
|
||||||
return false;
|
return false;
|
||||||
@ -371,11 +383,12 @@ static bool egl_filterFFXFSR1Setup(EGL_Filter * filter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void egl_filterFFXFSR1GetOutputRes(EGL_Filter * filter,
|
static void egl_filterFFXFSR1GetOutputRes(EGL_Filter * filter,
|
||||||
unsigned int *width, unsigned int *height)
|
unsigned int *width, unsigned int *height, enum EGL_PixelFormat *pixFmt)
|
||||||
{
|
{
|
||||||
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
|
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
|
||||||
*width = this->width;
|
*width = this->width;
|
||||||
*height = this->height;
|
*height = this->height;
|
||||||
|
*pixFmt = this->pixFmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool egl_filterFFXFSR1Prepare(EGL_Filter * filter)
|
static bool egl_filterFFXFSR1Prepare(EGL_Filter * filter)
|
||||||
@ -395,15 +408,15 @@ static bool egl_filterFFXFSR1Prepare(EGL_Filter * filter)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GLuint egl_filterFFXFSR1Run(EGL_Filter * filter,
|
static EGL_Texture * egl_filterFFXFSR1Run(EGL_Filter * filter,
|
||||||
EGL_FilterRects * rects, GLuint texture)
|
EGL_FilterRects * rects, EGL_Texture * texture)
|
||||||
{
|
{
|
||||||
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
|
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
|
||||||
|
|
||||||
// pass 1, Easu
|
// pass 1, Easu
|
||||||
egl_framebufferBind(this->easuFb);
|
egl_framebufferBind(this->easuFb);
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
egl_textureBind(texture);
|
||||||
glBindSampler(0, this->sampler);
|
glBindSampler(0, this->sampler);
|
||||||
egl_shaderUse(this->easu);
|
egl_shaderUse(this->easu);
|
||||||
egl_filterRectsRender(this->easu, rects);
|
egl_filterRectsRender(this->easu, rects);
|
||||||
@ -412,7 +425,7 @@ static GLuint egl_filterFFXFSR1Run(EGL_Filter * filter,
|
|||||||
// pass 2, Rcas
|
// pass 2, Rcas
|
||||||
egl_framebufferBind(this->rcasFb);
|
egl_framebufferBind(this->rcasFb);
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
glBindTexture(GL_TEXTURE_2D, texture);
|
egl_textureBind(texture);
|
||||||
glBindSampler(0, this->sampler);
|
glBindSampler(0, this->sampler);
|
||||||
egl_shaderUse(this->rcas);
|
egl_shaderUse(this->rcas);
|
||||||
egl_filterRectsRender(this->rcas, rects);
|
egl_filterRectsRender(this->rcas, rects);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
extern EGL_FilterOps egl_filter24bitOps;
|
||||||
extern EGL_FilterOps egl_filterDownscaleOps;
|
extern EGL_FilterOps egl_filterDownscaleOps;
|
||||||
extern EGL_FilterOps egl_filterFFXCASOps;
|
extern EGL_FilterOps egl_filterFFXCASOps;
|
||||||
extern EGL_FilterOps egl_filterFFXFSR1Ops;
|
extern EGL_FilterOps egl_filterFFXFSR1Ops;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -43,6 +43,7 @@ bool egl_framebufferInit(EGL_Framebuffer ** fb)
|
|||||||
if (!egl_textureInit(&this->tex, NULL, EGL_TEXTYPE_BUFFER))
|
if (!egl_textureInit(&this->tex, NULL, EGL_TEXTYPE_BUFFER))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to initialize the texture");
|
DEBUG_ERROR("Failed to initialize the texture");
|
||||||
|
free(this);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,14 +65,14 @@ void egl_framebufferFree(EGL_Framebuffer ** fb)
|
|||||||
bool egl_framebufferSetup(EGL_Framebuffer * this, enum EGL_PixelFormat pixFmt,
|
bool egl_framebufferSetup(EGL_Framebuffer * this, enum EGL_PixelFormat pixFmt,
|
||||||
unsigned int width, unsigned int height)
|
unsigned int width, unsigned int height)
|
||||||
{
|
{
|
||||||
if (!egl_textureSetup(this->tex, pixFmt, width, height, 0))
|
if (!egl_textureSetup(this->tex, pixFmt, width, height, 0, 0))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to setup the texture");
|
DEBUG_ERROR("Failed to setup the texture");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLuint tex;
|
GLuint tex;
|
||||||
egl_textureGet(this->tex, &tex, NULL, NULL);
|
egl_textureGet(this->tex, &tex, NULL, NULL, NULL);
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, tex);
|
glBindTexture(GL_TEXTURE_2D, tex);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
@ -100,9 +101,7 @@ void egl_framebufferBind(EGL_Framebuffer * this)
|
|||||||
glViewport(0, 0, this->tex->format.width, this->tex->format.height);
|
glViewport(0, 0, this->tex->format.width, this->tex->format.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLuint egl_framebufferGetTexture(EGL_Framebuffer * this)
|
EGL_Texture * egl_framebufferGetTexture(EGL_Framebuffer * this)
|
||||||
{
|
{
|
||||||
GLuint output;
|
return this->tex;
|
||||||
egl_textureGet(this->tex, &output, NULL, NULL);
|
|
||||||
return output;
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -32,4 +32,4 @@ bool egl_framebufferSetup(EGL_Framebuffer * this, enum EGL_PixelFormat pixFmt,
|
|||||||
|
|
||||||
void egl_framebufferBind(EGL_Framebuffer * this);
|
void egl_framebufferBind(EGL_Framebuffer * this);
|
||||||
|
|
||||||
GLuint egl_framebufferGetTexture(EGL_Framebuffer * this);
|
EGL_Texture * egl_framebufferGetTexture(EGL_Framebuffer * this);
|
||||||
|
@ -10,4 +10,4 @@ function process(line, second) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{ process($0, $2) }
|
{ process($0, $2) } END { print "\0"; }
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -48,8 +48,8 @@ static const EGL_FilterOps * EGL_Filters[] =
|
|||||||
|
|
||||||
struct EGL_PostProcess
|
struct EGL_PostProcess
|
||||||
{
|
{
|
||||||
Vector filters;
|
Vector filters, internalFilters;
|
||||||
GLuint output;
|
EGL_Texture * output;
|
||||||
unsigned int outputX, outputY;
|
unsigned int outputX, outputY;
|
||||||
_Atomic(bool) modified;
|
_Atomic(bool) modified;
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ void egl_postProcessEarlyInit(void)
|
|||||||
option_register(options);
|
option_register(options);
|
||||||
|
|
||||||
for (int i = 0; i < ARRAY_LENGTH(EGL_Filters); ++i)
|
for (int i = 0; i < ARRAY_LENGTH(EGL_Filters); ++i)
|
||||||
EGL_Filters[i]->earlyInit();
|
egl_filterEarlyInit(EGL_Filters[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void loadPreset(struct EGL_PostProcess * this, const char * name);
|
static void loadPreset(struct EGL_PostProcess * this, const char * name);
|
||||||
@ -464,7 +464,8 @@ static void configUI(void * opaque, int * id)
|
|||||||
static size_t mouseIdx = -1;
|
static size_t mouseIdx = -1;
|
||||||
static bool moving = false;
|
static bool moving = false;
|
||||||
static size_t moveIdx = 0;
|
static size_t moveIdx = 0;
|
||||||
bool doMove = false;
|
|
||||||
|
bool doMove = false;
|
||||||
|
|
||||||
ImVec2 window, pos;
|
ImVec2 window, pos;
|
||||||
igGetWindowPos(&window);
|
igGetWindowPos(&window);
|
||||||
@ -518,9 +519,16 @@ static void configUI(void * opaque, int * id)
|
|||||||
{
|
{
|
||||||
EGL_Filter * tmp = filters[moveIdx];
|
EGL_Filter * tmp = filters[moveIdx];
|
||||||
if (mouseIdx > moveIdx) // moving down
|
if (mouseIdx > moveIdx) // moving down
|
||||||
memmove(filters + moveIdx, filters + moveIdx + 1, (mouseIdx - moveIdx) * sizeof(EGL_Filter *));
|
memmove(
|
||||||
|
filters + moveIdx,
|
||||||
|
filters + moveIdx + 1,
|
||||||
|
(mouseIdx - moveIdx) * sizeof(EGL_Filter *));
|
||||||
else // moving up
|
else // moving up
|
||||||
memmove(filters + mouseIdx + 1, filters + mouseIdx, (moveIdx - mouseIdx) * sizeof(EGL_Filter *));
|
memmove(
|
||||||
|
filters + mouseIdx + 1,
|
||||||
|
filters + mouseIdx,
|
||||||
|
(moveIdx - mouseIdx) * sizeof(EGL_Filter *));
|
||||||
|
|
||||||
filters[mouseIdx] = tmp;
|
filters[mouseIdx] = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,16 +548,24 @@ bool egl_postProcessInit(EGL_PostProcess ** pp)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vector_create(&this->filters, sizeof(EGL_Filter *), ARRAY_LENGTH(EGL_Filters)))
|
if (!vector_create(&this->filters,
|
||||||
|
sizeof(EGL_Filter *), ARRAY_LENGTH(EGL_Filters)))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to allocate the filter list");
|
DEBUG_ERROR("Failed to allocate the filter list");
|
||||||
goto error_this;
|
goto error_this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!vector_create(&this->internalFilters,
|
||||||
|
sizeof(EGL_Filter *), ARRAY_LENGTH(EGL_Filters)))
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to allocate the filter list");
|
||||||
|
goto error_filters;
|
||||||
|
}
|
||||||
|
|
||||||
if (!egl_desktopRectsInit(&this->rects, 1))
|
if (!egl_desktopRectsInit(&this->rects, 1))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to initialize the desktop rects");
|
DEBUG_ERROR("Failed to initialize the desktop rects");
|
||||||
goto error_filters;
|
goto error_internal;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadPresetList(this);
|
loadPresetList(this);
|
||||||
@ -559,6 +575,9 @@ bool egl_postProcessInit(EGL_PostProcess ** pp)
|
|||||||
*pp = this;
|
*pp = this;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
error_internal:
|
||||||
|
vector_destroy(&this->internalFilters);
|
||||||
|
|
||||||
error_filters:
|
error_filters:
|
||||||
vector_destroy(&this->filters);
|
vector_destroy(&this->filters);
|
||||||
|
|
||||||
@ -579,6 +598,10 @@ void egl_postProcessFree(EGL_PostProcess ** pp)
|
|||||||
egl_filterFree(filter);
|
egl_filterFree(filter);
|
||||||
vector_destroy(&this->filters);
|
vector_destroy(&this->filters);
|
||||||
|
|
||||||
|
vector_forEachRef(filter, &this->internalFilters)
|
||||||
|
egl_filterFree(filter);
|
||||||
|
vector_destroy(&this->internalFilters);
|
||||||
|
|
||||||
free(this->presetDir);
|
free(this->presetDir);
|
||||||
if (this->presets)
|
if (this->presets)
|
||||||
stringlist_free(&this->presets);
|
stringlist_free(&this->presets);
|
||||||
@ -595,7 +618,10 @@ bool egl_postProcessAdd(EGL_PostProcess * this, const EGL_FilterOps * ops)
|
|||||||
if (!egl_filterInit(ops, &filter))
|
if (!egl_filterInit(ops, &filter))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
vector_push(&this->filters, &filter);
|
if (ops->type == EGL_FILTER_TYPE_INTERNAL)
|
||||||
|
vector_push(&this->internalFilters, &filter);
|
||||||
|
else
|
||||||
|
vector_push(&this->filters, &filter);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -606,7 +632,7 @@ bool egl_postProcessConfigModified(EGL_PostProcess * this)
|
|||||||
|
|
||||||
bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
|
bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
|
||||||
EGL_DesktopRects * rects, int desktopWidth, int desktopHeight,
|
EGL_DesktopRects * rects, int desktopWidth, int desktopHeight,
|
||||||
unsigned int targetX, unsigned int targetY)
|
unsigned int targetX, unsigned int targetY, bool useDMA)
|
||||||
{
|
{
|
||||||
if (targetX == 0 && targetY == 0)
|
if (targetX == 0 && targetY == 0)
|
||||||
DEBUG_FATAL("targetX || targetY == 0");
|
DEBUG_FATAL("targetX || targetY == 0");
|
||||||
@ -614,8 +640,11 @@ bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
|
|||||||
EGL_Filter * lastFilter = NULL;
|
EGL_Filter * lastFilter = NULL;
|
||||||
unsigned int sizeX, sizeY;
|
unsigned int sizeX, sizeY;
|
||||||
|
|
||||||
GLuint texture;
|
//TODO: clean this up
|
||||||
if (egl_textureGet(tex, &texture, &sizeX, &sizeY) != EGL_TEX_STATUS_OK)
|
GLuint _unused;
|
||||||
|
EGL_PixelFormat pixFmt;
|
||||||
|
if (egl_textureGet(tex, &_unused,
|
||||||
|
&sizeX, &sizeY, &pixFmt) != EGL_TEX_STATUS_OK)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (atomic_exchange(&this->modified, false))
|
if (atomic_exchange(&this->modified, false))
|
||||||
@ -636,22 +665,36 @@ bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
|
|||||||
};
|
};
|
||||||
|
|
||||||
EGL_Filter * filter;
|
EGL_Filter * filter;
|
||||||
vector_forEach(filter, &this->filters)
|
EGL_Texture * texture = tex;
|
||||||
|
|
||||||
|
const Vector * lists[] =
|
||||||
{
|
{
|
||||||
egl_filterSetOutputResHint(filter, targetX, targetY);
|
&this->internalFilters,
|
||||||
|
&this->filters,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
if (!egl_filterSetup(filter, tex->format.pixFmt, sizeX, sizeY) ||
|
for(const Vector ** filters = lists; *filters; ++filters)
|
||||||
!egl_filterPrepare(filter))
|
vector_forEach(filter, *filters)
|
||||||
continue;
|
{
|
||||||
|
egl_filterSetOutputResHint(filter, targetX, targetY);
|
||||||
|
|
||||||
texture = egl_filterRun(filter, &filterRects, texture);
|
if (!egl_filterSetup(filter, pixFmt, sizeX, sizeY,
|
||||||
egl_filterGetOutputRes(filter, &sizeX, &sizeY);
|
desktopWidth, desktopHeight, useDMA) ||
|
||||||
|
!egl_filterPrepare(filter))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (lastFilter)
|
texture = egl_filterRun(filter, &filterRects, texture);
|
||||||
egl_filterRelease(lastFilter);
|
egl_filterGetOutputRes(filter, &sizeX, &sizeY, &pixFmt);
|
||||||
|
|
||||||
lastFilter = filter;
|
if (lastFilter)
|
||||||
}
|
egl_filterRelease(lastFilter);
|
||||||
|
|
||||||
|
lastFilter = filter;
|
||||||
|
|
||||||
|
// the first filter to run will convert to a normal texture
|
||||||
|
useDMA = false;
|
||||||
|
}
|
||||||
|
|
||||||
this->output = texture;
|
this->output = texture;
|
||||||
this->outputX = sizeX;
|
this->outputX = sizeX;
|
||||||
@ -659,7 +702,7 @@ bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLuint egl_postProcessGetOutput(EGL_PostProcess * this,
|
EGL_Texture * egl_postProcessGetOutput(EGL_PostProcess * this,
|
||||||
unsigned int * outputX, unsigned int * outputY)
|
unsigned int * outputX, unsigned int * outputY)
|
||||||
{
|
{
|
||||||
*outputX = this->outputX;
|
*outputX = this->outputX;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -41,7 +41,7 @@ bool egl_postProcessConfigModified(EGL_PostProcess * this);
|
|||||||
* targetX/Y is the final target output dimension hint if scalers are present */
|
* targetX/Y is the final target output dimension hint if scalers are present */
|
||||||
bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
|
bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
|
||||||
EGL_DesktopRects * rects, int desktopWidth, int desktopHeight,
|
EGL_DesktopRects * rects, int desktopWidth, int desktopHeight,
|
||||||
unsigned int targetX, unsigned int targetY);
|
unsigned int targetX, unsigned int targetY, bool useDMA);
|
||||||
|
|
||||||
GLuint egl_postProcessGetOutput(EGL_PostProcess * this,
|
EGL_Texture * egl_postProcessGetOutput(EGL_PostProcess * this,
|
||||||
unsigned int * outputX, unsigned int * outputY);
|
unsigned int * outputX, unsigned int * outputY);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include "shader.h"
|
#include "shader.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
|
#include "common/stringutils.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -64,7 +65,9 @@ void egl_shaderFree(EGL_Shader ** shader)
|
|||||||
*shader = NULL;
|
*shader = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool egl_shaderLoad(EGL_Shader * this, const char * vertex_file, const char * fragment_file)
|
bool egl_shaderLoad(EGL_Shader * this,
|
||||||
|
const char * vertex_file, const char * fragment_file, bool useDMA,
|
||||||
|
const EGL_ShaderDefine * defines)
|
||||||
{
|
{
|
||||||
char * vertex_code, * fragment_code;
|
char * vertex_code, * fragment_code;
|
||||||
size_t vertex_size, fragment_size;
|
size_t vertex_size, fragment_size;
|
||||||
@ -86,13 +89,16 @@ bool egl_shaderLoad(EGL_Shader * this, const char * vertex_file, const char * fr
|
|||||||
|
|
||||||
DEBUG_INFO("Loaded fragment shader: %s", fragment_file);
|
DEBUG_INFO("Loaded fragment shader: %s", fragment_file);
|
||||||
|
|
||||||
bool ret = egl_shaderCompile(this, vertex_code, vertex_size, fragment_code, fragment_size);
|
bool ret = egl_shaderCompile(this,
|
||||||
|
vertex_code, vertex_size, fragment_code, fragment_size,
|
||||||
|
useDMA, defines);
|
||||||
|
|
||||||
free(vertex_code);
|
free(vertex_code);
|
||||||
free(fragment_code);
|
free(fragment_code);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
|
static bool shaderCompile(EGL_Shader * this, const char * vertex_code,
|
||||||
size_t vertex_size, const char * fragment_code, size_t fragment_size)
|
size_t vertex_size, const char * fragment_code, size_t fragment_size)
|
||||||
{
|
{
|
||||||
if (this->hasShader)
|
if (this->hasShader)
|
||||||
@ -204,6 +210,149 @@ bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool egl_shaderCompile(EGL_Shader * this, const char * vertex_code,
|
||||||
|
size_t vertex_size, const char * fragment_code, size_t fragment_size,
|
||||||
|
bool useDMA, const EGL_ShaderDefine * defines)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
char * processed = NULL;
|
||||||
|
char * newCode = NULL;
|
||||||
|
|
||||||
|
if (useDMA)
|
||||||
|
{
|
||||||
|
const char search[] = "sampler2D";
|
||||||
|
const char replace[] = "samplerExternalOES";
|
||||||
|
|
||||||
|
const char * offset = NULL;
|
||||||
|
int instances = 0;
|
||||||
|
|
||||||
|
while((offset = memsearch(
|
||||||
|
fragment_code, fragment_size,
|
||||||
|
search , sizeof(search)-1,
|
||||||
|
offset)))
|
||||||
|
{
|
||||||
|
++instances;
|
||||||
|
offset += sizeof(search)-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int diff = (sizeof(replace) - sizeof(search)) * instances;
|
||||||
|
const int newLen = fragment_size + diff;
|
||||||
|
newCode = malloc(newLen + 1);
|
||||||
|
if (!newCode)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Out of memory");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * src = fragment_code;
|
||||||
|
char * dst = newCode;
|
||||||
|
for(int i = 0; i < instances; ++i)
|
||||||
|
{
|
||||||
|
const char * pos = strstr(src, search);
|
||||||
|
const int offset = pos - src;
|
||||||
|
|
||||||
|
memcpy(dst, src, offset);
|
||||||
|
dst += offset;
|
||||||
|
src = pos + sizeof(search)-1;
|
||||||
|
|
||||||
|
memcpy(dst, replace, sizeof(replace)-1);
|
||||||
|
dst += sizeof(replace)-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int final = fragment_size - (src - fragment_code);
|
||||||
|
memcpy(dst, src, final);
|
||||||
|
dst[final] = '\0';
|
||||||
|
|
||||||
|
fragment_code = newCode;
|
||||||
|
fragment_size = newLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defines)
|
||||||
|
{
|
||||||
|
// find the end of any existing lines starting with #
|
||||||
|
bool newLine = true;
|
||||||
|
bool skip = false;
|
||||||
|
int insertPos = 0;
|
||||||
|
for(int i = 0; i < fragment_size; ++i)
|
||||||
|
{
|
||||||
|
if (skip)
|
||||||
|
{
|
||||||
|
if (fragment_code[i] == '\n')
|
||||||
|
skip = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(fragment_code[i])
|
||||||
|
{
|
||||||
|
case '\n':
|
||||||
|
newLine = true;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case ' ':
|
||||||
|
case '\t':
|
||||||
|
case '\r':
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case '#':
|
||||||
|
if (newLine)
|
||||||
|
{
|
||||||
|
skip = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//fallthrough
|
||||||
|
|
||||||
|
default:
|
||||||
|
newLine = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newLine)
|
||||||
|
{
|
||||||
|
insertPos = i;
|
||||||
|
if (insertPos > 0)
|
||||||
|
--insertPos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int processedLen = fragment_size;
|
||||||
|
const char * defineFormat = "#define %s %s\n";
|
||||||
|
for(const EGL_ShaderDefine * define = defines; define->name; ++define)
|
||||||
|
processedLen += snprintf(NULL, 0, defineFormat, define->name, define->value);
|
||||||
|
|
||||||
|
processed = malloc(processedLen);
|
||||||
|
if (!processed)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Out of memory");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(processed, fragment_code, insertPos);
|
||||||
|
|
||||||
|
int offset = insertPos;
|
||||||
|
for(const EGL_ShaderDefine * define = defines; define->name; ++define)
|
||||||
|
offset += sprintf(processed + offset, defineFormat,
|
||||||
|
define->name, define->value);
|
||||||
|
|
||||||
|
memcpy(
|
||||||
|
processed + offset,
|
||||||
|
fragment_code + insertPos,
|
||||||
|
fragment_size - insertPos);
|
||||||
|
|
||||||
|
fragment_code = processed;
|
||||||
|
fragment_size = processedLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = shaderCompile(this,
|
||||||
|
vertex_code , vertex_size,
|
||||||
|
fragment_code, fragment_size);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
free(processed);
|
||||||
|
free(newCode);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void egl_shaderSetUniforms(EGL_Shader * this, EGL_Uniform * uniforms, int count)
|
void egl_shaderSetUniforms(EGL_Shader * this, EGL_Uniform * uniforms, int count)
|
||||||
{
|
{
|
||||||
egl_shaderFreeUniforms(this);
|
egl_shaderFreeUniforms(this);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Looking Glass
|
* Looking Glass
|
||||||
* Copyright © 2017-2022 The Looking Glass Authors
|
* Copyright © 2017-2025 The Looking Glass Authors
|
||||||
* https://looking-glass.io
|
* https://looking-glass.io
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
@ -93,14 +93,22 @@ typedef struct EGL_Uniform
|
|||||||
}
|
}
|
||||||
EGL_Uniform;
|
EGL_Uniform;
|
||||||
|
|
||||||
|
typedef struct EGL_ShaderDefine
|
||||||
|
{
|
||||||
|
const char * name;
|
||||||
|
const char * value;
|
||||||
|
}
|
||||||
|
EGL_ShaderDefine;
|
||||||
|
|
||||||
bool egl_shaderInit(EGL_Shader ** shader);
|
bool egl_shaderInit(EGL_Shader ** shader);
|
||||||
void egl_shaderFree(EGL_Shader ** shader);
|
void egl_shaderFree(EGL_Shader ** shader);
|
||||||
|
|
||||||
bool egl_shaderLoad(EGL_Shader * model, const char * vertex_file,
|
bool egl_shaderLoad(EGL_Shader * model, const char * vertex_file,
|
||||||
const char * fragment_file);
|
const char * fragment_file, bool useDMA, const EGL_ShaderDefine * defines);
|
||||||
|
|
||||||
bool egl_shaderCompile(EGL_Shader * model, const char * vertex_code,
|
bool egl_shaderCompile(EGL_Shader * model, const char * vertex_code,
|
||||||
size_t vertex_size, const char * fragment_code, size_t fragment_size);
|
size_t vertex_size, const char * fragment_code, size_t fragment_size,
|
||||||
|
bool useDMA, const EGL_ShaderDefine * defines);
|
||||||
|
|
||||||
void egl_shaderSetUniforms(EGL_Shader * shader, EGL_Uniform * uniforms,
|
void egl_shaderSetUniforms(EGL_Shader * shader, EGL_Uniform * uniforms,
|
||||||
int count);
|
int count);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#version 300 es
|
#version 300 es
|
||||||
precision mediump float;
|
precision highp float;
|
||||||
|
|
||||||
layout(location = 0) in vec2 vertex;
|
layout(location = 0) in vec2 vertex;
|
||||||
out vec2 fragCoord;
|
out vec2 fragCoord;
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
#if __VERSION__ == 300
|
#if __VERSION__ == 300
|
||||||
vec4 textureGather(sampler2D tex, vec2 uv, int comp)
|
vec4 textureGather(sampler2D tex, vec2 uv, int comp)
|
||||||
{
|
{
|
||||||
vec4 c0 = textureOffset(tex, uv, ivec2(0,1));
|
vec2 res = vec2(textureSize(tex, 0));
|
||||||
vec4 c1 = textureOffset(tex, uv, ivec2(1,1));
|
ivec2 p = ivec2((uv * res) - 0.5f);
|
||||||
vec4 c2 = textureOffset(tex, uv, ivec2(1,0));
|
|
||||||
vec4 c3 = textureOffset(tex, uv, ivec2(0,0));
|
// NOTE: we can't use texelFecthOffset because sampler2D may actually be samplerExternalOES
|
||||||
|
vec4 c0 = texelFetch(tex, p+ivec2(0,1), 0);
|
||||||
|
vec4 c1 = texelFetch(tex, p+ivec2(1,1), 0);
|
||||||
|
vec4 c2 = texelFetch(tex, p+ivec2(1,0), 0);
|
||||||
|
vec4 c3 = texelFetch(tex, p+ivec2(0,0), 0);
|
||||||
|
|
||||||
return vec4(c0[comp], c1[comp], c2[comp],c3[comp]);
|
return vec4(c0[comp], c1[comp], c2[comp],c3[comp]);
|
||||||
}
|
}
|
||||||
#elif __VERSION__ < 300
|
#elif __VERSION__ < 300
|
||||||
vec4 textureGather(sampler2D tex, vec2 uv, int comp)
|
vec4 textureGather(sampler2D tex, vec2 uv, int comp)
|
||||||
{
|
{
|
||||||
vec4 c3 = texture2D(tex, uv);
|
vec4 c3 = texture(tex, uv);
|
||||||
return vec4(c3[comp], c3[comp], c3[comp],c3[comp]);
|
return vec4(c3[comp], c3[comp], c3[comp],c3[comp]);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
32
client/renderers/EGL/shader/convert_24bit.frag
Normal file
32
client/renderers/EGL/shader/convert_24bit.frag
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#version 300 es
|
||||||
|
#extension GL_OES_EGL_image_external_essl3 : enable
|
||||||
|
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
in vec2 fragCoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform sampler2D sampler1;
|
||||||
|
uniform vec2 outputSize;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
uvec2 inputSize = uvec2(textureSize(sampler1, 0));
|
||||||
|
uvec2 outputPos = uvec2(fragCoord * outputSize);
|
||||||
|
|
||||||
|
uint fst = outputPos.x * 3u / 4u;
|
||||||
|
vec4 color_0 = texelFetch(sampler1, ivec2(fst, outputPos.y), 0);
|
||||||
|
|
||||||
|
uint snd = (outputPos.x * 3u + 1u) / 4u;
|
||||||
|
vec4 color_1 = texelFetch(sampler1, ivec2(snd, outputPos.y), 0);
|
||||||
|
|
||||||
|
uint trd = (outputPos.x * 3u + 2u) / 4u;
|
||||||
|
vec4 color_2 = texelFetch(sampler1, ivec2(trd, outputPos.y), 0);
|
||||||
|
|
||||||
|
OUTPUT = vec4(
|
||||||
|
color_0.barg[outputPos.x % 4u],
|
||||||
|
color_1.gbar[outputPos.x % 4u],
|
||||||
|
color_2.rgba[outputPos.x % 4u],
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
#version 300 es
|
#version 300 es
|
||||||
precision mediump float;
|
precision highp float;
|
||||||
|
|
||||||
layout(location = 0) in vec3 vertexPosition_modelspace;
|
layout(location = 0) in vec3 vertexPosition_modelspace;
|
||||||
layout(location = 1) in vec2 vertexUV;
|
layout(location = 1) in vec2 vertexUV;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#version 300 es
|
#version 300 es
|
||||||
precision mediump float;
|
precision highp float;
|
||||||
|
|
||||||
in vec2 uv;
|
in vec2 uv;
|
||||||
out vec4 color;
|
out vec4 color;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#version 300 es
|
#version 300 es
|
||||||
precision mediump float;
|
precision highp float;
|
||||||
|
|
||||||
#include "color_blind.h"
|
#include "color_blind.h"
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user