Compare commits

...

63 Commits
B7 ... master

Author SHA1 Message Date
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
72 changed files with 4315 additions and 584 deletions

7
.gitignore vendored
View File

@ -11,7 +11,10 @@ module/modules.order
__pycache__
*.py[co]
*/.vs
*.user
idd/Debug
idd/x64
idd/LGIdd/x64
idd/LGIdd/Debug
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

@ -113,6 +113,7 @@ set(SOURCES
src/main.c
src/core.c
src/app.c
src/message.c
src/audio.c
src/config.c
src/keybind.c
@ -124,6 +125,7 @@ set(SOURCES
src/eglutil.c
src/overlay_utils.c
src/render_queue.c
src/evdev.c
src/overlay/splash.c
src/overlay/alert.c

View File

@ -209,7 +209,7 @@ done:
close(fd);
}
static int getCharcode(uint32_t key)
int waylandGetCharCode(int key)
{
key += 8; // xkb scancode is evdev scancode + 8
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;
wl_array_for_each(key, keys)
app_handleKeyPress(*key, getCharcode(*key));
app_handleKeyPress(*key);
}
static void keyboardLeaveHandler(void * data, struct wl_keyboard * keyboard,
@ -253,9 +253,9 @@ static void keyboardKeyHandler(void * data, struct wl_keyboard * keyboard,
return;
if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
app_handleKeyPress(key, getCharcode(key));
app_handleKeyPress(key);
else
app_handleKeyRelease(key, getCharcode(key));
app_handleKeyRelease(key);
if (!wlWm.xkbState || !app_isOverlayMode() || state != WL_KEYBOARD_KEY_STATE_PRESSED)
return;

View File

@ -263,6 +263,7 @@ struct LG_DisplayServerOps LGDS_Wayland =
.uncapturePointer = waylandUncapturePointer,
.grabKeyboard = waylandGrabKeyboard,
.ungrabKeyboard = waylandUngrabKeyboard,
.getCharCode = waylandGetCharCode,
.warpPointer = waylandWarpPointer,
.realignPointer = waylandRealignPointer,
.isValidPointerPos = waylandIsValidPointerPos,

View File

@ -277,6 +277,7 @@ void waylandUncapturePointer(void);
void waylandRealignPointer(void);
void waylandWarpPointer(int x, int y, bool exiting);
void waylandGuestPointerUpdated(double x, double y, double localX, double localY);
int waylandGetCharCode(int key);
// output module
bool waylandOutputInit(void);

View File

@ -1087,8 +1087,9 @@ static void setFocus(bool focused, double x, double y)
app_handleFocusEvent(focused);
}
static int getCharcode(int detail)
static int x11GetCharCode(int detail)
{
detail += x11.minKeycode;
if (detail < x11.minKeycode || detail > x11.maxKeycode)
return 0;
@ -1229,8 +1230,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIDeviceEvent *device = cookie->data;
app_handleKeyPress(device->detail - x11.minKeycode,
getCharcode(device->detail));
app_handleKeyPress(device->detail - x11.minKeycode);
if (!x11.xic || !app_isOverlayMode())
return;
@ -1280,8 +1280,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIDeviceEvent *device = cookie->data;
app_handleKeyRelease(device->detail - x11.minKeycode,
getCharcode(device->detail));
app_handleKeyRelease(device->detail - x11.minKeycode);
if (!x11.xic || !app_isOverlayMode())
return;
@ -1310,8 +1309,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIRawEvent *raw = cookie->data;
app_handleKeyPress(raw->detail - x11.minKeycode,
getCharcode(raw->detail));
app_handleKeyPress(raw->detail - x11.minKeycode);
return;
}
@ -1321,8 +1319,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return;
XIRawEvent *raw = cookie->data;
app_handleKeyRelease(raw->detail - x11.minKeycode,
getCharcode(raw->detail));
app_handleKeyRelease(raw->detail - x11.minKeycode);
return;
}
@ -2017,6 +2014,7 @@ struct LG_DisplayServerOps LGDS_X11 =
.ungrabPointer = x11UngrabPointer,
.capturePointer = x11CapturePointer,
.uncapturePointer = x11UncapturePointer,
.getCharCode = x11GetCharCode,
.grabKeyboard = x11GrabKeyboard,
.ungrabKeyboard = x11UngrabKeyboard,
.warpPointer = x11WarpPointer,

View File

@ -59,8 +59,8 @@ void app_handleButtonPress(int button);
void app_handleButtonRelease(int button);
void app_handleWheelMotion(double motion);
void app_handleKeyboardTyped(const char * typed);
void app_handleKeyPress(int scancode, int charcode);
void app_handleKeyRelease(int scancode, int charcode);
void app_handleKeyPress(int scancode);
void app_handleKeyRelease(int scancode);
void app_handleKeyboardModifiers(bool ctrl, bool shift, bool alt, bool super);
void app_handleKeyboardLEDs(bool numLock, bool capsLock, bool scrollLock);
void app_handleEnterEvent(bool entered);

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

@ -183,6 +183,9 @@ struct LG_DisplayServerOps
void (*capturePointer)(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 */
void (*warpPointer)(int x, int y, bool exiting);
@ -253,6 +256,7 @@ struct LG_DisplayServerOps
DEBUG_ASSERT((x)->ungrabPointer ); \
DEBUG_ASSERT((x)->capturePointer ); \
DEBUG_ASSERT((x)->uncapturePointer ); \
DEBUG_ASSERT((x)->getCharCode ); \
DEBUG_ASSERT((x)->warpPointer ); \
DEBUG_ASSERT((x)->realignPointer ); \
DEBUG_ASSERT((x)->isValidPointerPos ); \

View File

@ -18,13 +18,14 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "app.h"
#include "app_internal.h"
#include "main.h"
#include "core.h"
#include "util.h"
#include "clipboard.h"
#include "render_queue.h"
#include "evdev.h"
#include "kb.h"
@ -119,7 +120,7 @@ void app_handleFocusEvent(bool focused)
if (g_params.releaseKeysOnFocusLoss)
for (int key = 0; key < KEY_MAX; key++)
if (g_state.keyDown[key])
app_handleKeyRelease(key, 0);
app_handleKeyReleaseInternal(key);
g_state.escapeActive = false;
@ -311,7 +312,7 @@ void app_handleWheelMotion(double motion)
g_state.io->MouseWheel -= motion;
}
void app_handleKeyPress(int sc, int charcode)
void app_handleKeyPressInternal(int sc)
{
if (!app_isOverlayMode() || !g_state.io->WantCaptureKeyboard)
{
@ -327,6 +328,7 @@ void app_handleKeyPress(int sc, int charcode)
{
g_state.escapeAction = sc;
KeybindHandle handle;
int charcode = g_state.ds->getCharCode(sc);
ll_forEachNL(g_state.bindings, item, handle)
{
if ((handle->sc && handle->sc == sc ) ||
@ -374,7 +376,7 @@ void app_handleKeyPress(int sc, int charcode)
}
}
void app_handleKeyRelease(int sc, int charcode)
void app_handleKeyReleaseInternal(int sc)
{
if (g_state.escapeActive)
{
@ -419,6 +421,18 @@ void app_handleKeyRelease(int sc, int charcode)
}
}
void app_handleKeyPress(int sc)
{
if (!evdev_isExclusive())
app_handleKeyPressInternal(sc);
}
void app_handleKeyRelease(int sc)
{
if (!evdev_isExclusive())
app_handleKeyReleaseInternal(sc);
}
void app_handleKeyboardTyped(const char * typed)
{
ImGuiIO_AddInputCharactersUTF8(g_state.io, typed);

View File

@ -18,11 +18,12 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#pragma once
#ifndef _H_LG_APP_INTERNAL_
#define _H_LG_APP_INTERNAL_
#include <Windows.h>
#include <tchar.h>
#include "app.h"
VOID _DBGPRINT(PCSTR kszFunction, INT iLineNumber, LPCSTR kszDebugFormatString, ...);
#define DBGPRINT(kszDebugFormatString, ...) \
_DBGPRINT(__FUNCTION__, __LINE__, kszDebugFormatString, __VA_ARGS__)
void app_handleKeyPressInternal(int scancode);
void app_handleKeyReleaseInternal(int scancode);
#endif

View File

@ -220,6 +220,14 @@ static struct Option options[] =
.type = OPTION_TYPE_BOOL,
.value.x_bool = false,
},
{
.module = "win",
.name = "setGuestRes",
.description = "On window size change, request the guest to match"
" resoution (if supported by the guest, currently LG IDD only)",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
},
{
.module = "win",
.name = "fpsMin",
@ -717,6 +725,7 @@ bool config_load(int argc, char * argv[])
g_params.helpMenuDelayUs = option_get_int("input", "helpMenuDelay") * (uint64_t) 1000;
g_params.minimizeOnFocusLoss = option_get_bool("win", "minimizeOnFocusLoss");
g_params.setGuestRes = option_get_bool("win", "setGuestRes" );
if ((g_params.useSpice = option_get_bool("spice", "enable")))
{

View File

@ -23,6 +23,8 @@
#include "app.h"
#include "util.h"
#include "kb.h"
#include "message.h"
#include "message.h"
#include "common/time.h"
#include "common/debug.h"
@ -141,6 +143,13 @@ void core_setGrabQuiet(bool enable)
core_setCursorInView(true);
g_state.ignoreInput = false;
/* ensure the local mouse is inside the window before we capture, this fixes
* odd UI behaviour if the user is using focus follows mouse and the window
* was focused without the cursor being in window already */
struct DoublePoint local;
util_guestCurToLocal(&local);
core_warpPointer(local.x, local.y, true);
if (g_params.grabKeyboard)
g_state.ds->grabKeyboard();
@ -181,8 +190,44 @@ bool core_warpPointer(int x, int y, bool exiting)
return true;
}
void core_onWindowSizeChanged(unsigned width, unsigned height)
{
if (!g_state.pointerQueue)
return;
if (g_state.srcSize.x == width && g_state.srcSize.y == height)
return;
const KVMFRWindowSize msg =
{
.msg.type = KVMFR_MESSAGE_WINDOWSIZE,
.w = width,
.h = height
};
uint32_t serial;
LGMP_STATUS status;
if ((status = lgmpClientSendData(g_state.pointerQueue,
&msg, sizeof(msg), &serial)) != LGMP_OK)
DEBUG_WARN("Message send failed: %s", lgmpStatusString(status));
}
void core_updatePositionInfo(void)
{
if (g_params.setGuestRes && g_state.kvmfrFeatures & KVMFR_FEATURE_WINDOWSIZE)
{
LGMsg msg =
{
.type = LG_MSG_WINDOWSIZE,
.windowSize =
{
.width = g_state.windowW,
.height = g_state.windowH
}
};
lgMessage_post(&msg);
}
if (!g_state.haveSrcSize)
goto done;

View File

@ -29,6 +29,7 @@ void core_setCursorInView(bool enable);
void core_setGrab(bool enable);
void core_setGrabQuiet(bool enable);
bool core_warpPointer(int x, int y, bool exiting);
void core_onWindowSizeChanged(unsigned width, unsigned height);
void core_updatePositionInfo(void);
void core_alignToGuest(void);
bool core_isValidPointerPos(int x, int y);

441
client/src/evdev.c Normal file
View File

@ -0,0 +1,441 @@
/**
* 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 "evdev.h"
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <linux/input.h>
#include <string.h>
#include <sys/epoll.h>
#include "app_internal.h"
#include "core.h"
#include "main.h"
#include "common/debug.h"
#include "common/option.h"
#include "common/stringlist.h"
#include "common/thread.h"
typedef struct
{
char * path;
int fd;
bool grabbed;
}
EvdevDevice;
struct EvdevState
{
char * deviceList;
EvdevDevice * devices;
int deviceCount;
bool exclusive;
int keys[KEY_MAX];
void (*dsGrabKeyboard)(void);
void (*dsUngrabKeyboard)(void);
int epoll;
LGThread * thread;
bool grabbed;
enum
{
PENDING_NONE,
PENDING_GRAB,
PENDING_UNGRAB
}
pending;
};
static struct EvdevState state = {};
static struct Option options[] =
{
{
.module = "input",
.name = "evdev",
.description = "csv list of evdev input devices to use "
"for capture mode (ie: /dev/input/by-id/usb-some_device-event-kbd)",
.type = OPTION_TYPE_STRING,
.value.x_string = NULL,
},
{
.module = "input",
.name = "evdevExclusive",
.description = "Only use evdev devices for input when in capture mode",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{0}
};
void evdev_earlyInit(void)
{
option_register(options);
}
static bool evdev_grabDevice(EvdevDevice * device)
{
if (device->grabbed)
return true;
if (ioctl(device->fd, EVIOCGRAB, (void *)1) < 0)
{
DEBUG_ERROR("EVIOCGRAB=1 failed: %s", strerror(errno));
return false;
}
DEBUG_INFO("Grabbed %s", device->path);
device->grabbed = true;
return true;
}
static bool evdev_openDevice(EvdevDevice * device, bool quiet)
{
device->fd = open(device->path, O_RDWR);
if (device->fd < 0)
{
if (errno != ENOENT || !quiet)
DEBUG_ERROR("Unable to open %s (%s)", device->path, strerror(errno));
goto err;
}
struct epoll_event event =
{
.events = EPOLLIN,
.data.ptr = device
};
if (epoll_ctl(state.epoll, EPOLL_CTL_ADD, device->fd, &event) != 0)
{
DEBUG_ERROR("Failed to add fd to epoll");
goto err;
}
DEBUG_INFO("Opened: %s", device->path);
if (state.grabbed)
evdev_grabDevice(device);
return true;
err:
close(device->fd);
device->fd = 0;
return false;
}
static int evdev_thread(void * opaque)
{
struct epoll_event * events = alloca(sizeof(*events) * state.deviceCount);
struct input_event msgs[256];
DEBUG_INFO("evdev_thread Started");
while(app_isRunning())
{
int openDevices = 0;
for(int i = 0; i < state.deviceCount; ++i)
{
EvdevDevice * dev = &state.devices[i];
if (dev->fd <= 0)
{
if (evdev_openDevice(dev, true))
++openDevices;
}
else
++openDevices;
}
if (openDevices == 0)
{
usleep(1000);
continue;
}
int waiting = epoll_wait(state.epoll, events, state.deviceCount, 100);
for(int i = 0; i < waiting; ++i)
{
EvdevDevice * device = (EvdevDevice *)events[i].data.ptr;
int n = read(device->fd, msgs, sizeof(msgs));
if (n < 0)
{
if (errno == ENODEV)
{
DEBUG_WARN("Device was removed: %s", device->path);
epoll_ctl(state.epoll, EPOLL_CTL_DEL, device->fd, NULL);
close(device->fd);
device->fd = 0;
device->grabbed = false;
continue;
}
DEBUG_WARN("Failed to read evdev event: %s (%s)",
device->path, strerror(errno));
continue;
}
if (n % sizeof(*msgs) != 0)
DEBUG_WARN("Incomplete evdev read: %s", device->path);
bool grabbed = state.grabbed;
int count = n / sizeof(*msgs);
struct input_event *ev = msgs;
int mouseX = 0, mouseY = 0;
for(int i = 0; i < count; ++i, ++ev)
{
switch(ev->type)
{
case EV_KEY:
{
bool isMouseBtn = ev->code >= BTN_MOUSE && ev->code <= BTN_BACK;
static const int mouseBtnMap[] = {1, 3, 2, 6, 7, 0, 0};
if (ev->value == 1)
{
++state.keys[ev->code];
if (grabbed && state.keys[ev->code] == 1)
{
if (isMouseBtn)
app_handleButtonPress(mouseBtnMap[ev->code - BTN_MOUSE]);
else
app_handleKeyPressInternal(ev->code);
}
}
else if (ev->value == 0 && --state.keys[ev->code] <= 0)
{
state.keys[ev->code] = 0;
if (state.pending == PENDING_GRAB)
{
state.pending = PENDING_NONE;
evdev_grabKeyboard();
}
else if (state.pending == PENDING_UNGRAB)
{
state.pending = PENDING_NONE;
evdev_ungrabKeyboard();
}
if (grabbed)
{
if (isMouseBtn)
app_handleButtonRelease(mouseBtnMap[ev->code - BTN_MOUSE]);
else
app_handleKeyReleaseInternal(ev->code);
}
}
break;
}
case EV_REL:
if (!grabbed)
continue;
switch(ev->code)
{
case REL_X:
mouseX += ev->value;
break;
case REL_Y:
mouseY += ev->value;
break;
case REL_WHEEL:
{
int btn;
if (ev->value > 0)
btn = 4;
else
btn = 5;
app_handleButtonPress (btn);
app_handleButtonRelease(btn);
break;
}
}
break;
}
}
if (mouseX != 0 || mouseY != 0)
core_handleMouseGrabbed(mouseX, mouseY);
}
}
DEBUG_INFO("evdev_thread Stopped");
return 0;
}
bool evdev_start(void)
{
const char * deviceList = option_get_string("input", "evdev");
if (!deviceList)
return false;
state.deviceList = strdup(deviceList);
StringList sl = stringlist_new(false);
char * token = strtok(state.deviceList, ",");
while(token != NULL)
{
stringlist_push(sl, token);
token = strtok(NULL, ",");
}
state.deviceCount = stringlist_count(sl);
state.devices = calloc(state.deviceCount, sizeof(*state.devices));
for(int i = 0; i < state.deviceCount; ++i)
state.devices[i].path = stringlist_at(sl, i);
stringlist_free(&sl);
// nothing to do if there are no configured devices
if (state.deviceCount == 0)
return false;
state.exclusive = option_get("input", "evdevExclusive");
state.epoll = epoll_create1(0);
if (state.epoll < 0)
{
DEBUG_ERROR("Failed to create epoll (%s)", strerror(errno));
return false;
}
for(int i = 0; i < state.deviceCount; ++i)
{
EvdevDevice * device = &state.devices[i];
if (!evdev_openDevice(device, false))
return false;
}
if (!lgCreateThread("Evdev", evdev_thread, NULL, &state.thread))
{
DEBUG_ERROR("Failed to create the evdev thread");
return false;
}
//hook the display server's grab methods
state.dsGrabKeyboard = g_state.ds->grabKeyboard;
state.dsUngrabKeyboard = g_state.ds->ungrabKeyboard;
g_state.ds->grabKeyboard = &evdev_grabKeyboard;
g_state.ds->ungrabKeyboard = &evdev_ungrabKeyboard;
return true;
}
void evdev_stop(void)
{
if (state.deviceList)
{
free(state.deviceList);
state.deviceList = NULL;
}
if (state.thread)
{
lgJoinThread(state.thread, NULL);
state.thread = NULL;
}
if (state.epoll >= 0)
{
close(state.epoll);
state.epoll = 0;
}
for(EvdevDevice * device = state.devices; device->path; ++device)
{
if (device->fd <= 0)
continue;
close(device->fd);
device->fd = 0;
}
}
void evdev_grabKeyboard(void)
{
if (state.grabbed)
return;
// we must be in a neutral state
for(int i = 0; i < KEY_MAX; ++i)
if (state.keys[i] > 0)
{
state.pending = PENDING_GRAB;
return;
}
// state.dsGrabKeyboard();
for(EvdevDevice * device = state.devices; device->path; ++device)
{
if (device->fd > 0)
evdev_grabDevice(device);
}
state.grabbed = true;
}
void evdev_ungrabKeyboard(void)
{
if (!state.grabbed)
return;
// we must be in a neutral state
for(int i = 0; i < KEY_MAX; ++i)
if (state.keys[i] > 0)
{
state.pending = PENDING_UNGRAB;
return;
}
for(EvdevDevice * device = state.devices; device->path; ++device)
{
if (device->fd <= 0 || !device->grabbed)
continue;
if (ioctl(device->fd, EVIOCGRAB, (void *)0) < 0)
{
DEBUG_ERROR("EVIOCGRAB=0 failed: %s", strerror(errno));
continue;
}
DEBUG_INFO("Ungrabbed %s", device->path);
device->grabbed = false;
}
// state.dsUngrabKeyboard();
state.grabbed = false;
}
bool evdev_isExclusive(void)
{
return state.exclusive && state.grabbed && !app_isOverlayMode();
}

View File

@ -25,6 +25,7 @@
#include "audio.h"
#include "core.h"
#include "kb.h"
#include "message.h"
#include <purespice.h>
#include <stdio.h>
@ -125,6 +126,26 @@ static void bind_toggleKey(int sc, void * opaque)
purespice_keyUp((uintptr_t) opaque);
}
static void bind_setGuestRes(int sc, void * opaque)
{
if (!(g_state.kvmfrFeatures & KVMFR_FEATURE_WINDOWSIZE))
{
app_alert(LG_ALERT_INFO, "The guest doesn't support this feature");
return;
}
LGMsg msg =
{
.type = LG_MSG_WINDOWSIZE,
.windowSize =
{
.width = g_state.windowW,
.height = g_state.windowH
}
};
lgMessage_post(&msg);
}
void keybind_commonRegister(void)
{
app_registerKeybind(0, 'F', bind_fullscreen , NULL,
@ -137,6 +158,8 @@ void keybind_commonRegister(void)
"Quit");
app_registerKeybind(0, 'O', bind_toggleOverlay, NULL,
"Toggle overlay");
app_registerKeybind(0, '=', bind_setGuestRes, NULL,
"Set guest resolution to match window size");
}
#if ENABLE_AUDIO

View File

@ -52,6 +52,7 @@
#include "common/cpuinfo.h"
#include "common/ll.h"
#include "message.h"
#include "core.h"
#include "app.h"
#include "audio.h"
@ -64,6 +65,7 @@
#include "overlay_utils.h"
#include "util.h"
#include "render_queue.h"
#include "evdev.h"
// forwards
static int renderThread(void * unused);
@ -1240,6 +1242,9 @@ static int lg_run(void)
return -1;
}
if (evdev_start())
DEBUG_INFO("Using evdev for keyboard capture");
// override the SIGINIT handler so that we can tell the difference between
// SIGINT and the user sending a close event, such as ALT+F4
signal(SIGINT , intHandler);
@ -1752,6 +1757,7 @@ restart:
g_state.state = APP_STATE_RESTART;
break;
}
lgMessage_process();
g_state.ds->wait(100);
}
@ -1868,6 +1874,9 @@ int main(int argc, char * argv[])
egl_dynProcsInit();
gl_dynProcsInit();
if (!lgMessage_init())
return -1;
g_state.bindings = ll_new();
g_state.overlays = ll_new();
@ -1891,11 +1900,14 @@ int main(int argc, char * argv[])
if (LG_AudioDevs[i]->earlyInit)
LG_AudioDevs[i]->earlyInit();
evdev_earlyInit();
if (!config_load(argc, argv))
return -1;
const int ret = lg_run();
lg_shutdown();
lgMessage_deinit();
config_free();

View File

@ -72,6 +72,7 @@ struct AppState
bool modSuper;
uint64_t lastImGuiFrame;
bool renderImGuiTwice;
bool exclusiveEvdev;
struct LG_DisplayServerOps * ds;
bool dsInitialized;
@ -171,6 +172,7 @@ struct AppParams
bool center;
int x, y;
unsigned int w, h;
bool setGuestRes;
int fpsMin;
LG_RendererRotate winRotate;
bool useSpice;

146
client/src/message.c Normal file
View File

@ -0,0 +1,146 @@
/**
* 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 "message.h"
#include "core.h"
#include "common/debug.h"
#include "common/ll.h"
#include "common/time.h"
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
typedef struct MsgEvent
{
uint64_t timestamp;
LGMsg msg;
}
MsgEvent;
struct MsgState
{
struct ll * list;
struct
{
unsigned width;
unsigned height;
}
lastWindowSize;
};
static struct MsgState this = {0};
bool lgMessage_init(void)
{
this.list = ll_new();
if (!this.list)
{
DEBUG_ERROR("Failed to create the message list");
return false;
}
return true;
}
void lgMessage_deinit(void)
{
if (this.list)
{
void * tmp;
while(ll_shift(this.list, &tmp))
free(tmp);
ll_free(this.list);
this.list = NULL;
}
}
void lgMessage_post(const LGMsg * msg)
{
MsgEvent * event = (MsgEvent *)malloc(sizeof(*event));
if (!event)
{
DEBUG_ERROR("Out of memory");
return;
}
event->timestamp = microtime();
memcpy(&event->msg, msg, sizeof(event->msg));
if (!ll_push(this.list, event))
{
DEBUG_ERROR("Failed to post a message to the list");
free(event);
}
}
void lgMessage_process(void)
{
MsgEvent * event;
MsgEvent * windowSize = NULL;
while(ll_shift(this.list, (void **)&event))
{
switch(event->msg.type)
{
case LG_MSG_WINDOWSIZE:
{
// retain the last/latest windowsize event
if (windowSize)
free(windowSize);
windowSize = event;
continue;
}
default:
DEBUG_ERROR("Unhandled %d", event->msg.type);
break;
}
free(event);
}
// if there was a windowSize event, then process it
if (windowSize)
{
const uint64_t time = microtime();
if (time - windowSize->timestamp < 500000)
{
// requeue the event for later
if (!ll_push(this.list, event))
{
DEBUG_ERROR("Failed to re-queue the windowSize event");
free(windowSize);
}
}
else
{
if (event->msg.windowSize.width != this.lastWindowSize.width ||
event->msg.windowSize.height != this.lastWindowSize.height)
{
this.lastWindowSize.width = event->msg.windowSize.width;
this.lastWindowSize.height = event->msg.windowSize.height;
core_onWindowSizeChanged(
event->msg.windowSize.width,
event->msg.windowSize.height);
}
free(windowSize);
}
}
}

66
client/src/message.h Normal file
View File

@ -0,0 +1,66 @@
/**
* 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_LG_MSG_
#define _H_LG_MSG_
#include <stdbool.h>
typedef enum LGMsgType
{
/* The LG client window size changed
* Note:
* This message is debounced to avoid flooding the guest with resize events
*/
LG_MSG_WINDOWSIZE
}
LGMsgType;
typedef struct LGMsg
{
LGMsgType type;
union
{
//LG_MSG_WINDOWSIZE
struct
{
unsigned width;
unsigned height;
}
windowSize;
//LG_MSG_VIDEO
struct
{
bool enabled;
}
video;
};
}
LGMsg;
bool lgMessage_init(void);
void lgMessage_deinit(void);
void lgMessage_process(void);
void lgMessage_post(const LGMsg * msg);
#endif

View File

@ -9,10 +9,6 @@ include_directories(
add_definitions(-D_GNU_SOURCE)
if(ENABLE_BACKTRACE)
add_definitions(-DENABLE_BACKTRACE)
endif()
if (CMAKE_C_COMPILER_ID STREQUAL "Clang")
add_compile_options(
"-Wno-unknown-warning-option"
@ -41,6 +37,10 @@ set(COMMON_SOURCES
add_library(lg_common STATIC ${COMMON_SOURCES})
target_link_libraries(lg_common lg_common_platform)
if(ENABLE_BACKTRACE)
target_compile_definitions(lg_common PUBLIC -DENABLE_BACKTRACE)
endif()
target_include_directories(lg_common
INTERFACE
include

View File

@ -56,14 +56,16 @@ typedef uint32_t KVMFRCursorFlags;
enum
{
KVMFR_FEATURE_SETCURSORPOS = 0x1
KVMFR_FEATURE_SETCURSORPOS = 0x1,
KVMFR_FEATURE_WINDOWSIZE = 0x2
};
typedef uint32_t KVMFRFeatureFlags;
enum
{
KVMFR_MESSAGE_SETCURSORPOS
KVMFR_MESSAGE_SETCURSORPOS,
KVMFR_MESSAGE_WINDOWSIZE
};
typedef uint32_t KVMFRMessageType;
@ -176,6 +178,13 @@ typedef struct KVMFRSetCursorPos
}
KVMFRSetCursorPos;
typedef struct KVMFRWindowSize
{
KVMFRMessage msg;
uint32_t w, h;
}
KVMFRWindowSize;
#ifdef _MSC_VER
#pragma warning(pop)
#endif

View File

@ -42,7 +42,7 @@ struct ll
struct ll * ll_new(void);
void ll_free (struct ll * list);
void ll_push (struct ll * list, void * data);
bool ll_push (struct ll * list, void * data);
bool ll_shift (struct ll * list, void ** data);
bool ll_shift_nl (struct ll * list, void ** data);
bool ll_peek_head (struct ll * list, void ** data);

View File

@ -48,13 +48,13 @@ void ll_free(struct ll * list)
free(list);
}
void ll_push(struct ll * list, void * data)
bool ll_push(struct ll * list, void * data)
{
struct ll_item * item = malloc(sizeof(*item));
if (!item)
{
DEBUG_ERROR("out of memory");
return;
return false;
}
item->data = data;
@ -69,7 +69,7 @@ void ll_push(struct ll * list, void * data)
list->head = item;
list->tail = item;
LG_UNLOCK(list->lock);
return;
return true;
}
item->prev = list->tail;
@ -78,6 +78,7 @@ void ll_push(struct ll * list, void * data)
list->tail = item;
LG_UNLOCK(list->lock);
return true;
}
bool ll_shift(struct ll * list, void ** data)

View File

@ -131,16 +131,16 @@ bool option_register(struct Option options[])
for(int i = 0; options[i].type != OPTION_TYPE_NONE; ++i)
++new;
state.options = realloc(
void * tmp = realloc(
state.options,
sizeof(*state.options) * (state.oCount + new)
);
if (!state.options)
if (!tmp)
{
DEBUG_ERROR("out of memory");
return false;
}
state.options = tmp;
for(int i = 0; options[i].type != OPTION_TYPE_NONE; ++i)
{
@ -229,15 +229,16 @@ bool option_register(struct Option options[])
continue;
found = true;
group->options = realloc(
void * tmp = realloc(
group->options,
sizeof(*group->options) * (group->count + 1)
);
if (!group->options)
if (!tmp)
{
DEBUG_ERROR("out of memory");
return false;
}
group->options = tmp;
group->options[group->count] = o;
int len = strlen(o->name);
@ -250,15 +251,16 @@ bool option_register(struct Option options[])
if (!found)
{
state.groups = realloc(
void * new = realloc(
state.groups,
sizeof(*state.groups) * (state.gCount + 1)
);
if (!state.groups)
if (!new)
{
DEBUG_ERROR("out of memory");
return false;
}
state.groups = new;
struct OptionGroup * group = &state.groups[state.gCount];
++state.gCount;

View File

@ -164,7 +164,14 @@ static int dl_iterate_phdr_callback(struct dl_phdr_info * info, size_t size, voi
ttl += hdr.p_memsz;
}
crash.ranges = realloc(crash.ranges, sizeof(*crash.ranges) * (crash.rangeCount + 1));
void * tmp = realloc(crash.ranges,
sizeof(*crash.ranges) * (crash.rangeCount + 1));
if (!tmp)
{
DEBUG_ERROR("out of memory");
return 1;
}
crash.ranges = tmp;
crash.ranges[crash.rangeCount].start = info->dlpi_addr;
crash.ranges[crash.rangeCount].end = info->dlpi_addr + ttl;
++crash.rangeCount;

View File

@ -626,12 +626,13 @@ static bool appendData(KVMFRUserData * dst, const void * src, const size_t size)
if (size > dst->size - dst->used)
{
size_t newSize = dst->size + max(1024, size);
dst->data = realloc(dst->data, newSize);
if (!dst->data)
void * tmp = realloc(dst->data, newSize);
if (!tmp)
{
DEBUG_ERROR("Out of memory");
return false;
}
dst->data = tmp;
memset(dst->data + dst->size, 0, newSize - dst->size);
dst->size = newSize;

204
idd/LGCommon/CDebug.cpp Normal file
View File

@ -0,0 +1,204 @@
/**
* 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 <Windows.h>
#include <string>
#include <malloc.h>
#include <strsafe.h>
#include "CDebug.h"
CDebug g_debug;
void CDebug::Init(const char * name)
{
// don't redirect the debug output if running under a debugger
if (IsDebuggerPresent())
return;
// get the system temp directory
char tempPath[MAX_PATH];
DWORD pathLen = GetTempPathA(sizeof(tempPath), tempPath);
if (pathLen == 0)
{
DEBUG_ERROR_HR(GetLastError(), "Failed to get the temp path");
return;
}
std::string folder = tempPath;
std::string baseName = name;
std::string ext = ".txt";
std::string logFile = folder + baseName + ext;
//rotate out old logs
DeleteFileA((folder + baseName + ".4" + ext).c_str());
for (int i = 3; i >= 0; --i)
{
std::string oldPath;
std::string newPath;
if (i == 0)
{
oldPath = logFile;
newPath = folder + baseName + ".1" + ext;
}
else
{
oldPath = folder + baseName + "." + std::to_string(i) + ext;
newPath = folder + baseName + "." + std::to_string(i + 1) + ext;
}
MoveFileA(oldPath.c_str(), newPath.c_str());
}
/// open the new log file
std::ofstream stream(logFile, std::ios::out | std::ios::trunc);
if (!stream.is_open())
{
DEBUG_ERROR_HR(GetLastError(), "Failed to open the log file %s", logFile.c_str());
return;
}
else
DEBUG_INFO("Logging to: %s", logFile.c_str());
m_stream = std::move(stream);
}
void CDebug::Log_va(CDebug::Level level, const char* function, int line, const char* fmt, va_list args)
{
if (level < 0 || level >= LEVEL_MAX)
level = LEVEL_NONE;
static const char* fmtTemplate = "[%s] %40s:%-4d | ";
const char* levelStr = m_levelStr[level];
int length = 0;
length = _scprintf(fmtTemplate, levelStr, function, line);
length += _vscprintf(fmt, args);
length += 2;
/* Depending on the size of the format string, allocate space on the stack or the heap. */
PCHAR buffer;
buffer = (PCHAR)_malloca(length);
if (!buffer)
return;
/* Populate the buffer with the contents of the format string. */
StringCbPrintfA(buffer, length, fmtTemplate, levelStr, function, line);
size_t offset = 0;
StringCbLengthA(buffer, length, &offset);
StringCbVPrintfA(&buffer[offset], length - offset, fmt, args);
buffer[length - 2] = '\n';
buffer[length - 1] = '\0';
Write(buffer);
_freea(buffer);
}
void CDebug::Log(CDebug::Level level, const char * function, int line, const char * fmt, ...)
{
va_list args;
va_start(args, fmt);
Log_va(level, function, line, fmt, args);
va_end(args);
}
void CDebug::LogHR(CDebug::Level level, HRESULT hr, const char * function, int line, const char * fmt, ...)
{
if (level < 0 || level >= LEVEL_MAX)
level = LEVEL_NONE;
char * hrBuffer;
if (!FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
hr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(char*)&hrBuffer,
1024,
NULL
))
{
DEBUG_INFO("FormatMessage failed with code 0x%08x", GetLastError());
va_list args;
va_start(args, fmt);
Log_va(level, function, line, fmt, args);
va_end(args);
return;
}
// Remove trailing CRLF in hrBuffer
size_t len = strlen(hrBuffer);
while (len && (hrBuffer[len - 1] == '\n' || hrBuffer[len - 1] == '\r'))
hrBuffer[--len] = '\0';
static const char* fmtTemplate = "[%s] %40s:%-4d | ";
const char* levelStr = m_levelStr[level];
va_list args;
va_start(args, fmt);
int length = 0;
length = _scprintf(fmtTemplate, levelStr, function, line);
length += _vscprintf(fmt, args);
length += 2 + 4 + (int)strlen(hrBuffer) + 1;
/* Depending on the size of the format string, allocate space on the stack or the heap. */
PCHAR buffer;
buffer = (PCHAR)_malloca(length);
if (!buffer)
{
va_end(args);
return;
}
/* Populate the buffer with the contents of the format string. */
StringCbPrintfA(buffer, length, fmtTemplate, levelStr, function, line);
size_t offset = 0;
StringCbLengthA(buffer, length, &offset);
StringCbVPrintfA(&buffer[offset], length - offset, fmt, args);
va_end(args);
/* append the formatted error */
StringCbLengthA(buffer, length, &offset);
StringCbPrintfA(&buffer[offset], length - offset, " (%s)\n", hrBuffer);
Write(buffer);
_freea(buffer);
LocalFree(hrBuffer);
}
void CDebug::Write(const char * line)
{
if (m_stream.is_open())
{
m_stream << line;
m_stream.flush();
}
else
OutputDebugStringA(line);
}

80
idd/LGCommon/CDebug.h Normal file
View File

@ -0,0 +1,80 @@
/**
* 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
*/
#pragma once
#include <Windows.h>
#include <fstream>
class CDebug
{
private:
std::ofstream m_stream;
void Write(const char * line);
public:
enum Level
{
LEVEL_NONE = 0,
LEVEL_INFO,
LEVEL_WARN,
LEVEL_ERROR,
LEVEL_TRACE,
LEVEL_FIXME,
LEVEL_FATAL,
LEVEL_MAX
};
void Init(const char * name);
void Log_va(CDebug::Level level, const char* function, int line, const char* fmt, va_list args);
void Log(CDebug::Level level, const char * function, int line, const char * fmt, ...);
void LogHR(CDebug::Level level, HRESULT hr, const char* function, int line, const char* fmt, ...);
private:
const char* m_levelStr[LEVEL_MAX] =
{
" ",
"I",
"W",
"E",
"T",
"!",
"F"
};
};
extern CDebug g_debug;
#define DEBUG_INFO(fmt, ...) g_debug.Log(CDebug::LEVEL_INFO, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#define DEBUG_WARN(fmt, ...) g_debug.Log(CDebug::LEVEL_WARN, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#define DEBUG_ERROR(fmt, ...) g_debug.Log(CDebug::LEVEL_ERROR, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#define DEBUG_TRACE(fmt, ...) g_debug.Log(CDebug::LEVEL_TRACE, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#define DEBUG_FIXME(fmt, ...) g_debug.Log(CDebug::LEVEL_FIXME, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#define DEBUG_FATAL(fmt, ...) g_debug.Log(CDebug::LEVEL_FATAL, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#define DEBUG_INFO_HR(hr, fmt, ...) g_debug.LogHR(CDebug::LEVEL_INFO, hr, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#define DEBUG_WARN_HR(hr, fmt, ...) g_debug.LogHR(CDebug::LEVEL_WARN, hr, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#define DEBUG_ERROR_HR(hr, fmt, ...) g_debug.LogHR(CDebug::LEVEL_ERROR, hr, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#define DEBUG_TRACE_HR(hr, fmt, ...) g_debug.LogHR(CDebug::LEVEL_TRACE, hr, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#define DEBUG_FIXME_HR(hr, fmt, ...) g_debug.LogHR(CDebug::LEVEL_FIXME, hr, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)
#define DEBUG_FATAL_HR(hr, fmt, ...) g_debug.LogHR(CDebug::LEVEL_FATAL, hr, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__)

51
idd/LGCommon/PipeMsg.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
*/
#pragma once
#include <stdint.h>
#define LG_PIPE_NAME "\\\\.\\pipe\\LookingGlassIDD"
struct LGPipeMsg
{
unsigned size;
enum
{
SETCURSORPOS,
SETDISPLAYMODE
}
type;
union
{
struct
{
uint32_t x;
uint32_t y;
}
curorPos;
struct
{
uint32_t width;
uint32_t height;
}
displayMode;
};
};

View File

@ -1,15 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.33423.256
# Visual Studio Version 17
VisualStudioVersion = 17.5.33530.505
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LGIdd", "LGIdd\LGIdd.vcxproj", "{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}"
ProjectSection(ProjectDependencies) = postProject
{0045D7AD-3F26-4B87-81CB-78D18839596D} = {0045D7AD-3F26-4B87-81CB-78D18839596D}
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65} = {FFCC376C-4D98-4B50-B431-E1BBC9C67E65}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LGMP", "..\repos\LGMP\LGMP.vcxproj", "{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LGIddHelper", "LGIddHelper\LGIddHelper.vcxproj", "{0045D7AD-3F26-4B87-81CB-78D18839596D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LGCommon", "LGCommon", "{ACB90E34-01CA-4B86-813B-3D20904994C6}"
ProjectSection(SolutionItems) = preProject
LGCommon\CDebug.cpp = LGCommon\CDebug.cpp
LGCommon\CDebug.h = LGCommon\CDebug.h
LGCommon\PipeMsg.h = LGCommon\PipeMsg.h
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
@ -50,7 +60,6 @@ Global
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|ARM64.ActiveCfg = Debug|Win32
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x64.ActiveCfg = Debug|x64
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x64.Build.0 = Debug|x64
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x64.Deploy.0 = Debug|x64
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x86.ActiveCfg = Debug|Win32
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x86.Build.0 = Debug|Win32
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|x86.Deploy.0 = Debug|Win32
@ -58,10 +67,25 @@ Global
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|ARM64.ActiveCfg = Release|Win32
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x64.ActiveCfg = Release|x64
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x64.Build.0 = Release|x64
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x64.Deploy.0 = Release|x64
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x86.ActiveCfg = Release|Win32
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x86.Build.0 = Release|Win32
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|x86.Deploy.0 = Release|Win32
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|ARM.ActiveCfg = Debug|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|ARM.Build.0 = Debug|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|ARM64.ActiveCfg = Debug|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|ARM64.Build.0 = Debug|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x64.ActiveCfg = Debug|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x64.Build.0 = Debug|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x86.ActiveCfg = Debug|Win32
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Debug|x86.Build.0 = Debug|Win32
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|ARM.ActiveCfg = Release|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|ARM.Build.0 = Release|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|ARM64.ActiveCfg = Release|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|ARM64.Build.0 = Release|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x64.ActiveCfg = Release|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x64.Build.0 = Release|x64
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x86.ActiveCfg = Release|Win32
{0045D7AD-3F26-4B87-81CB-78D18839596D}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -18,9 +18,10 @@
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "Direct3DDevice.h"
#include "CD3D11Device.h"
#include "CDebug.h"
HRESULT Direct3DDevice::Init()
HRESULT CD3D11Device::Init()
{
HRESULT hr = CreateDXGIFactory2(0, IID_PPV_ARGS(&m_factory));
if (FAILED(hr))
@ -29,18 +30,37 @@ HRESULT Direct3DDevice::Init()
hr = m_factory->EnumAdapterByLuid(m_adapterLuid, IID_PPV_ARGS(&m_adapter));
if (FAILED(hr))
return hr;
// only 11.1 supports DX12 interoperabillity
static const D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1
};
D3D_FEATURE_LEVEL featureLevel;
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> context;
hr = D3D11CreateDevice(
m_adapter.Get(),
D3D_DRIVER_TYPE_UNKNOWN,
nullptr,
D3D11_CREATE_DEVICE_BGRA_SUPPORT,
nullptr,
0,
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
&m_device,
nullptr,
&m_context);
&device,
&featureLevel,
&context);
if (FAILED(hr))
return hr;
DEBUG_INFO("Feature Level: 0x%x", featureLevel);
hr = device.As(&m_device);
if (FAILED(hr))
return hr;
hr = context.As(&m_context);
if (FAILED(hr))
return hr;

View File

@ -26,21 +26,29 @@
#include <dxgi1_5.h>
#include <d3d11_4.h>
struct Direct3DDevice
using namespace Microsoft::WRL;
struct CD3D11Device
{
Direct3DDevice(LUID adapterLuid) :
private:
LUID m_adapterLuid;
ComPtr<IDXGIFactory5 > m_factory;
ComPtr<IDXGIAdapter1 > m_adapter;
ComPtr<ID3D11Device5 > m_device;
ComPtr<ID3D11DeviceContext4> m_context;
public:
CD3D11Device(LUID adapterLuid) :
m_adapterLuid(adapterLuid) {};
Direct3DDevice()
CD3D11Device()
{
m_adapterLuid = LUID{};
}
HRESULT Init();
LUID m_adapterLuid;
Microsoft::WRL::ComPtr<IDXGIFactory5 > m_factory;
Microsoft::WRL::ComPtr<IDXGIAdapter1 > m_adapter;
Microsoft::WRL::ComPtr<ID3D11Device > m_device;
Microsoft::WRL::ComPtr<ID3D11DeviceContext> m_context;
ComPtr<ID3D11Device5> GetDevice() { return m_device; }
ComPtr<ID3D11DeviceContext4> GetContext() { return m_context; }
};

View File

@ -0,0 +1,163 @@
#include "CD3D12CommandQueue.h"
#include "CDebug.h"
bool CD3D12CommandQueue::Init(ID3D12Device3 * device, D3D12_COMMAND_LIST_TYPE type, const WCHAR* name, CallbackMode callbackMode)
{
HRESULT hr;
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = type;
queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_HIGH;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
hr = device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&m_queue));
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to create the CommandQueue (%ls)", name);
return false;
}
m_queue->SetName(name);
hr = device->CreateCommandAllocator(type, IID_PPV_ARGS(&m_allocator));
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to create the CommandAllocator (%ls)", name);
return false;
}
hr = device->CreateCommandList(0, type, m_allocator.Get(), NULL, IID_PPV_ARGS(&m_gfxList));
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to create the Graphics CommandList (%ls)", name);
return false;
}
m_gfxList->SetName(name);
m_cmdList = m_gfxList;
if (!m_cmdList)
{
DEBUG_ERROR_HR(hr, "Failed to get the CommandList (%ls)", name);
return false;
}
hr = device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence));
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to create the Fence (%ls)", name);
return false;
}
m_event.Attach(CreateEvent(NULL, FALSE, FALSE, NULL));
if (m_event.Get() == INVALID_HANDLE_VALUE)
{
DEBUG_ERROR_HR(GetLastError(), "Failed to create the completion event (%ls)", name);
return false;
}
if (callbackMode != DISABLED)
{
ULONG flags = (callbackMode == FAST) ?
WT_EXECUTEINWAITTHREAD : WT_EXECUTEINPERSISTENTTHREAD;
RegisterWaitForSingleObject(
&m_waitHandle,
m_event.Get(),
[](PVOID param, BOOLEAN timeout){
CD3D12CommandQueue * queue = (CD3D12CommandQueue*)param;
if (timeout)
queue->m_completionResult = false;
queue->OnCompletion();
},
this,
INFINITE,
flags);
}
m_name = name;
m_fenceValue = 0;
DEBUG_INFO("Created CD3D12CommandQueue(%ls)", name);
return true;
}
void CD3D12CommandQueue::DeInit()
{
if (m_waitHandle != INVALID_HANDLE_VALUE)
{
UnregisterWait(m_waitHandle);
m_waitHandle = INVALID_HANDLE_VALUE;
}
}
bool CD3D12CommandQueue::Execute()
{
m_needsReset = true;
m_completionResult = true;
HRESULT hr = m_gfxList->Close();
if (FAILED(hr))
{
m_completionResult = false;
SetEvent(m_event.Get());
DEBUG_ERROR_HR(hr, "Failed to close the command list (%ls)", m_name);
return false;
}
ID3D12CommandList * lists[] = { m_cmdList.Get() };
m_queue->ExecuteCommandLists(1, lists);
++m_fenceValue;
hr = m_fence->SetEventOnCompletion(m_fenceValue, m_event.Get());
if (FAILED(hr))
{
m_completionResult = false;
SetEvent(m_event.Get());
DEBUG_ERROR_HR(hr, "Failed to set the fence signal (%ls)", m_name);
return false;
}
m_pending = true;
m_queue->Signal(m_fence.Get(), m_fenceValue);
return true;
}
#if 0
void CD3D12CommandQueue::Wait()
{
if (m_fence->GetCompletedValue() >= m_fenceValue)
{
m_pending = false;
return;
}
m_fence->SetEventOnCompletion(m_fenceValue, m_event.Get());
WaitForSingleObject(m_event.Get(), INFINITE);
m_pending = false;
}
#endif
bool CD3D12CommandQueue::Reset()
{
if (!m_needsReset)
return true;
HRESULT hr;
hr = m_allocator->Reset();
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to reset the command allocator (%ls)", m_name);
return false;
}
hr = m_gfxList->Reset(m_allocator.Get(), NULL);
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to reset the graphics command list (%ls)", m_name);
return false;
}
m_needsReset = false;
return true;
}

