Compare commits

..

182 Commits

Author SHA1 Message Date
Quantum
4882a3fbd6 [idd] helper: remove compiler warnings 2025-11-09 21:51:28 -05:00
Quantum
3ed08ba56c [idd] helper: require numeric input for modes 2025-11-10 12:01:13 +11:00
Quantum
70da98c1a1 [idd] helper: implement UI for default refresh rate
The UI is not yet hooked up to anything.
2025-11-10 11:58:52 +11:00
Quantum
ed1602fc74 [idd] helper: fix notification icon creation on explorer start 2025-11-10 11:44:47 +11:00
Quantum
f9ecddab98 [idd] helper: implement mode deletion
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 / idd (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-11-09 03:36:19 +11:00
Quantum
fad7af7740 [idd] helper: fix crash when saving invalid values 2025-11-09 03:34:24 +11:00
Quantum
6c70e36cb1 [idd] helper: allow scrolling when mode list is long 2025-11-09 03:30:07 +11:00
Quantum
dca54a79fd [idd] helper: implement adding new modes
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 / idd (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-11-07 00:28:20 +11:00
Quantum
113199b6dd [idd] helper: prevent saving when no mode is selected 2025-11-07 00:28:20 +11:00
Quantum
7d33b93a50 [idd] helper: save mode changes into registry and update listview 2025-11-07 00:28:20 +11:00
Quantum
bf77128053 [idd] helper: add mode save button 2025-11-07 00:28:20 +11:00
Quantum
124b4742e0 [idd] helper: add mode edit controls 2025-11-07 00:28:20 +11:00
Quantum
569619384c [idd] helper: add mode edit labels 2025-11-07 00:28:20 +11:00
Quantum
1d7cb35de8 [idd] helper: detect mode listbox selection change 2025-11-07 00:28:20 +11:00
Quantum
e082acbe44 [idd] helper: use common control v6 with styles 2025-11-07 00:28:20 +11:00
Quantum
ab6e2c89d5 [idd] helper: add basic group box around mode config 2025-11-07 00:28:20 +11:00
Quantum
7a3833782c [idd] helper: display configured modes in listbox 2025-11-07 00:28:20 +11:00
Quantum
96367e83f1 [idd] helper: add CListBox widget 2025-11-07 00:28:20 +11:00
Quantum
c99f516b29 [idd] helper/resize: move logic to .cpp and add error checking 2025-11-07 00:28:20 +11:00
Quantum
2647678b0f [idd] helper: add parser for mode settings in registry 2025-11-07 00:28:20 +11:00
Quantum
40d606890b [repos] wayland-protocols: switch to a mirror on GitHub
anongit.freedesktop.org has unfortunately been very flaky in our CI
pipelines and causing builds to fail. I am running my own mirror which
syncs from the official FreeDesktop GitLab instance to GitHub, which
should allow CI pipelines to continue working on the last available
release should the GitLab sync fail.
2025-11-07 00:22:24 +11:00
Quantum
fddcb7f2d4 [idd] helper: define WIN32_LEAN_AND_MEAN globally
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 / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
It doesn't really help that much, but since we are defining it in
some headers, we might as well do it everywhere so it actually has
some effect in case the headers were included in a different order.
2025-09-23 02:07:18 +10:00
Quantum
48ab317fa0 [idd] helper/notify: delete icon when exiting 2025-09-23 02:03:06 +10:00
Quantum
08e54fd1b1 [idd] installer: fix ivshmem driver installation 2025-09-17 21:03:27 +10:00
Quantum
b504627145 [idd] helper/notify: correctly handle menu dismissal 2025-09-17 20:47:52 +10:00
Quantum
4585362f54 [idd] helper/config: implement window positioning, fonts, and high DPI support 2025-09-17 18:05:00 +10:00
Quantum
e123e02e15 [idd] helper/CWindow: use sane value for hbrBackground 2025-09-17 18:05:00 +10:00
Quantum
85e728b59e [idd] helper: add simple static widget implementation 2025-09-17 18:05:00 +10:00
Quantum
042450a708 [idd] helper: implement basic config class 2025-09-17 18:05:00 +10:00
Quantum
9009217366 [idd] helper: stop using MsgWaitForMultipleObjects
`MsgWaitForMultipleObjects` doesn't handle inner message loops,
which may happen during `TrackPopupMenu`, causing exits to fail.
2025-09-15 09:22:27 +10:00
Quantum
d006dbb547 [idd] helper/CWindow: add onClose and onDestroy hooks 2025-09-15 09:22:27 +10:00
Quantum
c4f3936d98 [idd] helper: split CWindow into base class and CNotifyWindow 2025-09-15 09:22:27 +10:00
Geoffrey McRae
118e300f2f [repos] wayland-protocols: swith to anongit.freedesktop.org
gitlab.freedesktop.org has become unreliable over the last year,
to prevent the ci pipeline from failing, fall back to the more stable
and trusted anongit.
2025-09-15 02:03:34 +10:00
Quantum
19989ce9fb [idd] helper: add icon context menu with log directory open 2025-09-15 01:38:13 +10:00
Quantum
e1a2fa790d [idd] debug: use better log path determination algorithm 2025-09-14 06:23:13 -04:00
Quantum
daa78bcf47 [idd] debug: full Unicode handling and log as UTF-8
This commit makes `CDebug` use Unicode internally instead of whatever
random code page is in use. It also gets rid of the horrible character
counting and replaces that with `vasprintf` and `vaswprintf` helpers
(partially inspired by Linux) which allocates a buffer.

For HRESULT logging, the error code in both hex and decimal are included.

The output is now guaranteed to be UTF-8.
2025-09-14 20:00:25 +10:00
Quantum
e2bc1856b6 [idd] install: add missing io.h include 2025-09-14 19:44:50 +10:00
Quantum
30869c1c9c [idd] debug: log to C:\ProgramData\Looking Glass (IDD) 2025-09-14 17:18:44 +10:00
Quantum
9e8cb2f919 [idd] install: allow unicode errors 2025-09-14 17:18:10 +10:00
Quantum
70e0c356fc [idd] common/debug: add UTC timestamp and raw error code to logs 2025-09-14 17:17:56 +10:00
Quantum
8c3a2d01bc [idd] helper/PipeClient: use event and async I/O to interrupt read
An event, `m_signal`, is created and signalled when either `m_running` or
`m_connected` is changed by another thread, so that the pipe thread knows
to interrupt the read.

The pipe is now opened as async to allow interruption, and the I/O
operations now use overlapped I/O.

Other changes include:
* Changing `m_pipe` to `HandleT<HANDLETraits>` since `CreateFile` returns
  `INVALID_HANDLE_VALUE` instead of `NULL` on error.
* Remove the call to `WaitNamedPipeA` because it's useless and returns
  immediately without waiting if the pipe doesn't exist.
2025-09-14 17:17:21 +10:00
Quantum
eff8555f9b [idd] helper: wait on parent process directly 2025-09-14 17:16:23 +10:00
Quantum
c873aa4c4e [idd] helper: use separate log for child process 2025-09-14 17:16:23 +10:00
Quantum
5c1d604a22 [idd] helper: correctly pump message and destroy window 2025-09-14 17:16:23 +10:00
Quantum
67e1574d20 [idd] helper: add notification icon 2025-09-14 09:49:39 +10:00
Quantum
94550e09b4 [idd] helper: use CWindow helper to avoid global state 2025-09-14 09:49:39 +10:00
Quantum
9d48e70983 [idd] helper: switch to fully Unicode to avoid encoding issues 2025-09-14 09:49:39 +10:00
Quantum
48dc7a90f9 [idd] installer: use icon embedded in LGIddHelper.exe 2025-09-14 09:49:39 +10:00
Quantum
4ff5ce02d7 [idd] helper: add icon to resource 2025-09-14 09:49:39 +10:00
Geoffrey McRae
b7e3ef6824 [client] nanosvg: disable warning for release builds 2025-09-14 01:12:16 +10:00
Geoffrey McRae
870eb8c670 [repos] lgmp: update to fix queue empty state bug 2025-09-14 00:14:37 +10:00
Geoffrey McRae
d02d46283c [obs] dmabuf: fix repeated re-creation of dmabuf texture 2025-09-14 00:14:37 +10:00
Quantum
d5ee54e23e [idd] install: use CreateWellKnownSid 2025-09-13 19:49:39 +10:00
Quantum
41d3e7a981 [idd] install: rework error handling for ensureKeyWithAce
Also avoid weird allocators like LocalAlloc in favour of malloc.
2025-09-13 19:33:53 +10:00
Quantum
e75c7cff46 [idd] install: call ensureKeyWithAce once and fail properly 2025-09-13 19:10:57 +10:00
Quantum
e81462176a [idd] installer: update help text
Also rename /driver to /ivshmem since this is a driver package.
2025-09-13 14:53:22 +10:00
Geoffrey McRae
d00f12875c [idd] installer: dont make the reg key owned by USER MODE DRIVERS
Windows 11 does not allow the key to be owned by USER MODE DRIVERS
granting the user control via the ACL should resolve this
2025-09-13 14:34:04 +10:00
Geoffrey McRae
b6ceb72855 [idd] driver: cleanup duplicated string constant 2025-09-13 14:08:33 +10:00
Geoffrey McRae
13ae3441cf [idd] driver: move the ExtraMode registry key now permissions are correct
This resolves the problem of this setting being essentially lost
between driver upgrades.
2025-09-13 14:08:33 +10:00
Geoffrey McRae
b83d70a068 [idd] installer: create the IDD registry key for the driver
The driver runs under the account `NT AUTHORITY\USER MODE DRIVERS`
and as a result requires the key to be owned by this user so that
it is able to write to it.
2025-09-13 14:08:33 +10:00
Geoffrey McRae
2d28d27e90 Revert idd reg changes due to missing nsis support 2025-09-13 11:21:48 +10:00
Geoffrey McRae
e6b3b7fa76 [idd] nsis: add missed includes 2025-09-13 11:13:21 +10:00
Geoffrey McRae
9220c85e85 [idd] nsis: fix syntax error in if statements 2025-09-13 11:09:58 +10:00
Geoffrey McRae
2c370847e7 [idd] nsis: create a registry key the driver can write to 2025-09-13 11:06:01 +10:00
Quantum
e1170f8e01 [idd] installer: fix copy pasta from host installer 2025-09-13 10:39:55 +10:00
Quantum
41d2210624 [idd] installer: add option to disable old host application 2025-09-13 10:39:39 +10:00
Quantum
037a61a1c7 [host] ci/windows: use explicit windows-2025
This allows forks for whom windows-latest doesn't point to windows-2025
to build successfully.
2025-09-13 10:39:26 +10:00
Quantum
c6eda7e3a0 [idd] ci: build installer 2025-09-13 10:39:11 +10:00
Quantum
ff8650829c [idd] install: create NSIS installer script 2025-09-13 00:32:00 +10:00
Geoffrey McRae
df6d22455e [idd] install: copy executable to staging directory 2025-09-12 22:53:14 +10:00
Quantum
52849bc1d7 [idd] all: delete ARM and ARM64 targets 2025-09-12 22:36:10 +10:00
Quantum
155540a8f7 [idd] install: create new driver installer/uninstaller 2025-09-12 18:54:33 +10:00
Quantum
6e00dedc2b [idd] helper: enable per-monitor DPI awareness 2025-09-11 18:45:43 +10:00
Quantum
3da40f046d [ci] idd: add job 2025-09-11 17:05:38 +10:00
Quantum
e816847fb1 [idd] all: use UTF-8 source code 2025-09-11 17:00:35 +10:00
Geoffrey McRae
eec9aa3ae3 [client] egl: fix uninitialized use of variables 2025-09-08 11:00:06 +10:00
Geoffrey McRae
f07dd827ae [client] egl: fix null pointer dereference crash 2025-09-08 10:57:08 +10:00
Geoffrey McRae
08064efb7f [idd] driver: fix failure to track last pointer ID
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2025-09-07 20:28:55 +00:00
Geoffrey McRae
01cae959d8 [client] core: workaround windows mouse information problem
Under windows there is no cursor enabled at all until it has been moved
for the fist time by the user. If our cursor information is invalid we
assume that this has occured and induce a wiggle to force windows to
create the cursor.
2025-09-07 20:24:17 +10:00
Geoffrey McRae
da154a5591 [github] ci: install NSIS (take 6)
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-09-07 13:56:42 +10:00
Geoffrey McRae
a85b666238 [github] ci: install NSIS (take 6) 2025-09-07 13:49:13 +10:00
Geoffrey McRae
eff1e4d7ea [github] ci: install NSIS (take 4) 2025-09-07 13:42:26 +10:00
Geoffrey McRae
4affd03c2d [github] ci: install NSIS (take 3) 2025-09-07 13:37:36 +10:00
Geoffrey McRae
a1cc58c9f2 [github] ci: install NSIS (take 2) 2025-09-07 13:35:42 +10:00
Geoffrey McRae
9a6e03114e [github] ci: install nsis for windows native builds
As of the Windows Server 2025 image github provides, nsis is no longer
included, as such we must install it ourself.
2025-09-07 13:25:53 +10:00
Geoffrey McRae
0feaa716c5 [idd] helper: implement enable/disable priv
Implements `EnablePriv` and `DisablePriv` so the helper can now
interact with the desktop. Fixes issues with setting cursor position
2025-09-07 13:07:15 +00:00
Geoffrey McRae
5f9649b4a2 [client] egl: set the dmabuf sampler state at creation
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-09-02 13:12:03 +10:00
Geoffrey McRae
e40610ea77 [client] egl: sync on fence without force glFlush in non-dmabuf path 2025-09-02 13:09:39 +10:00
Geoffrey McRae
d5839c7efd [client] egl: improve dmabuf import hot path performance
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
* Removes the vector in favour of a two element array
* Removes iteration on the vector
* Create and track a per-image fence
* bind the EGLImage to the texture at creation, avoiding re-binding
* sync on GL_SYNC_FLUSH_COMMANDS_BIT and remove glFlush
2025-08-31 13:48:08 +10:00
Geoffrey McRae
2a2250b9bd [idd] driver: added files missed in the last commit 2025-08-30 18:08:53 +00:00
Geoffrey McRae
16a283717a [idd] driver; support custom resolution and refresh rate list
Custom modes can now be configured via the registry under

HKEY_LOCAL_MACHINE\SOFTWARE\LookingGlass\IDD

Create the value "Modes" as a REG_MULTI_SZ with the value as
a list of modes, for example:

1024x768@60
1920x1080@60
1920x1080@120*

The '*' denotes the preferred mode to default to if one has not
been selected by the user.
2025-08-30 18:02:55 +00:00
Geoffrey McRae
5a4a2529af [idd] driver: remove static EDID
We do not need this as we are configuring the avialable modes directly
2025-08-30 14:13:53 +00:00
Geoffrey McRae
403bf87d6d [idd] driver: ignore failure due to normal operation
If the screen is blanked or removed through a user action
`IddCxMonitorQueryHardwareCursor` will return with a failure. This
is normal and we should not log this as an error.
2025-08-30 13:05:28 +00:00
Geoffrey McRae
e1a585ad6f [idd] driver: relocate the HW cursor thread into CSwapChainProcessor
It is invalid to call `IddCxMonitorSetupHardwareCursor` before
`IddCxSwapChainSetDevice`. This fixes this by moving the thread into
CSwapChainProcessor and starting it after `IddCxSwapChainSetDevice`
has succeeded.
2025-08-30 11:56:30 +00:00
Geoffrey McRae
7c2e0ec4e9 [repo] lgmp: update to fix packing to align with MSVC properly 2025-08-30 05:14:07 +10:00
Geoffrey McRae
33600682cf [repos] lgmp: fix msvc build (take 5) 2025-08-30 03:57:02 +10:00
Geoffrey McRae
60bda2befc [repos] lgmp: fix msvc build (take 4) 2025-08-30 03:00:25 +10:00
Geoffrey McRae
04a55e1419 [repos] lgmp: fix MSVC build (take 3) 2025-08-30 02:35:55 +10:00
Geoffrey McRae
ca98b5b1fd [repos] lgmp: fix client build under MSVC 2025-08-30 01:30:27 +10:00
Geoffrey McRae
c156044423 [repos] lgmp: update to fix MSVC build 2025-08-30 01:26:18 +10:00
Geoffrey McRae
85ceae91bf [repos] lgmp: update to latest version
The queue size for the cursor needed to be increased to a power of two
with the new design for messages queues in LGMP.
2025-08-30 01:16:26 +10:00
Geoffrey McRae
f8f05c36b8 [client] egl: remove invalid call to glTexImage2D for dmabuf
GL_TEXTURE_EXTERNAL_OES is not a valid target for glTexImage2D and there
is no need to "create" the textures as this is done during import by
eglCreateImage
2025-08-29 19:25:39 +10:00
Geoffrey McRae
05bf816b45 [ci] woodpecker: restrict the entire pipeline 2025-08-29 18:56:05 +10:00
Jonathan Rubenstein
3f35b0b0af [client] wayland: xdg handles resizable
Added missing resizable functionality to xdg_shellInit
when win:allowResize is set to true
2025-08-29 18:15:58 +10:00
Jonathan Rubenstein
f7e0b9746a [client] wayland: libdecor handles borderless
Added missing borderless functionality to libdecor_shellInit
when win:borderless is set to true
This works properly when going full screen as well
2025-08-29 18:15:58 +10:00
Jacob McNamee
b7d044de5d [client] opengl: initialize scale to 1.0
Fixes SPICE cursor rendering unconditionally at (0, 0) prior to KVMFR
frame format configuration
2025-08-29 18:15:28 +10:00
Netboy3
28b653b112 [all] Update to new issue template workflow 2025-08-29 18:14:33 +10:00
Netboy3
26848ba70a [client] pipewire: Stop loading properties from client-rt.conf
Pipewire now automatically moves non-rt clients into non-rt
threads so client-rt.conf is obsolete. Stop loading it during
context initialization for Pipewire 1.3.81 and newer.
See: 24bcacc619
2025-08-29 18:14:17 +10:00
Netboy3
482f66ab9b [doc] kvmfr: Update udev rule example
Starting with version 258, "systemd-udevd ignores OWNER=/GROUP=
settings with a non-system user/group specified in udev rules files".
Change the example udev rule to remove the explicit user assignment and
instead use the "uaccess" tag to auto-assign the logged-in user
permissions. As "uaccess" tags are processed using the 73-seat-late
udev rule, the KVMFR rule file name must precede it (i.e. should use
a lower ordinal value), so change the file name in the example to reflect that.
Credit goes to Discord user "w1kl4s" for bringing this systemd change
to our attention and assisting in testing.
2025-08-29 18:12:58 +10:00
Jonathan Rubenstein
8d9806bb0e [doc] words: Add ie and evdev 2025-08-29 17:58:35 +10:00
Jonathan Rubenstein
fa661aed64 [doc] usage: Update command-line options 2025-08-29 17:58:35 +10:00
Jonathan Rubenstein
2c648b99ad [client] config: Correct spelling in win:setGuestRes
Misspelled word "resoution" corrected to "resolution"
2025-08-29 17:58:35 +10:00
Geoffrey McRae
6a6cfadbf2 [ci] woodpecker: only build pushes to gnif/LookingGlass master 2025-08-29 17:54:30 +10:00
Geoffrey McRae
11c86273d2 [idd] driver: correct reported stride 2025-08-29 15:53:42 +10:00
Stewart Borle
60d7c84972 [client] egl: make int highp in fragment shaders
https://gitlab.freedesktop.org/mesa/mesa/-/issues/12990#note_3038479
2025-08-29 15:33:52 +10:00
Marco Rodolfi
cb304115f6 Update AUTHORS
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-06-30 14:25:08 +10:00
Marco Rodolfi
c349f704d6 [doc] client: add documentation for disableWaitingMessage option 2025-06-30 14:25:08 +10:00
Marco Rodolfi
add45347b5 [client] app: add option to disable waiting for host message 2025-06-30 14:25:08 +10:00
Geoffrey McRae
f15d72cdfe [host] ivshmem: fix missing bounds check on device vector
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2025-05-10 11:12:57 +10:00
Geoffrey McRae
54d811e098 [client] keybind: add keybind to set the guest resolution
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2025-04-04 10:23:00 +11:00
Geoffrey McRae
9593301511 [client] config: add option to disable auto client resolution switching 2025-04-04 10:13:19 +11:00
Geoffrey McRae
c2a19f5e76 [client] message: free any pending messages on deinit 2025-04-04 10:13:09 +11:00
Geoffrey McRae
d8baa62c6a [client] core: check if the resolution already matches the window
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2025-04-02 14:03:48 +11:00
Geoffrey McRae
81162b460e [client] message: fix compile failure 2025-04-02 13:52:45 +11:00
Geoffrey McRae
852eb6bf69 [client] core: new message event system to debounce window size events 2025-04-02 13:46:55 +11:00
Geoffrey McRae
4b11743f18 [common] ll: make ll_push report success/failure 2025-04-02 13:46:07 +11:00
Geoffrey McRae
656d01a694 [idd] driver: add additional logging to CIVSHMEM
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2025-03-31 00:25:54 +11:00
Geoffrey McRae
a4406ac867 [idd] driver: fix free copy queue search method 2025-03-30 23:28:04 +00:00
Geoffrey McRae
57a2f68931 [idd] driver: reduce CopyQueue contention 2025-03-30 23:18:14 +00:00
Geoffrey McRae
f4df3f0ec7 [idd] driver: pre-calculae addresses of structs and offsets 2025-03-30 22:58:12 +00:00
Geoffrey McRae
4e951184f1 [idd] driver: fix hardcoded pitch values
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
2025-03-30 21:40:10 +00:00
Geoffrey McRae
be4782b062 [idd] driver: fix header 2025-03-30 19:04:04 +11:00
Geoffrey McRae
b17c66d6bb [idd] driver: fix failure to intiialize command queue pending 2025-03-30 18:59:42 +11:00
Geoffrey McRae
94fbbad21c [idd] driver: handle command queue failures gracefully 2025-03-30 18:58:03 +11:00
Geoffrey McRae
63a9365377 [idd] driver: fix failure to report completion when an error occurs 2025-03-30 18:21:34 +00:00
Geoffrey McRae
cadcfe4b39 [idd] driver: fix deadlock caused by command queue completion callback
The callback runs in a random thread, we can't call directx methods
safely from it, so move reset so it's called automatically when a free
copy list is obtained.
2025-03-30 16:52:58 +00:00
Geoffrey McRae
3b883bf9fe [idd] driver: use a timeout instead of an event wait.
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
Waiting on the events is dangerous as there is a race here where
we may end up stuck as the events need to be auto-reset
2025-03-30 04:28:49 +00:00
Geoffrey McRae
b58171c3e1 [idd] driver: very experimental change to syncronization
This may get reverted, at this point it's an experiment for the
testers to trial.
2025-03-30 02:47:40 +00:00
Geoffrey McRae
7afb9b93eb [idd] driver: improve display mode support and resolution switch 2025-03-29 22:42:49 +00:00
Geoffrey McRae
6396ff1e9c [repos] lgmp: update for the new wdk version 2025-03-29 22:42:49 +00:00
Geoffrey McRae
db1dda00c8 [idd] all: update to sdk 10.0.26100 and iddcx 1.10 2025-03-29 22:42:48 +00:00
Geoffrey McRae
5b07286c65 [idd] driver: report we are finished with the frame earlier
`IddCxSwapChainFinishedProcessingFrame` must be called after every
frame, but we should do it as early as possible once all commands
are queued that use the frame.
2025-03-29 22:29:47 +00:00
Geoffrey McRae
6dad0de8b8 [idd] driver: reset cursor thread events on reinit 2025-03-29 22:27:55 +00:00
Geoffrey McRae
35c975d334 [idd] driver: fix loss of mouse cursor on guest wakeup
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
2025-03-29 01:16:53 +00:00
Geoffrey McRae
530e83e7bf [idd] driver: cosmetics 2025-03-29 00:56:54 +00:00
Geoffrey McRae
40a4debfda [idd] driver: fix debug messages 2025-03-29 00:56:39 +00:00
Geoffrey McRae
75f07cb28c [idd] driver: improve reInit logic on failure 2025-03-29 00:56:16 +00:00
Geoffrey McRae
f26fa17bc1 [idd] driver: make m_indirectCopy static so it persists on retry 2025-03-28 23:50:54 +00:00
Geoffrey McRae
648fca7caa [idd] driver: re-plug the monitor if the heap test failed 2025-03-28 23:47:31 +00:00
Geoffrey McRae
868504d22d [client] core: fix dynamic res switch race issue 2025-03-28 23:18:41 +11:00
Geoffrey McRae
cb423730e4 [idd] driver: implement dynamic mode switch support 2025-03-28 23:17:31 +00:00
Geoffrey McRae
86de1c9ac6 [idd] common: still print the message if FormatMsg failed 2025-03-28 21:38:36 +00:00
Geoffrey McRae
d839a45d0b [client] kvmfr: report the local window size to the VM
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
2025-03-28 16:38:02 +11:00
Geoffrey McRae
9ffb800e93 [idd] driver: use the frameSize instead of the resource size
As the resource size can be larger then the actual frame data, we
need to track this seperately so that we don't waste cycles copying
data the client will never use.
2025-03-28 14:35:19 +00:00
Geoffrey McRae
91e8440c9d [idd] vs: remove dynamically generated VersionInfo.h from the repo 2025-03-28 14:13:34 +00:00
Geoffrey McRae
15eff234ec [idd] driver: make indirectCopy functional 2025-03-28 14:11:48 +00:00
Geoffrey McRae
6a4edfc6b6 [idd] helper: added new helper service
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run
As the IDD itself runs in a WUMDF sandbox, it doesn't have enough
access to perform interactive operations such as moving the cursor.

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

This reverts commit b1b6da32ce.
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
130 changed files with 7511 additions and 1221 deletions

View File

@@ -1,3 +1,9 @@
---
name: Bug report
about: Report bugs in Looking Glass (only confirmed bugs and feature requests please)
---
### Issues are for Bug Reports and Feature Requests Only!
If you are looking for help or support please use one of the following methods
@@ -64,3 +70,4 @@ https://www.youtube.com/watch?v=EqxxJK9Yo64
```
PASTE FULL BACKTRACE HERE
```

9
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,9 @@
blank_issues_enabled: false
contact_links:
- name: Looking Glass on Level1Tech's Forum
url: https://forum.level1techs.com/c/software/lookingGlass/142
about: Ask for help by creating a New Topic on the Level1Tech's forum
- name: Looking Glass Discord Server
url: https://discord.gg/52SMupxkvt
about: Ask for help in the Looking Glass discord server

View File

@@ -120,11 +120,33 @@ jobs:
makensis -DIVSHMEM platform/Windows/installer.nsi
host-windows-native:
runs-on: windows-latest
runs-on: windows-2025
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Install NSIS
shell: powershell
run: |
winget install -e --id NSIS.NSIS --silent --accept-source-agreements --accept-package-agreements
$paths = @(
(Join-Path ${env:ProgramFiles} 'NSIS\Bin'),
(Join-Path ${env:ProgramFiles(x86)} 'NSIS\Bin')
) | Where-Object { Test-Path $_ }
if ($paths.Count -eq 0) {
Write-Error "NSIS 'Bin' folder not found after install."
exit 1
}
$paths | ForEach-Object {
Add-Content -Path $env:GITHUB_PATH -Value $_
}
Write-Host "makensis location(s): $($paths -join ', ')"
- name: Test NSIS
run: |
makensis /VERSION
- name: Configure Windows host for native MinGW-w64
run: |
mkdir host\build
@@ -139,6 +161,36 @@ jobs:
cd host\build
makensis -DBUILD_32BIT platform\Windows\installer.nsi
idd:
runs-on: windows-2022
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v2
with:
msbuild-architecture: x64
- name: Install NuGet packages
run: |
cd idd
nuget restore LGIdd.sln
- name: Build IDD
run: |
cd idd
msbuild.exe LGIdd.sln /t:Build /p:Configuration=Release /p:Platform=x64
- name: Build NSIS Installer
run: |
cd idd\x64\Release\LGIdd
makensis -DBUILD_32BIT installer.nsi
- name: Build NSIS installer with IVSHMEM drivers
run: |
cd idd\x64\Release
Invoke-WebRequest https://dl.quantum2.xyz/ivshmem.tar.gz -OutFile ivshmem.tar.gz
tar -xzvf ivshmem.tar.gz
cd LGIdd
makensis -DBUILD_32BIT -DIVSHMEM installer.nsi
obs:
runs-on: ubuntu-latest
strategy:

8
.gitignore vendored
View File

@@ -11,7 +11,11 @@ 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/*/VERSION
idd/packages

2
.gitmodules vendored
View File

@@ -9,7 +9,7 @@
url = https://github.com/cimgui/cimgui.git
[submodule "repos/wayland-protocols"]
path = repos/wayland-protocols
url = https://gitlab.freedesktop.org/wayland/wayland-protocols.git
url = https://github.com/quantum5/wayland-protocols.git
[submodule "repos/nanosvg"]
path = repos/nanosvg
url = https://github.com/memononen/nanosvg.git

34
.woodpecker/idd.yaml Normal file
View File

@@ -0,0 +1,34 @@
---
labels:
platform: windows/amd64
matrix:
BUILD_TYPE:
- Debug
- Release
when:
- event: push
branch: master
repo: gnif/LookingGlass
clone:
- name: clone
image: woodpeckerci/plugin-git
pull: false
settings:
tags: true
steps:
- name: idd
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

@@ -72,3 +72,5 @@ Jacob McNamee <jacob@jacobmcnamee.com> (jacobmcnamee)
Marco Antonio J. Costa <marco.antonio.costa@gmail.com> (majcosta)
rs189 <35667100+rs189@users.noreply.github.com> (rs189)
Jérôme Poulin <jeromepoulin@gmail.com> (ticpu)
Marco Rodolfi <marco.rodolfi@tuta.io> (RodoMa92)
Stewart Borle <stewi1014@gmail.com> (stewi1014)

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

@@ -168,6 +168,12 @@ static bool pipewire_init(void)
pw_init(NULL, NULL);
pw.loop = pw_loop_new(NULL);
#if PW_CHECK_VERSION(1, 3, 81)
pw.context = pw_context_new(
pw.loop,
NULL,
0);
#else
pw.context = pw_context_new(
pw.loop,
pw_properties_new(
@@ -176,6 +182,7 @@ static bool pipewire_init(void)
NULL
),
0);
#endif
if (!pw.context)
{
DEBUG_ERROR("Failed to create a context");

View File

@@ -46,6 +46,7 @@ typedef struct LibDecorState
int32_t width, height;
bool needsResize;
bool fullscreen;
bool borderless;
uint32_t resizeSerial;
}
LibDecorState;
@@ -147,6 +148,10 @@ static bool libdecor_shellInit(
if (maximize)
libdecor_frame_set_maximized(state.libdecorFrame);
if (borderless)
libdecor_frame_set_visibility(state.libdecorFrame, false);
state.borderless = borderless;
if (resizable)
libdecor_frame_set_capabilities(state.libdecorFrame,
LIBDECOR_ACTION_RESIZE);
@@ -183,7 +188,8 @@ static void libdecor_setFullscreen(bool fs)
else
libdecor_frame_unset_fullscreen(state.libdecorFrame);
libdecor_frame_set_visibility(state.libdecorFrame, !fs);
if (!state.borderless)
libdecor_frame_set_visibility(state.libdecorFrame, !fs);
}
static bool libdecor_getFullscreen(void)

View File

@@ -49,6 +49,7 @@ typedef struct XDGState
uint32_t resizeSerial;
bool fullscreen;
bool floating;
bool resizable;
int displayFd;
}
XDGState;
@@ -155,6 +156,13 @@ bool xdg_shellInit(struct wl_display * display, struct wl_surface * surface,
if (maximize)
xdg_toplevel_set_maximized(state.toplevel);
if (!resizable)
{
xdg_toplevel_set_min_size(state.toplevel, state.width, state.height);
xdg_toplevel_set_max_size(state.toplevel, state.width, state.height);
}
state.resizable = resizable;
if (state.decorationManager)
{
state.toplevelDecoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
@@ -200,7 +208,7 @@ static void xdg_minimize(void)
static void xdg_shellResize(int w, int h)
{
if (!state.floating)
if (!state.floating || !state.resizable)
return;
state.width = w;

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

@@ -479,6 +479,13 @@ bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
EGL_Texture * texture = egl_postProcessGetOutput(desktop->pp,
&finalSizeX, &finalSizeY);
if (unlikely(!texture))
{
texture = tex;
finalSizeX = width;
finalSizeY = height;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
egl_resetViewport(desktop->egl);

View File

@@ -2,6 +2,7 @@
#extension GL_OES_EGL_image_external_essl3 : enable
precision highp float;
precision highp int;
in vec2 fragCoord;
out vec4 fragColor;

View File

@@ -1,5 +1,6 @@
#version 300 es
precision highp float;
precision highp int;
in vec2 uv;
out vec4 color;

View File

@@ -1,5 +1,6 @@
#version 300 es
precision highp float;
precision highp int;
#include "color_blind.h"

View File

@@ -1,5 +1,6 @@
#version 300 es
precision highp float;
precision highp int;
out vec4 color;

View File

@@ -2,6 +2,7 @@
#extension GL_OES_EGL_image_external_essl3 : enable
precision highp float;
precision highp int;
#define EGL_SCALE_AUTO 0
#define EGL_SCALE_NEAREST 1

View File

@@ -2,6 +2,7 @@
#extension GL_OES_EGL_image_external_essl3 : enable
precision highp float;
precision highp int;
in vec2 fragCoord;
out vec4 fragColor;

View File

@@ -2,6 +2,7 @@
#extension GL_OES_EGL_image_external_essl3 : enable
precision highp float;
precision highp int;
#define PI 3.141592653589793

View File

@@ -2,6 +2,7 @@
#extension GL_OES_EGL_image_external_essl3 : enable
precision highp float;
precision highp int;
in vec2 fragCoord;
out vec4 fragColor;

View File

@@ -2,6 +2,7 @@
#extension GL_OES_EGL_image_external_essl3 : enable
precision highp float;
precision highp int;
#include "compat.h"

View File

@@ -2,6 +2,7 @@
#extension GL_OES_EGL_image_external_essl3 : enable
precision highp float;
precision highp int;
#include "compat.h"

View File

@@ -1,5 +1,6 @@
#version 300 es
precision highp float;
precision highp int;
#include "compat.h"

View File

@@ -255,7 +255,6 @@ EGL_TexStatus egl_texBufferStreamProcess(EGL_Texture * texture)
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
this->sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
}
return EGL_TEX_STATUS_OK;
@@ -271,7 +270,8 @@ EGL_TexStatus egl_texBufferStreamGet(EGL_Texture * texture, GLuint * tex,
if (this->sync)
{
switch(glClientWaitSync(this->sync, 0, 40000000)) // 40ms
switch(glClientWaitSync(
this->sync, GL_SYNC_FLUSH_COMMANDS_BIT, 40000000)) //40ms
{
case GL_ALREADY_SIGNALED:
case GL_CONDITION_SATISFIED:

View File

@@ -22,7 +22,7 @@
#include "texture_buffer.h"
#include "util.h"
#include "common/vector.h"
#include "common/array.h"
#include "egl_dynprocs.h"
#include "egldebug.h"
@@ -30,6 +30,8 @@ struct FdImage
{
int fd;
EGLImage image;
GLsync sync;
int texIndex;
};
typedef struct TexDMABUF
@@ -37,7 +39,9 @@ typedef struct TexDMABUF
TextureBuffer base;
EGLDisplay display;
Vector images;
struct FdImage images[2];
int lastIndex;
EGL_PixelFormat pixFmt;
unsigned fourcc;
@@ -59,21 +63,26 @@ static void egl_texDMABUFCleanup(EGL_Texture * texture)
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
TexDMABUF * this = UPCAST(TexDMABUF , parent);
struct FdImage * image;
vector_forEachRef(image, &this->images)
g_egl_dynProcs.eglDestroyImage(this->display, image->image);
vector_clear(&this->images);
for(int i = 0; i < ARRAY_LENGTH(this->images); ++i)
{
if (this->images[i].image != EGL_NO_IMAGE)
{
g_egl_dynProcs.eglDestroyImage(this->display, this->images[i].image);
this->images[i].image = EGL_NO_IMAGE;
}
if (this->images[i].sync)
{
glDeleteSync(this->images[i].sync);
this->images[i].sync = 0;
}
this->images[i].fd = -1;
this->images[i].texIndex = -1;
}
egl_texUtilFreeBuffers(parent->buf, parent->texCount);
if (parent->tex[0])
glDeleteTextures(parent->texCount, parent->tex);
if (parent->sync)
{
glDeleteSync(parent->sync);
parent->sync = 0;
}
}
// dmabuf functions
@@ -84,17 +93,18 @@ static bool egl_texDMABUFInit(EGL_Texture ** texture, EGL_TexType type,
TexDMABUF * this = calloc(1, sizeof(*this));
*texture = &this->base.base;
if (!vector_create(&this->images, sizeof(struct FdImage), 2))
for(int i = 0; i < ARRAY_LENGTH(this->images); ++i)
{
free(this);
*texture = NULL;
return false;
this->images[i].fd = -1;
this->images[i].image = EGL_NO_IMAGE;
this->images[i].sync = 0;
this->images[i].texIndex = -1;
}
this->lastIndex = -1;
EGL_Texture * parent = &this->base.base;
if (!egl_texBufferStreamInit(&parent, type, display))
{
vector_destroy(&this->images);
free(this);
*texture = NULL;
return false;
@@ -119,8 +129,6 @@ static void egl_texDMABUFFree(EGL_Texture * texture)
TexDMABUF * this = UPCAST(TexDMABUF , parent);
egl_texDMABUFCleanup(texture);
vector_destroy(&this->images);
egl_texBufferFree(&parent->base);
free(this);
}
@@ -141,22 +149,8 @@ static bool texDMABUFSetup(EGL_Texture * texture)
egl_texDMABUFCleanup(texture);
glGenTextures(parent->texCount, parent->tex);
for(int i = 0; i < parent->texCount; ++i)
{
glBindTexture(GL_TEXTURE_EXTERNAL_OES, parent->tex[i]);
glTexImage2D(GL_TEXTURE_EXTERNAL_OES,
0,
texture->format.intFormat,
this->width,
texture->format.height,
0,
this->format,
texture->format.dataType,
NULL);
}
glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
parent->rIndex = -1;
return true;
}
@@ -212,16 +206,12 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture,
DEBUG_ASSERT(update->type == EGL_TEXTYPE_DMABUF);
EGLImage image = EGL_NO_IMAGE;
struct FdImage * fdImage;
vector_forEachRef(fdImage, &this->images)
if (fdImage->fd == update->dmaFD)
{
image = fdImage->image;
break;
}
struct FdImage *fdImage =
(this->images[0].fd == update->dmaFD) ? &this->images[0] :
(this->images[1].fd == update->dmaFD) ? &this->images[1] :
(this->images[0].fd == -1) ? &this->images[0] :
&this->images[1];
EGLImage image = fdImage->image;
if (unlikely(image == EGL_NO_IMAGE))
{
bool setup = false;
@@ -248,28 +238,31 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture,
return false;
}
if (unlikely(!vector_push(&this->images, &(struct FdImage) {
.fd = update->dmaFD,
.image = image,
})))
fdImage->fd = update->dmaFD;
fdImage->image = image;
int slot = (fdImage == &this->images[0]) ? 0 : 1;
fdImage->texIndex = slot;
INTERLOCKED_SECTION(parent->copyLock,
{
DEBUG_ERROR("Failed to store EGLImage");
g_egl_dynProcs.eglDestroyImage(this->display, image);
return false;
}
glBindTexture(GL_TEXTURE_EXTERNAL_OES, parent->tex[slot]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
g_egl_dynProcs.glEGLImageTargetTexture2DOES(
GL_TEXTURE_EXTERNAL_OES, image);
});
}
this->lastIndex = (fdImage == &this->images[0]) ? 0 : 1;
INTERLOCKED_SECTION(parent->copyLock,
{
glBindTexture(GL_TEXTURE_EXTERNAL_OES, parent->tex[parent->bufIndex]);
g_egl_dynProcs.glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
if (likely(parent->sync))
glDeleteSync(parent->sync);
parent->sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
if (fdImage->sync)
glDeleteSync(fdImage->sync);
fdImage->sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
});
glFlush();
return true;
}
@@ -284,23 +277,22 @@ static EGL_TexStatus egl_texDMABUFGet(EGL_Texture * texture, GLuint * tex,
TextureBuffer * parent = UPCAST(TextureBuffer, texture);
TexDMABUF * this = UPCAST(TexDMABUF , parent);
if (unlikely(this->lastIndex < 0))
return EGL_TEX_STATUS_NOTREADY;
struct FdImage *cur = &this->images[this->lastIndex];
GLsync sync = 0;
INTERLOCKED_SECTION(parent->copyLock,
{
if (parent->sync)
{
sync = parent->sync;
parent->sync = 0;
parent->rIndex = parent->bufIndex;
if (++parent->bufIndex == parent->texCount)
parent->bufIndex = 0;
INTERLOCKED_SECTION(parent->copyLock, {
if (cur->sync) {
sync = cur->sync;
cur->sync = 0;
}
});
if (sync)
{
switch(glClientWaitSync(sync, 0, 20000000)) // 20ms
switch (glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 20000000)) //20ms
{
case GL_ALREADY_SIGNALED:
case GL_CONDITION_SATISFIED:
@@ -308,10 +300,11 @@ static EGL_TexStatus egl_texDMABUFGet(EGL_Texture * texture, GLuint * tex,
break;
case GL_TIMEOUT_EXPIRED:
// Put it back for next try
INTERLOCKED_SECTION(parent->copyLock,
{
if (!parent->sync)
parent->sync = sync;
if (!cur->sync)
cur->sync = sync;
else
glDeleteSync(sync);
});
@@ -325,7 +318,7 @@ static EGL_TexStatus egl_texDMABUFGet(EGL_Texture * texture, GLuint * tex,
}
}
*tex = parent->tex[parent->rIndex];
*tex = parent->tex[cur->texIndex];
if (fmt)
*fmt = this->pixFmt;

View File

@@ -205,6 +205,9 @@ bool opengl_create(LG_Renderer ** renderer, const LG_RendererParams params,
this->opt.preventBuffer = option_get_bool("opengl", "preventBuffer");
this->opt.amdPinnedMem = option_get_bool("opengl", "amdPinnedMem" );
this->scaleX = 1.0f;
this->scaleY = 1.0f;
LG_LOCK_INIT(this->formatLock);
LG_LOCK_INIT(this->frameLock );
LG_LOCK_INIT(this->mouseLock );

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"
" resolution (if supported by the guest, currently LG IDD only)",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
},
{
.module = "win",
.name = "fpsMin",
@@ -309,6 +317,13 @@ static struct Option options[] =
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
{
.module = "win",
.name = "disableWaitingMessage",
.description = "Disables the confirmation message for a cleaner UI",
.type = OPTION_TYPE_BOOL,
.value.x_bool = false
},
// input options
{
@@ -651,33 +666,34 @@ bool config_load(int argc, char * argv[])
}
// setup the application params for the basic types
g_params.cursorPollInterval = option_get_int ("app" , "cursorPollInterval");
g_params.framePollInterval = option_get_int ("app" , "framePollInterval" );
g_params.allowDMA = option_get_bool ("app" , "allowDMA" );
g_params.cursorPollInterval = option_get_int ("app" , "cursorPollInterval");
g_params.framePollInterval = option_get_int ("app" , "framePollInterval" );
g_params.allowDMA = option_get_bool ("app" , "allowDMA" );
g_params.windowTitle = option_get_string("win", "title" );
g_params.appId = option_get_string("win", "appId" );
g_params.autoResize = option_get_bool ("win", "autoResize" );
g_params.allowResize = option_get_bool ("win", "allowResize" );
g_params.keepAspect = option_get_bool ("win", "keepAspect" );
g_params.forceAspect = option_get_bool ("win", "forceAspect" );
g_params.dontUpscale = option_get_bool ("win", "dontUpscale" );
g_params.intUpscale = option_get_bool ("win", "intUpscale" );
g_params.shrinkOnUpscale = option_get_bool ("win", "shrinkOnUpscale" );
g_params.borderless = option_get_bool ("win", "borderless" );
g_params.fullscreen = option_get_bool ("win", "fullScreen" );
g_params.maximize = option_get_bool ("win", "maximize" );
g_params.fpsMin = option_get_int ("win", "fpsMin" );
g_params.ignoreQuit = option_get_bool ("win", "ignoreQuit" );
g_params.noScreensaver = option_get_bool ("win", "noScreensaver" );
g_params.autoScreensaver = option_get_bool ("win", "autoScreensaver" );
g_params.showAlerts = option_get_bool ("win", "alerts" );
g_params.quickSplash = option_get_bool ("win", "quickSplash" );
g_params.overlayDim = option_get_bool ("win", "overlayDimsDesktop");
g_params.uiFont = option_get_string("win", "uiFont" );
g_params.uiSize = option_get_int ("win", "uiSize" );
g_params.jitRender = option_get_bool ("win", "jitRender" );
g_params.requestActivation = option_get_bool ("win", "requestActivation" );
g_params.windowTitle = option_get_string("win", "title" );
g_params.appId = option_get_string("win", "appId" );
g_params.autoResize = option_get_bool ("win", "autoResize" );
g_params.allowResize = option_get_bool ("win", "allowResize" );
g_params.keepAspect = option_get_bool ("win", "keepAspect" );
g_params.forceAspect = option_get_bool ("win", "forceAspect" );
g_params.dontUpscale = option_get_bool ("win", "dontUpscale" );
g_params.intUpscale = option_get_bool ("win", "intUpscale" );
g_params.shrinkOnUpscale = option_get_bool ("win", "shrinkOnUpscale" );
g_params.borderless = option_get_bool ("win", "borderless" );
g_params.fullscreen = option_get_bool ("win", "fullScreen" );
g_params.maximize = option_get_bool ("win", "maximize" );
g_params.fpsMin = option_get_int ("win", "fpsMin" );
g_params.ignoreQuit = option_get_bool ("win", "ignoreQuit" );
g_params.noScreensaver = option_get_bool ("win", "noScreensaver" );
g_params.autoScreensaver = option_get_bool ("win", "autoScreensaver" );
g_params.showAlerts = option_get_bool ("win", "alerts" );
g_params.quickSplash = option_get_bool ("win", "quickSplash" );
g_params.overlayDim = option_get_bool ("win", "overlayDimsDesktop");
g_params.uiFont = option_get_string("win", "uiFont" );
g_params.uiSize = option_get_int ("win", "uiSize" );
g_params.jitRender = option_get_bool ("win", "jitRender" );
g_params.requestActivation = option_get_bool ("win", "requestActivation" );
g_params.disableWaitingMessage = option_get_bool ("win", "disableWaitingMessage");
if (g_params.noScreensaver && g_params.autoScreensaver)
{
@@ -717,6 +733,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;
@@ -434,7 +479,19 @@ void core_handleMouseNormal(double ex, double ey)
{
// prevent cursor handling outside of capture if the position is not known
if (!g_cursor.guest.valid)
{
if (app_guestIsWindows())
{
// wiggle the mouse when the guest has not provided any information, we need
// to do this because windows doesn't enable a cursor at all until it has
// been moved for the first time.
if (!purespice_mouseMotion(1, 1))
DEBUG_ERROR("failed to send mouse motion message");
if (!purespice_mouseMotion(-1, -1))
DEBUG_ERROR("failed to send mouse motion message");
}
return;
}
if (g_cursor.realigning)
return;

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);
@@ -1495,18 +1500,21 @@ restart:
if (waitCount == 30)
{
DEBUG_BREAK();
msgs[msgsCount++] = app_msgBox(
"Host Application Not Running",
"It seems the host application is not running or your\n"
"virtual machine is still starting up\n"
"\n"
"If the the VM is running and booted please check the\n"
"host application log for errors. You can find the\n"
"log through the shortcut in your start menu\n"
"\n"
"Continuing to wait...");
if (!g_params.disableWaitingMessage)
{
msgs[msgsCount++] = app_msgBox(
"Host Application Not Running",
"It seems the host application is not running or your\n"
"virtual machine is still starting up\n"
"\n"
"If the the VM is running and booted please check the\n"
"host application log for errors. You can find the\n"
"log through the shortcut in your start menu\n"
"\n"
"Continuing to wait...");
msgs[msgsCount++] = showSpiceInputHelp();
msgs[msgsCount++] = showSpiceInputHelp();
}
DEBUG_INFO("Check the host log in your guest at %%ProgramData%%\\Looking Glass (host)\\looking-glass-host.txt");
DEBUG_INFO("Continuing to wait...");
@@ -1752,6 +1760,7 @@ restart:
g_state.state = APP_STATE_RESTART;
break;
}
lgMessage_process();
g_state.ds->wait(100);
}
@@ -1868,6 +1877,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 +1903,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;
@@ -202,6 +204,7 @@ struct AppParams
int uiSize;
bool jitRender;
bool requestActivation;
bool disableWaitingMessage;
unsigned int cursorPollInterval;
unsigned int framePollInterval;

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

@@ -29,7 +29,15 @@
#define NANOSVG_IMPLEMENTATION
#define NANOSVG_ALL_COLOR_KEYWORDS
#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvgrast.h"
#ifndef __clang__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#include "nanosvgrast.h"
#pragma GCC diagnostic pop
#else
#include "nanosvgrast.h"
#endif
void overlayGetImGuiRect(struct Rect * rect)
{

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

@@ -36,7 +36,7 @@
#define LGMP_Q_FRAME 2
#define LGMP_Q_FRAME_LEN 2
#define LGMP_Q_POINTER_LEN 20
#define LGMP_Q_POINTER_LEN 32
#ifdef _MSC_VER
@@ -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

@@ -81,7 +81,8 @@ bool ivshmemInit(struct IVSHMEM * dev)
SP_DEVICE_INTERFACE_DATA devInterfaceData = {0};
Vector devices;
devInfoSet = SetupDiGetClassDevs(&GUID_DEVINTERFACE_IVSHMEM, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
devInfoSet = SetupDiGetClassDevs(&GUID_DEVINTERFACE_IVSHMEM, NULL, NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
@@ -96,17 +97,19 @@ bool ivshmemInit(struct IVSHMEM * dev)
struct IVSHMEMData * device = vector_push(&devices, NULL);
DWORD bus, addr;
if (!SetupDiGetDeviceRegistryProperty(devInfoSet, &devInfoData, SPDRP_BUSNUMBER,
NULL, (void*) &bus, sizeof(bus), NULL))
if (!SetupDiGetDeviceRegistryProperty(devInfoSet, &devInfoData,
SPDRP_BUSNUMBER, NULL, (void*) &bus, sizeof(bus), NULL))
{
DEBUG_WINERROR("Failed to SetupDiGetDeviceRegistryProperty", GetLastError());
DEBUG_WINERROR("Failed to SetupDiGetDeviceRegistryProperty",
GetLastError());
bus = 0xFFFF;
}
if (!SetupDiGetDeviceRegistryProperty(devInfoSet, &devInfoData, SPDRP_ADDRESS,
NULL, (void*) &addr, sizeof(addr), NULL))
if (!SetupDiGetDeviceRegistryProperty(devInfoSet, &devInfoData,
SPDRP_ADDRESS, NULL, (void*) &addr, sizeof(addr), NULL))
{
DEBUG_WINERROR("Failed to SetupDiGetDeviceRegistryProperty", GetLastError());
DEBUG_WINERROR("Failed to SetupDiGetDeviceRegistryProperty",
GetLastError());
addr = 0xFFFFFFFF;
}
@@ -121,22 +124,37 @@ bool ivshmemInit(struct IVSHMEM * dev)
return false;
}
if (vector_size(&devices) == 0)
{
vector_destroy(&devices);
DEBUG_ERROR("Failed to find any IVSHMEM devices, unable to continue");
DEBUG_ERROR("Did you remember to add the device to your VM "
"and is the driver installed?");
return false;
}
const int shmDevice = option_get_int("os", "shmDevice");
qsort(vector_data(&devices), vector_size(&devices), sizeof(struct IVSHMEMData), ivshmemComparator);
qsort(vector_data(&devices), vector_size(&devices), sizeof(struct IVSHMEMData),
ivshmemComparator);
struct IVSHMEMData * device;
vector_forEachRefIdx(i, device, &devices)
{
DWORD bus = device->busAddr >> 32;
DWORD bus = device->busAddr >> 32;
DWORD addr = device->busAddr & 0xFFFFFFFF;
DEBUG_INFO("IVSHMEM %" PRIuPTR "%c on bus 0x%lx, device 0x%lx, function 0x%lx", i,
i == shmDevice ? '*' : ' ', bus, addr >> 16, addr & 0xFFFF);
DEBUG_INFO(
"IVSHMEM %" PRIuPTR "%c on bus 0x%lx, device 0x%lx, function 0x%lx",
i,
i == shmDevice ? '*' : ' ',
bus,
addr >> 16,
addr & 0xFFFF);
}
if (!device)
if (shmDevice >= vector_size(&devices))
{
vector_destroy(&devices);
DEBUG_ERROR("Unable to find a IVSHMEM device");
DEBUG_ERROR("os:shmDevice %d does not exist", shmDevice);
return false;
}
@@ -144,14 +162,16 @@ bool ivshmemInit(struct IVSHMEM * dev)
memcpy(&devInfoData, &device->devInfoData, sizeof(SP_DEVINFO_DATA));
vector_destroy(&devices);
if (SetupDiEnumDeviceInterfaces(devInfoSet, &devInfoData, &GUID_DEVINTERFACE_IVSHMEM, 0, &devInterfaceData) == FALSE)
if (SetupDiEnumDeviceInterfaces(devInfoSet, &devInfoData,
&GUID_DEVINTERFACE_IVSHMEM, 0, &devInterfaceData) == FALSE)
{
DEBUG_WINERROR("SetupDiEnumDeviceInterfaces failed", GetLastError());
return false;
}
DWORD reqSize = 0;
SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterfaceData, NULL, 0, &reqSize, NULL);
SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterfaceData,
NULL, 0, &reqSize, NULL);
if (!reqSize)
{
DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", GetLastError());
@@ -160,7 +180,8 @@ bool ivshmemInit(struct IVSHMEM * dev)
infData = calloc(1, reqSize);
infData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
if (!SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterfaceData, infData, reqSize, NULL, NULL))
if (!SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterfaceData, infData,
reqSize, NULL, NULL))
{
free(infData);
DEBUG_WINERROR("SetupDiGetDeviceInterfaceDetail", GetLastError());
@@ -195,7 +216,8 @@ bool ivshmemOpen(struct IVSHMEM * dev)
struct IVSHMEMInfo * info = (struct IVSHMEMInfo *)dev->opaque;
IVSHMEM_SIZE size;
if (!DeviceIoControl(info->handle, IOCTL_IVSHMEM_REQUEST_SIZE, NULL, 0, &size, sizeof(IVSHMEM_SIZE), NULL, NULL))
if (!DeviceIoControl(info->handle, IOCTL_IVSHMEM_REQUEST_SIZE, NULL, 0, &size,
sizeof(IVSHMEM_SIZE), NULL, NULL))
{
DEBUG_WINERROR("DeviceIoControl Failed", GetLastError());
return false;
@@ -225,7 +247,8 @@ void ivshmemClose(struct IVSHMEM * dev)
struct IVSHMEMInfo * info = (struct IVSHMEMInfo *)dev->opaque;
if (!DeviceIoControl(info->handle, IOCTL_IVSHMEM_RELEASE_MMAP, NULL, 0, NULL, 0, NULL, NULL))
if (!DeviceIoControl(info->handle, IOCTL_IVSHMEM_RELEASE_MMAP, NULL, 0, NULL,
0, NULL, NULL))
DEBUG_WINERROR("DeviceIoControl failed", GetLastError());
dev->size = 0;

View File

@@ -120,12 +120,17 @@ change its ownership manually, i.e.:
sudo chown user:kvm /dev/kvmfr0
As an example, you can create a new file in ``/etc/udev/rules.d/99-kvmfr.rules``
(replace ``user`` with your username)
As an example, you can create a new file in ``/etc/udev/rules.d/70-kvmfr.rules``
with the following contents::
SUBSYSTEM=="kvmfr", OWNER="user", GROUP="kvm", MODE="0660"
SUBSYSTEM=="kvmfr", GROUP="kvm", MODE="0660", TAG+="uaccess"
(replace ``user`` with your username)
.. note::
Make sure the udev rule file name ordinal value is below (lexically sorts before) ``73-seat-late.rules``
to allow the uaccess tag to be processed properly.
.. _ivshmem_kvmfr_libvirt:

View File

@@ -224,7 +224,7 @@ All command line options
+========================+=======+=============+=========================================================================================+
| app:configFile | -C | NULL | A file to read additional configuration from |
+------------------------+-------+-------------+-----------------------------------------------------------------------------------------+
| app:renderer | -g | EGL | Specify the renderer to use |
| app:renderer | -g | auto | Specify the renderer to use |
+------------------------+-------+-------------+-----------------------------------------------------------------------------------------+
| app:license | -l | no | Show the license for this application and then terminate |
+------------------------+-------+-------------+-----------------------------------------------------------------------------------------+
@@ -237,139 +237,149 @@ All command line options
| app:shmFile | -f | /dev/kvmfr0 | The path to the shared memory file, or the name of the kvmfr device to use, e.g. kvmfr0 |
+------------------------+-------+-------------+-----------------------------------------------------------------------------------------+
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| Long | Short | Value | Description |
+=========================+=======+========================+======================================================================+
| win:title | | Looking Glass (client) | The window title |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:position | | center | Initial window position at startup |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:size | | 1024x768 | Initial window size at startup |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:autoResize | -a | no | Auto resize the window to the guest |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:allowResize | -n | yes | Allow the window to be manually resized |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:keepAspect | -r | yes | Maintain the correct aspect ratio |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:forceAspect | | yes | Force the window to maintain the aspect ratio |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:dontUpscale | | no | Never try to upscale the window |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:intUpscale | | no | Allow only integer upscaling |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:shrinkOnUpscale | | no | Limit the window dimensions when dontUpscale is enabled |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:borderless | -d | no | Borderless mode |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:fullScreen | -F | no | Launch in fullscreen borderless mode |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:maximize | -T | no | Launch window maximized |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:minimizeOnFocusLoss | | no | Minimize window on focus loss |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:fpsMin | -K | -1 | Frame rate minimum (0 = disable - not recommended, -1 = auto detect) |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:ignoreQuit | -Q | no | Ignore requests to quit (i.e. Alt+F4) |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:noScreensaver | -S | yes | Prevent the screensaver from starting |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:autoScreensaver | | no | Prevent the screensaver from starting when guest requests it |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:alerts | -q | yes | Show on screen alert messages |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:quickSplash | | no | Skip fading out the splash screen when a connection is established |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:overlayDimsDesktop | | no | Dim the desktop when in interactive overlay mode |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:rotate | | 0 | Rotate the displayed image (0, 90, 180, 270) |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:uiFont | | DejaVu Sans Mono | The font to use when rendering on-screen UI |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:uiSize | | 14 | The font size to use when rendering on-screen UI |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:jitRender | | no | Enable just-in-time rendering |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:requestActivation | | yes | Request activation when attention is needed |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
| win:showFPS | -k | no | Enable the FPS & UPS display |
+-------------------------+-------+------------------------+----------------------------------------------------------------------+
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| Long | Short | Value | Description |
+===========================+=======+========================+=================================================================================================================+
| win:title | | Looking Glass (client) | The window title |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:appId | | looking-glass-client | The application Id |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:position | | center | Initial window position at startup |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:size | | 1024x768 | Initial window size at startup |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:autoResize | -a | no | Auto resize the window to the guest |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:allowResize | -n | yes | Allow the window to be manually resized |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:keepAspect | -r | yes | Maintain the correct aspect ratio |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:forceAspect | | yes | Force the window to maintain the aspect ratio |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:dontUpscale | | no | Never try to upscale the window |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:intUpscale | | no | Allow only integer upscaling |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:shrinkOnUpscale | | no | Limit the window dimensions when dontUpscale is enabled |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:borderless | -d | no | Borderless mode |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:fullScreen | -F | no | Launch in fullscreen borderless mode |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:maximize | -T | no | Launch window maximized |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:minimizeOnFocusLoss | | no | Minimize window on focus loss |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:setGuestRes | | yes | On window size change, request the guest to match resolution (if supported by the guest, currently LG IDD only) |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:fpsMin | -K | -1 | Frame rate minimum (0 = disable - not recommended, -1 = auto detect) |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:ignoreQuit | -Q | no | Ignore requests to quit (i.e. Alt+F4) |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:noScreensaver | -S | no | Prevent the screensaver from starting |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:autoScreensaver | | no | Prevent the screensaver from starting when guest requests it |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:alerts | -q | yes | Show on screen alert messages |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:quickSplash | | no | Skip fading out the splash screen when a connection is established |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:overlayDimsDesktop | | yes | Dim the desktop when in interactive overlay mode |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:rotate | | 0 | Rotate the displayed image (0, 90, 180, 270) |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:uiFont | | DejaVu Sans Mono | The font to use when rendering on-screen UI |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:uiSize | | 14 | The font size to use when rendering on-screen UI |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:jitRender | | no | Enable just-in-time rendering |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:requestActivation | | yes | Request activation when attention is needed |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:disableWaitingMessage | | no | Disables the confirmation message for a cleaner UI |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
| win:showFPS | -k | no | Enable the FPS & UPS display |
+---------------------------+-------+------------------------+-----------------------------------------------------------------------------------------------------------------+
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| Long | Short | Value | Description |
+==============================+=======+=====================+==================================================================================+
| input:captureOnFocus | | no | Enable capture mode when the window becomes focused |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:grabKeyboard | -G | yes | Grab the keyboard in capture mode |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:grabKeyboardOnFocus | | no | Grab the keyboard when focused |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:releaseKeysOnFocusLoss | | yes | On focus loss, send key up events to guest for all held keys |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:escapeKey | -m | 70 = KEY_SCROLLLOCK | Specify the escape/menu key to use (use "help" to see valid values) |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:ignoreWindowsKeys | | no | Do not pass events for the windows keys to the guest |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:hideCursor | -M | yes | Hide the local mouse cursor |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:mouseSens | | 0 | Initial mouse sensitivity when in capture mode (-9 to 9) |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:mouseSmoothing | | yes | Apply simple mouse smoothing when rawMouse is not in use (helps reduce aliasing) |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:rawMouse | | yes | Use RAW mouse input when in capture mode (good for gaming) |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:mouseRedraw | | yes | Mouse movements trigger redraws (ignores FPS minimum) |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:autoCapture | | no | Try to keep the mouse captured when needed |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:captureOnly | | no | Only enable input via SPICE if in capture mode |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
| input:helpMenuDelay | | 200 | Show help menu after holding down the escape key for this many milliseconds |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------+
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| Long | Short | Value | Description |
+==============================+=======+=====================+==========================================================================================================+
| input:captureOnFocus | | no | Enable capture mode when the window becomes focused |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| input:grabKeyboard | -G | yes | Grab the keyboard in capture mode |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| input:grabKeyboardOnFocus | | no | Grab the keyboard when focused |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| input:releaseKeysOnFocusLoss | | yes | On focus loss, send key up events to guest for all held keys |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| input:escapeKey | -m | 70 = KEY_SCROLLLOCK | Specify the escape/menu key to use (use "help" to see valid values) |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| input:ignoreWindowsKeys | | no | Do not pass events for the windows keys to the guest |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| input:hideCursor | -M | yes | Hide the local mouse cursor |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| input:mouseSens | | 0 | Initial mouse sensitivity when in capture mode (-9 to 9) |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| input:mouseSmoothing | | yes | Apply simple mouse smoothing when rawMouse is not in use (helps reduce aliasing) |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| input:rawMouse | | no | Use RAW mouse input when in capture mode (good for gaming) |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| input:mouseRedraw | | yes | Mouse movements trigger redraws (ignores FPS minimum) |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| input:autoCapture | | no | Try to keep the mouse captured when needed |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| input:captureOnly | | no | Only enable input via SPICE if in capture mode |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| input:helpMenuDelay | | 200 | Show help menu after holding down the escape key for this many milliseconds |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| input:evdev | | NULL | csv list of evdev input devices to use for capture mode (ie: /dev/input/by-id/usb-some_device-event-kbd) |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
| input:evdevExclusive | | yes | Only use evdev devices for input when in capture mode |
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
+------------------------+-------+-----------------------------------+---------------------------------------------------------------------+
| Long | Short | Value | Description |
+========================+=======+===================================+=====================================================================+
| spice:enable | -s | yes | Enable the built in SPICE client for input and/or clipboard support |
+------------------------+-------+-----------------------------------+---------------------------------------------------------------------+
| spice:host | -c | /opt/PVM/vms/Windows/windows.sock | The SPICE server host or UNIX socket |
+------------------------+-------+-----------------------------------+---------------------------------------------------------------------+
| spice:port | -p | 0 | The SPICE server port (0 = unix socket) |
+------------------------+-------+-----------------------------------+---------------------------------------------------------------------+
| spice:input | | yes | Use SPICE to send keyboard and mouse input events to the guest |
+------------------------+-------+-----------------------------------+---------------------------------------------------------------------+
| spice:clipboard | | yes | Use SPICE to synchronize the clipboard contents with the guest |
+------------------------+-------+-----------------------------------+---------------------------------------------------------------------+
| spice:clipboardToVM | | yes | Allow the clipboard to be synchronized TO the VM |
+------------------------+-------+-----------------------------------+---------------------------------------------------------------------+
| spice:clipboardToLocal | | yes | Allow the clipboard to be synchronized FROM the VM |
+------------------------+-------+-----------------------------------+---------------------------------------------------------------------+
| spice:audio | | yes | Enable SPICE audio support |
+------------------------+-------+-----------------------------------+---------------------------------------------------------------------+
| spice:scaleCursor | -j | yes | Scale cursor input position to screen size when up/down scaled |
+------------------------+-------+-----------------------------------+---------------------------------------------------------------------+
| spice:captureOnStart | | no | Capture mouse and keyboard on start |
+------------------------+-------+-----------------------------------+---------------------------------------------------------------------+
| spice:alwaysShowCursor | | no | Always show host cursor |
+------------------------+-------+-----------------------------------+---------------------------------------------------------------------+
| spice:showCursorDot | | yes | Use a "dot" cursor when the window does not have focus |
+------------------------+-------+-----------------------------------+---------------------------------------------------------------------+
| spice:largeCursorDot | | yes | Use a larger version of the "dot" cursor |
+------------------------+-------+-----------------------------------+---------------------------------------------------------------------+
+------------------------+-------+-----------+---------------------------------------------------------------------+
| Long | Short | Value | Description |
+========================+=======+===========+=====================================================================+
| spice:enable | -s | yes | Enable the built in SPICE client for input and/or clipboard support |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:host | -c | 127.0.0.1 | The SPICE server host or UNIX socket |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:port | -p | 5900 | The SPICE server port (0 = unix socket) |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:input | | yes | Use SPICE to send keyboard and mouse input events to the guest |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:clipboard | | yes | Use SPICE to synchronize the clipboard contents with the guest |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:clipboardToVM | | yes | Allow the clipboard to be synchronized TO the VM |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:clipboardToLocal | | yes | Allow the clipboard to be synchronized FROM the VM |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:audio | | yes | Enable SPICE audio support |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:scaleCursor | -j | yes | Scale cursor input position to screen size when up/down scaled |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:captureOnStart | | no | Capture mouse and keyboard on start |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:alwaysShowCursor | | no | Always show host cursor |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:showCursorDot | | yes | Use a "dot" cursor when the window does not have focus |
+------------------------+-------+-----------+---------------------------------------------------------------------+
| spice:largeCursorDot | | no | Use a larger version of the "dot" cursor |
+------------------------+-------+-----------+---------------------------------------------------------------------+
+------------------------+-------+-------+-------------------------------------------------------------------------------+
| Long | Short | Value | Description |
+========================+=======+=======+===============================================================================+
| audio:periodSize | | 256 | Requested audio device period size in samples |
+------------------------+-------+-------+-------------------------------------------------------------------------------+
| audio:bufferLatency | | 12 | Additional buffer latency in milliseconds |
+------------------------+-------+-------+-------------------------------------------------------------------------------+
| audio:micDefault | | allow | Default action when an application opens the microphone (prompt, allow, deny) |
+------------------------+-------+-------+-------------------------------------------------------------------------------+
| audio:micShowIndicator | | yes | Display microphone usage indicator |
+------------------------+-------+-------+-------------------------------------------------------------------------------+
| audio:syncVolume | | yes | Synchronize the volume level with the guest |
+------------------------+-------+-------+-------------------------------------------------------------------------------+
+------------------------+-------+--------+-------------------------------------------------------------------------------+
| Long | Short | Value | Description |
+========================+=======+========+===============================================================================+
| audio:periodSize | | 2048 | Requested audio device period size in samples |
+------------------------+-------+--------+-------------------------------------------------------------------------------+
| audio:bufferLatency | | 13 | Additional buffer latency in milliseconds |
+------------------------+-------+--------+-------------------------------------------------------------------------------+
| audio:micDefault | | prompt | Default action when an application opens the microphone (prompt, allow, deny) |
+------------------------+-------+--------+-------------------------------------------------------------------------------+
| audio:micShowIndicator | | yes | Display microphone usage indicator |
+------------------------+-------+--------+-------------------------------------------------------------------------------+
| audio:syncVolume | | yes | Synchronize the volume level with the guest |
+------------------------+-------+--------+-------------------------------------------------------------------------------+
+-------------------+-------+-------+---------------------------------------------------------------------------+
| Long | Short | Value | Description |
@@ -428,16 +438,16 @@ All command line options
+---------------------+-------+-------+----------------------------------------------------------+
| Long | Short | Value | Description |
+=====================+=======+=======+==========================================================+
| i3:globalFullScreen | | yes | Use i3's global full screen feature (spans all monitors) |
| i3:globalFullScreen | | no | Use i3's global full screen feature (spans all monitors) |
+---------------------+-------+-------+----------------------------------------------------------+
+--------------------+-------+---------------+------------------------------------+
| Long | Short | Value | Description |
+====================+=======+===============+====================================+
| pipewire:outDevice | | Looking Glass | The default playback device to use |
+--------------------+-------+---------------+------------------------------------+
| pipewire:recDevice | | PureNoise Mic | The default record device to use |
+--------------------+-------+---------------+------------------------------------+
+--------------------+-------+-------+------------------------------------+
| Long | Short | Value | Description |
+====================+=======+=======+====================================+
| pipewire:outDevice | | NULL | The default playback device to use |
+--------------------+-------+-------+------------------------------------+
| pipewire:recDevice | | NULL | The default record device to use |
+--------------------+-------+-------+------------------------------------+
.. _host_usage:

View File

@@ -20,6 +20,7 @@ downsample
downsampling
downscaler
downscaling
evdev
framebuffer
fullscreen
gcc
@@ -27,10 +28,12 @@ globalFullScreen
gnif
hypervisor
i3
ie
imgui
ini
kvmfr
laggy
lexically
libdecor
libpipewire
libpulse
@@ -73,6 +76,7 @@ Threadripper
toolchain
tritanope
tunable
uaccess
udev
UEFI
uncheck

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;

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

@@ -0,0 +1,313 @@
/**
* 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 <stdio.h>
#include <malloc.h>
#include <strsafe.h>
#include <shlobj.h>
#include "CDebug.h"
CDebug g_debug;
const char *CDebug::s_levelStr[CDebug::LEVEL_MAX] =
{
" ",
"I",
"W",
"E",
"T",
"!",
"F"
};
int vasprintf(char **pstr, const char *fmt, va_list args)
{
int len = _vscprintf(fmt, args);
if (len < 0)
return -1;
char *str = (char *)malloc(len + 1);
if (!str)
return -1;
int r = vsprintf_s(str, len + 1, fmt, args);
if (r < 0)
{
free(str);
return -1;
}
*pstr = str;
return r;
}
int vaswprintf(wchar_t **pstr, const wchar_t *fmt, va_list args)
{
int len = _vscwprintf(fmt, args);
if (len < 0)
return -1;
wchar_t *str = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
if (!str)
return -1;
int r = vswprintf_s(str, len + 1, fmt, args);
if (r < 0)
{
free(str);
return -1;
}
*pstr = str;
return r;
}
int aswprintf(wchar_t **pstr, const wchar_t *fmt, ...) {
va_list ap;
va_start(ap, fmt);
int r = vaswprintf(pstr, fmt, ap);
va_end(ap);
return r;
}
inline static void iso8601(wchar_t *buf, size_t count)
{
struct tm utc;
time_t unix;
time(&unix);
gmtime_s(&utc, &unix);
wcsftime(buf, count, L"%Y-%m-%d %H:%M:%SZ", &utc);
}
inline static std::wstring getLogPath()
{
PWSTR pszPath;
if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, &pszPath)))
{
DEBUG_ERROR("Failed to get ProgramData path");
return L"";
}
std::wstring result(pszPath);
CoTaskMemFree(pszPath);
result += L"\\Looking Glass (IDD)\\";
return result;
}
void CDebug::Init(const wchar_t * name)
{
m_logDir = getLogPath();
// don't redirect the debug output if running under a debugger
if (IsDebuggerPresent())
return;
std::wstring baseName = name;
std::wstring ext = L".txt";
std::wstring logFile = m_logDir + baseName + ext;
//rotate out old logs
DeleteFileW((m_logDir + baseName + L".4" + ext).c_str());
for (int i = 3; i >= 0; --i)
{
std::wstring oldPath;
std::wstring newPath;
if (i == 0)
{
oldPath = logFile;
newPath = m_logDir + baseName + L".1" + ext;
}
else
{
oldPath = m_logDir + baseName + L"." + std::to_wstring(i) + ext;
newPath = m_logDir + baseName + L"." + std::to_wstring(i + 1) + ext;
}
MoveFileW(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;
}
DEBUG_INFO(L"Logging to: %s", logFile.c_str());
m_stream = std::move(stream);
}
void CDebug::LogStr(CDebug::Level level, const char *function, int line, bool wide, const void *str)
{
if (level < 0 || level >= LEVEL_MAX)
level = LEVEL_NONE;
wchar_t timestamp[50];
iso8601(timestamp, ARRAYSIZE(timestamp));
wchar_t *result;
if (aswprintf(&result, wide ? L"[%s] [%S] %40S:%-4d | %s\n" : L"[%s] [%S] %40S:%-4d | %S\n",
timestamp, s_levelStr[level], function, line, str) < 0)
{
Write(L"Out of memory while logging");
return;
}
Write(result);
free(result);
}
void CDebug::Log_va(CDebug::Level level, const char *function, int line, const char *fmt, va_list args)
{
char *result;
if (vasprintf(&result, fmt, args) < 0)
{
Write(L"Out of memory while logging");
return;
}
LogStr(level, function, line, false, result);
free(result);
}
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::Log_va(CDebug::Level level, const char *function, int line, const wchar_t *fmt, va_list args)
{
wchar_t *result;
if (vaswprintf(&result, fmt, args) < 0)
{
Write(L"Out of memory while logging");
return;
}
LogStr(level, function, line, true, result);
free(result);
}
void CDebug::Log(CDebug::Level level, const char *function, int line, const wchar_t *fmt, ...)
{
va_list args;
va_start(args, fmt);
Log_va(level, function, line, fmt, args);
va_end(args);
}
void CDebug::LogStrHR(CDebug::Level level, HRESULT hr, const char *function, int line, bool wide, const void *str)
{
wchar_t *hrBuffer;
if (!FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
hr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&hrBuffer,
1024,
NULL
))
{
DEBUG_ERROR("FormatMessage for 0x%08lX (%u) failed with code 0x%08x", hr, hr, GetLastError());
LogStr(level, function, line, wide, str);
return;
}
// Remove trailing CRLF in hrBuffer
size_t len = wcslen(hrBuffer);
while (len && (hrBuffer[len - 1] == L'\n' || hrBuffer[len - 1] == L'\r'))
hrBuffer[--len] = L'\0';
wchar_t *result;
if (aswprintf(&result, wide ? L"%s (0x%08lX (%u): %s)" : L"%S (0x%08lX (%u): %s)", str, hr, hr, hrBuffer) < 0)
{
Write(L"Out of memory while logging");
return;
}
LogStr(level, function, line, true, result);
free(result);
}
void CDebug::LogHR(CDebug::Level level, HRESULT hr, const char *function, int line, const char *fmt, ...)
{
char *result;
va_list args;
va_start(args, fmt);
if (vasprintf(&result, fmt, args) < 0)
{
va_end(args);
Write(L"Out of memory while logging");
return;
}
va_end(args);
LogStrHR(level, hr, function, line, false, result);
free(result);
}
void CDebug::LogHR(CDebug::Level level, HRESULT hr, const char *function, int line, const wchar_t *fmt, ...)
{
wchar_t *result;
va_list args;
va_start(args, fmt);
if (vaswprintf(&result, fmt, args) < 0)
{
va_end(args);
Write(L"Out of memory while logging");
return;
}
va_end(args);
LogStrHR(level, hr, function, line, true, result);
free(result);
}
void CDebug::Write(const wchar_t *line)
{
if (!m_stream.is_open())
{
OutputDebugStringW(line);
return;
}
DWORD cbRequired = WideCharToMultiByte(CP_UTF8, 0, line, -1, NULL, 0, NULL, NULL);
LPSTR utf8 = (LPSTR) malloc(cbRequired);
if (!utf8)
{
m_stream << "Out of memory while logging" << std::endl;
return;
}
WideCharToMultiByte(CP_UTF8, 0, line, -1, utf8, cbRequired, NULL, NULL);
m_stream << utf8 << std::flush;
free(utf8);
}

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

@@ -0,0 +1,78 @@
/**
* 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;
std::wstring m_logDir;
void Write(const wchar_t *line);
public:
enum Level
{
LEVEL_NONE = 0,
LEVEL_INFO,
LEVEL_WARN,
LEVEL_ERROR,
LEVEL_TRACE,
LEVEL_FIXME,
LEVEL_FATAL,
LEVEL_MAX
};
const wchar_t *logDir() { return m_logDir.c_str(); }
void Init(const wchar_t * name);
void Log_va(CDebug::Level level, const char *function, int line, const wchar_t *fmt, va_list args);
void Log(CDebug::Level level, const char *function, int line, const wchar_t *fmt, ...);
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, ...);
void LogHR(CDebug::Level level, HRESULT hr, const char *function, int line, const wchar_t *fmt, ...);
private:
void LogStr(CDebug::Level level, const char *function, int line, bool wide, const void *str);
void LogStrHR(CDebug::Level level, HRESULT hr, const char *function, int line, bool wide, const void *str);
static const char *s_levelStr[LEVEL_MAX];
};
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,67 +1,73 @@
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
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LGIddInstall", "LGIddInstall\LGIddInstall.vcxproj", "{23EC8370-5F3B-4D18-8C31-E89A154F3666}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|ARM = Release|ARM
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|ARM.ActiveCfg = Debug|ARM
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|ARM.Build.0 = Debug|ARM
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|ARM.Deploy.0 = Debug|ARM
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|ARM64.ActiveCfg = Debug|ARM64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|ARM64.Build.0 = Debug|ARM64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|ARM64.Deploy.0 = Debug|ARM64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|x64.ActiveCfg = Debug|x64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|x64.Build.0 = Debug|x64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|x64.Deploy.0 = Debug|x64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|x86.ActiveCfg = Debug|Win32
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|x86.Build.0 = Debug|Win32
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Debug|x86.Deploy.0 = Debug|Win32
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|ARM.ActiveCfg = Release|ARM
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|ARM.Build.0 = Release|ARM
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|ARM.Deploy.0 = Release|ARM
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|ARM64.ActiveCfg = Release|ARM64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|ARM64.Build.0 = Release|ARM64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|ARM64.Deploy.0 = Release|ARM64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|x64.ActiveCfg = Release|x64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|x64.Build.0 = Release|x64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|x64.Deploy.0 = Release|x64
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|x86.ActiveCfg = Release|Win32
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|x86.Build.0 = Release|Win32
{1CBF3DAA-0726-4F5F-88A2-04D95FB6591A}.Release|x86.Deploy.0 = Release|Win32
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Debug|ARM.ActiveCfg = Debug|Win32
{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
{FFCC376C-4D98-4B50-B431-E1BBC9C67E65}.Release|ARM.ActiveCfg = Release|Win32
{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|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|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
{23EC8370-5F3B-4D18-8C31-E89A154F3666}.Debug|x64.ActiveCfg = Debug|x64
{23EC8370-5F3B-4D18-8C31-E89A154F3666}.Debug|x64.Build.0 = Debug|x64
{23EC8370-5F3B-4D18-8C31-E89A154F3666}.Debug|x86.ActiveCfg = Debug|Win32
{23EC8370-5F3B-4D18-8C31-E89A154F3666}.Debug|x86.Build.0 = Debug|Win32
{23EC8370-5F3B-4D18-8C31-E89A154F3666}.Release|x64.ActiveCfg = Release|x64
{23EC8370-5F3B-4D18-8C31-E89A154F3666}.Release|x64.Build.0 = Release|x64
{23EC8370-5F3B-4D18-8C31-E89A154F3666}.Release|x86.ActiveCfg = Release|Win32
{23EC8370-5F3B-4D18-8C31-E89A154F3666}.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

@@ -21,8 +21,11 @@
#include "CIndirectDeviceContext.h"
#include "CIndirectMonitorContext.h"
#include "CSettings.h"
#include "CPlatformInfo.h"
#include "Debug.h"
#include "CPipeServer.h"
#include "CDebug.h"
#include "VersionInfo.h"
#include <sstream>
@@ -40,31 +43,23 @@ static const struct LGMPQueueConfig POINTER_QUEUE_CONFIG =
1000 //subTimeout
};
CIndirectDeviceContext::~CIndirectDeviceContext()
void CIndirectDeviceContext::PopulateDefaultModes()
{
if (m_lgmp == nullptr)
return;
g_settings.LoadModes();
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);
m_displayModes.clear();
m_displayModes.reserve(g_settings.GetDisplayModes().size());
for (auto& dm : g_settings.GetDisplayModes())
m_displayModes.push_back(dm);
}
void CIndirectDeviceContext::InitAdapter()
{
if (!SetupLGMP())
if (!m_ivshmem.Init() || !m_ivshmem.Open())
return;
PopulateDefaultModes();
IDDCX_ADAPTER_CAPS caps = {};
caps.Size = sizeof(caps);
@@ -82,7 +77,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 +99,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 +128,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;
@@ -158,18 +141,10 @@ void CIndirectDeviceContext::FinishInit(UINT connectorIndex)
info.MonitorType = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_HDMI;
info.ConnectorIndex = connectorIndex;
info.MonitorDescription.Size = sizeof(info.MonitorDescription);
info.MonitorDescription.Type = IDDCX_MONITOR_DESCRIPTION_TYPE_EDID;
if (connectorIndex >= 1)
{
info.MonitorDescription.DataSize = 0;
info.MonitorDescription.pData = nullptr;
}
else
{
info.MonitorDescription.DataSize = sizeof(EDID);
info.MonitorDescription.pData = const_cast<BYTE*>(EDID);
}
info.MonitorDescription.Size = sizeof(info.MonitorDescription);
info.MonitorDescription.Type = IDDCX_MONITOR_DESCRIPTION_TYPE_EDID;
info.MonitorDescription.DataSize = 0;
info.MonitorDescription.pData = nullptr;
CoCreateGuid(&info.MonitorContainerId);
@@ -181,7 +156,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 +166,234 @@ 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_doSetMode)
{
m_doSetMode = false;
g_pipe.SetDisplayMode(m_setMode.width, m_setMode.height); //FIXME: refresh
}
}
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_setMode.width = width;
m_setMode.height = height;
m_setMode.refresh = 120; //FIXME
m_setMode.preferred = true;
g_settings.SetExtraMode(m_setMode);
PopulateDefaultModes();
m_doSetMode = 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 +403,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 +412,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 +433,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 +444,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 +462,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 +482,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 +492,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 +545,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 +553,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 +582,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 +601,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 +618,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 +642,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 +664,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 = pitch / 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 +747,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 +779,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 +807,7 @@ void CIndirectDeviceContext::ResendCursor()
continue;
}
DBGPRINT("lgmpHostQueuePost Failed (Pointer): %s", lgmpStatusString(status));
DEBUG_ERROR("lgmpHostQueuePost Failed (Pointer): %s", lgmpStatusString(status));
break;
}
}

View File

@@ -23,8 +23,10 @@
#include <Windows.h>
#include <wdf.h>
#include <IddCx.h>
#include <vector>
#include "CIVSHMEM.h"
#include "CSettings.h"
extern "C" {
#include "lgmp/host.h"
@@ -34,12 +36,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 +69,71 @@ 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();
CSettings::DisplayModes m_displayModes;
CSettings::DisplayMode m_setMode;
bool m_doSetMode;
public:
CIndirectDeviceContext(_In_ WDFDEVICE wdfDevice) :
m_wdfDevice(wdfDevice) {};
virtual ~CIndirectDeviceContext();
virtual ~CIndirectDeviceContext() { DeInitLGMP(); }
bool SetupLGMP(size_t alignSize);
void PopulateDefaultModes();
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,97 +19,62 @@
*/
#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),
m_devContext(device)
{
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());
delete[] m_shapeBuffer;
UnassignSwapChain();
}
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;
IDARG_IN_SETUP_HWCURSOR c = {};
c.CursorInfo.Size = sizeof(c.CursorInfo);
c.CursorInfo.AlphaCursorSupport = TRUE;
c.CursorInfo.ColorXorCursorSupport = IDDCX_XOR_CURSOR_SUPPORT_FULL;
c.CursorInfo.MaxX = 512;
c.CursorInfo.MaxY = 512;
c.hNewCursorDataAvailable = m_cursorDataEvent.Get();
NTSTATUS status = IddCxMonitorSetupHardwareCursor(m_monitor, &c);
if (!NT_SUCCESS(status))
DBGPRINT("IddCxMonitorSetupHardwareCursor Failed: %08x", status);
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;
}
m_swapChain.reset(new CSwapChainProcessor(m_monitor, m_devContext, swapChain, m_dx11Device, m_dx12Device, newFrameEvent));
}
void CIndirectMonitorContext::UnassignSwapChain()
{
m_swapChain.reset();
}
DWORD CALLBACK CIndirectMonitorContext::_CursorThread(LPVOID arg)
{
reinterpret_cast<CIndirectMonitorContext*>(arg)->CursorThread();
return 0;
}
void CIndirectMonitorContext::CursorThread()
{
HRESULT hr = 0;
for (;;)
{
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)
{
hr = HRESULT_FROM_WIN32(waitResult);
DBGPRINT("WaitForMultipleObjects: %08", hr);
return;
}
IDARG_IN_QUERY_HWCURSOR in = {};
in.LastShapeId = m_lastShapeId;
in.pShapeBuffer = m_shapeBuffer;
in.ShapeBufferSizeInBytes = 512 * 512 * 4;
IDARG_OUT_QUERY_HWCURSOR out = {};
NTSTATUS status = IddCxMonitorQueryHardwareCursor(m_monitor, &in, &out);
if (FAILED(status))
{
DBGPRINT("IddCxMonitorQueryHardwareCursor failed: %08x", status);
return;
}
m_devContext->SendCursor(out, m_shapeBuffer);
}
m_swapChain.reset();
m_dx11Device.reset();
m_dx12Device.reset();
}

