Compare commits

...

432 Commits
B6 ... master

Author SHA1 Message Date
Geoffrey McRae
f15d72cdfe [host] ivshmem: fix missing bounds check on device vector
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2025-05-10 11:12:57 +10:00
Geoffrey McRae
54d811e098 [client] keybind: add keybind to set the guest resolution
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2025-04-04 10:23:00 +11:00
Geoffrey McRae
9593301511 [client] config: add option to disable auto client resolution switching 2025-04-04 10:13:19 +11:00
Geoffrey McRae
c2a19f5e76 [client] message: free any pending messages on deinit 2025-04-04 10:13:09 +11:00
Geoffrey McRae
d8baa62c6a [client] core: check if the resolution already matches the window
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2025-04-02 14:03:48 +11:00
Geoffrey McRae
81162b460e [client] message: fix compile failure 2025-04-02 13:52:45 +11:00
Geoffrey McRae
852eb6bf69 [client] core: new message event system to debounce window size events 2025-04-02 13:46:55 +11:00
Geoffrey McRae
4b11743f18 [common] ll: make ll_push report success/failure 2025-04-02 13:46:07 +11:00
Geoffrey McRae
656d01a694 [idd] driver: add additional logging to CIVSHMEM
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2025-03-31 00:25:54 +11:00
Geoffrey McRae
a4406ac867 [idd] driver: fix free copy queue search method 2025-03-30 23:28:04 +00:00
Geoffrey McRae
57a2f68931 [idd] driver: reduce CopyQueue contention 2025-03-30 23:18:14 +00:00
Geoffrey McRae
f4df3f0ec7 [idd] driver: pre-calculae addresses of structs and offsets 2025-03-30 22:58:12 +00:00
Geoffrey McRae
4e951184f1 [idd] driver: fix hardcoded pitch values
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2025-03-30 21:40:10 +00:00
Geoffrey McRae
be4782b062 [idd] driver: fix header 2025-03-30 19:04:04 +11:00
Geoffrey McRae
b17c66d6bb [idd] driver: fix failure to intiialize command queue pending 2025-03-30 18:59:42 +11:00
Geoffrey McRae
94fbbad21c [idd] driver: handle command queue failures gracefully 2025-03-30 18:58:03 +11:00
Geoffrey McRae
63a9365377 [idd] driver: fix failure to report completion when an error occurs 2025-03-30 18:21:34 +00:00
Geoffrey McRae
cadcfe4b39 [idd] driver: fix deadlock caused by command queue completion callback
The callback runs in a random thread, we can't call directx methods
safely from it, so move reset so it's called automatically when a free
copy list is obtained.
2025-03-30 16:52:58 +00:00
Geoffrey McRae
3b883bf9fe [idd] driver: use a timeout instead of an event wait.
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
Waiting on the events is dangerous as there is a race here where
we may end up stuck as the events need to be auto-reset
2025-03-30 04:28:49 +00:00
Geoffrey McRae
b58171c3e1 [idd] driver: very experimental change to syncronization
This may get reverted, at this point it's an experiment for the
testers to trial.
2025-03-30 02:47:40 +00:00
Geoffrey McRae
7afb9b93eb [idd] driver: improve display mode support and resolution switch 2025-03-29 22:42:49 +00:00
Geoffrey McRae
6396ff1e9c [repos] lgmp: update for the new wdk version 2025-03-29 22:42:49 +00:00
Geoffrey McRae
db1dda00c8 [idd] all: update to sdk 10.0.26100 and iddcx 1.10 2025-03-29 22:42:48 +00:00
Geoffrey McRae
5b07286c65 [idd] driver: report we are finished with the frame earlier
`IddCxSwapChainFinishedProcessingFrame` must be called after every
frame, but we should do it as early as possible once all commands
are queued that use the frame.
2025-03-29 22:29:47 +00:00
Geoffrey McRae
6dad0de8b8 [idd] driver: reset cursor thread events on reinit 2025-03-29 22:27:55 +00:00
Geoffrey McRae
35c975d334 [idd] driver: fix loss of mouse cursor on guest wakeup
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
2025-03-29 01:16:53 +00:00
Geoffrey McRae
530e83e7bf [idd] driver: cosmetics 2025-03-29 00:56:54 +00:00
Geoffrey McRae
40a4debfda [idd] driver: fix debug messages 2025-03-29 00:56:39 +00:00
Geoffrey McRae
75f07cb28c [idd] driver: improve reInit logic on failure 2025-03-29 00:56:16 +00:00
Geoffrey McRae
f26fa17bc1 [idd] driver: make m_indirectCopy static so it persists on retry 2025-03-28 23:50:54 +00:00
Geoffrey McRae
648fca7caa [idd] driver: re-plug the monitor if the heap test failed 2025-03-28 23:47:31 +00:00
Geoffrey McRae
868504d22d [client] core: fix dynamic res switch race issue 2025-03-28 23:18:41 +11:00
Geoffrey McRae
cb423730e4 [idd] driver: implement dynamic mode switch support 2025-03-28 23:17:31 +00:00
Geoffrey McRae
86de1c9ac6 [idd] common: still print the message if FormatMsg failed 2025-03-28 21:38:36 +00:00
Geoffrey McRae
d839a45d0b [client] kvmfr: report the local window size to the VM
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
2025-03-28 16:38:02 +11:00
Geoffrey McRae
9ffb800e93 [idd] driver: use the frameSize instead of the resource size
As the resource size can be larger then the actual frame data, we
need to track this seperately so that we don't waste cycles copying
data the client will never use.
2025-03-28 14:35:19 +00:00
Geoffrey McRae
91e8440c9d [idd] vs: remove dynamically generated VersionInfo.h from the repo 2025-03-28 14:13:34 +00:00
Geoffrey McRae
15eff234ec [idd] driver: make indirectCopy functional 2025-03-28 14:11:48 +00:00
Geoffrey McRae
6a4edfc6b6 [idd] helper: added new helper service
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
As the IDD itself runs in a WUMDF sandbox, it doesn't have enough
access to perform interactive operations such as moving the cursor.

This helper service communicates with the IDD over a named pipe,
so that we can perform these things, as well as in the future provide
a configuration GUI.
2025-03-28 12:05:02 +00:00
Geoffrey McRae
bf59e45118 [idd] cosmetic: it's a device, not a driver!
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2025-03-24 16:46:52 +00:00
Geoffrey McRae
9cc99e7db9 [idd] ci: fetch the tags so the version info is correct 2025-03-24 16:15:36 +00:00
Geoffrey McRae
af5a48deca [idd] ci: fix build take 3
Revert "[idd] ci: fix build take 2"
Issue was with the docker container account