View File

@ -0,0 +1,78 @@
#pragma once
#include <Windows.h>
#include <wdf.h>
#include <wrl.h>
#include <d3d12.h>
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace Microsoft::WRL::Wrappers::HandleTraits;
class CD3D12CommandQueue
{
private:
const WCHAR * m_name = nullptr;
ComPtr<ID3D12CommandQueue > m_queue;
ComPtr<ID3D12CommandAllocator > m_allocator;
ComPtr<ID3D12GraphicsCommandList> m_gfxList;
ComPtr<ID3D12CommandList > m_cmdList;
ComPtr<ID3D12Fence > m_fence;
bool m_pending = false;
HandleT<HANDLENullTraits> m_event;
HANDLE m_waitHandle = INVALID_HANDLE_VALUE;
UINT64 m_fenceValue = 0;
bool m_needsReset = false;
typedef void (*CompletionFunction)(CD3D12CommandQueue * queue,
bool result, void * param1, void * param2);
CompletionFunction m_completionCallback = nullptr;
void * m_completionParams[2];
bool m_completionResult = true;
void OnCompletion()
{
if (m_completionCallback)
m_completionCallback(
this,
m_completionResult,
m_completionParams[0],
m_completionParams[1]);
m_pending = false;
}
public:
~CD3D12CommandQueue() { DeInit(); }
enum CallbackMode
{
DISABLED, // no callbacks
FAST, // callback is expected to return almost immediately
NORMAL // normal callback
};
bool Init(ID3D12Device3 * device, D3D12_COMMAND_LIST_TYPE type, const WCHAR * name,
CallbackMode callbackMode = DISABLED);
void DeInit();
void SetCompletionCallback(CompletionFunction fn, void * param1, void * param2)
{
m_completionCallback = fn;
m_completionParams[0] = param1;
m_completionParams[1] = param2;
}
bool Reset();
bool Execute();
//void Wait();
bool IsReady () { return !m_pending; }
HANDLE GetEvent() { return m_event.Get(); }
ComPtr<ID3D12CommandQueue > GetCmdQueue() { return m_queue; }
ComPtr<ID3D12GraphicsCommandList> GetGfxList() { return m_gfxList; }
};