View File

@@ -34,19 +34,13 @@ 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;
Wrappers::Event m_terminateEvent;
Wrappers::Event m_cursorDataEvent;
Wrappers::HandleT<Wrappers::HandleTraits::HANDLENullTraits> m_thread;
BYTE * m_shapeBuffer;
DWORD m_lastShapeId = 0;
static DWORD CALLBACK _CursorThread(LPVOID arg);
void CursorThread();
public:
CIndirectMonitorContext(_In_ IDDCX_MONITOR monitor, CIndirectDeviceContext * device);
@@ -55,11 +49,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;
}

216
idd/LGIdd/CSettings.cpp Normal file
View File

@@ -0,0 +1,216 @@
#include "CSettings.h"
#include "CDebug.h"
#include <wdf.h>
CSettings g_settings;
#define LGIDD_REGKEY L"SOFTWARE\\LookingGlass\\IDD"
static const DWORD DefaultDisplayModes[][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}
};
static const DWORD DefaultPreferredDisplayMode = 19;
CSettings::CSettings()
{
}
void CSettings::LoadModes()
{
m_displayModes.clear();
bool hasPreferred = false;
DisplayMode m;
if (GetExtraMode(m))
{
DEBUG_INFO("ExtraMode: %ux%u@%u%s", m.width, m.height, m.refresh, m.preferred ? "*" : "");
m_displayModes.push_back(m);
hasPreferred = m.preferred;
}
std::vector<std::wstring> entries;
if (!ReadModesValue(entries))
{
m_displayModes.reserve(m_displayModes.size() +
ARRAYSIZE(DefaultDisplayModes));
for (int i = 0; i < ARRAYSIZE(DefaultDisplayModes); ++i)
{
m.width = DefaultDisplayModes[i][0];
m.height = DefaultDisplayModes[i][1];
m.refresh = DefaultDisplayModes[i][2];
m.preferred = !hasPreferred && (i == DefaultPreferredDisplayMode);
m_displayModes.push_back(m);
}
return;
}
DEBUG_INFO("Parsed Modes:");
for (const auto& line : entries)
if (ParseModeString(line, m))
{
DEBUG_INFO(" %ux%u@%u%s", m.width, m.height, m.refresh, m.preferred ? "*" : "");
if (hasPreferred)
m.preferred = false;
m_displayModes.push_back(m);
}
}
void CSettings::SetExtraMode(const DisplayMode& mode)
{
WCHAR buf[64];
_snwprintf_s(buf, _countof(buf), _TRUNCATE, L"%ux%u@%u%s",
mode.width, mode.height, mode.refresh,
mode.preferred ? L"*" : L"");
HKEY hKey = NULL;
DWORD disp = 0;
LONG ec = RegCreateKeyExW(
HKEY_LOCAL_MACHINE,
LGIDD_REGKEY,
0, NULL, REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE,
NULL, &hKey, &disp);
if (ec != ERROR_SUCCESS)
{
DEBUG_INFO("Failed to write key");
return;
}
const WCHAR* valueName = L"ExtraMode";
const DWORD cb = (DWORD)((wcslen(buf) + 1) * sizeof(WCHAR));
RegSetValueExW(hKey, valueName, 0, REG_SZ, (const BYTE*)buf, cb);
RegCloseKey(hKey);
}
bool CSettings::GetExtraMode(DisplayMode& mode)
{
HKEY hKey = nullptr;
LONG ec = RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
LGIDD_REGKEY,
0,
KEY_QUERY_VALUE,
&hKey
);
if (ec != ERROR_SUCCESS)
return false;
DWORD type = 0;
DWORD cb = 0;
ec = RegQueryValueExW(hKey, L"ExtraMode", nullptr, &type, nullptr, &cb);
if (ec != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ) || cb == 0)
{
RegCloseKey(hKey);
return false;
}
std::vector<wchar_t> buf(cb / sizeof(wchar_t) + 1);
ec = RegQueryValueExW(hKey, L"ExtraMode", nullptr, &type,
reinterpret_cast<LPBYTE>(buf.data()), &cb);
RegCloseKey(hKey);
if (ec != ERROR_SUCCESS)
return false;
buf.back() = L'\0';
std::wstring s(buf.data());
return ParseModeString(s, mode);
}
bool CSettings::ReadModesValue(std::vector<std::wstring> &out) const
{
HKEY hKey = nullptr;
LONG st = RegOpenKeyExW(HKEY_LOCAL_MACHINE, LGIDD_REGKEY, 0, KEY_QUERY_VALUE, &hKey);
if (st != ERROR_SUCCESS)
return false;
DWORD type = 0, cb = 0;
st = RegGetValueW(hKey, nullptr, L"Modes", RRF_RT_REG_MULTI_SZ, &type, nullptr, &cb);
if (st != ERROR_SUCCESS || cb == 0)
{
RegCloseKey(hKey);
return false;
}
std::vector<wchar_t> buf(cb / sizeof(wchar_t));
st = RegGetValueW(hKey, nullptr, L"Modes", RRF_RT_REG_MULTI_SZ, &type, buf.data(), &cb);
RegCloseKey(hKey);
if (st != ERROR_SUCCESS)
return false;
const wchar_t* p = buf.data();
while (*p)
{
out.emplace_back(p);
p += (wcslen(p) + 1);
}
return !out.empty();
}
static std::wstring trim(const std::wstring &s)
{
size_t b = 0, e = s.size();
while (b < e && iswspace(s[b]))
++b;
while (e > b && iswspace(s[e - 1]))
--e;
return s.substr(b, e - b);
}
static bool toUnsigned(const std::wstring &t, unsigned &v)
{
if (t.empty())
return false;
wchar_t* end = nullptr;
unsigned long tmp = wcstoul(t.c_str(), &end, 10);
if (!end || *end != L'\0')
return false;
v = (unsigned)tmp;
return true;
}
bool CSettings::ParseModeString(const std::wstring& in, DisplayMode& out)
{
std::wstring s = trim(in);
if (s.empty())
return false;
out.preferred = s[s.size() - 1] == L'*';
if (out.preferred)
s = trim(s.substr(0, s.size() - 1));
size_t xPos = s.find(L'x');
size_t atPos = s.find(L'@', (xPos == std::wstring::npos ? 0 : xPos + 1));
if (xPos == std::wstring::npos || atPos == std::wstring::npos ||
xPos == 0 || atPos <= xPos + 1 || atPos + 1 >= s.size())
return false;
if (!toUnsigned(s.substr(0, xPos), out.width) ||
!toUnsigned(s.substr(xPos + 1, atPos - (xPos + 1)), out.height) ||
!toUnsigned(s.substr(atPos + 1), out.refresh))
return false;
return true;
}