This reverts commit b1b6da32cea7fdb8952bbea6717e677416d99671.
2025-03-24 15:58:59 +00:00
Geoffrey McRae
b1b6da32ce [idd] ci: fix build take 2 2025-03-24 15:54:07 +00:00
Geoffrey McRae
17b24cc8ff [idd] ci: fix compilation 2025-03-24 15:48:26 +00:00
Geoffrey McRae
7248b666ea [idd] nuget: added missing packages.config to the repo 2025-03-24 15:40:47 +00:00
Geoffrey McRae
2084a9fee3 [idd] all: generate versioning information 2025-03-24 15:37:17 +00:00
Geoffrey McRae
a2a771f94e [idd] cosmetic: single change just to trigger the ci workflow
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2025-03-23 03:43:28 +00:00
Geoffrey McRae
b526eb3da0 [idd] ci: added woodpecker CI configuration for the IDD
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
As the IDD requires being built in MSVC2022 with the WDK and then
signed to create a usable driver, this must be done on the LG build
server instead of github directly. This workflow will trigger this on
each commit
2025-03-20 16:46:23 +11:00
Geoffrey McRae
7a88a49f1c [idd] handle frame re-send for when a new client connects
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2025-03-16 19:37:03 +00:00
Geoffrey McRae
ce23cff12e [idd] remove deprecated locking defines 2025-03-16 19:28:07 +00:00
Geoffrey McRae
09df8c41aa [idd] debug: add debug defines and make use of them 2025-03-16 16:31:44 +00:00
Geoffrey McRae
0db9d3a27b [idd] CDebug: implement new debug print class and write to a file 2025-03-16 16:11:21 +00:00
Geoffrey McRae
4f2eb984d3 [idd] remove unconditional debug enablement for DirectX11
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
2025-03-16 12:35:59 +00:00
Geoffrey McRae
8b198091ce [idd] rewrite to support DirectX12 copy 2025-03-16 12:32:52 +00:00
Geoffrey McRae
62c075cfb5 [idd] make the dirver functional again 2025-03-11 10:27:46 +00:00
Geoffrey McRae
bea198735a [repos] LGMP: update to the latest version
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2025-03-09 02:57:38 +11:00
Geoffrey McRae
a421329d9a [all] general: fix possible memory leaks with realloc usage 2025-03-09 02:56:20 +11:00
Geoffrey McRae
5382a94945 [client] evdev: implement mouse support
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
2025-03-08 19:14:46 +11:00
Geoffrey McRae
4278a10fe1 [client] evdev: read up to 256 events at a time 2025-03-08 18:19:10 +11:00
Geoffrey McRae
35e6a6e81a [client] evdev: handle device removal and hot-plug 2025-03-08 13:28:31 +11:00
Geoffrey McRae
afbd844be8 [client] evdev: add new exclusive evdev while captured option
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
2025-03-07 13:47:01 +11:00
Geoffrey McRae
7c285a45fb [app] core: place the local cursor inside the window on capture 2025-03-07 12:21:20 +11:00
Geoffrey McRae
968fd42d46 [all] common: set the backtrace define for the entire project 2025-03-07 11:32:24 +11:00
Geoffrey McRae
66ac453c98 [client] input: add support for evdev keyboard capture mode
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
The new configuration option `input:evdev` accepts a comma separated
list of `/dev/input/` keyboard devices to use for input when in capture
mode. This makes it possible to capture only a specific keyboard instead
of all keyboards.
2025-03-07 02:36:00 +11:00
Geoffrey McRae
27fe47cbe2 [obs] dmabuf: enable dmabuf by default
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
2025-03-06 14:39:51 +11:00
Geoffrey McRae
aefbebff9c [obs] cosmetics: adhere to 80 column rule 2025-03-06 14:39:31 +11:00
Geoffrey McRae
9174b1ae0f [host] d12: perform full re-initialization if the heapTest fails
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
2025-03-05 16:44:29 +11:00
Geoffrey McRae
f6b7ea11c8 [host] app: fix complation on windows with mingw 2025-03-05 16:34:07 +11:00
Geoffrey McRae
2c50ce4dbd [github] build: add libfontconfig-dev dependency 2025-03-05 16:25:01 +11:00
Geoffrey McRae
f6b0752e45 [github] build: use native packages now ubuntu 24.04 is available 2025-03-05 16:22:00 +11:00
Geoffrey McRae
50fee59b29 [client] app: fix broken keyboard input after imgui update 2025-03-05 16:07:06 +11:00
Geoffrey McRae
554f5bf75d [client] overlay: use graph address as imgui id 2025-03-05 15:25:34 +11:00
Geoffrey McRae
b43f572af0 Revert "[client] overlay/status: don't return damage rect if nothing was drawn"
This reverts commit 7e9e38faa5d3144dac9e9c3abd357435b25c16ff as it
causes screen corruption when moving imgui overlay dialogs around.
2025-03-05 15:18:29 +11:00
Geoffrey McRae
d9f2df361d [client] cimgui: update to 1.91.8 2025-03-05 15:16:31 +11:00
Geoffrey McRae
6dcf178879 [github] linux-host: add missing dependency for xcb/shm 2025-03-05 13:32:33 +11:00
Geoffrey McRae
ea00b623ed [github] linux-host: add missing dependency for gio/gio-unix 2025-03-05 13:27:43 +11:00
Geoffrey McRae
711c932380 [all] common: fix compilation on clang 2025-03-05 12:48:20 +11:00
Geoffrey McRae
be52a86a9a [all] repos: update LGMP and PureSpice submodules 2025-03-05 12:44:58 +11:00
Geoffrey McRae
420eaebb71 [cmake] all: update cmake_minimum_required version to 3.10 2025-03-05 12:27:23 +11:00
Geoffrey McRae
551298ed5b [doc] all: update copyright year 2025-03-05 12:24:38 +11:00
Mark Stosberg
41008add12 [client] linux: Install .desktop file and icon
SVGs have had a great support for Linux for about a decade, so only
install that for simplicity.
2025-03-05 11:12:42 +11:00
Jonathan Rubenstein
32d1b8063e [doc] requirements: Modify language of DMABUF for iGPUS 2025-03-05 11:11:54 +11:00
Jonathan Rubenstein
b0227a8ff8 [doc] Add hypervisor and framebuffer to words.txt
Used `python ./sort_words.py -s -a framebuffer -a hypervisor`
This also properly sorted the list
2025-03-05 11:11:54 +11:00
Jonathan Rubenstein
a1c713556d [doc] install_libvirt: Link to iGPUs should use DMABUF 2025-03-05 11:11:54 +11:00
Jonathan Rubenstein
a0cf34df73 [doc] requirements: Add section encouraging DMABUF use for iGPUs 2025-03-05 11:11:54 +11:00
Jonathan Rubenstein
fbb489b9b6 [github] pr-check: Automatically add review requesting changes when PR author is not found in AUTHORS 2025-03-05 11:02:45 +11:00
Chris Spencer
03ca20d3e4 [client] overlay/msg: fix race condition in render
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
If an overlay is closed with overlayMsg_close, the message can be freed
while it is still being used by msg_render, resulting in a segfault. Lock
the message list for the duration of msg_render to fix this.
2025-03-05 10:59:05 +11:00
Chris Spencer
7e9e38faa5 [client] overlay/status: don't return damage rect if nothing was drawn
The renderer contains an optimization to skip ImGui if there is nothing to
show, but the status overlay always returns a damage rect, even if it
didn't draw anything, so the optimization can never take effect.
2025-03-05 10:58:10 +11:00
Chris Spencer
0fd6f59bbb [client] egl: fix desktop render dimensions check
Code now matches the error message.
2025-03-05 10:57:20 +11:00
Justin Gatzen
661efd107e [host] app: fix app state race in lgmpTimer thread
While resuming from hibernation lgmpTimer may transition app.state to
APP_STATE_REINIT_LGMP. Sometimes the transition is lost if the app_main
processing loop also changes app.state simultaneously. This seems to
occur frequently with the hibernation use case.

Separate lgmp timer state into its own enum. Move resulting app.state
transitions to the app_main loop so they are serialized.
2025-03-05 10:56:39 +11:00
Jérôme Poulin
f6094de919 [module] MODULE_IMPORT_NS now requires a string literal in 6.13
Fixes: #1155
References: 33def8498fdde180023444b08e12b72a9efed41d
Co-authored-by: HikariKnightt <2557889+HikariKnight@users.noreply.github.com>
Co-authored-by: zeule <zeule@users.noreply.github.com>
Reviewed-by: netboy3 <1472804+netboy3@users.noreply.github.com>
2025-03-05 10:52:31 +11:00
Geoffrey McRae
77f6054f0a d12: implement indirectCopy fallback for compatibillity
When the heapTest fails for DMA copies to shared memory, fallback to
conventional CPU copy via mapped resources. While this is less optimal,
it is still faster then the older DXGI capture backend.
2025-02-19 16:30:27 +11:00
Geoffrey McRae
c169d4ab23 [obs] dmabuf: fix failure to display frames in both buffers 2025-02-14 19:24:36 +11:00
rs189
e25492a3a3 [client] x11/wayland: add application id
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2024-10-15 11:52:10 +11:00
Geoffrey McRae
4e8201da07 [common] ivshmem: default to /dev/kvmfr0 if it's found
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2024-09-05 14:01:28 +10:00
Geoffrey McRae
6a0a635781 [common] rect: fix avx unaligned bytes copy
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
Thanks @D0ot for debugging and finding this this, the LG community are
very appreciate for a fix for this long outstanding bug.

Fixes #1129
Closes #1136
2024-08-30 09:12:19 +10:00
Geoffrey McRae
3ea37b86e3 [module] check vmf->pgoff before using it
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
As reported by @Crispy-fried-chicken in issue #1133 there is a potential
XXE vulnerability here. This fixes this problem by verifying the value
of `vmf->pgff` does not exceed the bounds of the memory mapping.

Fixes: #1133
2024-08-26 14:37:21 +10:00
Geoffrey McRae
d060e375ea [client] fix typo
Fixes #1122 - Thanks @MadelineRitchie
2024-06-07 19:04:03 +10:00
Geoffrey McRae
7305ce36af [module] fix build on linux 6.10
Fixes #1124 - Thanks @pongo1231
2024-06-07 19:02:23 +10:00
vmfortress
c04f84b85c [client] pipewire: Add pipewire application name 2024-06-07 18:59:14 +10:00
majcosta
d21bdebc52 [doc] kvmfr/libvirt: change double quotes to single quotes
virt-manager (or libvirt itself) tends to preserve outer quotation marks but explode inner double-quotes into '&quot' which is annoying.

this looks nicer when pasted into the XML editor and applied

Also used double-quotes for the QEMU <6.2 example for consistency