195
idd/LGIdd/CD3D12Device.cpp Normal file
View File

@ -0,0 +1,195 @@
#include "CD3D12Device.h"
#include "CDebug.h"
bool CD3D12Device::m_indirectCopy = false;
CD3D12Device::CD3D12Device(LUID adapterLuid) :
m_adapterLuid(adapterLuid),
m_debug(false)
{
if (m_debug)
{
HRESULT hr = D3D12GetDebugInterface(IID_PPV_ARGS(&m_dxDebug));
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to get the debug interface");
return;
}
m_dxDebug->EnableDebugLayer();
m_dxDebug->SetEnableGPUBasedValidation(TRUE);
m_dxDebug->SetEnableSynchronizedCommandQueueValidation(TRUE);
m_dxDebug->SetForceLegacyBarrierValidation(TRUE);
}
}
static void CALLBACK _D3D12DebugCallback(
D3D12_MESSAGE_CATEGORY category,
D3D12_MESSAGE_SEVERITY severity,
D3D12_MESSAGE_ID id,
LPCSTR description,
void *context
)
{
(void)context;
DEBUG_INFO("category:%d severity:%d id:%d desc:%s",
category,
severity,
id,
description);
}
CD3D12Device::InitResult CD3D12Device::Init(CIVSHMEM &ivshmem, UINT64 &alignSize)
{
HRESULT hr;
hr = CreateDXGIFactory2(m_debug ? DXGI_CREATE_FACTORY_DEBUG : 0, IID_PPV_ARGS(&m_factory));
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to create the DXGI factory");
return InitResult::FAILURE;
}
hr = m_factory->EnumAdapterByLuid(m_adapterLuid, IID_PPV_ARGS(&m_adapter));
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to enumerate the adapter");
return InitResult::FAILURE;
}
hr = D3D12CreateDevice(m_adapter.Get(), D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&m_device));
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to create the DirectX12 device");
return InitResult::FAILURE;
}
if (m_debug)
{
hr = m_device.As(&m_infoQueue);
if (FAILED(hr))
{
DEBUG_WARN_HR(hr, "Failed to get the ID3D12InfoQueue1 interface");
//non-fatal do not exit
}
else
{
m_infoQueue->RegisterMessageCallback(
_D3D12DebugCallback, D3D12_MESSAGE_CALLBACK_FLAG_NONE, NULL, &m_callbackCookie);
}
}
if (!m_indirectCopy)
{
hr = m_device->OpenExistingHeapFromAddress(ivshmem.GetMem(), IID_PPV_ARGS(&m_ivshmemHeap));
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to open IVSHMEM as a D3D12Heap");
m_indirectCopy = true;
return InitResult::RETRY;
}
m_ivshmemHeap->SetName(L"IVSHMEM");
D3D12_HEAP_DESC heapDesc = m_ivshmemHeap->GetDesc();
alignSize = heapDesc.Alignment;
// test that the heap is usable
if (!HeapTest())
{
DEBUG_WARN("Unable to create resources in the IVSHMEM heap, falling back to indirect copy");
// failure often results in the device being removed and we need to completely reinit when this occurs
m_indirectCopy = true;
return InitResult::RETRY;
}
DEBUG_INFO("Using IVSHMEM as a D3D12Heap");
}
for(int i = 0; i < ARRAYSIZE(m_copyQueue); ++i)
if (!m_copyQueue[i].Init(m_device.Get(), D3D12_COMMAND_LIST_TYPE_COPY, L"Copy",
m_indirectCopy ? CD3D12CommandQueue::NORMAL : CD3D12CommandQueue::FAST))
return InitResult::FAILURE;
//if (!m_computeQueue.Init(m_device.Get(), D3D12_COMMAND_LIST_TYPE_COMPUTE, L"Compute"))
//return InitResult::FAILURE;
DEBUG_INFO("Created CD3D12Device");
return InitResult::SUCCESS;
}
void CD3D12Device::DeInit()
{
if (m_debug && m_infoQueue)
m_infoQueue->UnregisterMessageCallback(m_callbackCookie);
m_infoQueue.Reset();
}
bool CD3D12Device::HeapTest()
{
D3D12_RESOURCE_DESC desc = {};
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
desc.Width = 1048576;
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER;
HRESULT hr;
ComPtr<ID3D12Resource> resource;
hr = m_device->CreatePlacedResource(
m_ivshmemHeap.Get(),
0,
&desc,
D3D12_RESOURCE_STATE_COPY_DEST,
NULL,
IID_PPV_ARGS(&resource));
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to create the ivshmem ID3D12Resource");
return false;
}
resource->SetName(L"HeapTest");
/* the above may succeed even if there was a fault, as such we also need to check
* check if the device was removed */
hr = m_device->GetDeviceRemovedReason();
if (hr != S_OK)
{
DEBUG_ERROR_HR(hr, "Device Removed");
return false;
}
return true;
}
CD3D12CommandQueue * CD3D12Device::GetCopyQueue()
{
// try for up to a maximum of 100ms to find a free copy queue
for (int c = 0; c < 100; ++c)
{
for (int i = 0; i < ARRAYSIZE(m_copyQueue); ++i)
{
auto& queue = m_copyQueue[m_copyQueueIndex++];
if (m_copyQueueIndex == ARRAYSIZE(m_copyQueue))
m_copyQueueIndex = 0;
if (queue.IsReady())
{
queue.Reset();
return &queue;
}
}
Sleep(1);
}
DEBUG_ERROR("Failed to get a copy queue");
return nullptr;
}

58
idd/LGIdd/CD3D12Device.h Normal file
View File

@ -0,0 +1,58 @@
#pragma once
#include <Windows.h>
#include <wdf.h>
#include <wrl.h>
#include <dxgi1_5.h>
#include <d3d12.h>
#include "CIVSHMEM.h"
#include "CD3D12CommandQueue.h"
using namespace Microsoft::WRL;
struct CD3D12Device
{
private:
LUID m_adapterLuid;
bool m_debug;
// static as this needs to persist if set
static bool m_indirectCopy;
ComPtr<ID3D12Debug6 > m_dxDebug;
ComPtr<ID3D12InfoQueue1> m_infoQueue;
DWORD m_callbackCookie;
ComPtr<IDXGIFactory5> m_factory;
ComPtr<IDXGIAdapter1> m_adapter;
ComPtr<ID3D12Device3> m_device;
ComPtr<ID3D12Heap > m_ivshmemHeap;
CD3D12CommandQueue m_copyQueue[4];
unsigned m_copyQueueIndex = 0;
CD3D12CommandQueue m_computeQueue;
bool HeapTest();
public:
CD3D12Device(LUID adapterLUID);
~CD3D12Device() { DeInit(); }
enum InitResult
{
RETRY,
FAILURE,
SUCCESS
};
InitResult Init(CIVSHMEM &ivshmem, UINT64 &alignSize);
void DeInit();
ComPtr<ID3D12Device3> GetDevice() { return m_device; }
ComPtr<ID3D12Heap > GetHeap() { return m_ivshmemHeap; }
bool IsIndirectCopy() { return m_indirectCopy; }
CD3D12CommandQueue * GetCopyQueue();
CD3D12CommandQueue & GetComputeQueue() { return m_computeQueue; }
};

View File

@ -0,0 +1,33 @@
#include "CFrameBufferPool.h"
#include "CSwapChainProcessor.h"
#include <stdint.h>
void CFrameBufferPool::Init(CSwapChainProcessor * swapChain)
{
m_swapChain = swapChain;
}
void CFrameBufferPool::Reset()
{
for (int i = 0; i < ARRAYSIZE(m_buffers); ++i)
m_buffers[i].Reset();
}
CFrameBufferResource * CFrameBufferPool::Get(
const CIndirectDeviceContext::PreparedFrameBuffer& buffer,
size_t minSize)
{
if (buffer.frameIndex > ARRAYSIZE(m_buffers) - 1)
return nullptr;
CFrameBufferResource* fbr = &m_buffers[buffer.frameIndex];
if (!fbr->IsValid() || fbr->GetBase() != buffer.mem || fbr->GetSize() < minSize)
{
fbr->Reset();
if (!fbr->Init(m_swapChain, buffer.frameIndex, buffer.mem, minSize))
return nullptr;
}
return fbr;
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "CFrameBufferResource.h"
#include "CIndirectDeviceContext.h"
#include "common/KVMFR.h"
//class CSwapChainProcessor;
class CFrameBufferPool
{
CSwapChainProcessor * m_swapChain;
CFrameBufferResource m_buffers[LGMP_Q_FRAME_LEN];
public:
void Init(CSwapChainProcessor * swapChain);
void Reset();
CFrameBufferResource* CFrameBufferPool::Get(
const CIndirectDeviceContext::PreparedFrameBuffer& buffer,
size_t minSize);
};

View File

@ -0,0 +1,114 @@
#include "CFrameBufferResource.h"
#include "CSwapChainProcessor.h"
#include "CDebug.h"
bool CFrameBufferResource::Init(CSwapChainProcessor * swapChain, unsigned frameIndex, uint8_t * base, size_t size)
{
m_frameIndex = frameIndex;
if (size > swapChain->GetDevice()->GetMaxFrameSize())
{
DEBUG_ERROR("Frame size of %lu is too large to fit in available shared ram");
return false;
}
// nothing to do if the resource already exists and is large enough
if (m_base == base && m_size >= size)
{
m_frameSize = size;
return true;
}
Reset();
D3D12_RESOURCE_DESC desc = {};
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Width = size;
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
HRESULT hr;
const WCHAR * resName;
if (swapChain->GetD3D12Device()->IsIndirectCopy())
{
DEBUG_TRACE("Creating standard resource for %p", base);
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_READBACK;
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapProps.CreationNodeMask = 1;
heapProps.VisibleNodeMask = 1;
hr = swapChain->GetD3D12Device()->GetDevice()->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_COPY_DEST,
NULL,
IID_PPV_ARGS(&m_res)
);
resName = L"STAGING";
if (SUCCEEDED(hr))
{
D3D12_RANGE range = {0, 0};
hr = m_res->Map(0, &range, &m_map);
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to map the resource");
return false;
}
}
}
else
{
DEBUG_TRACE("Creating ivshmem resource for %p", base);
desc.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;
desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_CROSS_ADAPTER;
hr = swapChain->GetD3D12Device()->GetDevice()->CreatePlacedResource(
swapChain->GetD3D12Device()->GetHeap().Get(),
(uintptr_t)base - (uintptr_t)swapChain->GetDevice()->GetIVSHMEM().GetMem(),
&desc,
D3D12_RESOURCE_STATE_COPY_DEST,
NULL,
IID_PPV_ARGS(&m_res)
);
resName = L"IVSHMEM";
}
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to create the FrameBuffer ID3D12Resource");
return false;
}
m_res->SetName(resName);
m_base = base;
m_size = size;
m_frameSize = size;
m_valid = true;
return true;
}
void CFrameBufferResource::Reset()
{
if (m_map)
{
m_res->Unmap(0, NULL);
m_map = NULL;
}
m_base = nullptr;
m_size = 0;
m_res.Reset();
m_valid = false;
}

View File

@ -0,0 +1,36 @@
#pragma once
#include <Windows.h>
#include <wdf.h>
#include <wrl.h>
#include <d3d12.h>
#include <stdint.h>
class CSwapChainProcessor;
using namespace Microsoft::WRL;
class CFrameBufferResource
{
private:
bool m_valid;
unsigned m_frameIndex;
uint8_t * m_base;
size_t m_size;
size_t m_frameSize;
ComPtr<ID3D12Resource> m_res;
void * m_map;
public:
bool Init(CSwapChainProcessor * swapChain, unsigned frameIndex, uint8_t * base, size_t size);
void Reset();
bool IsValid() { return m_valid; }
unsigned GetFrameIndex() { return m_frameIndex; }
uint8_t * GetBase() { return m_base; }
size_t GetSize() { return m_size; }
size_t GetFrameSize() { return m_frameSize; }
void * GetMap() { return m_map; }
ComPtr<ID3D12Resource> Get() { return m_res; }
};

View File

@ -25,7 +25,7 @@
#include <algorithm>
#include <winioctl.h>
#include "Debug.h"
#include "CDebug.h"
#include "ivshmem/ivshmem.h"
CIVSHMEM::CIVSHMEM()
@ -72,10 +72,12 @@ bool CIVSHMEM::Init()
m_devices.push_back(data);
}
if (GetLastError() != ERROR_NO_MORE_ITEMS)
HRESULT hr = GetLastError();
if (hr != ERROR_NO_MORE_ITEMS)
{
m_devices.clear();
SetupDiDestroyDeviceInfoList(devInfoSet);
DEBUG_ERROR_HR(hr, "Enumeration Failed");
return false;
}
@ -102,7 +104,7 @@ bool CIVSHMEM::Init()
{
DWORD bus = it->busAddr >> 32;
DWORD addr = it->busAddr & 0xFFFFFFFF;
DBGPRINT("IVSHMEM %u%c on bus 0x%lx, device 0x%lx, function 0x%lx\n",
DEBUG_INFO("IVSHMEM %u%c on bus 0x%lx, device 0x%lx, function 0x%lx",
i, i == shmDevice ? '*' : ' ', bus, addr >> 16, addr & 0xFFFF);
if (i == shmDevice)
@ -111,12 +113,14 @@ bool CIVSHMEM::Init()
if (!device)
{
DEBUG_ERROR("Failed to match a shmDevice");
SetupDiDestroyDeviceInfoList(devInfoSet);
return false;
}
if (SetupDiEnumDeviceInterfaces(devInfoSet, &devInfoData, &GUID_DEVINTERFACE_IVSHMEM, 0, &devInterfaceData) == FALSE)
{
DEBUG_ERROR_HR(GetLastError(), "SetupDiEnumDeviceInterfaces");
SetupDiDestroyDeviceInfoList(devInfoSet);
return false;
}
@ -125,6 +129,7 @@ bool CIVSHMEM::Init()
SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterfaceData, nullptr, 0, &reqSize, nullptr);
if (!reqSize)
{
DEBUG_ERROR_HR(GetLastError(), "SetupDiGetDeviceInterfaceDetail");
SetupDiDestroyDeviceInfoList(devInfoSet);
return false;
}
@ -133,6 +138,7 @@ bool CIVSHMEM::Init()
infData->cbSize = sizeof(PSP_DEVICE_INTERFACE_DETAIL_DATA);
if (!SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterfaceData, infData, reqSize, nullptr, nullptr))
{
DEBUG_ERROR_HR(GetLastError(), "SetupDiGetDeviceInterfaceDetail");
SetupDiDestroyDeviceInfoList(devInfoSet);
return false;
}
@ -140,11 +146,13 @@ bool CIVSHMEM::Init()
m_handle = CreateFile(infData->DevicePath, 0, 0, nullptr, OPEN_EXISTING, 0, 0);
if (m_handle == INVALID_HANDLE_VALUE)
{
DEBUG_ERROR_HR(GetLastError(), "CreateFile");
SetupDiDestroyDeviceInfoList(devInfoSet);
return false;
}
SetupDiDestroyDeviceInfoList(devInfoSet);
DEBUG_TRACE("IVSHMEM Initialized");
return true;
}
@ -153,7 +161,7 @@ bool CIVSHMEM::Open()
IVSHMEM_SIZE size;
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_REQUEST_SIZE, nullptr, 0, &size, sizeof(size), nullptr, nullptr))
{
DBGPRINT("Failed to request ivshmem size");
DEBUG_ERROR_HR(GetLastError(), "Failed to request ivshmem size");
return false;
}
@ -163,7 +171,7 @@ bool CIVSHMEM::Open()
config.cacheMode = IVSHMEM_CACHE_WRITECOMBINED;
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_REQUEST_MMAP, &config, sizeof(config), &map, sizeof(map), nullptr, nullptr))
{
DBGPRINT("Failed to request ivshmem mmap");
DEBUG_ERROR_HR(GetLastError(), "Failed to request ivshmem mmap");
return false;
}
@ -180,7 +188,7 @@ void CIVSHMEM::Close()
if (!DeviceIoControl(m_handle, IOCTL_IVSHMEM_RELEASE_MMAP, nullptr, 0, nullptr, 0, nullptr, nullptr))
{
DBGPRINT("Failed to release ivshmem mmap");
DEBUG_ERROR("Failed to release ivshmem mmap");
return;
}