32
idd/LGIdd/CSettings.h Normal file
View File

@@ -0,0 +1,32 @@
#pragma once
#include <windows.h>
#include <vector>
#include <string>
class CSettings
{
public:
struct DisplayMode
{
unsigned width;
unsigned height;
unsigned refresh;
bool preferred;
};
typedef std::vector<DisplayMode> DisplayModes;
CSettings();
void LoadModes();
const DisplayModes& GetDisplayModes() { return m_displayModes; }
void SetExtraMode(const DisplayMode & mode);
bool GetExtraMode(DisplayMode & mode);
private:
DisplayModes m_displayModes;
bool ReadModesValue(std::vector<std::wstring> &out) const;
bool ParseModeString(const std::wstring& in, DisplayMode& out);
};
extern CSettings g_settings;

View File

@@ -21,29 +21,25 @@
#include "CSwapChainProcessor.h"
#include <avrt.h>
#include "Debug.h"
#include "CDebug.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);
CSwapChainProcessor::CSwapChainProcessor(CIndirectDeviceContext* devContext, IDDCX_SWAPCHAIN hSwapChain,
std::shared_ptr<Direct3DDevice> device, HANDLE newFrameEvent) :
CSwapChainProcessor::CSwapChainProcessor(IDDCX_MONITOR monitor, CIndirectDeviceContext* devContext, IDDCX_SWAPCHAIN hSwapChain,
std::shared_ptr<CD3D11Device> dx11Device, std::shared_ptr<CD3D12Device> dx12Device, HANDLE newFrameEvent) :
m_monitor(monitor),
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));
m_cursorDataEvent.Attach(CreateEvent(nullptr, FALSE, FALSE, nullptr));
m_shapeBuffer = new BYTE[512 * 512 * 4];
}
CSwapChainProcessor::~CSwapChainProcessor()
@@ -54,12 +50,9 @@ 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();
delete[] m_shapeBuffer;
}
DWORD CALLBACK CSwapChainProcessor::_SwapChainThread(LPVOID arg)
@@ -73,7 +66,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 +76,65 @@ 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;
}
IDARG_IN_SETUP_HWCURSOR c = {};
c.CursorInfo.Size = sizeof(c.CursorInfo);
c.CursorInfo.AlphaCursorSupport = TRUE;
c.CursorInfo.ColorXorCursorSupport = IDDCX_XOR_CURSOR_SUPPORT_FULL;
c.CursorInfo.MaxX = 512;
c.CursorInfo.MaxY = 512;
c.hNewCursorDataAvailable = m_cursorDataEvent.Get();
NTSTATUS status = IddCxMonitorSetupHardwareCursor(m_monitor, &c);
if (!NT_SUCCESS(status))
{
DEBUG_ERROR("IddCxMonitorSetupHardwareCursor Failed (0x%08x)", status);
return;
}
m_lastShapeId = 0;
m_thread[1].Attach(CreateThread(nullptr, 0, _CursorThread, this, 0, nullptr));
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 +156,200 @@ 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;
}
CInteropResource * srcRes = m_resPool.Get(texture);
if (!srcRes)
{
DEBUG_ERROR("Failed to get a CInteropResource from the pool");
return false;
}
/**
* 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;
}
void CSwapChainProcessor::ResendLastFrame()
DWORD CALLBACK CSwapChainProcessor::_CursorThread(LPVOID arg)
{
LOCK_CONTEXT()
StagingTexture & st = m_cpuTex[m_lastIndex];
LOCK_ST(st);
UNLOCK_CONTEXT();
reinterpret_cast<CSwapChainProcessor*>(arg)->CursorThread();
return 0;
}
if (!st.map.pData)
bool CSwapChainProcessor::QueryHWCursor()
{
IDARG_IN_QUERY_HWCURSOR in = {};
in.LastShapeId = m_lastShapeId;
in.pShapeBuffer = m_shapeBuffer;
in.ShapeBufferSizeInBytes = 512 * 512 * 4;
IDARG_OUT_QUERY_HWCURSOR out = {};
NTSTATUS status = IddCxMonitorQueryHardwareCursor(m_monitor, &in, &out);
if (FAILED(status))
{
UNLOCK_ST(st);
return;
// this occurs if the display went away (ie, screen blanking or disabled)
if (status == ERROR_GRAPHICS_PATH_NOT_IN_TOPOLOGY)
return false;
DEBUG_ERROR("IddCxMonitorQueryHardwareCursor failed (0x%08x)", status);
return false;
}
m_devContext->SendFrame(st.width, st.height, st.map.RowPitch, st.format, st.map.pData);
UNLOCK_ST(st);
if (out.IsCursorShapeUpdated)
m_lastShapeId = out.CursorShapeInfo.ShapeId;
m_devContext->SendCursor(out, m_shapeBuffer);
return true;
}
void CSwapChainProcessor::CursorThread()
{
HRESULT hr = 0;
bool running = true;
while (running)
{
HANDLE waitHandles[] =
{
m_cursorDataEvent.Get(),
m_terminateEvent.Get()
};
DWORD waitResult = WaitForMultipleObjects(
ARRAYSIZE(waitHandles), waitHandles, FALSE, 100);
switch (waitResult)
{
case WAIT_TIMEOUT:
continue;
// cursorDataEvent
case WAIT_OBJECT_0:
if (!QueryHWCursor())
return;
continue;
// terminateEvent
case WAIT_OBJECT_0 + 1:
running = false;
continue;
default:
hr = HRESULT_FROM_WIN32(waitResult);
DEBUG_ERROR_HR(hr, "WaitForMultipleObjects");
return;
}
}
}

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>
@@ -35,47 +38,40 @@ using namespace Microsoft::WRL;
class CSwapChainProcessor
{
private:
IDDCX_MONITOR m_monitor;
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);
Wrappers::Event m_cursorDataEvent;
BYTE* m_shapeBuffer;
DWORD m_lastShapeId = 0;
static DWORD CALLBACK _SwapChainThread(LPVOID arg);
void SwapChainThread();
void SwapChainThreadCore();
void SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer);
void FrameThread();
static DWORD CALLBACK _CursorThread(LPVOID arg);
bool QueryHWCursor();
void CursorThread();
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);
CSwapChainProcessor(IDDCX_MONITOR monitor, CIndirectDeviceContext * devContext, IDDCX_SWAPCHAIN hSwapChain,
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,16 @@
#include <avrt.h>
#include <wrl.h>
#include "Debug.h"
#include "CDebug.h"
#include "CIndirectDeviceContext.h"
#include "CIndirectMonitorContext.h"
#include "CSettings.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 +83,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 +117,7 @@ NTSTATUS LGIddMonitorUnassignSwapChain(IDDCX_MONITOR monitor)
{
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
wrapper->context->UnassignSwapChain();
wrapper->context->GetDeviceContext()->OnUnassignedSwapChain();
return STATUS_SUCCESS;
}
@@ -158,10 +128,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 +159,7 @@ NTSTATUS LGIddCreateDevice(_Inout_ PWDFDEVICE_INIT deviceInit)
auto * wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(object);
if (wrapper)
wrapper->Cleanup();
l_wdfDevice = nullptr;
};
WDFDEVICE device = nullptr;
@@ -196,6 +167,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

@@ -1,4 +1,4 @@
/**
/**
* Looking Glass
* Copyright © 2017-2025 The Looking Glass Authors
* https://looking-glass.io
@@ -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(L"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>
@@ -17,45 +18,46 @@
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
</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="CSettings.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="CSettings.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 +74,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,32 +87,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>
<PropertyGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<DriverTargetPlatform>Universal</DriverTargetPlatform>
</PropertyGroup>
<PropertyGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<DriverTargetPlatform>Universal</DriverTargetPlatform>
</PropertyGroup>
<PropertyGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<DriverTargetPlatform>Universal</DriverTargetPlatform>
</PropertyGroup>
<PropertyGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
<ConfigurationType>DynamicLibrary</ConfigurationType>
<DriverTargetPlatform>Universal</DriverTargetPlatform>
</PropertyGroup>
@@ -145,11 +125,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,31 +138,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>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
<UMDF_VERSION_MAJOR>2</UMDF_VERSION_MAJOR>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
<UMDF_VERSION_MAJOR>2</UMDF_VERSION_MAJOR>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>true</UseDebugLibraries>
<UMDF_VERSION_MAJOR>2</UMDF_VERSION_MAJOR>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<UseDebugLibraries>false</UseDebugLibraries>
<UMDF_VERSION_MAJOR>2</UMDF_VERSION_MAJOR>
<PlatformToolset>WindowsUserModeDriver10.0</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
@@ -206,18 +168,6 @@
<DebuggerFlavor>DbgengRemoteDebugger</DebuggerFlavor>
<Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<DebuggerFlavor>DbgengRemoteDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<DebuggerFlavor>DbgengRemoteDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<DebuggerFlavor>DbgengRemoteDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<DebuggerFlavor>DbgengRemoteDebugger</DebuggerFlavor>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WppEnabled>true</WppEnabled>
@@ -254,10 +204,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,43 +219,15 @@
<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>
</DriverSign>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<ClCompile>
<WppEnabled>true</WppEnabled>
<WppRecorderEnabled>true</WppRecorderEnabled>
<WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<ClCompile>
<WppEnabled>true</WppEnabled>
<WppRecorderEnabled>true</WppRecorderEnabled>
<WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<WppEnabled>true</WppEnabled>
<WppRecorderEnabled>true</WppRecorderEnabled>
<WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<WppEnabled>true</WppEnabled>
<WppRecorderEnabled>true</WppRecorderEnabled>
<WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<FilesToPackage Include="$(TargetPath)" />
</ItemGroup>
@@ -315,6 +237,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,18 +49,44 @@
<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">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CPlatformInfo.h">
<Filter>Header Files</Filter>
</ClInclude>
<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>
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
<ClInclude Include="CSettings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
<ClInclude Include="$(SolutionDir)/LGCommon/*.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Device.cpp">
@@ -77,17 +104,41 @@
<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>
<ClCompile Include="$(SolutionDir)LGCommon/*.cpp" />
<ClCompile Include="CSettings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(SolutionDir)LGCommon/*.cpp" />
</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,10 @@
#include "CButton.h"
#include <commctrl.h>
#include <CDebug.h>
CButton::CButton(LPCWSTR title, DWORD style, HWND parent)
{
m_hwnd = createWindowSimple(WC_BUTTON, title, style, parent);
if (!m_hwnd)
DEBUG_ERROR_HR(GetLastError(), "Failed to create button");
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "CWidget.h"
class CButton : public CWidget
{
public:
CButton(LPCWSTR title, DWORD style, HWND parent);
};

View File

@@ -0,0 +1,243 @@
#include "CConfigWindow.h"
#include "CListBox.h"
#include "CGroupBox.h"
#include "CEditWidget.h"
#include "CButton.h"
#include <CDebug.h>
#include <windowsx.h>
#include <strsafe.h>
#include "VersionInfo.h"
ATOM CConfigWindow::s_atom = 0;
bool CConfigWindow::registerClass()
{
WNDCLASSEX wx = {};
populateWindowClass(wx);
wx.hIconSm = wx.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
wx.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
wx.lpszClassName = L"LookingGlassIddConfig";
s_atom = RegisterClassEx(&wx);
return s_atom;
}
CConfigWindow::CConfigWindow() : m_scale(1)
{
LSTATUS error = m_settings.open();
if (error != ERROR_SUCCESS)
DEBUG_ERROR_HR(error, "Failed to load settings");
else
m_modes = m_settings.getModes();
if (!CreateWindowEx(0, MAKEINTATOM(s_atom), L"Looking Glass IDD Configuration",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 400,
NULL, NULL, hInstance, this))
{
DEBUG_ERROR_HR(GetLastError(), "Failed to create window");
}
}
void CConfigWindow::updateFont()
{
NONCLIENTMETRICS ncmMetrics = { sizeof(NONCLIENTMETRICS) };
UINT dpi = GetDpiForWindow(m_hwnd);
if (!SystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS, sizeof ncmMetrics, &ncmMetrics, 0, dpi))
{
DEBUG_ERROR_HR(GetLastError(), "SystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS)");
return;
}
m_font.Attach(CreateFontIndirect(&ncmMetrics.lfMessageFont));
if (!m_font.IsValid())
{
DEBUG_ERROR_HR(GetLastError(), "CreateFontIndirect(lfMessageFont)");
return;
}
for (HWND child : std::initializer_list<HWND>({
*m_version, *m_modeGroup, *m_modeBox, *m_widthLabel, *m_heightLabel, *m_refreshLabel,
*m_modeWidth, *m_modeHeight, *m_modeRefresh, *m_modeUpdate, *m_modeDelete,
*m_autosizeGroup, *m_defRefreshLabel, *m_defRefresh, *m_defRefreshHz,
}))
SendMessage(child, WM_SETFONT, (WPARAM)m_font.Get(), 1);
}
void CConfigWindow::updateModeList()
{
m_modeBox->addItem(L"<add new>", -1);
auto &modes = *m_modes;
for (size_t i = 0; i < modes.size(); ++i)
m_modeBox->addItem(modes[i].toString(), i);
}
LRESULT CConfigWindow::handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SIZE:
return onResize(LOWORD(lParam), HIWORD(lParam));
case WM_DPICHANGED:
{
LPRECT lpBox = (LPRECT)lParam;
m_scale = LOWORD(wParam) / 96.0;
updateFont();
SetWindowPos(m_hwnd, NULL, lpBox->left, lpBox->top, lpBox->right - lpBox->left,
lpBox->bottom - lpBox->top, SWP_NOZORDER | SWP_NOACTIVATE);
onResize(lpBox->right - lpBox->left, lpBox->bottom - lpBox->top);
RedrawWindow(m_hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);
return 0;
}
case WM_COMMAND:
return onCommand(LOWORD(wParam), HIWORD(wParam), (HWND)lParam);
default:
return CWindow::handleMessage(uMsg, wParam, lParam);
}
}
LRESULT CConfigWindow::onCreate()
{
m_scale = GetDpiForWindow(m_hwnd) / 96.0;
m_version.reset(new CStaticWidget(L"Looking Glass IDD " LG_VERSION_STR, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, m_hwnd));
m_modeGroup.reset(new CGroupBox(L"Custom modes", WS_CHILD | WS_VISIBLE, m_hwnd));
m_modeBox.reset(new CListBox(WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_NOTIFY, m_hwnd));
if (m_modes)
updateModeList();
m_widthLabel.reset(new CStaticWidget(L"Width:", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, m_hwnd));
m_heightLabel.reset(new CStaticWidget(L"Height:", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, m_hwnd));
m_refreshLabel.reset(new CStaticWidget(L"Refresh:", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, m_hwnd));
m_modeWidth.reset(new CEditWidget(WS_CHILD | WS_VISIBLE | ES_LEFT | ES_NUMBER, m_hwnd));
m_modeHeight.reset(new CEditWidget(WS_CHILD | WS_VISIBLE | ES_LEFT | ES_NUMBER, m_hwnd));
m_modeRefresh.reset(new CEditWidget(WS_CHILD | WS_VISIBLE | ES_LEFT | ES_NUMBER, m_hwnd));
m_modeUpdate.reset(new CButton(L"Save", WS_CHILD | WS_VISIBLE, m_hwnd));
m_modeDelete.reset(new CButton(L"Delete", WS_CHILD | WS_VISIBLE, m_hwnd));
EnableWindow(*m_modeUpdate, FALSE);
EnableWindow(*m_modeDelete, FALSE);
m_autosizeGroup.reset(new CGroupBox(L"Autosizing", WS_CHILD | WS_VISIBLE, m_hwnd));
m_defRefreshLabel.reset(new CStaticWidget(L"Default refresh:", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, m_hwnd));
m_defRefresh.reset(new CEditWidget(WS_CHILD | WS_VISIBLE | ES_LEFT | ES_NUMBER, m_hwnd));
m_defRefreshHz.reset(new CStaticWidget(L"Hz", WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, m_hwnd));
RECT client = { 0, 0, (LONG)(436 * m_scale), (LONG)(300 * m_scale) };
AdjustWindowRect(&client, WS_OVERLAPPEDWINDOW, FALSE);
SetWindowPos(m_hwnd, NULL, 0, 0, client.right - client.left, client.bottom - client.top, SWP_NOMOVE | SWP_NOZORDER);
updateFont();
return 0;
}
LRESULT CConfigWindow::onFinal()
{
if (m_onDestroy)
m_onDestroy();
return CWindow::onFinal();
}
LRESULT CConfigWindow::onResize(DWORD width, DWORD height)
{
WidgetPositioner pos(m_scale, width, height);
pos.pinTopLeftRight(*m_version, 12, 12, 12, 20);
pos.pinLeftTopBottom(*m_modeGroup, 12, 40, 200, 12);
pos.pinLeftTopBottom(*m_modeBox, 24, 64, 176, 120);
pos.pinBottomLeft(*m_widthLabel, 24, 96, 50, 20);
pos.pinBottomLeft(*m_heightLabel, 24, 72, 50, 20);
pos.pinBottomLeft(*m_refreshLabel, 24, 48, 50, 20);
pos.pinBottomLeft(*m_modeWidth, 75, 96, 50, 20);
pos.pinBottomLeft(*m_modeHeight, 75, 72, 50, 20);
pos.pinBottomLeft(*m_modeRefresh, 75, 48, 50, 20);
pos.pinBottomLeft(*m_modeUpdate, 24, 20, 50, 24);
pos.pinBottomLeft(*m_modeDelete, 75, 20, 50, 24);
pos.pinTopLeft(*m_autosizeGroup, 224, 40, 200, 52);
pos.pinTopLeft(*m_defRefreshLabel, 236, 64, 95, 20);
pos.pinTopLeft(*m_defRefresh, 331, 64, 63, 20);
pos.pinTopLeft(*m_defRefreshHz, 398, 64, 16, 20);
return 0;
}
void CConfigWindow::onModeListSelectChange()
{
int sel = m_modeBox->getSel();
if (sel == LB_ERR)
{
EnableWindow(*m_modeUpdate, FALSE);
EnableWindow(*m_modeDelete, FALSE);
return;
}
int index = m_modeBox->getData(sel);
if (index >= 0)
{
auto &mode = (*m_modes)[index];
m_modeWidth->setNumericValue(mode.width);
m_modeHeight->setNumericValue(mode.height);
m_modeRefresh->setNumericValue(mode.refresh);
}
EnableWindow(*m_modeUpdate, TRUE);
EnableWindow(*m_modeDelete, index >= 0);
}
LRESULT CConfigWindow::onCommand(WORD id, WORD code, HWND hwnd)
{
if (hwnd == *m_modeBox && code == LBN_SELCHANGE && m_modes)
{
onModeListSelectChange();
}
else if (hwnd == *m_modeUpdate && code == BN_CLICKED && m_modes)
{
int sel = m_modeBox->getSel();
if (sel == LB_ERR)
return 0;
int index = m_modeBox->getData(sel);
auto &mode = index >= 0 ? (*m_modes)[index] : m_modes->emplace_back();
try
{
mode.width = m_modeWidth->getNumericValue();
mode.height = m_modeHeight->getNumericValue();
mode.refresh = m_modeRefresh->getNumericValue();
}
catch (std::logic_error&)
{
return 0;
}
if (index >= 0)
m_modeBox->delItem(sel);
m_modeBox->setSel(m_modeBox->addItem(mode.toString().c_str(), index));
LRESULT result = m_settings.setModes(*m_modes);
if (result != ERROR_SUCCESS)
DEBUG_ERROR_HR((HRESULT) result, "Failed to save modes");
}
else if (hwnd == *m_modeDelete && code == BN_CLICKED && m_modes)
{
int sel = m_modeBox->getSel();
if (sel == LB_ERR)
return 0;
int index = m_modeBox->getData(sel);
m_modeBox->clear();
m_modes->erase(m_modes->begin() + index);
LRESULT result = m_settings.setModes(*m_modes);
if (result != ERROR_SUCCESS)
DEBUG_ERROR_HR((HRESULT) result, "Failed to save modes");
updateModeList();
onModeListSelectChange();
}
return 0;
}

View File

@@ -0,0 +1,61 @@
#pragma once
#include "CWindow.h"
#include "CStaticWidget.h"
#include "CRegistrySettings.h"
#include <functional>
#include <memory>
#include <optional>
#include <wrl.h>
#include "UIHelpers.h"
class CListBox;
class CGroupBox;
class CEditWidget;
class CButton;
class CConfigWindow : public CWindow
{
static ATOM s_atom;
std::unique_ptr<CStaticWidget> m_version;
std::unique_ptr<CGroupBox> m_modeGroup;
std::unique_ptr<CListBox> m_modeBox;
std::unique_ptr<CStaticWidget> m_widthLabel;
std::unique_ptr<CStaticWidget> m_heightLabel;
std::unique_ptr<CStaticWidget> m_refreshLabel;
std::unique_ptr<CEditWidget> m_modeWidth;
std::unique_ptr<CEditWidget> m_modeHeight;
std::unique_ptr<CEditWidget> m_modeRefresh;
std::unique_ptr<CButton> m_modeUpdate;
std::unique_ptr<CButton> m_modeDelete;
std::unique_ptr<CGroupBox> m_autosizeGroup;
std::unique_ptr<CStaticWidget> m_defRefreshLabel;
std::unique_ptr<CEditWidget> m_defRefresh;
std::unique_ptr<CStaticWidget> m_defRefreshHz;
std::function<void()> m_onDestroy;
double m_scale;
Microsoft::WRL::Wrappers::HandleT<FontTraits> m_font;
CRegistrySettings m_settings;
std::optional<std::vector<DisplayMode>> m_modes;
void updateFont();
void updateModeList();
void onModeListSelectChange();
virtual LRESULT handleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) override;
virtual LRESULT onCreate() override;
virtual LRESULT onFinal() override;
LRESULT onResize(DWORD width, DWORD height);
LRESULT onCommand(WORD id, WORD code, HWND hwnd);
public:
CConfigWindow();
static bool registerClass();
void onDestroy(std::function<void()> func) { m_onDestroy = std::move(func); }
};

View File

@@ -0,0 +1,36 @@
#include "CEditWidget.h"
#include <commctrl.h>
#include <windows.h>
#include <windowsx.h>
#include <CDebug.h>
CEditWidget::CEditWidget(DWORD style, HWND parent)
{
m_hwnd = createWindowSimple(WC_EDIT, nullptr, style, parent, WS_EX_CLIENTEDGE);
if (!m_hwnd)
DEBUG_ERROR_HR(GetLastError(), "Failed to create edit control");
}
std::wstring CEditWidget::getValue()
{
std::wstring result;
result.resize(Edit_GetTextLength(m_hwnd));
Edit_GetText(m_hwnd, result.data(), (int) (result.size() + 1));
return result;
}
int CEditWidget::getNumericValue()
{
return std::stoi(getValue());
}
void CEditWidget::setValue(const std::wstring &value)
{
if (!Edit_SetText(m_hwnd, value.c_str()))
DEBUG_ERROR("Failed to update text for edit control");
}
void CEditWidget::setNumericValue(int value)
{
setValue(std::to_wstring(value));
}

Some files were not shown because too many files have changed in this diff Show More