PS: added myself to AUTHORS since this is my first contribution
2024-06-02 12:42:13 +10:00
Jacob McNamee
695cbc2d61 [all] update authors 2024-05-20 19:42:21 +10:00
Jacob McNamee
2067b21d47 [client] egl: fix line copy size computation in texBufferStreamUpdate 2024-05-20 19:42:21 +10:00
Jacob McNamee
9bc82ab1b4 [client] egl: fix misuse of stride vs. pitch in texBufferStreamUpdate 2024-05-20 19:42:21 +10:00
Jacob McNamee
82c9df54c5 [client] egl: fix unintended vertical clipping in spiceDrawFill 2024-05-20 19:42:21 +10:00
Jacob McNamee
c48bd35b3a [client] opengl: fix unintended vertical clipping in spiceDrawFill 2024-05-20 19:42:21 +10:00
Geoffrey McRae
ecd3692e1e [host] dxgi: fix crash during init due to out of order accesses 2024-04-04 22:44:49 +11:00
Geoffrey McRae
23b773ad80 [client] wayland: fix failure to select libdecor
Fixes #1116 - Thanks @blu3bird
2024-03-27 01:57:01 +11:00
kamplom
a626a1142d [client] wayland: Let viewporter use full wl_buffer 2024-03-22 03:56:26 +11:00
Netboy3
e70718600c [doc] Update module libvirt setup 2024-03-21 13:00:05 +11:00
Geoffrey McRae
0990c59eff [obs] fix compilation for older versions of OBS 2024-03-18 17:42:40 +11:00
Geoffrey McRae
fb1d96e147 [obs] tell OBS if we are operating in HDR 2024-03-18 17:35:25 +11:00
Geoffrey McRae
7f515c54b3 [client] x11/i3: fix fullscreen at launch via parameter/config 2024-03-14 12:56:39 +11:00
Geoffrey McRae
20972cfd9b [client] cmake: move X11 config directives to displayservers 2024-03-13 11:17:25 +11:00
Geoffrey McRae
dc9065b62f [client] egl: do not use DMA when using the spice display 2024-03-12 13:57:23 +11:00
Geoffrey McRae
13b9756e80 [client] egl: fix desktop spice toggle race 2024-03-12 11:58:16 +11:00
Geoffrey McRae
d902afa3dc [host] d12: fix error output string format 2024-03-11 20:32:34 +11:00
Geoffrey McRae
6e37305765 [host] d12: check if the device was removed during init 2024-03-11 20:27:36 +11:00
Geoffrey McRae
dd6c79594b [host] d12: check for failure to obtain d3d12 functions 2024-03-11 20:16:00 +11:00
Geoffrey McRae
ce9ed5da5d [host] d12: add additional debug tracing 2024-03-11 20:12:41 +11:00
Geoffrey McRae
fdad5daff8 [host/common] d12: add debug tracing 2024-03-11 19:14:54 +11:00
Geoffrey McRae
8d25469d27 [host] d12: limit the dx11 interop level to one version 2024-03-11 18:44:26 +11:00
Geoffrey McRae
8c5d1d47ee [host] nvfbc: mark NvFBC as deprecated 2024-03-11 14:16:12 +11:00
Geoffrey McRae
90398bc04f [host] app: do not try to use deprecated interfaces automatically 2024-03-11 14:16:12 +11:00
Geoffrey McRae
989fe2bb0b [host] d12: test if creating resources in the heap works at init
AMD GPUs and older NVidia GPUs can initialize fine but fail when we
start to create resources in the shared memory heap, we must test it
early to detect this so we can fallback to a working capture method.
2024-03-11 14:16:12 +11:00
Geoffrey McRae
7a41169104 [doc] consistency fix in example output 2024-03-11 00:59:46 +11:00
Geoffrey McRae
0b4322d921 [doc] remove NvFBC tuning recommendation now D12 is faster 2024-03-09 23:06:37 +11:00
Geoffrey McRae
b251b22a64 [doc] fix ivshmem_kvmfr tree structure 2024-03-09 13:36:22 +11:00
Geoffrey McRae
90b27ae1f7 [host] d12: revert ivshmem heap order change
Moving this prevented the RX580 crash early in init, but later
presents during the capture. As we want to ensure fallback to DXGI
if this happens we need to catch this fault during init. This moves
the order back so that we fail back into a working state.
2024-03-09 12:11:54 +11:00
Geoffrey McRae
4d388d6b9b [host] d12: move ivshmem heap creation to later in init
This has been moved to try to isolate a failure with RX580 startup
2024-03-09 11:56:14 +11:00
Geoffrey McRae
a88783e5b7 [doc] fix lack of consistency of Recommended 2024-03-09 11:35:52 +11:00
Geoffrey McRae
da820769e8 [host] windows: fix crash on failure to init a capture interface 2024-03-09 11:30:35 +11:00
Geoffrey McRae
d5b32225f4 [host] d12: don't attempt to use realtime priority
Some GPUs such as the RX580 seem to completely crash when attempting
to use `D3D12_COMMAND_QUEUE_PRIORITY_GLOBAL_REALTIME`.
2024-03-09 11:00:44 +11:00
Geoffrey McRae
778c21070c [doc] ammend ivshmem_kvmfr to make cgroups and permissions clearer 2024-03-08 23:31:32 +11:00
Geoffrey McRae
cae410d4de [doc] the nvidia open drivers now support DMABUF 2024-03-08 22:57:22 +11:00
Geoffrey McRae
064a605208 [doc] fix broken reference 2024-03-08 22:55:09 +11:00
Geoffrey McRae
279357e205 [doc] remove the module page as it's now part of installation 2024-03-08 22:53:43 +11:00
Geoffrey McRae
98aade2ec9 [doc] update host usage documentation 2024-03-08 22:41:32 +11:00
Geoffrey McRae
4acea9fa25 [doc] fix document reference 2024-03-08 18:37:40 +11:00
Geoffrey McRae
01efbc62c4 [doc] fix typos (again) 2024-03-08 18:34:22 +11:00
Geoffrey McRae
ed512f5943 [doc] fix typos 2024-03-08 18:32:38 +11:00
Geoffrey McRae
4a4f72ba38 [doc] update and restructure installation documentation 2024-03-08 18:22:07 +11:00
Geoffrey McRae
eb31815b46 [host] windows: add OutputDebugString capture for diagnostics 2024-03-07 11:34:46 +11:00
Geoffrey McRae
545e736389 [host] windows: handle graceful shutdown on user switch 2024-03-06 15:02:37 +11:00
Geoffrey McRae
6a72633674 [host] windows: move the service log to the temp directory
Often this log is provided instead of the actual host log, as this
log is largely useless for debugging this moves it to the temp
directory out of view of the user.
2024-03-06 13:11:48 +11:00
Geoffrey McRae
9123984ecc [host] windows: rotate the host log out keeping at most 3 prior versions 2024-03-06 12:59:15 +11:00
Geoffrey McRae
d81395b672 [common] option: send all output to stderr 2024-03-04 21:21:46 +11:00
Geoffrey McRae
bfadf0a427 [host] app: print list of valid capture interfaces 2024-03-04 21:09:39 +11:00
Geoffrey McRae
03662f45a7 [host] nvfbc: add warning to encourage migration to D12 2024-03-04 16:34:47 +11:00
Geoffrey McRae
a4e761bedc [host] win: make D12 the default copy backend 2024-03-04 16:31:14 +11:00
Geoffrey McRae
beb8de922d [host] dxgi: remove the deprecated and broken d3d12 copyBackend
This backend has been deprecated and has been replaced by the new D12 capture
interface.
2024-03-04 16:29:43 +11:00
Geoffrey McRae
e247f1fc7b [host] d12: enlarge downsampler damage rects 2024-02-28 20:34:09 +11:00
Geoffrey McRae
4463ca15f6 [host] d12: added downsampler 2024-02-28 20:21:47 +11:00
Geoffrey McRae
97d91a32c8 [host] d12: make effects fully self-contained 2024-02-28 16:05:56 +11:00
Geoffrey McRae
2d41cda640 [host] d12: fix target for wait fence when effects are active 2024-02-28 12:31:56 +11:00
Geoffrey McRae
a894348530 [host] d12: correct the pitch when HDR16 is in use 2024-02-28 12:23:41 +11:00
Geoffrey McRae
ad7ac6540f [host] d12: implement hdr16 to hdr10 conversion 2024-02-28 11:59:58 +11:00
Geoffrey McRae
0184ddeedd [host] d12: properly handle format changes by effects 2024-02-28 09:40:35 +11:00
Geoffrey McRae
b87d8d2f33 [host] d12: disable RGB24 if the capture is HDR 2024-02-28 07:46:47 +11:00
Geoffrey McRae
5c4540ed8b [host] d12: pass frame information in a description structure 2024-02-28 07:30:39 +11:00
Geoffrey McRae
c7f1aadb9e [host] d12: pass back rotation metadata to the client 2024-02-28 05:54:40 +11:00
Geoffrey McRae
57ac020c8c [host] linux: update to build again 2024-02-27 19:14:58 +11:00
Geoffrey McRae
71b826458d [host] fix host-windows-cross github compilation 2024-02-27 19:14:58 +11:00
Geoffrey McRae
4408359597 [host] d12: fix damage tracking with RGB24 enabled 2024-02-24 09:04:59 +11:00
Geoffrey McRae
2f3ca443cf [host] d12: Use the gpu reported pitch instead of assuming w * 4 2024-02-24 07:57:50 +11:00
Geoffrey McRae
66049cf763 [host] d12: fix, send the current dirtyRects, not the old 2024-02-23 17:38:45 +11:00
Geoffrey McRae
a6dc8a9db3 [host] d12: fix damage tracking on re-init 2024-02-23 17:28:09 +11:00
Geoffrey McRae
dc4d93f50a [host] d12: remove extra copies in damage tracking 2024-02-23 17:24:25 +11:00
Geoffrey McRae
3b43dcb80d [host] d12: fix type of function argument 2024-02-23 11:00:18 +11:00
Geoffrey McRae
9de047d9cb [host] d12: implement damage aware copy 2024-02-23 10:54:08 +11:00
Geoffrey McRae
1098b7e6bd [host] d12: cosmetics 2024-02-23 08:38:32 +11:00
Geoffrey McRae
055d5527ef [host] d12: allow specifying adapter and output to capture 2024-02-23 08:14:24 +11:00
Geoffrey McRae
b1313980fb [host] d12: make RGB24 optional, disabled by default 2024-02-05 05:17:55 +11:00
Geoffrey McRae
319241b597 [host] d12: use a GPU side fence instead of CPU side 2024-02-05 04:27:04 +11:00
Geoffrey McRae
b0b851dd4b [host] d12: fix incorrect format presented to extra clients 2024-02-05 04:20:38 +11:00
Geoffrey McRae
60b01566e1 [host] d12: implement initial RGB24 support 2024-02-05 02:49:08 +11:00
Geoffrey McRae
4076377820 [host] win: add comRef helpers for leak identification and tracking 2024-02-05 02:49:08 +11:00
Geoffrey McRae
0b210a280d [all] update the copyright to 2024 2024-02-01 17:16:31 +11:00
Geoffrey McRae
a4fede01f3 [host] d12: general cleanups 2024-02-01 17:13:18 +11:00
Geoffrey McRae
071e4323fa [host] windows: make DXGI the default capture interface, D12 is not ready 2024-02-01 14:48:44 +11:00
Geoffrey McRae
be82b7e578 [host] d12: add wrappers for backend functions 2024-01-31 10:18:56 +11:00
Geoffrey McRae
c07b72883a [host] d12: adjust backend API to allow multiple instances 2024-01-31 09:43:01 +11:00
Geoffrey McRae
462d8187b6 [host] d12: handle error when windows switches to the secure desktop 2024-01-31 05:42:30 +11:00
Geoffrey McRae
4523b9ba00 [host] d12: fix failure to send mouse shape updates 2024-01-31 05:28:03 +11:00
Geoffrey McRae
cae4b2f4f9 [host] windows: fix compilation under gcc 2024-01-31 01:33:43 +11:00
Geoffrey McRae
72b25b99bc [host] add new D12 capture interface
Note, this capture interface is not yet feature complete but does seem
to be stable.
2024-01-31 00:29:55 +11:00
Geoffrey McRae
e376e6fb53 [host] app: revert unintended change from last commit 2024-01-27 23:19:42 +11:00
Geoffrey McRae
34e8a2255e [host] dxgi: d3d12 now writes direclty into ivshmem
This is still incomplete as d3d12 doesn't have any proper sync with the
captured frame and as such is still not suggested for general usage. This
change though is monumental for this project as it removes a full memory
copy reducing bandwidth consumption enormously.
2024-01-27 22:57:13 +11:00
Geoffrey McRae
adaf40e2bf [host] dxgi: Fix d3d12 32-bit output
Fixes #1100
2024-01-27 20:07:52 +11:00
Geoffrey McRae
84dd68dd2b [host] dxgi: correct 24-bit packed output height calculation bug 2024-01-27 11:14:04 +11:00
Geoffrey McRae
738a04d0bc [host] dxgi: fix packed texture height calculation 2024-01-27 10:56:23 +11:00
Geoffrey McRae
2316a5e64d [host] dxgi: ensure 24-bit packed output is properly aligned 2024-01-27 10:11:01 +11:00
Geoffrey McRae
47ad93f48d [host] common: fix compilation on clang 2024-01-27 10:10:48 +11:00
Geoffrey McRae
c5cbb8aa46 [host] dxgi: enable 24-bit mode by default
General community feedback with this enabled has been positive and we
can now transition to enabling by default.
2024-01-27 02:28:09 +11:00
Geoffrey McRae
ad00aaacd0 [obs] fix non-dmabuf 24-bit imports
OBS `GS_RGBA` maps to the sRGB color space which breaks our mapping
kludge, as such we need to use the UNORM variants to avoid this
2024-01-27 02:14:05 +11:00
Geoffrey McRae
1b75ae0762 [obs] fix 24-bit import support with dmabuf 2024-01-27 00:44:38 +11:00
Geoffrey McRae
5b3cc4cd48 [cmake] MakeObject: ensure embedded objects are null terminated 2024-01-27 00:44:38 +11:00
Geoffrey McRae
7247fadad8 [client] egl: fix post processing failure when converting pixel formats 2024-01-25 17:51:06 +11:00
Geoffrey McRae
c2237f29ae [client] x11: fix typo
Closes #1105
2024-01-02 08:51:13 +11:00
Tudor Brindus
e5a9c0242f [client] wayland: libdecor maximize request should maximize, not minimize 2023-12-24 15:30:19 +11:00
Quantum
537218d6ae [client] wayland: honour fullscreen and maximize in libdecor
We never added the functionality when the parameters were passed to
libdecor_shellInit.
2023-12-22 14:08:48 +11:00
Geoffrey McRae
f05151c9a6 [host] nvfbc: fix compilation with new debug code 2023-12-07 16:33:54 +11:00
Geoffrey McRae
b776b00a67 [common] host: fix compliation on windows 2023-12-07 16:11:28 +11:00
Geoffrey McRae
4b4e07875d [common] debug: fix compilation on windows 2023-12-07 15:46:31 +11:00
Geoffrey McRae
6104956a27 [common] reformat the debug output to be more useful 2023-12-07 15:37:54 +11:00
Geoffrey McRae
3668040892 [client] pipewire: correct pipewire_latency as per the docs
see: https://docs.pipewire.org/structpw__time.html
2023-12-06 22:26:20 +11:00
Geoffrey McRae
8cd002f1b2 [client] x11: fix incorrect pointer reference, fixes clipboard
Thanks to @JJRcop for bisecting to discover the fault
2023-12-01 09:12:38 +11:00
Geoffrey McRae
22d949c411 [client] egl: fix rgb24 regression
We need the alpha channel for this data type
2023-11-21 12:20:46 +11:00
Geoffrey McRae
43a3fb0db3 [client] egl: RGB24 improvements
This patch performs several changes.