View File

@ -22,7 +22,9 @@
#include "CIndirectMonitorContext.h"
#include "CPlatformInfo.h"
#include "Debug.h"
#include "CPipeServer.h"
#include "CDebug.h"
#include "VersionInfo.h"
#include <sstream>
@ -40,31 +42,58 @@ static const struct LGMPQueueConfig POINTER_QUEUE_CONFIG =
1000 //subTimeout
};
CIndirectDeviceContext::~CIndirectDeviceContext()
const DWORD DefaultDisplayModes[][3] =
{
if (m_lgmp == nullptr)
return;
{7680, 4800, 120}, {7680, 4320, 120}, {6016, 3384, 120}, {5760, 3600, 120},
{5760, 3240, 120}, {5120, 2800, 120}, {4096, 2560, 120}, {4096, 2304, 120},
{3840, 2400, 120}, {3840, 2160, 120}, {3200, 2400, 120}, {3200, 1800, 120},
{3008, 1692, 120}, {2880, 1800, 120}, {2880, 1620, 120}, {2560, 1600, 120},
{2560, 1440, 120}, {1920, 1440, 120}, {1920, 1200, 120}, {1920, 1080, 120},
{1600, 1200, 120}, {1600, 1024, 120}, {1600, 1050, 120}, {1600, 900 , 120},
{1440, 900 , 120}, {1400, 1050, 120}, {1366, 768 , 120}, {1360, 768 , 120},
{1280, 1024, 120}, {1280, 960 , 120}, {1280, 800 , 120}, {1280, 768 , 120},
{1280, 720 , 120}, {1280, 600 , 120}, {1152, 864 , 120}, {1024, 768 , 120},
{800 , 600 , 120}, {640 , 480 , 120}
};
if (m_lgmpTimer)
static const BYTE EDID[] =
{
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x30,0xE8,0x34,0x12,0xC9,0x07,0xCC,0x00,
0x01,0x21,0x01,0x04,0xA5,0x3C,0x22,0x78,0xFB,0x6C,0xE5,0xA5,0x55,0x50,0xA0,0x23,
0x0B,0x50,0x54,0x00,0x02,0x00,0xD1,0xC0,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x58,0xE3,0x00,0xA0,0xA0,0xA0,0x29,0x50,0x30,0x20,
0x35,0x00,0x55,0x50,0x21,0x00,0x00,0x1A,0x00,0x00,0x00,0xFF,0x00,0x4C,0x6F,0x6F,
0x6B,0x69,0x6E,0x67,0x47,0x6C,0x61,0x73,0x73,0x0A,0x00,0x00,0x00,0xFC,0x00,0x4C,
0x6F,0x6F,0x6B,0x69,0x6E,0x67,0x20,0x47,0x6C,0x61,0x73,0x73,0x00,0x00,0x00,0xFD,
0x00,0x28,0x9B,0xFA,0xFA,0x40,0x01,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x4A
};
const DWORD DefaultPreferredDisplayMode = 19;
void CIndirectDeviceContext::PopulateDefaultModes(bool setDefaultMode)
{
m_displayModes.reserve(m_displayModes.size() +
ARRAYSIZE(DefaultDisplayModes));
for (int i = 0; i < ARRAYSIZE(DefaultDisplayModes); ++i)
{
WdfTimerStop(m_lgmpTimer, TRUE);
m_lgmpTimer = nullptr;
DisplayMode m;
m.width = DefaultDisplayModes[i][0];
m.height = DefaultDisplayModes[i][1];
m.refresh = DefaultDisplayModes[i][2];
m.preferred = setDefaultMode && (i == DefaultPreferredDisplayMode);
m_displayModes.push_back(m);
}
for(int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
lgmpHostMemFree(&m_frameMemory[i]);
for (int i = 0; i < LGMP_Q_POINTER_LEN; ++i)
lgmpHostMemFree(&m_pointerMemory[i]);
for (int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
lgmpHostMemFree(&m_pointerShapeMemory[i]);
lgmpHostFree(&m_lgmp);
}
void CIndirectDeviceContext::InitAdapter()
{
if (!SetupLGMP())
if (!m_ivshmem.Init() || !m_ivshmem.Open())
return;
m_displayModes.clear();
PopulateDefaultModes(true);
IDDCX_ADAPTER_CAPS caps = {};
caps.Size = sizeof(caps);
@ -82,7 +111,7 @@ void CIndirectDeviceContext::InitAdapter()
caps.EndPointDiagnostics.GammaSupport = IDDCX_FEATURE_IMPLEMENTATION_NONE;
caps.EndPointDiagnostics.TransmissionType = IDDCX_TRANSMISSION_TYPE_OTHER;
caps.EndPointDiagnostics.pEndPointFriendlyName = L"Looking Glass IDD Device";
caps.EndPointDiagnostics.pEndPointFriendlyName = L"Looking Glass IDD Driver";
caps.EndPointDiagnostics.pEndPointManufacturerName = L"Looking Glass";
caps.EndPointDiagnostics.pEndPointModelName = L"Looking Glass";
@ -104,7 +133,7 @@ void CIndirectDeviceContext::InitAdapter()
NTSTATUS status = IddCxAdapterInitAsync(&init, &initOut);
if (!NT_SUCCESS(status))
{
DBGPRINT("IddCxAdapterInitAsync Failed");
DEBUG_ERROR_HR(status, "IddCxAdapterInitAsync Failed");
return;
}
@ -133,21 +162,9 @@ void CIndirectDeviceContext::InitAdapter()
factory->Release();
auto * wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(m_adapter);
wrapper->context = this;
wrapper->context = this;
}
static const BYTE EDID[] =
{
0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x30,0xE8,0x34,0x12,0xC9,0x07,0xCC,0x00,
0x01,0x21,0x01,0x04,0xA5,0x3C,0x22,0x78,0xFB,0x6C,0xE5,0xA5,0x55,0x50,0xA0,0x23,
0x0B,0x50,0x54,0x00,0x02,0x00,0xD1,0xC0,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x58,0xE3,0x00,0xA0,0xA0,0xA0,0x29,0x50,0x30,0x20,
0x35,0x00,0x55,0x50,0x21,0x00,0x00,0x1A,0x00,0x00,0x00,0xFF,0x00,0x4C,0x6F,0x6F,
0x6B,0x69,0x6E,0x67,0x47,0x6C,0x61,0x73,0x73,0x0A,0x00,0x00,0x00,0xFC,0x00,0x4C,
0x6F,0x6F,0x6B,0x69,0x6E,0x67,0x20,0x47,0x6C,0x61,0x73,0x73,0x00,0x00,0x00,0xFD,
0x00,0x28,0x9B,0xFA,0xFA,0x40,0x01,0x0A,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x4A
};
void CIndirectDeviceContext::FinishInit(UINT connectorIndex)
{
WDF_OBJECT_ATTRIBUTES attr;
@ -181,7 +198,7 @@ void CIndirectDeviceContext::FinishInit(UINT connectorIndex)
NTSTATUS status = IddCxMonitorCreate(m_adapter, &create, &createOut);
if (!NT_SUCCESS(status))
{
DBGPRINT("IddCxMonitorCreate Failed");
DEBUG_ERROR_HR(status, "IddCxMonitorCreate Failed");
return;
}
@ -191,20 +208,235 @@ void CIndirectDeviceContext::FinishInit(UINT connectorIndex)
IDARG_OUT_MONITORARRIVAL out;
status = IddCxMonitorArrival(m_monitor, &out);
if (FAILED(status))
{
DEBUG_ERROR_HR(status, "IddCxMonitorArrival Failed");
return;
}
}
bool CIndirectDeviceContext::SetupLGMP()
void CIndirectDeviceContext::ReplugMonitor()
{
if (!m_ivshmem.Init() || !m_ivshmem.Open())
return false;
if (m_monitor == WDF_NO_HANDLE)
{
FinishInit(0);
return;
}
if (m_replugMonitor)
return;
DEBUG_TRACE("ReplugMonitor");
m_replugMonitor = true;
NTSTATUS status = IddCxMonitorDeparture(m_monitor);
if (!NT_SUCCESS(status))
{
m_replugMonitor = false;
DEBUG_ERROR("IddCxMonitorDeparture Failed (0x%08x)", status);
return;
}
}
void CIndirectDeviceContext::OnAssignSwapChain()
{
if (m_setCustomMode)
{
m_setCustomMode = false;
g_pipe.SetDisplayMode(m_customMode.width, m_customMode.height);
}
}
void CIndirectDeviceContext::OnUnassignedSwapChain()
{
if (m_replugMonitor)
{
m_replugMonitor = false;
FinishInit(0);
}
}
static inline void FillSignalInfo(DISPLAYCONFIG_VIDEO_SIGNAL_INFO & mode, DWORD width, DWORD height, DWORD vsync, bool monitorMode)
{
mode.totalSize.cx = mode.activeSize.cx = width;
mode.totalSize.cy = mode.activeSize.cy = height;
mode.AdditionalSignalInfo.vSyncFreqDivider = monitorMode ? 0 : 1;
mode.AdditionalSignalInfo.videoStandard = 255;
mode.vSyncFreq.Numerator = vsync;
mode.vSyncFreq.Denominator = 1;
mode.hSyncFreq.Numerator = vsync * height;
mode.hSyncFreq.Denominator = 1;
mode.scanLineOrdering = DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE;
mode.pixelRate = ((UINT64)vsync) * ((UINT64)width) * ((UINT64)height);
}
NTSTATUS CIndirectDeviceContext::ParseMonitorDescription(
const IDARG_IN_PARSEMONITORDESCRIPTION* inArgs,
IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs)
{
outArgs->MonitorModeBufferOutputCount = (UINT)m_displayModes.size();
if (inArgs->MonitorModeBufferInputCount < (UINT)m_displayModes.size())
return (inArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
auto * mode = inArgs->pMonitorModes;
for (auto it = m_displayModes.cbegin(); it != m_displayModes.cend(); ++it, ++mode)
{
mode->Size = sizeof(IDDCX_MONITOR_MODE);
mode->Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR;
FillSignalInfo(mode->MonitorVideoSignalInfo, it->width, it->height, it->refresh, true);
if (it->preferred)
outArgs->PreferredMonitorModeIdx =
(UINT)std::distance(m_displayModes.cbegin(), it);
}
return STATUS_SUCCESS;
}
NTSTATUS CIndirectDeviceContext::MonitorGetDefaultModes(
const IDARG_IN_GETDEFAULTDESCRIPTIONMODES* inArgs,
IDARG_OUT_GETDEFAULTDESCRIPTIONMODES* outArgs)
{
outArgs->DefaultMonitorModeBufferOutputCount = (UINT)m_displayModes.size();
if (inArgs->DefaultMonitorModeBufferInputCount < (UINT)m_displayModes.size())
return (inArgs->DefaultMonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
auto* mode = inArgs->pDefaultMonitorModes;
for (auto it = m_displayModes.cbegin(); it != m_displayModes.cend(); ++it, ++mode)
{
mode->Size = sizeof(IDDCX_MONITOR_MODE);
mode->Origin = IDDCX_MONITOR_MODE_ORIGIN_DRIVER;
FillSignalInfo(mode->MonitorVideoSignalInfo, it->width, it->height, it->refresh, true);
if (it->preferred)
outArgs->PreferredMonitorModeIdx =
(UINT)std::distance(m_displayModes.cbegin(), it);
}
return STATUS_SUCCESS;
}
NTSTATUS CIndirectDeviceContext::MonitorQueryTargetModes(
const IDARG_IN_QUERYTARGETMODES* inArgs,
IDARG_OUT_QUERYTARGETMODES* outArgs)
{
outArgs->TargetModeBufferOutputCount = (UINT)m_displayModes.size();
if (inArgs->TargetModeBufferInputCount < (UINT)m_displayModes.size())
return (inArgs->TargetModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
auto* mode = inArgs->pTargetModes;
for (auto it = m_displayModes.cbegin(); it != m_displayModes.cend(); ++it, ++mode)
{
mode->Size = sizeof(IDDCX_TARGET_MODE);
FillSignalInfo(mode->TargetVideoSignalInfo.targetVideoSignalInfo, it->width, it->height, it->refresh, false);
}
return STATUS_SUCCESS;
}
void CIndirectDeviceContext::SetResolution(int width, int height)
{
m_displayModes.clear();
m_customMode.width = width;
m_customMode.height = height;
m_customMode.refresh = 120;
m_customMode.preferred = true;
m_displayModes.push_back(m_customMode);
PopulateDefaultModes(false);
m_setCustomMode = true;
#if 1
ReplugMonitor();
#else
if (IDD_IS_FUNCTION_AVAILABLE(IddCxMonitorUpdateModes2))
{
IDDCX_TARGET_MODE2* modes = (IDDCX_TARGET_MODE2*)_malloca(
m_displayModes.size() * sizeof(IDDCX_TARGET_MODE2));
if (!modes)
{
DEBUG_ERROR("Failed to allocate memory for the mode list");
return;
}
ZeroMemory(modes, m_displayModes.size() * sizeof(IDDCX_TARGET_MODE2));
IDARG_IN_UPDATEMODES2 um = {};
um.Reason = IDDCX_UPDATE_REASON_OTHER;
um.TargetModeCount = (UINT)m_displayModes.size();
um.pTargetModes = modes;
auto* mode = modes;
for (auto it = m_displayModes.cbegin(); it != m_displayModes.cend(); ++it, ++mode)
{
mode->Size = sizeof(IDDCX_TARGET_MODE2);
mode->RequiredBandwidth = (UINT64)(it->width * it->height * it->refresh * 32);
mode->BitsPerComponent.Rgb = IDDCX_BITS_PER_COMPONENT_8;
FillSignalInfo(mode->TargetVideoSignalInfo.targetVideoSignalInfo, it->width, it->height, it->refresh, true);
}
NTSTATUS status = IddCxMonitorUpdateModes2(m_monitor, &um);
if (!NT_SUCCESS(status))
DEBUG_ERROR("IddCxMonitorUpdateModes2 Failed (0x%08x)", status);
_freea(modes);
}
else
{
IDDCX_TARGET_MODE* modes = (IDDCX_TARGET_MODE*)_malloca(
m_displayModes.size() * sizeof(IDDCX_TARGET_MODE));
if (!modes)
{
DEBUG_ERROR("Failed to allocate memory for the mode list");
return;
}
IDARG_IN_UPDATEMODES um = {};
um.Reason = IDDCX_UPDATE_REASON_OTHER;
um.TargetModeCount = (UINT)m_displayModes.size();
um.pTargetModes = modes;
auto* mode = modes;
for (auto it = m_displayModes.cbegin(); it != m_displayModes.cend(); ++it, ++mode)
{
mode->Size = sizeof(IDDCX_TARGET_MODE);
mode->RequiredBandwidth = (UINT64)(it->width * it->height * it->refresh * 32);
FillSignalInfo(mode->TargetVideoSignalInfo.targetVideoSignalInfo, it->width, it->height, it->refresh, true);
}
NTSTATUS status = IddCxMonitorUpdateModes(m_monitor, &um);
if (!NT_SUCCESS(status))
DEBUG_ERROR("IddCxMonitorUpdateModes Failed (0x%08x)", status);
_freea(modes);
}
#endif
}
bool CIndirectDeviceContext::SetupLGMP(size_t alignSize)
{
// this may get called multiple times as we need to delay calling it until
// we can determine the required alignment from the GPU in use
if (m_lgmp)
return true;
m_alignSize = alignSize;
std::stringstream ss;
{
KVMFR kvmfr = {};
memcpy_s(kvmfr.magic, sizeof(kvmfr.magic), KVMFR_MAGIC, sizeof(KVMFR_MAGIC) - 1);
kvmfr.version = KVMFR_VERSION;
kvmfr.features = KVMFR_FEATURE_SETCURSORPOS;
strncpy_s(kvmfr.hostver, "FIXME-IDD", sizeof(kvmfr.hostver) - 1);
kvmfr.features =
KVMFR_FEATURE_SETCURSORPOS |
KVMFR_FEATURE_WINDOWSIZE;
strncpy_s(kvmfr.hostver, LG_VERSION_STR, sizeof(kvmfr.hostver) - 1);
ss.write(reinterpret_cast<const char *>(&kvmfr), sizeof(kvmfr));
}
@ -214,7 +446,7 @@ bool CIndirectDeviceContext::SetupLGMP()
KVMFRRecord_VMInfo * vmInfo = static_cast<KVMFRRecord_VMInfo *>(calloc(1, sizeof(*vmInfo)));
if (!vmInfo)
{
DBGPRINT("Failed to allocate KVMFRRecord_VMInfo");
DEBUG_ERROR("Failed to allocate KVMFRRecord_VMInfo");
return false;
}
vmInfo->cpus = static_cast<uint8_t>(CPlatformInfo::GetProcCount ());
@ -223,12 +455,12 @@ bool CIndirectDeviceContext::SetupLGMP()
const uint8_t * uuid = CPlatformInfo::GetUUID();
memcpy_s (vmInfo->uuid, sizeof(vmInfo->uuid), uuid, 16);
strncpy_s(vmInfo->capture, "Idd Driver", sizeof(vmInfo->capture));
strncpy_s(vmInfo->capture, "Looking Glass IDD Driver", sizeof(vmInfo->capture));
KVMFRRecord * record = static_cast<KVMFRRecord *>(calloc(1, sizeof(*record)));
if (!record)
{
DBGPRINT("Failed to allocate KVMFRRecord");
DEBUG_ERROR("Failed to allocate KVMFRRecord");
return false;
}
@ -244,7 +476,7 @@ bool CIndirectDeviceContext::SetupLGMP()
KVMFRRecord_OSInfo * osInfo = static_cast<KVMFRRecord_OSInfo *>(calloc(1, sizeof(*osInfo)));
if (!osInfo)
{
DBGPRINT("Failed to allocate KVMFRRecord_OSInfo");
DEBUG_ERROR("Failed to allocate KVMFRRecord_OSInfo");
return false;
}
@ -255,7 +487,7 @@ bool CIndirectDeviceContext::SetupLGMP()
KVMFRRecord* record = static_cast<KVMFRRecord*>(calloc(1, sizeof(*record)));
if (!record)
{
DBGPRINT("Failed to allocate KVMFRRecord");
DEBUG_ERROR("Failed to allocate KVMFRRecord");
return false;
}
@ -273,19 +505,19 @@ bool CIndirectDeviceContext::SetupLGMP()
if ((status = lgmpHostInit(m_ivshmem.GetMem(), (uint32_t)m_ivshmem.GetSize(),
&m_lgmp, (uint32_t)udata.size(), (uint8_t*)&udata[0])) != LGMP_OK)
{
DBGPRINT("lgmpHostInit Failed: %s", lgmpStatusString(status));
DEBUG_ERROR("lgmpHostInit Failed: %s", lgmpStatusString(status));
return false;
}
if ((status = lgmpHostQueueNew(m_lgmp, FRAME_QUEUE_CONFIG, &m_frameQueue)) != LGMP_OK)
{
DBGPRINT("lgmpHostQueueCreate Failed (Frame): %s", lgmpStatusString(status));
DEBUG_ERROR("lgmpHostQueueCreate Failed (Frame): %s", lgmpStatusString(status));
return false;
}
if ((status = lgmpHostQueueNew(m_lgmp, POINTER_QUEUE_CONFIG, &m_pointerQueue)) != LGMP_OK)
{
DBGPRINT("lgmpHostQueueCreate Failed (Pointer): %s", lgmpStatusString(status));
DEBUG_ERROR("lgmpHostQueueCreate Failed (Pointer): %s", lgmpStatusString(status));
return false;
}
@ -293,7 +525,7 @@ bool CIndirectDeviceContext::SetupLGMP()
{
if ((status = lgmpHostMemAlloc(m_lgmp, MAX_POINTER_SIZE, &m_pointerMemory[i])) != LGMP_OK)
{
DBGPRINT("lgmpHostMemAlloc Failed (Pointer): %s", lgmpStatusString(status));
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer): %s", lgmpStatusString(status));
return false;
}
memset(lgmpHostMemPtr(m_pointerMemory[i]), 0, MAX_POINTER_SIZE);
@ -303,25 +535,36 @@ bool CIndirectDeviceContext::SetupLGMP()
{
if ((status = lgmpHostMemAlloc(m_lgmp, MAX_POINTER_SIZE, &m_pointerShapeMemory[i])) != LGMP_OK)
{
DBGPRINT("lgmpHostMemAlloc Failed (Pointer Shapes): %s", lgmpStatusString(status));
DEBUG_ERROR("lgmpHostMemAlloc Failed (Pointer Shapes): %s", lgmpStatusString(status));
return false;
}
memset(lgmpHostMemPtr(m_pointerShapeMemory[i]), 0, MAX_POINTER_SIZE);
}
m_maxFrameSize = lgmpHostMemAvail(m_lgmp);
m_maxFrameSize = (m_maxFrameSize -(CPlatformInfo::GetPageSize() - 1)) & ~(CPlatformInfo::GetPageSize() - 1);
m_maxFrameSize = (m_maxFrameSize -(m_alignSize - 1)) & ~(m_alignSize - 1);
m_maxFrameSize /= LGMP_Q_FRAME_LEN;
DBGPRINT("Max Frame Size: %u MiB\n", (unsigned int)(m_maxFrameSize / 1048576LL));
DEBUG_INFO("Max Frame Size: %u MiB", (unsigned int)(m_maxFrameSize / 1048576LL));
for (int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
{
if ((status = lgmpHostMemAllocAligned(m_lgmp, (uint32_t)m_maxFrameSize,
(uint32_t)CPlatformInfo::GetPageSize(), &m_frameMemory[i])) != LGMP_OK)
(uint32_t)m_alignSize, &m_frameMemory[i])) != LGMP_OK)
{
DBGPRINT("lgmpHostMemAllocAligned Failed (Frame): %s", lgmpStatusString(status));
DEBUG_ERROR("lgmpHostMemAllocAligned Failed (Frame): %s", lgmpStatusString(status));
return false;
}
m_frame[i] = (KVMFRFrame *)lgmpHostMemPtr(m_frameMemory[i]);
/**
* put the framebuffer on the border of the next page, this is to allow for
* aligned DMA tranfers by the reciever */
const size_t alignOffset = alignSize - sizeof(FrameBuffer);
m_frame[i]->offset = (uint32_t)alignOffset;
m_frameBuffer[i] = (FrameBuffer*)(((uint8_t*)m_frame[i]) + alignOffset);
}
WDF_TIMER_CONFIG config;
WDF_TIMER_CONFIG_INIT_PERIODIC(&config,
[](WDFTIMER timer) -> void
@ -345,7 +588,7 @@ bool CIndirectDeviceContext::SetupLGMP()
NTSTATUS s = WdfTimerCreate(&config, &attribs, &m_lgmpTimer);
if (!NT_SUCCESS(s))
{
DBGPRINT("Timer creation failed: 0x%08x", s);
DEBUG_ERROR_HR(s, "Timer creation failed");
return false;
}
WdfTimerStart(m_lgmpTimer, WDF_REL_TIMEOUT_IN_MS(10));
@ -353,6 +596,28 @@ bool CIndirectDeviceContext::SetupLGMP()
return true;
}
void CIndirectDeviceContext::DeInitLGMP()
{
m_hasFrame = false;
if (m_lgmp == nullptr)
return;
if (m_lgmpTimer)
{
WdfTimerStop(m_lgmpTimer, TRUE);
m_lgmpTimer = nullptr;
}
for (int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
lgmpHostMemFree(&m_frameMemory[i]);
for (int i = 0; i < LGMP_Q_POINTER_LEN; ++i)
lgmpHostMemFree(&m_pointerMemory[i]);
for (int i = 0; i < POINTER_SHAPE_BUFFERS; ++i)
lgmpHostMemFree(&m_pointerShapeMemory[i]);
lgmpHostFree(&m_lgmp);
}
void CIndirectDeviceContext::LGMPTimer()
{
LGMP_STATUS status;
@ -360,12 +625,12 @@ void CIndirectDeviceContext::LGMPTimer()
{
if (status == LGMP_ERR_CORRUPTED)
{
DBGPRINT("LGMP reported the shared memory has been corrupted, attempting to recover\n");
DEBUG_WARN("LGMP reported the shared memory has been corrupted, attempting to recover\n");
//TODO: fixme - reinit
return;
}
DBGPRINT("lgmpHostProcess Failed: %s", lgmpStatusString(status));
DEBUG_ERROR("lgmpHostProcess Failed: %s", lgmpStatusString(status));
//TODO: fixme - shutdown
return;
}
@ -379,10 +644,16 @@ void CIndirectDeviceContext::LGMPTimer()
{
case KVMFR_MESSAGE_SETCURSORPOS:
{
KVMFRSetCursorPos *sp = (KVMFRSetCursorPos *)msg;
SetCursorPos(sp->x, sp->y);
KVMFRSetCursorPos* sp = (KVMFRSetCursorPos*)msg;
g_pipe.SetCursorPos(sp->x, sp->y);
break;
}
case KVMFR_MESSAGE_WINDOWSIZE:
{
KVMFRWindowSize* ws = (KVMFRWindowSize*)msg;
SetResolution(ws->w, ws->h);
}
}
lgmpHostAckData(m_pointerQueue);
@ -390,19 +661,20 @@ void CIndirectDeviceContext::LGMPTimer()
if (lgmpHostQueueNewSubs(m_frameQueue) && m_monitor)
{
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(m_monitor);
if (wrapper)
wrapper->context->ResendLastFrame();
if (m_hasFrame)
lgmpHostQueuePost(m_frameQueue, 0, m_frameMemory[m_frameIndex]);
}
if (lgmpHostQueueNewSubs(m_pointerQueue))
ResendCursor();
}
void CIndirectDeviceContext::SendFrame(int width, int height, int pitch, DXGI_FORMAT format, void* data)
CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrameBuffer(int width, int height, int pitch, DXGI_FORMAT format)
{
PreparedFrameBuffer result = {};
if (!m_lgmp || !m_frameQueue)
return;
return result;
if (m_width != width || m_height != height || m_pitch != pitch || m_format != format)
{
@ -413,13 +685,15 @@ void CIndirectDeviceContext::SendFrame(int width, int height, int pitch, DXGI_FO
++m_formatVer;
}
while (lgmpHostQueuePending(m_frameQueue) == LGMP_Q_FRAME_LEN)
Sleep(0);
if (++m_frameIndex == LGMP_Q_FRAME_LEN)
m_frameIndex = 0;
KVMFRFrame * fi = (KVMFRFrame *)lgmpHostMemPtr(m_frameMemory[m_frameIndex]);
KVMFRFrame * fi = m_frame[m_frameIndex];
// wait until there is room in the queue
while (lgmpHostQueuePending(m_frameQueue) == LGMP_Q_FRAME_LEN)
Sleep(0);
int bpp = 4;
switch (format)
{
@ -433,40 +707,54 @@ void CIndirectDeviceContext::SendFrame(int width, int height, int pitch, DXGI_FO
break;
default:
DBGPRINT("Unsuppoted DXGI format");
return;
DEBUG_ERROR("Unsuppoted DXGI format 0x%08x", format);
return result;
}
//FIXME: this should not really be done here, this is a hack
#pragma warning(push)
#pragma warning(disable: 4200)
struct FrameBuffer
{
volatile uint32_t wp;
uint8_t data[0];
};
#pragma warning(pop)
fi->formatVer = m_formatVer;
fi->frameSerial = m_frameSerial++;
fi->screenWidth = width;
fi->screenHeight = height;
fi->frameWidth = width;
fi->frameHeight = height;
fi->stride = width * bpp;
fi->pitch = pitch;
fi->offset = (uint32_t)(CPlatformInfo::GetPageSize() - sizeof(FrameBuffer));
fi->flags = 0;
fi->rotation = FRAME_ROT_0;
fi->formatVer = m_formatVer;
fi->frameSerial = m_frameSerial++;
fi->screenWidth = width;
fi->screenHeight = height;
fi->dataWidth = width;
fi->dataHeight = height;
fi->frameWidth = width;
fi->frameHeight = height;
fi->stride = width * bpp;
fi->pitch = pitch;
// fi->offset is initialized at startup
fi->flags = 0;
fi->rotation = FRAME_ROT_0;
fi->damageRectsCount = 0;
FrameBuffer * fb = (FrameBuffer *)(((uint8_t*)fi) + fi->offset);
FrameBuffer* fb = m_frameBuffer[m_frameIndex];
fb->wp = 0;
lgmpHostQueuePost(m_frameQueue, 0, m_frameMemory[m_frameIndex]);
memcpy(fb->data, data, (size_t)height * (size_t)pitch);
fb->wp = height * pitch;
result.frameIndex = m_frameIndex;
result.mem = fb->data;
m_hasFrame = true;
return result;
}
void CIndirectDeviceContext::WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos)
{
FrameBuffer * fb = m_frameBuffer[frameIndex];
memcpy(
(void *)((uintptr_t)fb->data + offset),
(void *)((uintptr_t)src + offset),
len);
if (setWritePos)
fb->wp = (uint32_t)(offset + len);
}
void CIndirectDeviceContext::FinalizeFrameBuffer(unsigned frameIndex)
{
FrameBuffer * fb = m_frameBuffer[frameIndex];
fb->wp = m_height * m_pitch;
}
void CIndirectDeviceContext::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, const BYTE * data)
@ -502,7 +790,7 @@ void CIndirectDeviceContext::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, co
if (info.CursorShapeInfo.CursorType != IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED)
{
memcpy(cursor + 1, data,
(size_t)(info.CursorShapeInfo.Height * info.CursorShapeInfo.Pitch));
(size_t)info.CursorShapeInfo.Height * info.CursorShapeInfo.Pitch);
cursor->hx = (int8_t )info.CursorShapeInfo.XHot;
cursor->hy = (int8_t )info.CursorShapeInfo.YHot;
@ -534,7 +822,7 @@ void CIndirectDeviceContext::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, co
continue;
}
DBGPRINT("lgmpHostQueuePost Failed (Pointer): %s", lgmpStatusString(status));
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer): %s", lgmpStatusString(status));
break;
}
}
@ -562,7 +850,7 @@ void CIndirectDeviceContext::ResendCursor()
continue;
}
DBGPRINT("lgmpHostQueuePost Failed (Pointer): %s", lgmpStatusString(status));
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer): %s", lgmpStatusString(status));
break;
}
}

View File

@ -23,6 +23,7 @@
#include <Windows.h>
#include <wdf.h>
#include <IddCx.h>
#include <vector>
#include "CIVSHMEM.h"
@ -34,12 +35,23 @@ extern "C" {
#define MAX_POINTER_SIZE (sizeof(KVMFRCursor) + (512 * 512 * 4))
#define POINTER_SHAPE_BUFFERS 3
//FIXME: this should not really be done here, this is a hack
#pragma warning(push)
#pragma warning(disable: 4200)
struct FrameBuffer
{
volatile uint32_t wp;
uint8_t data[0];
};
#pragma warning(pop)
class CIndirectDeviceContext
{
private:
WDFDEVICE m_wdfDevice;
IDDCX_ADAPTER m_adapter = nullptr;
IDDCX_MONITOR m_monitor = nullptr;
IDDCX_ADAPTER m_adapter = nullptr;
IDDCX_MONITOR m_monitor = nullptr;
bool m_replugMonitor = false;
CIVSHMEM m_ivshmem;
@ -56,33 +68,77 @@ private:
bool m_cursorVisible = false;
int m_cursorX = 0, m_cursorY = 0;
size_t m_alignSize = 0;
size_t m_maxFrameSize = 0;
int m_frameIndex = 0;
uint32_t m_formatVer = 0;
uint32_t m_frameSerial = 0;
PLGMPMemory m_frameMemory[LGMP_Q_FRAME_LEN] = {};
KVMFRFrame * m_frame [LGMP_Q_FRAME_LEN] = {};
FrameBuffer * m_frameBuffer[LGMP_Q_FRAME_LEN] = {};
int m_width = 0;
int m_height = 0;
int m_pitch = 0;
DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN;
int m_width = 0;
int m_height = 0;
int m_pitch = 0;
DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN;
bool m_hasFrame = false;
bool SetupLGMP();
void DeInitLGMP();
void LGMPTimer();
void ResendCursor();
void ResendCursor();
struct DisplayMode
{
unsigned width;
unsigned height;
unsigned refresh;
bool preferred;
};
std::vector<DisplayMode> m_displayModes;
DisplayMode m_customMode = {};
bool m_setCustomMode = false;
public:
CIndirectDeviceContext(_In_ WDFDEVICE wdfDevice) :
m_wdfDevice(wdfDevice) {};
virtual ~CIndirectDeviceContext();
virtual ~CIndirectDeviceContext() { DeInitLGMP(); }
bool SetupLGMP(size_t alignSize);
void PopulateDefaultModes(bool setDefaultMode);
void InitAdapter();
void FinishInit(UINT connectorIndex);
void ReplugMonitor();
void OnAssignSwapChain();
void OnUnassignedSwapChain();
NTSTATUS ParseMonitorDescription(
const IDARG_IN_PARSEMONITORDESCRIPTION* inArgs, IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs);
NTSTATUS MonitorGetDefaultModes(
const IDARG_IN_GETDEFAULTDESCRIPTIONMODES* inArgs, IDARG_OUT_GETDEFAULTDESCRIPTIONMODES* outArgs);
NTSTATUS MonitorQueryTargetModes(
const IDARG_IN_QUERYTARGETMODES* inArgs, IDARG_OUT_QUERYTARGETMODES* outArgs);
void SetResolution(int width, int height);
size_t GetAlignSize() { return m_alignSize; }
size_t GetMaxFrameSize() { return m_maxFrameSize; }
struct PreparedFrameBuffer
{
unsigned frameIndex;
uint8_t* mem;
};
PreparedFrameBuffer PrepareFrameBuffer(int width, int height, int pitch, DXGI_FORMAT format);
void WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos);
void FinalizeFrameBuffer(unsigned frameIndex);
void SendFrame(int width, int height, int pitch, DXGI_FORMAT format, void* data);
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR & info, const BYTE * data);
CIVSHMEM &GetIVSHMEM() { return m_ivshmem; }
};
struct CIndirectDeviceContextWrapper

View File

@ -19,8 +19,8 @@
*/
#include "CIndirectMonitorContext.h"
#include "Direct3DDevice.h"
#include "Debug.h"
#include "CPlatformInfo.h"
#include "CDebug.h"
CIndirectMonitorContext::CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor, CIndirectDeviceContext * device) :
m_monitor(monitor),
@ -28,28 +28,50 @@ CIndirectMonitorContext::CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor, CIn
{
m_terminateEvent .Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
m_cursorDataEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
m_thread.Attach(CreateThread(nullptr, 0, _CursorThread, this, 0, nullptr));
m_shapeBuffer = new BYTE[512 * 512 * 4];
}
CIndirectMonitorContext::~CIndirectMonitorContext()
{
m_swapChain.reset();
SetEvent(m_terminateEvent.Get());
UnassignSwapChain();
delete[] m_shapeBuffer;
}
void CIndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID renderAdapter, HANDLE newFrameEvent)
{
m_swapChain.reset();
auto device = std::make_shared<Direct3DDevice>(renderAdapter);
if (FAILED(device->Init()))
reInit:
UnassignSwapChain();
m_dx11Device = std::make_shared<CD3D11Device>(renderAdapter);
if (FAILED(m_dx11Device->Init()))
{
WdfObjectDelete(swapChain);
return;
}
m_swapChain.reset(new CSwapChainProcessor(m_devContext, swapChain, device, newFrameEvent));
UINT64 alignSize = CPlatformInfo::GetPageSize();
m_dx12Device = std::make_shared<CD3D12Device>(renderAdapter);
switch (m_dx12Device->Init(m_devContext->GetIVSHMEM(), alignSize))
{
case CD3D12Device::SUCCESS:
break;
case CD3D12Device::FAILURE:
WdfObjectDelete(swapChain);
return;
case CD3D12Device::RETRY:
m_dx12Device.reset();
m_dx11Device.reset();
goto reInit;
}
if (!m_devContext->SetupLGMP(alignSize))
{
WdfObjectDelete(swapChain);
DEBUG_ERROR("SetupLGMP failed");
return;
}
IDARG_IN_SETUP_HWCURSOR c = {};
c.CursorInfo.Size = sizeof(c.CursorInfo);
@ -60,12 +82,30 @@ void CIndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID re
c.hNewCursorDataAvailable = m_cursorDataEvent.Get();
NTSTATUS status = IddCxMonitorSetupHardwareCursor(m_monitor, &c);
if (!NT_SUCCESS(status))
DBGPRINT("IddCxMonitorSetupHardwareCursor Failed: %08x", status);
{
WdfObjectDelete(swapChain);
DEBUG_ERROR("IddCxMonitorSetupHardwareCursor Failed (0x%08x)", status);
return;
}
m_swapChain.reset(new CSwapChainProcessor(m_devContext, swapChain, m_dx11Device, m_dx12Device, newFrameEvent));
m_lastShapeId = 0;
m_thread.Attach(CreateThread(nullptr, 0, _CursorThread, this, 0, nullptr));
}
void CIndirectMonitorContext::UnassignSwapChain()
{
m_swapChain.reset();
SetEvent(m_terminateEvent.Get());
if (m_thread.IsValid())
WaitForSingleObject(m_thread.Get(), INFINITE);
m_swapChain.reset();
m_dx11Device.reset();
m_dx12Device.reset();
ResetEvent(m_terminateEvent .Get());
ResetEvent(m_cursorDataEvent.Get());
}
DWORD CALLBACK CIndirectMonitorContext::_CursorThread(LPVOID arg)
@ -77,24 +117,37 @@ DWORD CALLBACK CIndirectMonitorContext::_CursorThread(LPVOID arg)
void CIndirectMonitorContext::CursorThread()
{
HRESULT hr = 0;
bool running = true;
for (;;)
while(running)
{
HANDLE waitHandles[] =
{
m_cursorDataEvent.Get(),
m_terminateEvent.Get()
};
DWORD waitResult = WaitForMultipleObjects(ARRAYSIZE(waitHandles), waitHandles, FALSE, 100);
if (waitResult == WAIT_TIMEOUT)
continue;
else if (waitResult == WAIT_OBJECT_0 + 1)
break;
else if (waitResult != WAIT_OBJECT_0)
DWORD waitResult = WaitForMultipleObjects(
ARRAYSIZE(waitHandles), waitHandles, FALSE, 100);
switch (waitResult)
{
hr = HRESULT_FROM_WIN32(waitResult);
DBGPRINT("WaitForMultipleObjects: %08", hr);
return;
case WAIT_TIMEOUT:
continue;
// cursorDataEvent
case WAIT_OBJECT_0:
break;
// terminateEvent
case WAIT_OBJECT_0 + 1:
running = false;
continue;
default:
hr = HRESULT_FROM_WIN32(waitResult);
DEBUG_ERROR_HR(hr, "WaitForMultipleObjects");
return;
}
IDARG_IN_QUERY_HWCURSOR in = {};
@ -106,7 +159,7 @@ void CIndirectMonitorContext::CursorThread()
NTSTATUS status = IddCxMonitorQueryHardwareCursor(m_monitor, &in, &out);
if (FAILED(status))
{
DBGPRINT("IddCxMonitorQueryHardwareCursor failed: %08x", status);
DEBUG_ERROR("IddCxMonitorQueryHardwareCursor failed (0x%08x)", status);
return;
}

View File

@ -34,6 +34,10 @@ class CIndirectMonitorContext
{
private:
IDDCX_MONITOR m_monitor;
std::shared_ptr<CD3D11Device> m_dx11Device;
std::shared_ptr<CD3D12Device> m_dx12Device;
CIndirectDeviceContext * m_devContext;
std::unique_ptr<CSwapChainProcessor> m_swapChain;
@ -55,11 +59,7 @@ public:
void AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID renderAdapter, HANDLE newFrameEvent);
void UnassignSwapChain();
inline void ResendLastFrame()
{
if (m_swapChain)
m_swapChain->ResendLastFrame();
}
CIndirectDeviceContext * GetDeviceContext() { return m_devContext; }
};
struct CIndirectMonitorContextWrapper

View File

@ -0,0 +1,126 @@
#include "CInteropResource.h"
#include "CDebug.h"
bool CInteropResource::Init(std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device, ComPtr<ID3D11Texture2D> srcTex)
{
HRESULT hr;
D3D11_TEXTURE2D_DESC srcDesc;
srcTex->GetDesc(&srcDesc);
ComPtr<IDXGIResource1> rSrcTex;
hr = srcTex.As(&rSrcTex);
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to obtain the IDXGIResource1 interface");
return false;
}
HANDLE h;
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> sharedHandle;
hr = rSrcTex->CreateSharedHandle(NULL, DXGI_SHARED_RESOURCE_READ, NULL, &h);
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to create the shared handle");
return false;
}
sharedHandle.Attach(h);
ComPtr<ID3D12Resource> dst;
hr = dx12Device->GetDevice()->OpenSharedHandle(sharedHandle.Get(), IID_PPV_ARGS(&dst));
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to open the D3D12Resource from the handle");
return false;
}
sharedHandle.Close();
ComPtr<ID3D11Fence> d11Fence;
hr = dx11Device->GetDevice()->CreateFence(0, D3D11_FENCE_FLAG_SHARED, IID_PPV_ARGS(&d11Fence));
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to create the d3d11 fence");
return false;
}
hr = d11Fence->CreateSharedHandle(NULL, GENERIC_ALL, NULL, &h);
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to create the d3d11 fence shared handle");
return false;
}
sharedHandle.Attach(h);
ComPtr<ID3D12Fence> d12Fence;
hr = dx12Device->GetDevice()->OpenSharedHandle(sharedHandle.Get(), IID_PPV_ARGS(&d12Fence));
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "Failed to open the D3D12Fence from the handle");
return false;
}
sharedHandle.Close();
m_dx11Device = dx11Device;
m_dx12Device = dx12Device;
memcpy(&m_format, &srcDesc, sizeof(m_format));
m_srcTex = srcTex.Get();
m_d12Res = dst;
m_d11Fence = d11Fence;
m_d12Fence = d12Fence;
m_fenceValue = 0;
m_ready = true;
return true;
}
void CInteropResource::Reset()
{
m_ready = false;
m_fenceValue = 0;
m_d12Fence.Reset();
m_d11Fence.Reset();
m_d12Res .Reset();
m_srcTex = NULL;
memset(&m_format, 0, sizeof(m_format));
m_dx12Device.reset();
m_dx11Device.reset();
}
bool CInteropResource::Compare(const ComPtr<ID3D11Texture2D>& srcTex)
{
if (srcTex.Get() != m_srcTex)
return false;
D3D11_TEXTURE2D_DESC format;
srcTex->GetDesc(&format);
return
m_format.Width == format.Width &&
m_format.Height == format.Height &&
m_format.Format == format.Format;
}
void CInteropResource::Signal()
{
++m_fenceValue;
m_dx11Device->GetContext()->Signal(m_d11Fence.Get(), m_fenceValue);
}
void CInteropResource::Sync(CD3D12CommandQueue& queue)
{
if (m_d11Fence->GetCompletedValue() < m_fenceValue)
queue.GetCmdQueue()->Wait(m_d12Fence.Get(), m_fenceValue);
}
void CInteropResource::SetFullDamage()
{
m_dirtyRects[0].left = 0;
m_dirtyRects[0].top = 0;
m_dirtyRects[0].right = m_format.Width;
m_dirtyRects[0].bottom = m_format.Height;
m_nbDirtyRects = 1;
}