* Alters the fourcc codes to types that ignore the alpha channel where
  possible to allow the gpu to internally use 24-bit formats.
* Attempts to use DRM_FORMAT_RGB888 first as some GPUs may support this
* If DMABUF is not in use the data is now imported directly as RGB24
  without the post-processing shader
2023-11-21 12:01:45 +11:00
Geoffrey McRae
8d27d9e2e2 [common] rect: fix avx alignment issue take 2 2023-11-19 18:01:34 +11:00
Geoffrey McRae
660b4b8ec8 [common] rects: fix avx implementation for unaligned accesses 2023-11-19 17:16:22 +11:00
Geoffrey McRae
4911e129f8 [common] time: improve flow with compiler hints 2023-11-19 16:31:45 +11:00
Geoffrey McRae
d3ee5bddde [common] rect/framebuffer: improve avx implementations 2023-11-19 15:45:15 +11:00
Geoffrey McRae
0ce4c34c37 [client] egl: fix non-dma RGB24 import path 2023-11-19 09:42:37 +11:00
Geoffrey McRae
584de4133f [common] fix compilation on clang 2023-11-19 03:20:34 +11:00
Geoffrey McRae
3330f83af6 [common] add runtime detection and selection of AVX/AVX2 support 2023-11-19 02:52:11 +11:00
Geoffrey McRae
5d4c1d348c [all] refactor cpuInfo function names 2023-11-19 01:22:09 +11:00
Geoffrey McRae
750cab83a3 Revert "[common] add AVX/AVX2 memory copy implementations"
This reverts commit e61678ef1b898b8d443e6a4cb00fe912f52d4647.
GCC only supports multi-versioning in C++
2023-11-19 00:18:48 +11:00
Geoffrey McRae
e61678ef1b [common] add AVX/AVX2 memory copy implementations 2023-11-19 00:09:42 +11:00
Geoffrey McRae
6357df1a7a [client] egl: fix non-dma texture stride issue 2023-11-15 17:40:57 +11:00
Geoffrey McRae
1f4395570c [obs] fix timeout when unsupported frame type is provided 2023-11-15 17:39:09 +11:00
Geoffrey McRae
accf300c6c [host] dxgi: fix failure to reset texture state on fast restarts 2023-11-15 17:31:37 +11:00
Geoffrey McRae
a0fd03d328 [repo] update LGMP submodule 2023-11-13 14:03:19 +11:00
Geoffrey McRae
929e88b9d3 [all] provide conditional path optimization hints to the compiler 2023-11-12 18:26:08 +11:00
Geoffrey McRae
7bea919352 [common] time: prevent possible div by zero on windows 2023-11-12 06:56:37 +11:00
Geoffrey McRae
96b5892c31 [host] app: prevent possible null deref and resource leak 2023-11-12 06:44:20 +11:00
Geoffrey McRae
b14aad7118 [host] dxgi: remove uneeded variable + minor cosmetic changes 2023-11-12 06:36:04 +11:00
Geoffrey McRae
7321ca6768 [host] windows: fix stdout/stderr resource leak 2023-11-12 06:20:29 +11:00
Geoffrey McRae
cd6485f2ed [host] downsample: fix resource leak on failure 2023-11-12 05:59:47 +11:00
Geoffrey McRae
7bcad37568 [host] platform: Close the exitEvent when done 2023-11-12 05:59:14 +11:00
Geoffrey McRae
ba8075a9fd [host] app: fix unchecked return value 2023-11-11 20:50:42 +11:00
Geoffrey McRae
3bad3837b4 [host] fix copy-paste error 2023-11-11 20:47:36 +11:00
Geoffrey McRae
084ebe5035 [host] dxgi: fix potential buffer out of bounds access 2023-11-11 20:41:10 +11:00
Geoffrey McRae
d480b674ca [host] dxgi: fix invalid array access via wrong variable 2023-11-11 20:37:58 +11:00
Geoffrey McRae
10e30eec57 [host] dxgi: fix possible null dereference 2023-11-11 20:36:53 +11:00
Geoffrey McRae
38b6b0ac40 [common] windows/event: fix control flow issue 2023-11-11 20:34:30 +11:00
Geoffrey McRae
503efdd0d8 [host] dxgi: fix failure to call FreeLibrary for d3d12 2023-11-11 20:31:53 +11:00
Geoffrey McRae
75e10688d4 [repo] update PureSpice submodule 2023-11-11 15:51:37 +11:00
Geoffrey McRae
ec88a52fe2 [common] ivshmem: fix possible resource leak 2023-11-11 14:04:54 +11:00
Geoffrey McRae
a28deae569 [client] util: fix invalid pointer arithmatic 2023-11-11 14:03:40 +11:00
Geoffrey McRae
cf51503a54 [client] X11/i3: write directly into sun_path to avoid strncpy 2023-11-11 13:48:41 +11:00
Geoffrey McRae
aa42751743 [client] common: fix time of check/time of use issue 2023-11-11 13:48:41 +11:00
Geoffrey McRae
9a53880b9b [common] ivshmem: fix failure to check ioctl for error result 2023-11-11 13:48:41 +11:00
Geoffrey McRae
a3b51220ed [common] option: remove const qualifier from shortopt 2023-11-11 13:48:41 +11:00
Geoffrey McRae
17fce1cf78 [client] util: fix failure to check result of ftell for error 2023-11-11 13:48:41 +11:00
Geoffrey McRae
9f3f8cc5bd [client] splash: remove 0 array specifier from func prototype 2023-11-11 13:48:41 +11:00
Geoffrey McRae
0524980cb4 [client] msg: make it clear we ignore the return for ll_shift 2023-11-11 13:48:41 +11:00
Geoffrey McRae
a0f5907cb6 [client] overlay: prevent possible divide by zero 2023-11-11 13:48:41 +11:00
Geoffrey McRae
0a9784d09d [client] main: fix possible dereference of null g_state.ds 2023-11-11 13:48:41 +11:00
Geoffrey McRae
120e063a10 [client] main: prevent possible null pointer dereference 2023-11-11 13:48:41 +11:00
Geoffrey McRae
f59ef4422a [client] app: prevent possible out of bounds array access 2023-11-11 13:48:41 +11:00
Geoffrey McRae
417c9cf092 [client] opengl: act on glBufferSubData failure 2023-11-11 13:48:41 +11:00
Geoffrey McRae
852825a97e [client] util: fix failure to dereference pointer 2023-11-11 13:48:41 +11:00
Geoffrey McRae
cce12508cc [egl] shader: fix reliance on null terminated strings
Compiled in resources often will not contain a null terminator, as such
we must not use functions that rely on it. This implements a memsearch
function that performs like strstr on a buffer instead of a null
terminated string.
2023-11-11 13:48:41 +11:00
Geoffrey McRae
43f9a4c0e1 [client] egl: fix potential integer underflow 2023-11-11 13:48:41 +11:00
Geoffrey McRae
ee5c02f72a [client] egl: fix potential resource leak 2023-11-11 13:48:41 +11:00
Geoffrey McRae
55fa5cc851 [client] egl: fix integer division into float 2023-11-11 13:48:41 +11:00
Geoffrey McRae
b70811dcb9 [client] x11: pass large struct by reference 2023-11-11 13:48:41 +11:00
Geoffrey McRae
3c1405719c [client] wayland: fix use after free bugs 2023-11-11 13:48:41 +11:00
Geoffrey McRae
f6befb4567 [client] wayland: formatting 2023-11-11 13:48:41 +11:00
Jonathan Rubenstein
62aef5a240 [doc] build: Move NvFBC note into don't build warning
Also:
   - Add link to warning (#dont-build-the-host)
2023-11-11 10:32:15 +11:00
Jonathan Rubenstein
4f508d320a [doc] words: Sort using sort_words.py 2023-11-11 10:31:51 +11:00
Jonathan Rubenstein
29f1434270 [doc] sort_words: Create sort_words.py to make words.txt easier to maintain
sort_words.py outputs a diff that if applied will sort words.txt

It has a few options:
    - '--add-word': Add a word before sorting (usable multiple times)
    - '--save': Save changes to file instead of only outputting a diff
    - '--quiet': Don't output a diff
2023-11-11 10:31:51 +11:00
Geoffrey McRae
27f3af8221 [host] nvfbc: cosmetics 2023-11-11 09:08:56 +11:00
Geoffrey McRae
cb849b287c [hosts] nvfbc: exit loop early if ystart is > then dataHeight 2023-11-11 09:08:08 +11:00
Geoffrey McRae
8630fd20ad [common] rects: simplify unaligned copy function 2023-11-11 09:07:00 +11:00
Geoffrey McRae
0057cf5377 [host] nvfbc: add additional debug output 2023-11-10 08:01:03 +11:00
Geoffrey McRae
e31874b809 [resources] update update minimum cmake version required 2023-11-10 06:28:06 +11:00
Geoffrey McRae
a2443cf926 [client] wayland: libdecor is not a requirement of building 2023-11-10 06:28:06 +11:00
Geoffrey McRae
eaaef65791 [repo] update PureSpice and nanosvg 2023-11-10 06:28:06 +11:00
Geoffrey McRae
11542d7ace [repos] update LGMP submodule 2023-11-10 06:28:06 +11:00
Geoffrey McRae
138a0aee53 [all] update cmake minimum version 2023-11-10 06:28:05 +11:00
Geoffrey McRae
7a30736ac4 [host] linux: fix compilation (untested) 2023-11-10 06:28:05 +11:00
Geoffrey McRae
174b51b144 [client] wayland: add additional debug output 2023-11-10 06:28:05 +11:00
Geoffrey McRae
aa9dbe654d [client] wayland: move libdecor and xdg into seperate backends
This allows us to build with libdecor enabled as the selection to use it
is decided upon at runtime if the compositor `gnome-shell` is detected.
If the libdecor development headers are installed, by default it will
now be compiled in unless overridden by the user at compile time.
2023-11-10 06:28:05 +11:00
Geoffrey McRae
d592f13f88 [host] all: don't combine the downsampler rules 2023-11-10 06:28:05 +11:00
Geoffrey McRae
905fea57f0 [host] nvfbc: fix stride for odd resolutions in 24-bit mode 2023-11-10 06:28:05 +11:00
Geoffrey McRae
3a6afd04d2 [client] egl: use the pitch to calculate the new width for rgb24 2023-11-10 06:28:05 +11:00
Geoffrey McRae
c0e09e13a5 [client] egl: make the bgr_bgra filter generic for 24-bit formats 2023-11-10 06:28:05 +11:00
Geoffrey McRae
3843afa927 [client] egl: fix texture import for RGB_24 2023-11-10 06:28:05 +11:00
Geoffrey McRae
49bdf046fe [client] egl: partially fix RGB_24 support 2023-11-10 06:28:05 +11:00
Geoffrey McRae
8605df8c8d [host] nvfbc: fix damage copy when operating in 24bpp 2023-11-10 06:28:05 +11:00
Geoffrey McRae
d847c2c144 [common] add new frame type string to KVMFR lookup table 2023-11-10 06:28:05 +11:00
Geoffrey McRae
6492c47e1e [client] egl: fix typo 2023-11-10 06:28:05 +11:00
Geoffrey McRae
86e8e99107 [all] add initial support for RGB24-bpp support 2023-11-10 06:28:05 +11:00
Geoffrey McRae
dcde981a17 [client] opengl: fix cursor location when the source is downsampled 2023-11-10 06:28:05 +11:00
Geoffrey McRae
c54a09ca25 [client] opengl: fix row alignment parameter bug 2023-11-10 06:28:05 +11:00
Geoffrey McRae
5bba4dfab5 [host] nvfbc: fix incorrect metadata when resampling is enabled 2023-11-10 06:28:05 +11:00
Geoffrey McRae
3af2cf54d6 [client] egl: remove deprecated BGR members and logic 2023-11-10 06:28:05 +11:00
Geoffrey McRae
4aba15f31c [client] egl: only scale damage rects if it's packed BGR 2023-11-10 06:28:05 +11:00
Geoffrey McRae
a455078e0f [host] dxgi: dont alter the damage rect array when scaling 2023-11-10 06:28:05 +11:00
Geoffrey McRae
f8586fd063 [host] dxgi: fix RGB24 damage rect rounding bug 2023-11-10 06:28:05 +11:00
Geoffrey McRae
ad13928c73 [client] egl: fix bgr filter re-init if DMA mode changes 2023-11-10 06:28:05 +11:00
Geoffrey McRae
f991f994f0 [host] dxgi: rename cpu to tex 2023-11-10 06:28:05 +11:00
Geoffrey McRae
772e0e3b4a [host] dxgi: fix d3d11 invalid memory allocation for backend struct 2023-11-10 06:28:05 +11:00
Geoffrey McRae
fd79bb1333 [host] dxgi: add option to enable RGB24 packing support 2023-11-10 06:28:05 +11:00
Geoffrey McRae
d6519c4486 [host] dxgi: remove unused define 2023-11-10 06:28:05 +11:00
Geoffrey McRae
9fefbae749 [host] dxgi: make dxgi structs private again 2023-11-10 06:28:05 +11:00
Geoffrey McRae
fa561c121e [host] dxgi: move and document dxgi_* exposed functions for backends 2023-11-10 06:28:05 +11:00
Geoffrey McRae
c2e3c37bab [host] dxgi: remove no longer used member for copy backends 2023-11-10 06:28:05 +11:00
Geoffrey McRae
54bd08c3cb [host] dxgi: decouple backends from the DXGI main struct 2023-11-10 06:28:05 +11:00
Geoffrey McRae
eb2796d40b [host] dxgi: move the backend interface into a separate header 2023-11-10 06:28:05 +11:00
Geoffrey McRae
748c9c177e [dxgi] increase the comRef global count 2023-11-10 06:28:05 +11:00
Geoffrey McRae
cc48257aeb [dxgi] d3d12: fix incorrect mapping range 2023-11-10 06:28:05 +11:00
Geoffrey McRae
3838e1f996 [host] dxgi: fix the return status of the downsampler 2023-11-10 06:28:05 +11:00
Geoffrey McRae
881aa9e179 [host] dxgi: fix the d3d12 copy backend 2023-11-10 06:28:05 +11:00
Geoffrey McRae
9a2638bfa0 [host] dxgi: fix unbalanced scope pop 2023-11-10 06:28:05 +11:00
Geoffrey McRae
8d7d5ba8fd [host] dxgi: fix comRef leak in the downsampler 2023-11-10 06:28:05 +11:00
Geoffrey McRae
09b6fee360 [host] dxgi: fix HDR content downsampling 2023-11-10 06:28:05 +11:00
Geoffrey McRae
561c45bcb9 [host] dxgi: fix support for non 24-bit BGR formats 2023-11-10 06:28:05 +11:00
Geoffrey McRae
5f613b09d6 [host] dxgi: implement downsampling to arbitrary sizes 2023-11-10 06:28:05 +11:00
Geoffrey McRae
30c577beeb [host] all: make the downsample rule matching common 2023-11-10 06:28:05 +11:00
Geoffrey McRae
6c7f3c4197 [host] nvfbc: make the downsampleParser available outside of NvFBC 2023-11-10 06:28:05 +11:00
Geoffrey McRae
139e98ac3b [client] OpenGL:fix RGB24 support for non 64-bit aligned pitches 2023-11-10 06:28:05 +11:00
Tudor Brindus
d02e3730b2 [client] EGL: implement damage-aware RGB24 copy 2023-11-10 06:28:05 +11:00
Tudor Brindus
ea5b6b4026 [host] DXGI: implement damage-aware RGB24 copy 2023-11-10 06:28:05 +11:00
Tudor Brindus
6329779893 [host] DXGI: rescale RGB24 texture to 3/4ths the input width
Now that data isn't packed across rows, we can decrease the amount of
texture memory we require.
2023-11-10 06:28:05 +11:00
Tudor Brindus
1da50d220e [client] EGL: stop unpacking data across rows 2023-11-10 06:28:05 +11:00
Tudor Brindus
3106d0e3e2 [host] DXGI: stop packing data across rows
This is a precursor to allowing damage-aware RGB24 copies.
2023-11-10 06:28:05 +11:00
Tudor Brindus
d44fc36fc4 [host] DXGI: stop rescaling RGB24 texture height
For the moment, this just increases texture memory usage, but does not
affect behavior.

In a future commit, I will modify the shaders to not pack data across
rows, in order to enable damage copies.
2023-11-10 06:28:05 +11:00
Tudor Brindus
c29404eea6 [host] DXGI: fixed swapped rows and cols variables
In practice this worked out because `rows = cols`, but this will change
in future commits as I implement RGB24 damage support.
2023-11-10 06:28:05 +11:00
Geoffrey McRae
c665044bfa [client] implement support for RGB24 packed data 2023-11-10 06:28:05 +11:00
Geoffrey McRae
578d98fd22 [host] DXGI: initial implementation of RGB24 support
This commit breaks damage tracking and the dx12 backend and is not in
a state where it should be used by the general public.
2023-11-10 06:28:05 +11:00
Geoffrey McRae
b3879ff1d7 [host] windows: the vertex shader is common to all post-processors 2023-11-10 06:28:05 +11:00
Jonathan Rubenstein
f6b2cec841 [doc] html_unescape: Create html.unescape extension
This new sphinx extension runs html.unescape
(from the Python Standard Library) on source files before they are
rendered, allowing escape sequences like  '&nbsp;' for the no-break
space character.

I have also published this extension in my own name under a different
license (the same one Sphinx uses) for others to use:
https://github.com/JJRcop/sphinxcontrib-html_unescape
2023-11-02 06:21:51 +11:00
Jonathan Rubenstein
626f5eb32e [doc] usage: Actually add non-breaking spaces to config file
In 36252078012e5609754868300df1dd02bc8e9292 I attempted to add
non-breaking spaces to a filepath so it would stay on one line.
Before this I had accidentally deleted my work but found it saved
in my sphinx build cache, so I copied my changes from that cache.

Unfortunately the cached version replaced non-breaking spaces with
real spaces and 36252078012e5609754868300df1dd02bc8e9292 was made
reverted.

This commit re-adds the non-breaking spaces.
2023-11-02 06:21:51 +11:00
Jonathan Rubenstein
29c797d7b6 [doc] usage: Add Selecting an IVSHMEM device 2023-10-30 14:16:28 +11:00
Jonathan Rubenstein
3625207801 [doc] usage: Add non-breaking spaces to config path 2023-10-30 14:16:28 +11:00
Jonathan Rubenstein
25d6dd3ba2 [doc] usage: Refresh opening paragraph in Host usage 2023-10-30 14:16:28 +11:00
Geoffrey McRae
1e30539fb2 [dxgi] cache the input shader resource view of the src texture 2023-10-29 21:53:48 +11:00
Geoffrey McRae
52410beea7 [host] dxgi: check for correct comRef usage 2023-10-29 20:29:14 +11:00
Geoffrey McRae
c591f7a8ae [common] vector: assert if the vector itemSize <= 0 2023-10-29 20:27:49 +11:00
Geoffrey McRae
21cd380cad [host] dxgi: seperate out and implement a post processor chain 2023-10-29 20:27:17 +11:00
Geoffrey McRae
e225f66cee [host] dxgi: explicitly flush the pipeline to reduce latency 2023-10-27 22:08:33 +11:00
Geoffrey McRae
2206752b66 [host] dxgi: fix d3d12 backend resource leak
The handle is only needed so we can open the resource, once we have
it we can close the handle. We then cache the shared resource for
future reuse if possible.
2023-10-27 21:56:49 +11:00
Geoffrey McRae
0510d06c4b [host] dxgi: fix d3d12 texture sharing when in HDR 2023-10-27 21:22:00 +11:00
Geoffrey McRae
699d95818d [host] dxgi: upate d3d12 backend to use comRef 2023-10-27 21:01:44 +11:00
Geoffrey McRae
fffac35300 [host] dxgi: update d3d11 backend to make use of comRef 2023-10-27 18:44:17 +11:00
Geoffrey McRae
35b0f8edf3 [host] dxgi: allow the HDR texture to be shared with other backends 2023-10-27 18:43:26 +11:00
Geoffrey McRae
544164f637 [host] dxgi: allow the backend to shutdown before freeing comRef globals 2023-10-27 18:42:38 +11:00
Geoffrey McRae
b94166177f [host] dxgi: update to make use of comRef (part 1 of 2) 2023-10-27 17:34:34 +11:00
Geoffrey McRae
69b984aa2c [host] dxgi: add helper to manage COM object memory 2023-10-27 17:33:40 +11:00
Geoffrey McRae
c100df4037 [all] common: debug assert should always abort 2023-10-27 17:32:58 +11:00
Geoffrey McRae
47329ebd89 [host] dxgi: move utility functions into util.c 2023-10-27 11:28:37 +11:00
Geoffrey McRae
5d7469d23e [host] linux: fix build with new HDR changes (untested) 2023-10-27 01:19:46 +11:00
Geoffrey McRae
6625cd733a [client] main: log if the frame format is using PQ or not 2023-10-27 01:16:08 +11:00
Geoffrey McRae
b41840b010 [host] all: tell the client if HDR PQ is in use or not 2023-10-27 01:11:57 +11:00
Geoffrey McRae
2f36aaff5c [client] egl: prepare for DXGI HDR10 support 2023-10-27 01:03:22 +11:00
Geoffrey McRae
200b7b732c [host] dxgi: properly convert HDR16 to HDR10
DXGI doesn't take into account the SDRWhiteLevel that has already been
applied to the monitor when it converts to HDR10 which results in clipping.
This change set implements a HLSL shader to reverse this while at the same
time converting to HDR10.

This is still not perfect but far better then doing nothing.
2023-10-26 22:57:41 +11:00
Geoffrey McRae
eeea24ebfb [client] egl: invalidate the window when HDR settings are changed 2023-10-24 13:46:51 +11:00
Geoffrey McRae
54066094bd [github] workflow: use latest ubuntu for cross compile build 2023-10-22 19:56:21 +11:00
Geoffrey McRae
c5923b9b4d [host] dxgi: obtain HDR color space and monitor information 2023-10-22 17:38:42 +11:00
Geoffrey McRae
bde2eef175 [host] dxgi: fix format for HDR10 when it needs correcting 2023-10-22 04:01:51 +11:00
Geoffrey McRae
e0bdd869d6 [all] fix client build on linux 2023-10-22 04:00:44 +11:00
Geoffrey McRae
ccd0a0bcf9 [host] dxgi: use HDR10 instead of HDR16 to save bandwidth 2023-10-22 03:47:32 +11:00
Geoffrey McRae
742e41c2c3 [host] dxgi: fix HDR damage aware copy 2023-10-22 03:15:12 +11:00
Geoffrey McRae
3ed71a09f4 [common] all: implement strdup directly
Dr.Memory on Windows complains bitterly about invalid heap free as
it doesn't seem to be able to track this function's allocations. As
it's such a trivial function we can just implement it locally.
2023-10-22 02:25:25 +11:00
Geoffrey McRae
12d051d8c0 [host] nvfbc: do not try to init on non NVIDIA hardware 2023-10-22 01:46:57 +11:00
Geoffrey McRae
a6a6b8779a [host] nvfbc: fix use of initialized variable 2023-10-22 01:21:20 +11:00
Geoffrey McRae
ad65561511 [host] app: cleanup local variables during init. 2023-10-21 22:03:22 +11:00
Geoffrey McRae
6b65c7e339 [all] lgmp: update to fix memory leak on shutdown 2023-10-21 22:02:33 +11:00
Geoffrey McRae
646f5b1be8 [host] fix windows compilation with mingw clang 2023-10-21 22:02:02 +11:00
Geoffrey McRae
128a8938c6 [host] nvfbc: remove useless update of member 2023-10-20 22:12:42 +11:00
Geoffrey McRae
2e515657dd [all] update/add license headers 2023-10-20 15:36:34 +11:00
Geoffrey McRae
1cf8e8c846 [doc] add luminance to words 2023-10-20 15:34:35 +11:00
Geoffrey McRae
df5c648377 [client] egl: remove unit qualifier from label 2023-10-20 15:30:36 +11:00
Geoffrey McRae
78df2073ff [client] egl: make it possible to configure HDR to SDR mapping 2023-10-20 15:26:27 +11:00
Geoffrey McRae
844a37a276 [all] update LGMP to fix invalid index access bug 2023-10-20 14:21:11 +11:00
Geoffrey McRae
e658c2e0a2 [client] x11: change to a more visible "dot" cursor
This change alters the small square dot cursor to a more visible 16x16
cursor for X11. A new option `spice:largeDotCursor` can be set to use an
alternative 32x32 cursor for the vision impaired.
2023-09-13 21:54:31 +10:00
Geoffrey McRae
b2ec60d2dc [client/host] common: fix trim value bug reading the wrong variable 2023-09-13 21:54:31 +10:00
Zenithal
e6aa2b85a9 [module] AMD SEV support 2023-09-13 15:12:40 +10:00
Geoffrey McRae
a3045e0b4a [client] main: don't request activation just because we re-connected 2023-09-13 14:08:18 +10:00
Geoffrey McRae
9cadb64942 [client] main: make activation requests optional 2023-09-13 14:07:42 +10:00
Geoffrey McRae
82607a7d6f [client] x11: read the i3 IPC response 2023-09-12 02:03:24 +10:00
Geoffrey McRae
8d90c9c2a5 [doc] add new words to words.txt 2023-09-12 01:27:34 +10:00
Geoffrey McRae
24d4fce17c [client] x11: add support for i3 global full screen toggle
This commit adds an interface to the X11 display server code to support
various window manage specific features, such as in this case, the i3
global full screen toggle.

This feature specifically uses the i3 IPC to cause looking glass to go
full screen across all monitors if the new option `i3:globalFullScreen`
is enabled.
2023-09-12 01:19:06 +10:00
Geoffrey McRae
8dba4b6c0b [client] x11: fix cleanup if XIQueryVersion fails 2023-09-11 23:41:52 +10:00
Geoffrey McRae
193977895b [client] add new option input:captureOnFocus
This option allows the client to automatically enter capture mode if the
window is focused.

Closes #1088
2023-09-11 21:02:32 +10:00
Jonathan Rubenstein
219c73edbe [doc] faq.rst: Reword libdecor package installation
Also change package name to Debian
package libdecor-0-dev
2023-07-25 10:15:28 +10:00
Jonathan Rubenstein
6522920ea1 [doc] faq.rst: Update libdecor source link
Source code moved from https://gitlab.gnome.org/jadahl/libdecor
to https://gitlab.freedesktop.org/libdecor/libdecor
2023-07-25 10:15:28 +10:00
esi
e32b292cc1 [module] Fix build on Linux 6.4 (fixes #1075) 2023-06-30 07:29:15 +10:00
Netboy3
53525847fd [common] options: Fix regression in option parsing logic 2023-05-03 18:20:01 +10:00
Geoffrey McRae
9d66a68403 [common] options: fix build on windows (no strndup) 2023-05-02 12:32:59 +10:00
Geoffrey McRae
07bcc54732 [common] options: trim whitespace from option values
This closes #1071
2023-05-02 12:27:17 +10:00
Geoffrey McRae
dab5618a6d [host] linux: fix compilation 2023-04-26 16:31:02 +10:00
Geoffrey McRae
ad43969c1a [client] egl: add support to map HDR to SDR 2023-04-26 16:26:54 +10:00
Geoffrey McRae
99333a03c1 [host] nvfbc: fix failure to store last hdr state 2023-04-26 15:58:22 +10:00
Geoffrey McRae
45318aa653 [host] nvfbc: bump the format version when HDR status changes 2023-04-26 15:55:26 +10:00
Geoffrey McRae
f84165ac66 [host] app: report to the client if the frame is actually HDR 2023-04-26 15:48:01 +10:00
Geoffrey McRae
e4a8424fad [host] nvfbc: fix ARGB10 support
There is no performance penalty to using ARGB10 as it's still a 32-bit copy per
pixel and the nvidia driver internally handles any conversions required to make
this work for both SDR and HDR.
2023-04-19 19:52:30 +10:00
Geoffrey McRae
880d8042a4 [client] egl: fix support for deep color modes (30bpp) 2023-04-19 18:02:14 +10:00
Geoffrey McRae
a629d24dc3 [client] egl: fix fourcc for RGBA10 dmabuf 2023-04-19 17:30:40 +10:00
MakiseKurisu
7c1bb13d70 [all] add MakiseKurisu to AUTHORS 2023-04-16 19:50:35 +10:00
MakiseKurisu
9d5c543a53 [module] add logging for kvmfr_module_init 2023-04-16 19:50:35 +10:00
MakiseKurisu
063a859de1 [module] add logging for kvmfr_pci_probe 2023-04-16 19:50:35 +10:00
Geoffrey McRae
4f4cf2be7d [client] audio: provide option to prevent volume level sync 2023-04-16 19:10:35 +10:00
Geoffrey McRae
9759b5aa8f [idd] the cursor pos is only valid if it's visible 2023-04-14 21:40:21 +10:00
Geoffrey McRae
bbd0c7a99b [idd] implement cursor shape & position transmission 2023-04-14 20:40:00 +10:00
Geoffrey McRae
c11748a76f [idd] remove unused member variable 2023-04-14 14:10:19 +10:00
Geoffrey McRae
d6b26b0eb1 [idd] resend the last captured frame if a new client connects 2023-04-14 14:08:09 +10:00
Geoffrey McRae
fd0cc6aa10 [idd] consistency: change t to st 2023-04-14 14:08:03 +10:00
Geoffrey McRae
80b9bda59d [idd] keep the textures mapped until overwrite for future re-use 2023-04-14 13:26:08 +10:00
Geoffrey McRae
0c176acf94 [idd] updated to a VS2022 project with Windows 10 support 2023-04-14 12:14:17 +10:00
Geoffrey McRae
0c3dce3ca6 [idd] implemented frame feed from the guest (very hacky)
This is NOT READY for general consumption, if you decide to make
use of this driver, DO NOT ASK FOR SUPPORT.
2023-04-11 16:55:58 +10:00
Geoffrey McRae
3c85957b99 [idd] implemented core shared memory functionallity and LGMP setup 2023-04-11 16:53:57 +10:00
Geoffrey McRae
77ddcfe489 [idd] implement CIVSHMEM and open it 2023-04-11 16:53:57 +10:00
Geoffrey McRae
d228ef135e [idd] target a specific version of windows 2023-04-11 16:53:57 +10:00
Geoffrey McRae
0afcf2c2ce [idd] add LGMP to the project 2023-04-11 16:53:57 +10:00
Geoffrey McRae
75da66a090 [idd] initial indirect driver with Looking Glass monitor 2023-04-11 16:53:57 +10:00
Geoffrey McRae
11676d3d56 [common] windows: fix invalid free of stack allocated memory 2023-04-11 16:51:25 +10:00
Geoffrey McRae
b13682a9ef [lgmp] update library to fix issues with MSVC builds 2023-04-11 16:51:02 +10:00
Geoffrey McRae
642634293d [lgmp] update to fix compilation under MSVC 2023-04-10 10:24:53 +10:00
Geoffrey McRae
01f9c2bfb5 [lgmp] update to fix alignment with MSVC compiled code 2023-04-10 10:21:39 +10:00
Geoffrey McRae
9385b2de7a [submodule] update LGMP to the latest version for incoming Idd support 2023-04-10 03:07:07 +10:00
Jonathan Rubenstein
d2f7667fae [github] CODEOWERS: Fix typo stopping JJRcop owning /doc/
Made an embarassing typo
2023-04-09 09:43:19 +10:00
Jonathan Rubenstein
cfef966603 [github] build: Use debian package for sphinxcontrib-spelling 2023-04-05 08:13:54 +10:00
Jonathan Rubenstein
4f09d5b771 [doc] troubleshooting: Rewrite missing desktop 2023-04-04 23:31:41 +10:00
Geoffrey McRae
b7b302334c [client] main: remove debug true from if statement 2023-04-02 20:14:16 +10:00
Geoffrey McRae
28e74a73a4 [client] main: detect and warn on sub-optimal guest CPU configuration 2023-04-02 20:13:14 +10:00
Geoffrey McRae
996e1b2f7a [client] overlay: fix race on startup that caused a segfault
Fixes #1065
2023-03-30 14:59:37 +11:00
Geoffrey McRae
0ee5751b3a [client] egl: fix buffer overflow 2023-03-30 14:16:51 +11:00
Geoffrey McRae
e067db7bb4 [doc] fix typos and correct 1440p resolution.
Closes #1064
Closes #1061
2023-03-19 12:57:16 +11:00
Geoffrey McRae
b4b4a37b2b [client] egl: switch to using GL_TEXTURE_EXTERNAL_OES for DMABUF
According to Erik @ NVidia the open source NVidia driver will not
create a EGLImage from a DMABUF if the target is not
GL_TEXTURE_EXTERNAL_OES. This change set converts the dmabuf texture
from GL_TEXTURE_2D to GL_TEXTURE_EXTERNAL_OES and at runtime performs a
global search & replace on fragment shaders as needed to remain
compatible, replacing `sampler2D` with `samplerExternalOES`.

Ref: https://github.com/NVIDIA/open-gpu-kernel-modules/discussions/243#discussioncomment-3283415
2023-03-09 09:20:01 +11:00
Geoffrey McRae
bbc9204bfe [client] egl: ensure the preprocessed files have a null terminator 2023-03-09 09:15:39 +11:00
Geoffrey McRae
894b8b0c22 [client] x11: generate synthetic EnterNotify event on cursor leave
If the cursor was grabbed the window the cursor moves over when it is
ungrabbed will recieve an EnterNotify event with the mode of
NotifyUngrab, unfortunatly some window manages such as i3 will ignore
this message and as such focus follows mouse will not function
correctly. This patch injects a normal EnterNotify to work around this
issue.
2023-03-05 16:42:47 +11:00
Geoffrey McRae
91b0cba145 [client] egl: switch from mediump to highp
This fixes a rounding issue on certain hardware (NVidia) which actually
implement mediump as half precision (16-bit) float. It's safe to assume
`highp` is available as if the GPU does not support it, then the shader
compiler will try to find a lower precision that is supported by the GPU
2023-03-05 14:10:21 +11:00
Geoffrey McRae
2f48798db9 [client] wayland: check for failure of wl_display_connect 2023-02-28 23:38:44 +11:00
Geoffrey McRae
254b370bbf [client] audio/pipewire: fix compilation under clang 2023-01-24 13:46:22 +11:00
Geoffrey McRae
86efc47505 [client] audio/pipewire: PW_KEY_TARGET_OBJECT only exists as of 0.3.44 2023-01-24 13:43:17 +11:00
Geoffrey McRae
b559d6b9bc [client] audio/pipewire: add options to set the default out/rec devices 2023-01-24 13:35:25 +11:00
Geoffrey McRae
48cd0c3789 [client] audio: add missing earlyInit call for audio devs 2023-01-24 13:35:18 +11:00
Geoffrey McRae
6f8e6f9a19 [client] x11: catch and print details of X11 errors with a backtrace 2023-01-05 17:33:01 +11:00
377 changed files with 39232 additions and 12373 deletions

2
.github/CODEOWNERS vendored
View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -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
View 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)

View File

@ -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)