View File

@ -0,0 +1,48 @@
#pragma once
#include <Windows.h>
#include <wdf.h>
#include <wrl.h>
#include <memory>
#include "CD3D11Device.h"
#include "CD3D12Device.h"
#include "CD3D12CommandQueue.h"
using namespace Microsoft::WRL;
#define LG_MAX_DIRTY_RECTS 256
class CInteropResource
{
private:
std::shared_ptr<CD3D11Device> m_dx11Device;
std::shared_ptr<CD3D12Device> m_dx12Device;
/* this value is likely released, it is only used to check if the texture supplied
is different, do not rely on it pointing to valid memory */
void * m_srcTex;
ComPtr<ID3D12Resource > m_d12Res;
D3D11_TEXTURE2D_DESC m_format;
ComPtr<ID3D11Fence > m_d11Fence;
ComPtr<ID3D12Fence > m_d12Fence;
UINT64 m_fenceValue;
bool m_ready;
RECT m_dirtyRects[LG_MAX_DIRTY_RECTS];
unsigned m_nbDirtyRects;
public:
bool Init(std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device, ComPtr<ID3D11Texture2D> srcTex);
void Reset();
bool IsReady() { return m_ready; }
bool Compare(const ComPtr<ID3D11Texture2D>& srcTex);
void Signal();
void Sync(CD3D12CommandQueue& queue);
void SetFullDamage();
const ComPtr<ID3D12Resource>& GetRes() { return m_d12Res; }
const D3D11_TEXTURE2D_DESC& GetFormat() { return m_format; }
};

View File

@ -0,0 +1,47 @@
#include "CInteropResourcePool.h"
#include "CDebug.h"
void CInteropResourcePool::Init(std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device)
{
Reset();
m_dx11Device = dx11Device;
m_dx12Device = dx12Device;
}
void CInteropResourcePool::Reset()
{
for (unsigned i = 0; i < POOL_SIZE; ++i)
m_pool[i].Reset();
m_dx11Device.reset();
m_dx12Device.reset();
}
CInteropResource* CInteropResourcePool::Get(ComPtr<ID3D11Texture2D> srcTex)
{
CInteropResource * res;
unsigned freeSlot = POOL_SIZE;
for (unsigned i = 0; i < POOL_SIZE; ++i)
{
res = &m_pool[i];
if (!res->IsReady())
{
freeSlot = min(freeSlot, i);
continue;
}
if (res->Compare(srcTex))
return res;
}
if (freeSlot == POOL_SIZE)
{
DEBUG_ERROR("Interop Resouce Pool Full");
return nullptr;
}
res = &m_pool[freeSlot];
if (!res->Init(m_dx11Device, m_dx12Device, srcTex))
return nullptr;
return res;
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <Windows.h>
#include <wdf.h>
#include <wrl.h>
#include <d3d11_4.h>
#include "CInteropResource.h"
using namespace Microsoft::WRL;
#define POOL_SIZE 10
class CInteropResourcePool
{
private:
CInteropResource m_pool[POOL_SIZE];
std::shared_ptr<CD3D11Device> m_dx11Device;
std::shared_ptr<CD3D12Device> m_dx12Device;
public:
void Init(std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device);
void Reset();
CInteropResource* Get(ComPtr<ID3D11Texture2D> srcTex);
};

176
idd/LGIdd/CPipeServer.cpp Normal file
View File

@ -0,0 +1,176 @@
/**
* 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 "CPipeServer.h"
#include "CDebug.h"
CPipeServer g_pipe;
bool CPipeServer::Init()
{
_DeInit();
m_pipe.Attach(CreateNamedPipeA(
LG_PIPE_NAME,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
1,
1024,
1024,
0,
NULL));
if (!m_pipe.IsValid())
{
DEBUG_ERROR_HR(GetLastError(), "Failed to create the named pipe");
return false;
}
m_running = true;
m_thread.Attach(CreateThread(
NULL,
0,
_pipeThread,
(LPVOID)this,
0,
NULL));
if (!m_thread.IsValid())
{
DEBUG_ERROR_HR(GetLastError(), "Failed to create the pipe thread");
return false;
}
DEBUG_TRACE("Pipe Initialized");
return true;
}
void CPipeServer::_DeInit()
{
m_running = false;
m_connected = false;
if (m_thread.IsValid())
{
CancelSynchronousIo(m_thread.Get());
WaitForSingleObject(m_thread.Get(), INFINITE);
m_thread.Close();
}
if (m_pipe.IsValid())
{
FlushFileBuffers(m_pipe.Get());
m_pipe.Close();
}
}
void CPipeServer::DeInit()
{
DEBUG_TRACE("Pipe Stopping");
_DeInit();
DEBUG_TRACE("Pipe Stopped");
}
void CPipeServer::Thread()
{
DEBUG_TRACE("Pipe thread started");
while(m_running)
{
m_connected = false;
bool result = ConnectNamedPipe(m_pipe.Get(), NULL);
DWORD err = GetLastError();
if (!result && err != ERROR_PIPE_CONNECTED)
{
// if graceful shutdown
if ((err == ERROR_OPERATION_ABORTED && !m_running) ||
err == ERROR_NO_DATA)
break;
// if timeout
if (err == ERROR_SEM_TIMEOUT)
continue;
DEBUG_FATAL_HR(err, "Error connecting to the named pipe");
break;
}
DEBUG_TRACE("Client connected");
m_connected = true;
while (m_running && m_connected)
{
//TODO: Read messages from the client
Sleep(1000);
}
DEBUG_TRACE("Client disconnected");
DisconnectNamedPipe(m_pipe.Get());
}
m_running = false;
m_connected = false;
DEBUG_TRACE("Pipe thread shutdown");
}
void CPipeServer::WriteMsg(LGPipeMsg & msg)
{
DWORD written;
if (!WriteFile(m_pipe.Get(), &msg, sizeof(msg), &written, NULL))
{
DWORD err = GetLastError();
if (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA)
{
DEBUG_WARN_HR(err, "Client disconnected, failed to write");
m_connected = false;
return;
}
DEBUG_WARN_HR(err, "WriteFile failed on the pipe");
return;
}
FlushFileBuffers(m_pipe.Get());
}
void CPipeServer::SetCursorPos(uint32_t x, uint32_t y)
{
if (!m_connected)
return;
LGPipeMsg msg;
msg.size = sizeof(msg);
msg.type = LGPipeMsg::SETCURSORPOS;
msg.curorPos.x = x;
msg.curorPos.y = y;
WriteMsg(msg);
}
void CPipeServer::SetDisplayMode(uint32_t width, uint32_t height)
{
if (!m_connected)
return;
LGPipeMsg msg;
msg.size = sizeof(msg);
msg.type = LGPipeMsg::SETDISPLAYMODE;
msg.displayMode.width = width;
msg.displayMode.height = height;
WriteMsg(msg);
}

60
idd/LGIdd/CPipeServer.h Normal file
View File

@ -0,0 +1,60 @@
/**
* 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
*/
#pragma once
#include <windows.h>
#include <wdf.h>
#include <stdint.h>
#include <wrl.h>
#include "PipeMsg.h"
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace Microsoft::WRL::Wrappers::HandleTraits;
class CPipeServer
{
private:
HandleT<HANDLENullTraits> m_pipe;
HandleT<HANDLENullTraits> m_thread;
bool m_running = false;
bool m_connected = false;
void _DeInit();
static DWORD WINAPI _pipeThread(LPVOID lpParam) { ((CPipeServer*)lpParam)->Thread(); return 0; }
void Thread();
void WriteMsg(LGPipeMsg & msg);
public:
~CPipeServer() { DeInit(); }
bool Init();
void DeInit();
void SetCursorPos(uint32_t x, uint32_t y);
void SetDisplayMode(uint32_t width, uint32_t height);
};
extern CPipeServer g_pipe;

View File

@ -20,7 +20,7 @@
#include "CPlatformInfo.h"
#include "Debug.h"
#include "CDebug.h"
#include <Windows.h>
size_t CPlatformInfo::m_pageSize = 0;
@ -60,7 +60,7 @@ void CPlatformInfo::Init()
if (status != ERROR_SUCCESS)
{
m_productName = "Unknown";
DBGPRINT("Failed to read the ProductName");
DEBUG_ERROR("Failed to read the ProductName");
}
else
m_productName.resize(bufferSize - 1); // remove the double null termination
@ -167,14 +167,14 @@ void CPlatformInfo::InitUUID()
smbData = (RawSMBIOSData*)new BYTE[smbDataSize];
if (!smbData)
{
DBGPRINT("out of memory");
DEBUG_ERROR("out of memory");
return;
}
if (GetSystemFirmwareTable(TABLE_SIG("RSMB"), 0, smbData, smbDataSize)
!= smbDataSize)
{
DBGPRINT("Failed to read the RSMB table");
DEBUG_ERROR("Failed to read the RSMB table");
delete[] smbData;
return;
}
@ -220,7 +220,7 @@ void CPlatformInfo::InitCPUInfo()
if (status != ERROR_SUCCESS)
{
m_model = "Unknown";
DBGPRINT("Failed to read the CPU Model");
DEBUG_ERROR("Failed to read the CPU Model");
}
else
{
@ -233,20 +233,20 @@ void CPlatformInfo::InitCPUInfo()
GetLogicalProcessorInformationEx(RelationAll, nullptr, &cb);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
DBGPRINT("Failed to call GetLogicalProcessorInformationEx");
DEBUG_ERROR("Failed to call GetLogicalProcessorInformationEx");
return;
}
BYTE * buffer = static_cast<BYTE *>(_malloca(cb));
if (!buffer)
{
DBGPRINT("Failed to allocate buffer for processor information");
DEBUG_ERROR("Failed to allocate buffer for processor information");
return;
}
if (!GetLogicalProcessorInformationEx(RelationAll,
(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)buffer, &cb))
{
DBGPRINT("Failed to call GetLogicalProcessorInformationEx");
DEBUG_ERROR("Failed to call GetLogicalProcessorInformationEx");
_freea(buffer);
return;
}

View File

@ -21,29 +21,21 @@
#include "CSwapChainProcessor.h"
#include <avrt.h>
#include "Debug.h"
#define LOCK(lock) \
while (InterlockedCompareExchange((volatile LONG*)&(lock), 1, 0) != 0) {};
#define UNLOCK(lock) \
InterlockedExchange((volatile LONG*)&(lock), 0);
#define LOCK_CONTEXT() LOCK(m_contextLock);
#define UNLOCK_CONTEXT() UNLOCK(m_contextLock);
#define LOCK_ST(st) LOCK((st).lock);
#define UNLOCK_ST(st) UNLOCK((st).lock);
#include "CDebug.h"
CSwapChainProcessor::CSwapChainProcessor(CIndirectDeviceContext* devContext, IDDCX_SWAPCHAIN hSwapChain,
std::shared_ptr<Direct3DDevice> device, HANDLE newFrameEvent) :
std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device, HANDLE newFrameEvent) :
m_devContext(devContext),
m_hSwapChain(hSwapChain),
m_device(device),
m_dx11Device(dx11Device),
m_dx12Device(dx12Device),
m_newFrameEvent(newFrameEvent)
{
{
m_resPool.Init(dx11Device, dx12Device);
m_fbPool.Init(this);
m_terminateEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
m_thread[0].Attach(CreateThread(nullptr, 0, _SwapChainThread, this, 0, nullptr));
m_thread[1].Attach(CreateThread(nullptr, 0, _FrameThread , this, 0, nullptr));
}
CSwapChainProcessor::~CSwapChainProcessor()
@ -54,12 +46,8 @@ CSwapChainProcessor::~CSwapChainProcessor()
if (m_thread[1].Get())
WaitForSingleObject(m_thread[1].Get(), INFINITE);
for(int i = 0; i < STAGING_TEXTURES; ++i)
if (m_cpuTex[i].map.pData)
{
m_device->m_context->Unmap(m_cpuTex[i].tex.Get(), 0);
m_cpuTex[i].map.pData = nullptr;
}
m_resPool.Reset();
m_fbPool.Reset();
}
DWORD CALLBACK CSwapChainProcessor::_SwapChainThread(LPVOID arg)
@ -73,7 +61,7 @@ void CSwapChainProcessor::SwapChainThread()
DWORD avTask = 0;
HANDLE avTaskHandle = AvSetMmThreadCharacteristicsW(L"Distribution", &avTask);
DBGPRINT("Start");
DEBUG_INFO("Start Thread");
SwapChainThreadCore();
WdfObjectDelete((WDFOBJECT)m_hSwapChain);
@ -83,54 +71,48 @@ void CSwapChainProcessor::SwapChainThread()
}
void CSwapChainProcessor::SwapChainThreadCore()
{
Microsoft::WRL::ComPtr<IDXGIDevice> dxgiDevice;
HRESULT hr = m_device->m_device.As(&dxgiDevice);
{
ComPtr<IDXGIDevice> dxgiDevice;
HRESULT hr = m_dx11Device->GetDevice().As(&dxgiDevice);
if (FAILED(hr))
{
DBGPRINT("Failed to get the dxgiDevice");
DEBUG_ERROR_HR(hr, "Failed to get the dxgiDevice");
return;
}
if (IDD_IS_FUNCTION_AVAILABLE(IddCxSetRealtimeGPUPriority))
{
DBGPRINT("Using IddCxSetRealtimeGPUPriority");
DEBUG_INFO("Using IddCxSetRealtimeGPUPriority");
IDARG_IN_SETREALTIMEGPUPRIORITY arg;
arg.pDevice = dxgiDevice.Get();
if (FAILED(IddCxSetRealtimeGPUPriority(m_hSwapChain, &arg)))
DBGPRINT("Failed to set realtime GPU thread priority");
hr = IddCxSetRealtimeGPUPriority(m_hSwapChain, &arg);
if (FAILED(hr))
DEBUG_ERROR_HR(hr, "Failed to set realtime GPU thread priority");
}
else
{
DBGPRINT("Using SetGPUThreadPriority");
DEBUG_INFO("Using SetGPUThreadPriority");
dxgiDevice->SetGPUThreadPriority(7);
}
IDARG_IN_SWAPCHAINSETDEVICE setDevice = {};
setDevice.pDevice = dxgiDevice.Get();
LOCK_CONTEXT();
hr = IddCxSwapChainSetDevice(m_hSwapChain, &setDevice);
UNLOCK_CONTEXT();
if (FAILED(hr))
{
DBGPRINT("IddCxSwapChainSetDevice Failed (%08x)", hr);
DEBUG_ERROR_HR(hr, "IddCxSwapChainSetDevice Failed");
return;
}
UINT lastFrameNumber = 0;
for (;;)
{
ComPtr<IDXGIResource> acquiredBuffer;
IDARG_OUT_RELEASEANDACQUIREBUFFER buffer = {};
LOCK_CONTEXT();
hr = IddCxSwapChainReleaseAndAcquireBuffer(m_hSwapChain, &buffer);
if (hr == E_PENDING)
{
UNLOCK_CONTEXT();
HANDLE waitHandles[] =
{
m_newFrameEvent,
@ -152,153 +134,128 @@ void CSwapChainProcessor::SwapChainThreadCore()
if (buffer.MetaData.PresentationFrameNumber != lastFrameNumber)
{
lastFrameNumber = buffer.MetaData.PresentationFrameNumber;
if (m_copyCount < STAGING_TEXTURES)
{
acquiredBuffer.Attach(buffer.MetaData.pSurface);
SwapChainNewFrame(acquiredBuffer);
acquiredBuffer.Reset();
}
}
SwapChainNewFrame(buffer.MetaData.pSurface);
hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain);
UNLOCK_CONTEXT();
if (FAILED(hr))
break;
// report that all GPU processing for this frame has been queued
hr = IddCxSwapChainFinishedProcessingFrame(m_hSwapChain);
if (FAILED(hr))
{
DEBUG_ERROR_HR(hr, "IddCxSwapChainFinishedProcessingFrame Failed");
break;
}
}
}
else
{
UNLOCK_CONTEXT();
break;
}
}
}
void CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer)
void CSwapChainProcessor::CompletionFunction(
CD3D12CommandQueue * queue, bool result, void * param1, void * param2)
{
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture;
if (FAILED(acquiredBuffer->QueryInterface(__uuidof(ID3D11Texture2D), (void**)&texture)))
UNREFERENCED_PARAMETER(queue);
auto sc = (CSwapChainProcessor *)param1;
auto fbRes = (CFrameBufferResource*)param2;
// fail gracefully
if (!result)
{
DBGPRINT("Failed to obtain the ID3D11Texture2D from the acquiredBuffer");
sc->m_devContext->FinalizeFrameBuffer(fbRes->GetFrameIndex());
return;
}
D3D11_TEXTURE2D_DESC desc;
texture->GetDesc(&desc);
StagingTexture &st = m_cpuTex[m_texWIndex];
if (st.map.pData)
{
m_device->m_context->Unmap(st.tex.Get(), 0);
st.map.pData = nullptr;
}
if (!SetupStagingTexture(st, desc.Width, desc.Height, desc.Format))
return;
m_device->m_context->CopyResource(st.tex.Get(), texture.Get());
InterlockedAdd(&m_copyCount, 1);
if (++m_texWIndex == STAGING_TEXTURES)
m_texWIndex = 0;
if (sc->m_dx12Device->IsIndirectCopy())
sc->m_devContext->WriteFrameBuffer(
fbRes->GetFrameIndex(),
fbRes->GetMap(), 0, fbRes->GetFrameSize(), true);
else
sc->m_devContext->FinalizeFrameBuffer(fbRes->GetFrameIndex());
}
DWORD CALLBACK CSwapChainProcessor::_FrameThread(LPVOID arg)
bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer)
{
reinterpret_cast<CSwapChainProcessor*>(arg)->FrameThread();
return 0;
}
void CSwapChainProcessor::FrameThread()
{
for(;;)
ComPtr<ID3D11Texture2D> texture;
HRESULT hr = acquiredBuffer.As(&texture);
if (FAILED(hr))
{
if (WaitForSingleObject(m_terminateEvent.Get(), 0) == WAIT_OBJECT_0)
break;
if (!m_copyCount)
{
Sleep(0);
continue;
}
StagingTexture & st = m_cpuTex[m_texRIndex];
LOCK(st);
LOCK_CONTEXT();
HRESULT status = m_device->m_context->Map(st.tex.Get(), 0, D3D11_MAP_READ,
D3D11_MAP_FLAG_DO_NOT_WAIT, &st.map);
UNLOCK_CONTEXT();
if (FAILED(status))
{
UNLOCK(st);
if (status == DXGI_ERROR_WAS_STILL_DRAWING)
continue;
DBGPRINT("Failed to map staging texture");
InterlockedAdd(&m_copyCount, -1);
if (++m_texRIndex == STAGING_TEXTURES)
m_texRIndex = 0;
continue;
}
m_devContext->SendFrame(st.width, st.height, st.map.RowPitch, st.format, st.map.pData);
InterlockedAdd(&m_copyCount, -1);
m_lastIndex = m_texRIndex;
if (++m_texRIndex == STAGING_TEXTURES)
m_texRIndex = 0;
UNLOCK(st);
}
}
bool CSwapChainProcessor::SetupStagingTexture(StagingTexture & st, int width, int height, DXGI_FORMAT format)
{
if (st.width == width && st.height == height && st.format == format)
return true;
st.tex.Reset();
st.width = width;
st.height = height;
st.format = format;
D3D11_TEXTURE2D_DESC desc = {};
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_STAGING;
desc.Format = format;
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;
if (FAILED(m_device->m_device->CreateTexture2D(&desc, nullptr, &st.tex)))
{
DBGPRINT("Failed to create staging texture");
DEBUG_ERROR_HR(hr, "Failed to obtain the ID3D11Texture2D from the acquiredBuffer");
return false;
}
return true;
}
void CSwapChainProcessor::ResendLastFrame()
{
LOCK_CONTEXT()
StagingTexture & st = m_cpuTex[m_lastIndex];
LOCK_ST(st);
UNLOCK_CONTEXT();
if (!st.map.pData)
CInteropResource * srcRes = m_resPool.Get(texture);
if (!srcRes)
{
UNLOCK_ST(st);
return;
DEBUG_ERROR("Failed to get a CInteropResource from the pool");
return false;
}
m_devContext->SendFrame(st.width, st.height, st.map.RowPitch, st.format, st.map.pData);
UNLOCK_ST(st);
}
/**
* Even though we have not performed any copy/draw operations we still need to
* use a fence. Because we share this texture with DirectX12 it is able to
* read from it before the desktop duplication API has finished updating it.
*/
srcRes->Signal();
//FIXME: handle dirty rects
srcRes->SetFullDamage();
D3D12_RESOURCE_DESC desc = srcRes->GetRes()->GetDesc();
D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout;
m_dx12Device->GetDevice()->GetCopyableFootprints(
&desc,
0,
1,
0,
&layout,
NULL,
NULL,
NULL);
auto buffer = m_devContext->PrepareFrameBuffer(
(int)desc.Width,
(int)desc.Height,
(int)layout.Footprint.RowPitch,
desc.Format);
if (!buffer.mem)
return false;
CFrameBufferResource * fbRes = m_fbPool.Get(buffer,
(size_t)layout.Footprint.RowPitch * desc.Height);
if (!fbRes)
{
DEBUG_ERROR("Failed to get a CFrameBufferResource from the pool");
return false;
}
auto copyQueue = m_dx12Device->GetCopyQueue();
if (!copyQueue)
{
DEBUG_ERROR("Failed to get a CopyQueue");
return false;
}
copyQueue->SetCompletionCallback(&CompletionFunction, this, fbRes);
D3D12_TEXTURE_COPY_LOCATION srcLoc = {};
srcLoc.pResource = srcRes->GetRes().Get();
srcLoc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
srcLoc.SubresourceIndex = 0;
D3D12_TEXTURE_COPY_LOCATION dstLoc = {};
dstLoc.pResource = fbRes->Get().Get();
dstLoc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
dstLoc.PlacedFootprint = layout;
srcRes->Sync(*copyQueue);
copyQueue->GetGfxList()->CopyTextureRegion(
&dstLoc, 0, 0, 0, &srcLoc, NULL);
copyQueue->Execute();
return true;
}

View File

@ -20,8 +20,11 @@
#pragma once
#include "Direct3DDevice.h"
#include "CD3D11Device.h"
#include "CD3D12Device.h"
#include "CIndirectDeviceContext.h"
#include "CInteropResourcePool.h"
#include "CFrameBufferPool.h"
#include <Windows.h>
#include <wrl.h>
@ -37,45 +40,30 @@ class CSwapChainProcessor
private:
CIndirectDeviceContext * m_devContext;
IDDCX_SWAPCHAIN m_hSwapChain;
std::shared_ptr<Direct3DDevice> m_device;
std::shared_ptr<CD3D11Device> m_dx11Device;
std::shared_ptr<CD3D12Device> m_dx12Device;
HANDLE m_newFrameEvent;
CInteropResourcePool m_resPool;
CFrameBufferPool m_fbPool;
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_thread[2];
Wrappers::Event m_terminateEvent;
static DWORD CALLBACK _SwapChainThread(LPVOID arg);
static DWORD CALLBACK _FrameThread(LPVOID arg);
void SwapChainThread();
void SwapChainThreadCore();
void SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer);
void FrameThread();
struct StagingTexture
{
volatile LONG lock = 0;
int width = 0;
int height = 0;
DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
Microsoft::WRL::ComPtr<ID3D11Texture2D> tex;
D3D11_MAPPED_SUBRESOURCE map = {};
};
StagingTexture m_cpuTex[STAGING_TEXTURES] = {};
volatile LONG m_copyCount = 0;
volatile LONG m_contextLock = 0;
int m_texRIndex = 0;
int m_texWIndex = 0;
int m_lastIndex = 0;
bool SetupStagingTexture(StagingTexture & st, int width, int height, DXGI_FORMAT format);
static void CompletionFunction(
CD3D12CommandQueue * queue, bool result, void * param1, void * param2);
bool SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer);
public:
CSwapChainProcessor(CIndirectDeviceContext * devContext, IDDCX_SWAPCHAIN hSwapChain,
std::shared_ptr<Direct3DDevice> device, HANDLE newFrameEvent);
std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device, HANDLE newFrameEvent);
~CSwapChainProcessor();
void ResendLastFrame();
};
CIndirectDeviceContext * GetDevice() { return m_devContext; }
std::shared_ptr<CD3D12Device> GetD3D12Device() { return m_dx12Device; }
};

View File

@ -1,52 +0,0 @@
/**
* 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 <Windows.h>
#include <malloc.h>
#include <strsafe.h>
/* credit: https://stackoverflow.com/questions/29049686/is-there-a-better-way-to-pass-formatted-output-to-outputdebugstring */
VOID _DBGPRINT(PCSTR kwszFunction, INT iLineNumber, LPCSTR kszDebugFormatString, ...) \
{
INT cbFormatString = 0;
va_list args;
PCHAR szDebugString = NULL;
size_t st_Offset = 0;
va_start(args, kszDebugFormatString);
cbFormatString = _scprintf("[%s:%d] ", kwszFunction, iLineNumber) * sizeof(CHAR);
cbFormatString += _vscprintf(kszDebugFormatString, args) * sizeof(CHAR) + 2;
/* Depending on the size of the format string, allocate space on the stack or the heap. */
szDebugString = (PCHAR)_malloca(cbFormatString);
if (!szDebugString)
return;
/* Populate the buffer with the contents of the format string. */
StringCbPrintfA(szDebugString, cbFormatString, "[%s:%d] ", kwszFunction, iLineNumber);
StringCbLengthA(szDebugString, cbFormatString, &st_Offset);
StringCbVPrintfA(&szDebugString[st_Offset / sizeof(CHAR)], cbFormatString - st_Offset, kszDebugFormatString, args);
OutputDebugStringA(szDebugString);
_freea(szDebugString);
va_end(args);
}

View File