View File

@ -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)

View File

@ -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")

View File

@ -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)

View File

@ -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,

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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")

View File

@ -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

View File

@ -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
@ -376,7 +376,7 @@ static void clipboardReadCancel(struct ClipboardRead * data)
static void clipboardReadCallback(uint32_t events, void * opaque) static void clipboardReadCallback(uint32_t events, void * opaque)
{ {
struct ClipboardRead * data = opaque; struct ClipboardRead * data = opaque;
if (events & EPOLLERR) if (events & EPOLLERR)
{ {
clipboardReadCancel(data); clipboardReadCancel(data);
return; return;
@ -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;

View File

@ -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

View 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
../
)

View File

@ -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(
"../../"
)

View 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
};

View 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(
"../../"
)

View 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
};

View File

@ -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

View File

@ -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

View File

@ -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);

View 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

View File

@ -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

View File

@ -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;

View File

@ -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);

View 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"
)

View File

@ -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,

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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

View File

@ -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,

View File

@ -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);

View File

@ -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)

View File

@ -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 .
) )

View File

@ -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

View File

@ -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

View File

@ -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;
} }

View File

@ -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);

View 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);
}

View 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

View 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

View 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

View 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

View File

@ -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,

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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
View 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);

View File

@ -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

View File

@ -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

View File

@ -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 ); \

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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);
} }

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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;
} }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
} }

View 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
};

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;
} }

View File

@ -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);

View File

@ -10,4 +10,4 @@ function process(line, second) {
} }
} }
{ process($0, $2) } { process($0, $2) } END { print "\0"; }

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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

View 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
);
}

View File

@ -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;

View File

@ -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;

View File

@ -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