@ -29,14 +29,15 @@
#include <avrt.h>
#include <wrl.h>
#include "Debug.h"
#include "CDebug.h"
#include "CIndirectDeviceContext.h"
#include "CIndirectMonitorContext.h"
WDFDEVICE l_wdfDevice = nullptr;
NTSTATUS LGIddDeviceD0Entry(WDFDEVICE device, WDF_POWER_DEVICE_STATE previousState)
{
UNREFERENCED_PARAMETER(previousState);
UNREFERENCED_PARAMETER(device);
auto * wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(device);
wrapper->context->InitAdapter();
@ -81,66 +82,33 @@ static inline void FillSignalInfo(DISPLAYCONFIG_VIDEO_SIGNAL_INFO & mode, DWORD
NTSTATUS LGIddParseMonitorDescription(const IDARG_IN_PARSEMONITORDESCRIPTION* inArgs,
IDARG_OUT_PARSEMONITORDESCRIPTION* outArgs)
{
outArgs->MonitorModeBufferOutputCount = ARRAYSIZE(DisplayModes);
if (inArgs->MonitorModeBufferInputCount < ARRAYSIZE(DisplayModes))
return (inArgs->MonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
for (UINT i = 0; i < ARRAYSIZE(DisplayModes); ++i)
{
inArgs->pMonitorModes[i].Size = sizeof(IDDCX_MONITOR_MODE);
inArgs->pMonitorModes[i].Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR;
FillSignalInfo(inArgs->pMonitorModes[i].MonitorVideoSignalInfo,
DisplayModes[i][0], DisplayModes[i][1], DisplayModes[i][2], true);
}
if (!l_wdfDevice)
return STATUS_INVALID_PARAMETER;
outArgs->PreferredMonitorModeIdx = PreferredDisplayMode;
return STATUS_SUCCESS;
auto* wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(l_wdfDevice);
return wrapper->context->ParseMonitorDescription(inArgs, outArgs);
}
NTSTATUS LGIddMonitorGetDefaultModes(IDDCX_MONITOR monitor, const IDARG_IN_GETDEFAULTDESCRIPTIONMODES * inArgs,
IDARG_OUT_GETDEFAULTDESCRIPTIONMODES * outArgs)
{
UNREFERENCED_PARAMETER(monitor);
outArgs->DefaultMonitorModeBufferOutputCount = ARRAYSIZE(DisplayModes);
if (inArgs->DefaultMonitorModeBufferInputCount < ARRAYSIZE(DisplayModes))
return (inArgs->DefaultMonitorModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
for (UINT i = 0; i < ARRAYSIZE(DisplayModes); ++i)
{
inArgs->pDefaultMonitorModes[i].Size = sizeof(IDDCX_MONITOR_MODE);
inArgs->pDefaultMonitorModes[i].Origin = IDDCX_MONITOR_MODE_ORIGIN_DRIVER;
FillSignalInfo(inArgs->pDefaultMonitorModes[i].MonitorVideoSignalInfo,
DisplayModes[i][0], DisplayModes[i][1], DisplayModes[i][2], true);
}
outArgs->PreferredMonitorModeIdx = PreferredDisplayMode;
return STATUS_SUCCESS;
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
return wrapper->context->GetDeviceContext()->MonitorGetDefaultModes(inArgs, outArgs);
}
NTSTATUS LGIddMonitorQueryTargetModes(IDDCX_MONITOR monitor, const IDARG_IN_QUERYTARGETMODES * inArgs,
IDARG_OUT_QUERYTARGETMODES * outArgs)
{
UNREFERENCED_PARAMETER(monitor);
outArgs->TargetModeBufferOutputCount = ARRAYSIZE(DisplayModes);
if (inArgs->TargetModeBufferInputCount < ARRAYSIZE(DisplayModes))
return (inArgs->TargetModeBufferInputCount > 0) ? STATUS_BUFFER_TOO_SMALL : STATUS_SUCCESS;
for (UINT i = 0; i < ARRAYSIZE(DisplayModes); ++i)
{
inArgs->pTargetModes[i].Size = sizeof(IDDCX_TARGET_MODE);
FillSignalInfo(inArgs->pTargetModes[i].TargetVideoSignalInfo.targetVideoSignalInfo,
DisplayModes[i][0], DisplayModes[i][1], DisplayModes[i][2], false);
}
return STATUS_SUCCESS;
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
return wrapper->context->GetDeviceContext()->MonitorQueryTargetModes(inArgs, outArgs);
}
NTSTATUS LGIddMonitorAssignSwapChain(IDDCX_MONITOR monitor, const IDARG_IN_SETSWAPCHAIN* inArgs)
{
auto * wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
wrapper->context->AssignSwapChain(inArgs->hSwapChain, inArgs->RenderAdapterLuid, inArgs->hNextSurfaceAvailable);
wrapper->context->AssignSwapChain(
inArgs->hSwapChain, inArgs->RenderAdapterLuid, inArgs->hNextSurfaceAvailable);
wrapper->context->GetDeviceContext()->OnAssignSwapChain();
return STATUS_SUCCESS;
}
@ -148,6 +116,7 @@ NTSTATUS LGIddMonitorUnassignSwapChain(IDDCX_MONITOR monitor)
{
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
wrapper->context->UnassignSwapChain();
wrapper->context->GetDeviceContext()->OnUnassignedSwapChain();
return STATUS_SUCCESS;
}
@ -158,10 +127,10 @@ NTSTATUS LGIddCreateDevice(_Inout_ PWDFDEVICE_INIT deviceInit)
status = IddCxGetVersion(&ver);
if (FAILED(status))
{
DBGPRINT("IddCxGetVersion Failed");
DEBUG_ERROR("IddCxGetVersion Failed");
return status;
}
DBGPRINT("Version: 0x%04x", ver.IddCxVersion);
DEBUG_INFO("Version: 0x%04x", ver.IddCxVersion);
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
@ -189,6 +158,7 @@ NTSTATUS LGIddCreateDevice(_Inout_ PWDFDEVICE_INIT deviceInit)
auto * wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(object);
if (wrapper)
wrapper->Cleanup();
l_wdfDevice = nullptr;
};
WDFDEVICE device = nullptr;
@ -196,6 +166,13 @@ NTSTATUS LGIddCreateDevice(_Inout_ PWDFDEVICE_INIT deviceInit)
if (!NT_SUCCESS(status))
return status;
/*
* Because we are limited to IddCx 1.5 to retain Windows 10 support we have
* no way of getting the device context in `LGIdddapterCommitModes`, as such
* we need to store this for later.
*/
l_wdfDevice = device;
status = IddCxDeviceInitialize(device);
auto wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(device);

View File

@ -24,33 +24,6 @@
#include <Windows.h>
#if 1
const DWORD DisplayModes[][3] =
{
{7680, 4800, 120}, {7680, 4320, 120}, {6016, 3384, 120}, {5760, 3600, 120},
{5760, 3240, 120}, {5120, 2800, 120}, {4096, 2560, 120}, {4096, 2304, 120},
{3840, 2400, 120}, {3840, 2160, 120}, {3200, 2400, 120}, {3200, 1800, 120},
{3008, 1692, 120}, {2880, 1800, 120}, {2880, 1620, 120}, {2560, 1600, 120},
{2560, 1440, 120}, {1920, 1440, 120}, {1920, 1200, 120}, {1920, 1080, 120},
{1600, 1200, 120}, {1600, 1024, 120}, {1600, 1050, 120}, {1600, 900 , 120},
{1440, 900 , 120}, {1400, 1050, 120}, {1366, 768 , 120}, {1360, 768 , 120},
{1280, 1024, 120}, {1280, 960 , 120}, {1280, 800 , 120}, {1280, 768 , 120},
{1280, 720 , 120}, {1280, 600 , 120}, {1152, 864 , 120}, {1024, 768 , 120},
{800 , 600 , 120}, {640 , 480 , 120}
};
const DWORD PreferredDisplayMode = 19;
#else
const DWORD DisplayModes[][3] =
{
{ 2560, 1440, 144 },
{ 1920, 1080, 60 },
{ 1024, 768, 60 },
};
const DWORD PreferredDisplayMode = 0;
#endif
typedef struct _DEVICE_CONTEXT
{
ULONG PrivateDeviceData; // just a placeholder

View File

@ -21,20 +21,33 @@
#include "driver.h"
#include "driver.tmh"
#include "CDebug.h"
#include "CPlatformInfo.h"
#include "VersionInfo.h"
#include "CPipeServer.h"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
WDF_DRIVER_CONFIG config;
NTSTATUS status;
WDF_OBJECT_ATTRIBUTES attributes;
g_debug.Init("looking-glass-idd");
DEBUG_INFO("Looking Glass IDD Driver (" LG_VERSION_STR ")");
NTSTATUS status = STATUS_SUCCESS;
#if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0
WPP_INIT_TRACING(MYDRIVER_TRACING_ID);
#else
WPP_INIT_TRACING(DriverObject, RegistryPath);
#endif
if (!g_pipe.Init())
{
status = STATUS_UNSUCCESSFUL;
DEBUG_ERROR("Failed to setup IPC pipe");
goto fail;
}
WDF_DRIVER_CONFIG config;
WDF_OBJECT_ATTRIBUTES attributes;
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
@ -46,16 +59,19 @@ NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING Reg
if (!NT_SUCCESS(status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfDriverCreate failed %!STATUS!", status);
#if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0
WPP_CLEANUP();
#else
WPP_CLEANUP(DriverObject);
#endif
return status;
goto fail;
}
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit");
return status;
fail:
#if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0
WPP_CLEANUP();
#else
WPP_CLEANUP(DriverObject);
#endif
return status;
}
NTSTATUS LGIddEvtDeviceAdd(_In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit)
@ -75,6 +91,8 @@ VOID LGIddEvtDriverContextCleanup(_In_ WDFOBJECT DriverObject)
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
g_pipe.DeInit();
#if UMDF_VERSION_MAJOR == 2 && UMDF_VERSION_MINOR == 0
WPP_CLEANUP();
#else

Binary file not shown.

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props" Condition="Exists('..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
@ -36,26 +37,41 @@
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(SolutionDir)LGCommon/*.cpp" />
<ClCompile Include="CD3D12CommandQueue.cpp" />
<ClCompile Include="CFrameBufferPool.cpp" />
<ClCompile Include="CFrameBufferResource.cpp" />
<ClCompile Include="CIndirectDeviceContext.cpp" />
<ClCompile Include="CIndirectMonitorContext.cpp" />
<ClCompile Include="CInteropResourcePool.cpp" />
<ClCompile Include="CInteropResource.cpp" />
<ClCompile Include="CIVSHMEM.cpp" />
<ClCompile Include="CPipeServer.cpp" />
<ClCompile Include="CPlatformInfo.cpp" />
<ClCompile Include="CSwapChainProcessor.cpp" />
<ClCompile Include="Debug.cpp" />
<ClCompile Include="CD3D12Device.cpp" />
<ClCompile Include="Device.cpp" />
<ClCompile Include="Direct3DDevice.cpp" />
<ClCompile Include="CD3D11Device.cpp" />
<ClCompile Include="Driver.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
<ClInclude Include="CD3D12CommandQueue.h" />
<ClInclude Include="CFrameBufferPool.h" />
<ClInclude Include="CFrameBufferResource.h" />
<ClInclude Include="CIndirectMonitorContext.h" />
<ClInclude Include="CInteropResourcePool.h" />
<ClInclude Include="CInteropResource.h" />
<ClInclude Include="CIVSHMEM.h" />
<ClInclude Include="CPipeServer.h" />
<ClInclude Include="CPlatformInfo.h" />
<ClInclude Include="CSwapChainProcessor.h" />
<ClInclude Include="Debug.h" />
<ClInclude Include="CD3D12Device.h" />
<ClInclude Include="Device.h" />
<ClInclude Include="Direct3DDevice.h" />
<ClInclude Include="CD3D11Device.h" />
<ClInclude Include="Driver.h" />
<ClInclude Include="CIndirectDeviceContext.h" />
<ClInclude Include="Public.h" />
@ -72,7 +88,7 @@
<Configuration>Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
<RootNamespace>LGIdd</RootNamespace>
<WindowsTargetPlatformVersion>10.0.22621.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.26100.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<PropertyGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
@ -85,12 +101,10 @@
<DriverTargetPlatform>Universal</DriverTargetPlatform>
</PropertyGroup>
<PropertyGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<DriverTargetPlatform>Universal</DriverTargetPlatform>
</PropertyGroup>
<PropertyGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<DriverTargetPlatform>Universal</DriverTargetPlatform>
</PropertyGroup>
@ -145,11 +159,12 @@
<UMDF_VERSION_MAJOR>2</UMDF_VERSION_MAJOR>
<IndirectDisplayDriver>true</IndirectDisplayDriver>
<IDDCX_VERSION_MAJOR>1</IDDCX_VERSION_MAJOR>
<IDDCX_VERSION_MINOR>9</IDDCX_VERSION_MINOR>
<IDDCX_VERSION_MINOR>10</IDDCX_VERSION_MINOR>
<Driver_SpectreMitigation>Spectre</Driver_SpectreMitigation>
<UMDF_VERSION_MINOR>25</UMDF_VERSION_MINOR>
<UMDF_MINIMUM_VERSION_REQUIRED>25</UMDF_MINIMUM_VERSION_REQUIRED>
<_NT_TARGET_VERSION>0xA000005</_NT_TARGET_VERSION>
<PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
@ -157,11 +172,12 @@
<UMDF_VERSION_MAJOR>2</UMDF_VERSION_MAJOR>
<IndirectDisplayDriver>true</IndirectDisplayDriver>
<IDDCX_VERSION_MAJOR>1</IDDCX_VERSION_MAJOR>
<IDDCX_VERSION_MINOR>9</IDDCX_VERSION_MINOR>
<IDDCX_VERSION_MINOR>10</IDDCX_VERSION_MINOR>
<Driver_SpectreMitigation>Spectre</Driver_SpectreMitigation>
<UMDF_VERSION_MINOR>25</UMDF_VERSION_MINOR>
<UMDF_MINIMUM_VERSION_REQUIRED>25</UMDF_MINIMUM_VERSION_REQUIRED>
<_NT_TARGET_VERSION>0xA000005</_NT_TARGET_VERSION>
<PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
@ -254,10 +270,10 @@
<WppRecorderEnabled>true</WppRecorderEnabled>
<WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData>
<AdditionalOptions>/EHsc /D_ATL_NO_WIN_SUPPORT /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=9 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\repos\LGMP\lgmp\include;$(ProjectDir)..\..\vendor;$(ProjectDir)..\..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;$(SolutionDir)..\repos\LGMP\lgmp\include;$(SolutionDir)..\vendor;$(SolutionDir)..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib;avrt.lib</AdditionalDependencies>
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;d3d12.lib</AdditionalDependencies>
</Link>
<DriverSign>
<FileDigestAlgorithm>SHA1</FileDigestAlgorithm>
@ -269,10 +285,10 @@
<WppRecorderEnabled>true</WppRecorderEnabled>
<WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData>
<AdditionalOptions>/EHsc /D_ATL_NO_WIN_SUPPORT /DIDDCX_VERSION_MAJOR=1 /DIDDCX_VERSION_MINOR=9 /DIDDCX_MINIMUM_VERSION_REQUIRED=4 %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\repos\LGMP\lgmp\include;$(ProjectDir)..\..\vendor;$(ProjectDir)..\..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;$(SolutionDir)..\repos\LGMP\lgmp\include;$(SolutionDir)..\vendor;$(SolutionDir)..\common\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib;avrt.lib</AdditionalDependencies>
<AdditionalDependencies>%(AdditionalDependencies);OneCoreUAP.lib;avrt.lib;d3d12.lib</AdditionalDependencies>
</Link>
<DriverSign>
<FileDigestAlgorithm>SHA1</FileDigestAlgorithm>
@ -315,6 +331,29 @@
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<ImportGroup Label="ExtensionTargets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props'))" />
</Target>
<Target Name="GenerateVersionInfo" BeforeTargets="ClCompile">
<Exec Command="&quot;$(Git)&quot; describe --always --abbrev=10 --dirty=+ --tags" ConsoleToMSBuild="true">
<Output TaskParameter="ConsoleOutput" PropertyName="GitVersion" />
</Exec>
<ItemGroup>
<VersionInfoLines Include="#define LG_VERSION_STR &quot;$(GitVersion)&quot;" />
<VersionInfoLines Include="#define LG_CURRENT_YEAR $(CurrentYear)" />
</ItemGroup>
<WriteLinesToFile File="VersionInfo.h" Lines="@(VersionInfoLines)" Overwrite="true" />
</Target>
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)" />
<PropertyGroup>
<CurrentYear>$([System.DateTime]::Now.ToString("yyyy"))</CurrentYear>
<BuildDependsOn>
GenerateVersionInfo;
$(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>
</Project>

View File

@ -20,6 +20,7 @@
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Inf Include="LGIdd.inf">
@ -48,16 +49,35 @@
<ClInclude Include="CSwapChainProcessor.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Direct3DDevice.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CIVSHMEM.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Debug.h">
<ClInclude Include="CPlatformInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CPlatformInfo.h">
<ClInclude Include="CD3D12CommandQueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CInteropResourcePool.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CInteropResource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CD3D12Device.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CD3D11Device.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CFrameBufferResource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CFrameBufferPool.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
<ClInclude Include="CPipeServer.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@ -77,17 +97,36 @@
<ClCompile Include="CSwapChainProcessor.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Direct3DDevice.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CIVSHMEM.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Debug.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CPlatformInfo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CD3D12CommandQueue.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CInteropResourcePool.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CInteropResource.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CD3D12Device.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CD3D11Device.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CFrameBufferResource.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CFrameBufferPool.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)LGCommon/*.cpp" />
<ClCompile Include="CPipeServer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<DebuggerFlavor>DbgengLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<DebuggerFlavor>DbgengLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<DebuggerFlavor>DbgengLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DebuggerFlavor>DbgengLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MSBuilder.Git" version="0.3.0" targetFramework="native" />
</packages>

View File

@ -0,0 +1,248 @@
/**
* 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 "CPipeClient.h"
#include "CDebug.h"
#include <setupapi.h>
#include <tchar.h>
CPipeClient g_pipe;
bool CPipeClient::Init()
{
DeInit();
if (!IsLGIddDeviceAttached())
{
DEBUG_ERROR("Looking Glass Indirect Display Device not found");
return false;
}
m_running = true;
m_thread.Attach(CreateThread(
NULL,
0,
_pipeThread,
(LPVOID)this,
0,
NULL));
if (!m_thread.IsValid())
{
DEBUG_ERROR_HR(GetLastError(), "Failed to create the pipe thread");
return false;
}
return true;
}
void CPipeClient::DeInit()
{
m_connected = false;
if (m_thread.IsValid())
{
m_running = false;
WaitForSingleObject(m_thread.Get(), INFINITE);
m_thread.Close();
}
if (m_pipe.IsValid())
{
FlushFileBuffers(m_pipe.Get());
m_pipe.Close();
}
}
bool CPipeClient::IsLGIddDeviceAttached()
{
HDEVINFO hDevInfo = SetupDiGetClassDevs(
NULL,
NULL,
NULL,
DIGCF_ALLCLASSES | DIGCF_PRESENT
);
if (hDevInfo == INVALID_HANDLE_VALUE)
return false;
SP_DEVINFO_DATA DeviceInfoData;
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
bool found = false;
for (DWORD i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData); i++)
{
DWORD DataT;
TCHAR buffer[1024];
DWORD buffersize = 0;
if (!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
SPDRP_HARDWAREID,
&DataT,
(PBYTE)buffer,
sizeof(buffer),
&buffersize))
continue;
for (LPCTSTR p = buffer; *p; p += _tcslen(p) + 1)
if (_tcsicmp(p, _T("Root\\LGIdd")) == 0)
{
found = true;
break;
}
if (found)
break;
}
SetupDiDestroyDeviceInfoList(hDevInfo);
return found;
}
/* APIs like SetCursorPos are applied to the desktop our thread is
* attached to. If the user switches to the secure desktop (UAC, etc)
* then these functions will not work, so call this first to ensure
* the call is effective */
void CPipeClient::SetActiveDesktop()
{
HDESK desktop = NULL;
desktop = OpenInputDesktop(0, FALSE, GENERIC_READ);
if (!desktop)
DEBUG_ERROR_HR(GetLastError(), "OpenInputDesktop Failed");
else
{
if (!SetThreadDesktop(desktop))
DEBUG_ERROR_HR(GetLastError(), "SetThreadDesktop Failed");
CloseDesktop(desktop);
}
}
void CPipeClient::WriteMsg(const LGPipeMsg& msg)
{
DWORD written;
if (!WriteFile(m_pipe.Get(), &msg, sizeof(msg), &written, NULL))
{
DWORD err = GetLastError();
if (err == ERROR_BROKEN_PIPE)
{
DEBUG_WARN_HR(err, "Client disconnected, failed to write");
m_connected = false;
return;
}
DEBUG_WARN_HR(err, "WriteFile failed on the pipe");
return;
}
FlushFileBuffers(m_pipe.Get());
}
void CPipeClient::Thread()
{
DEBUG_INFO("Pipe thread started");
while (m_running)
{
if (!WaitNamedPipeA(LG_PIPE_NAME, 5000))
{
if (!IsLGIddDeviceAttached())
{
m_running = false;
DEBUG_ERROR("Device is no longer available, shutting down");
break;
}
continue;
}
m_pipe.Attach(CreateFileA(
LG_PIPE_NAME,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL
));
if (!m_pipe.IsValid())
{
DEBUG_ERROR_HR(GetLastError(), "Failed to open the named pipe");
continue;
}
m_connected = true;
DEBUG_INFO("Pipe connected");
while (m_running && m_connected)
{
LGPipeMsg msg;
DWORD bytesRead;
if (!ReadFile(m_pipe.Get(), &msg, sizeof(msg), &bytesRead, NULL))
{
DEBUG_ERROR_HR(GetLastError(), "ReadFile Failed");
break;
}
if (bytesRead != sizeof(msg) || msg.size != sizeof(msg))
{
DEBUG_ERROR("Corrupted data");
break;
}
switch (msg.type)
{
case LGPipeMsg::SETCURSORPOS:
HandleSetCursorPos(msg);
break;
case LGPipeMsg::SETDISPLAYMODE:
HandleSetDisplayMode(msg);
break;
default:
DEBUG_ERROR("Unknown message type %d", msg.type);
break;
}
}
m_pipe.Close();
m_connected = false;
DEBUG_INFO("Pipe closed");
}
DEBUG_INFO("Pipe thread shutdown");
}
void CPipeClient::HandleSetCursorPos(const LGPipeMsg& msg)
{
SetActiveDesktop();
SetCursorPos(msg.curorPos.x, msg.curorPos.y);
}
void CPipeClient::HandleSetDisplayMode(const LGPipeMsg& msg)
{
DEVMODE dm = {};
dm.dmSize = sizeof(dm);
dm.dmPelsWidth = msg.displayMode.width;
dm.dmPelsHeight = msg.displayMode.height;
dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
LONG result = ChangeDisplaySettingsEx(NULL, &dm, NULL, CDS_UPDATEREGISTRY, NULL);
if (result != DISP_CHANGE_SUCCESSFUL)
DEBUG_ERROR("ChangeDisplaySettingsEx Failed (0x%08x)", result);
}

View File

@ -0,0 +1,61 @@
/**
* 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
*/
#pragma once
#include <windows.h>
#include <stdint.h>
#include <wrl.h>
#include "PipeMsg.h"
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace Microsoft::WRL::Wrappers::HandleTraits;
class CPipeClient
{
private:
HandleT<HANDLENullTraits> m_pipe;
HandleT<HANDLENullTraits> m_thread;
bool m_running = false;
bool m_connected = false;
static DWORD WINAPI _pipeThread(LPVOID lpParam) { ((CPipeClient*)lpParam)->Thread(); return 0; }
void Thread();
void WriteMsg(const LGPipeMsg& msg);
void SetActiveDesktop();
void HandleSetCursorPos(const LGPipeMsg& msg);
void HandleSetDisplayMode(const LGPipeMsg& msg);
public:
~CPipeClient() { DeInit(); }
static bool IsLGIddDeviceAttached();
bool Init();
void DeInit();
bool IsRunning() { return m_running; }
};
extern CPipeClient g_pipe;

View File

@ -0,0 +1,188 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props" Condition="Exists('..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{0045d7ad-3f26-4b87-81cb-78d18839596d}</ProjectGuid>
<RootNamespace>LGIddHelper</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>Default</LanguageStandard>
<LanguageStandard_C>Default</LanguageStandard_C>
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>%(AdditionalDependencies);Rpcrt4.lib;Userenv.lib;setupapi.lib</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>if not exist "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" mkdir "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd"
copy /Y "$(TargetPath)" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd\"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>Default</LanguageStandard>
<LanguageStandard_C>Default</LanguageStandard_C>
<AdditionalIncludeDirectories>$(SolutionDir)LGCommon;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>%(AdditionalDependencies);Rpcrt4.lib;Userenv.lib;setupapi.lib</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>if not exist "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd" mkdir "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd"
copy /Y "$(TargetPath)" "$(SolutionDir)$(Platform)\$(Configuration)\LGIdd\"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="$(SolutionDir)LGCommon\*.cpp" />
<ClCompile Include="CPipeClient.cpp" />
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<CLInclude Include="$(SolutionDir)LGCommon\*.h" />
<ClInclude Include="CPipeClient.h" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
<Target Name="GenerateVersionInfo" BeforeTargets="ClCompile">
<Exec Command="&quot;$(Git)&quot; describe --always --abbrev=10 --dirty=+ --tags" ConsoleToMSBuild="true">
<Output TaskParameter="ConsoleOutput" PropertyName="GitVersion" />
</Exec>
<ItemGroup>
<VersionInfoLines Include="#define LG_VERSION_STR &quot;$(GitVersion)&quot;" />
<VersionInfoLines Include="#define LG_CURRENT_YEAR $(CurrentYear)" />
</ItemGroup>
<WriteLinesToFile File="VersionInfo.h" Lines="@(VersionInfoLines)" Overwrite="true" />
</Target>
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)" />
<PropertyGroup>
<CurrentYear>$([System.DateTime]::Now.ToString("yyyy"))</CurrentYear>
<BuildDependsOn>
GenerateVersionInfo;
$(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSBuilder.Git.0.3.0\build\MSBuilder.Git.props'))" />
</Target>
</Project>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)LGCommon\*.cpp" />
<ClCompile Include="CPipeClient.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<CLInclude Include="$(SolutionDir)LGCommon\*.h" />
<ClInclude Include="CPipeClient.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

421
idd/LGIddHelper/main.cpp Normal file
View File

@ -0,0 +1,421 @@
#include <Windows.h>
#include <wrl.h>
#include <UserEnv.h>
#include <vector>
#include <string>
using namespace Microsoft::WRL::Wrappers;
using namespace Microsoft::WRL::Wrappers::HandleTraits;
#include "CDebug.h"
#include "VersionInfo.h"
#include "CPipeClient.h"
#define SVCNAME "Looking Glass (IDD Helper)"
static SERVICE_STATUS_HANDLE l_svcStatusHandle;
static SERVICE_STATUS l_svcStatus;
static HandleT<EventTraits> l_svcStopEvent;
bool HandleService();
static void WINAPI SvcMain(DWORD dwArgc, LPTSTR* lpszArgv);
static void WINAPI SvcCtrlHandler(DWORD dwControl);
static void ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);
static std::string l_executable;
static HandleT<HANDLENullTraits> l_process;
static HandleT<EventTraits> l_exitEvent;
static std::string l_exitEventName;
static void Launch();
LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hwnd, msg, wParam, lParam);
}
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
{
g_debug.Init("looking-glass-iddhelper");
DEBUG_INFO("Looking Glass IDD Helper (" LG_VERSION_STR ")");
char buffer[MAX_PATH];
DWORD result = GetModuleFileNameA(NULL, buffer, MAX_PATH);
if (result == 0)
{
DEBUG_ERROR("Failed to get the executable path");
return EXIT_FAILURE;
}
l_executable = buffer;
int argc = 0;
LPWSTR * wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
std::vector<std::string> args;
args.reserve(argc);
for (int i = 0; i < argc; ++i)
{
size_t len = wcslen(wargv[i]);
size_t bufSize = (len + 1) * 2;
std::vector<char> buffer(bufSize);
size_t converted = 0;
errno_t err = wcstombs_s(&converted, buffer.data(), bufSize, wargv[i], bufSize - 1);
if (err != 0)
{
DEBUG_ERROR("Conversion failed");
return EXIT_FAILURE;
}
args.emplace_back(buffer.data());
}
LocalFree(wargv);
if (argc == 1)
{
if (!HandleService())
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
// child process
if (argc != 2)
{
// the one and only value we should see is the exit event name
DEBUG_ERROR("Invalid invocation");
return EXIT_FAILURE;
}
l_exitEvent.Attach(OpenEvent(SYNCHRONIZE, FALSE, args[1].c_str()));
if (!l_exitEvent.IsValid())
{
DEBUG_ERROR_HR(GetLastError(), "Failed to open the exit event: %s", args[1].c_str());
return EXIT_FAILURE;
}
WNDCLASSEX wx = {};
wx.cbSize = sizeof(WNDCLASSEX);
wx.lpfnWndProc = DummyWndProc;
wx.hInstance = hInstance;
wx.lpszClassName = "DUMMY_CLASS";
wx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wx.hCursor = LoadCursor(NULL, IDC_ARROW);
wx.hbrBackground = (HBRUSH)COLOR_APPWORKSPACE;
ATOM aclass;
if (!(aclass = RegisterClassEx(&wx)))
{
DEBUG_ERROR("Failed to register message window class");
return EXIT_FAILURE;
}
HWND msgWnd = CreateWindowExA(0, MAKEINTATOM(aclass), NULL,
0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
bool running = g_pipe.Init();
while (running)
{
switch (MsgWaitForMultipleObjects(1, l_exitEvent.GetAddressOf(),
FALSE, INFINITE, QS_ALLINPUT))
{
case WAIT_OBJECT_0:
running = false;
break;
case WAIT_OBJECT_0 + 1:
{
MSG msg;
if (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
break;
}
case WAIT_FAILED:
DEBUG_ERROR_HR(GetLastError(), "MsgWaitForMultipleObjects Failed");
running = false;
break;
}
}
g_pipe.DeInit();
DestroyWindow(msgWnd);
return EXIT_SUCCESS;
}
bool HandleService()
{
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{ (char *)SVCNAME, SvcMain },
{ NULL, NULL }
};
if (StartServiceCtrlDispatcher(DispatchTable) == FALSE)
{
DEBUG_ERROR_HR(GetLastError(), "StartServiceCtrlDispatcher Failed");
return false;
}
return true;
}
static void WINAPI SvcCtrlHandler(DWORD dwControl)
{
switch (dwControl)
{
case SERVICE_CONTROL_STOP:
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
SetEvent(l_svcStopEvent.Get());
return;
default:
break;
}
ReportSvcStatus(l_svcStatus.dwCurrentState, NO_ERROR, 0);
}
static void WINAPI SvcMain(DWORD dwArgc, LPTSTR* lpszArgv)
{
l_svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
l_svcStatus.dwWin32ExitCode = 0;
l_svcStatusHandle = RegisterServiceCtrlHandler(SVCNAME, SvcCtrlHandler);
if (!l_svcStatusHandle)
{
DEBUG_ERROR_HR(GetLastError(), "RegisterServiceCtrlHandler Failed");
return;
}
if (!CPipeClient::IsLGIddDeviceAttached())
{
DEBUG_INFO("Looking Glass Indirect Display Device not found, not starting.");
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
return;
}
ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 0);
l_svcStopEvent.Attach(CreateEvent(NULL, TRUE, FALSE, NULL));
if (!l_svcStopEvent.IsValid())
{
DEBUG_ERROR_HR(GetLastError(), "CreateEvent Failed");
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
return;
}
UUID uuid;
RPC_CSTR uuidStr;
RPC_STATUS status = UuidCreate(&uuid);
if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY && status != RPC_S_UUID_NO_ADDRESS)
{
DEBUG_ERROR("UuidCreate Failed: 0x%x", status);
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
return;
}
if (UuidToString(&uuid, &uuidStr) == RPC_S_OK)
{
l_exitEventName = "Global\\";
l_exitEventName += (const char *)uuidStr;
RpcStringFree(&uuidStr);
l_exitEvent.Attach(CreateEvent(NULL, FALSE, FALSE, l_exitEventName.c_str()));
if (!l_exitEvent.IsValid())
{
DEBUG_ERROR_HR(GetLastError(), "CreateEvent Failed");
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
return;
}
}
ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
bool running = true;
while (running)
{
ULONGLONG launchTime = 0ULL;
DWORD interactiveSession = WTSGetActiveConsoleSessionId();
if (interactiveSession != 0 && interactiveSession != 0xFFFFFFFF)
{
if (!CPipeClient::IsLGIddDeviceAttached())
{
DEBUG_INFO("Looking Glass Indirect Display Device has gone away");
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
return;
}
Launch();
launchTime = GetTickCount64();
}
HANDLE waitOn[] = { l_svcStopEvent.Get(), l_process.Get()};
DWORD count = 2;
DWORD duration = INFINITE;
if (!l_process.IsValid())
{
count = 1;
duration = 1000;
}
switch (WaitForMultipleObjects(count, waitOn, FALSE, duration))
{
// stop requested by the service manager
case WAIT_OBJECT_0:
running = false;
break;
// child application exited
case WAIT_OBJECT_0 + 1:
{
DWORD code;
if (!GetExitCodeProcess(l_process.Get(), &code))
{
DEBUG_ERROR_HR(GetLastError(), "GetExitCodeProcess Failed");
break;
}
DEBUG_INFO("Child process exited with code 0x%lx", code);
l_process.Close();
break;
}
case WAIT_FAILED:
DEBUG_ERROR_HR(GetLastError(), "Failed to WaitForMultipleObjects");
running = false;
break;
}
if (!running)
break;
Sleep(1000);
}
SetEvent(l_exitEvent.Get());
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
}
static void ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 0;
l_svcStatus.dwCurrentState = dwCurrentState;
l_svcStatus.dwWin32ExitCode = dwWin32ExitCode;
l_svcStatus.dwWaitHint = dwWaitHint;
if (dwCurrentState == SERVICE_START_PENDING)
l_svcStatus.dwControlsAccepted = 0;
else
l_svcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
if ((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED))
l_svcStatus.dwCheckPoint = 0;
else
l_svcStatus.dwCheckPoint = ++dwCheckPoint;
SetServiceStatus(l_svcStatusHandle, &l_svcStatus);
}
//static void
static bool EnablePriv(const char * name)
{
return true;
}
static void DisablePriv(const char * name)
{
}
static void Launch()
{
l_process.Close();
HandleT<HANDLENullTraits> sysToken;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE |
TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_ADJUST_DEFAULT,
sysToken.GetAddressOf()))
{
DEBUG_ERROR_HR(GetLastError(), "OpenProcessToken failed");
return;
}
HandleT<HANDLENullTraits> token;
if (!DuplicateTokenEx(sysToken.Get(), 0, NULL, SecurityAnonymous,
TokenPrimary, token.GetAddressOf()))
{
DEBUG_ERROR_HR(GetLastError(), "DuplicateTokenEx failed");
return;
}
DWORD origSessionID, targetSessionID, returnedLen;
GetTokenInformation(token.Get(), TokenSessionId, &origSessionID,
sizeof(origSessionID), &returnedLen);
targetSessionID = WTSGetActiveConsoleSessionId();
if (origSessionID != targetSessionID)
{
if (!SetTokenInformation(token.Get(), TokenSessionId,
&targetSessionID, sizeof(targetSessionID)))
{
DEBUG_ERROR_HR(GetLastError(), "SetTokenInformation failed");
return;
}
}
LPVOID env = NULL;
if (!CreateEnvironmentBlock(&env, token.Get(), TRUE))
{
DEBUG_ERROR_HR(GetLastError(), "CreateEnvironmentBlock failed");
return;
}
if (!EnablePriv(SE_INCREASE_QUOTA_NAME))
{
DEBUG_ERROR("Failed to enable %s", SE_INCREASE_QUOTA_NAME);
return;
}
PROCESS_INFORMATION pi = {0};
STARTUPINFO si = {0};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
si.lpDesktop = (LPSTR)"WinSta0\\Default";
char * cmdLine = NULL;
char cmdBuf[128];
if (l_exitEvent.IsValid())
{
snprintf(cmdBuf, sizeof(cmdBuf), "LGIddHelper.exe %s",
l_exitEventName.c_str());
cmdLine = cmdBuf;
}
if (!CreateProcessAsUserA(
token.Get(),
l_executable.c_str(),
cmdLine,
NULL,
NULL,
FALSE,
DETACHED_PROCESS | HIGH_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT,
env,
NULL,
&si,
&pi
))
{
DEBUG_ERROR_HR(GetLastError(), "CreateProcessAsUser failed");
return;
}
DisablePriv(SE_INCREASE_QUOTA_NAME);
l_process.Attach(pi.hProcess);
CloseHandle(pi.hThread);
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MSBuilder.Git" version="0.3.0" targetFramework="native" />
</packages>

@ -1 +1 @@
Subproject commit de89c63bfe5e31f4ff83ea999c17022e235d96e4
Subproject commit daf0a360b73ee94c945f9be1001106218944b7ab